ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
lwip_raw_common_impl.cpp
Go to the documentation of this file.
3
4#ifdef USE_SOCKET_IMPL_LWIP_TCP
5
6#include <cerrno>
7#include <cstring>
8
9namespace esphome::socket {
10
11int lwip_ip_to_sockaddr(sa_family_t family, const ip_addr_t *ip, uint16_t port_host, struct sockaddr *name,
12 socklen_t *addrlen) {
13 if (family == AF_INET) {
14 if (*addrlen < sizeof(struct sockaddr_in)) {
15 errno = EINVAL;
16 return -1;
17 }
18 auto *addr = reinterpret_cast<struct sockaddr_in *>(name);
19 addr->sin_family = AF_INET;
20 *addrlen = addr->sin_len = sizeof(struct sockaddr_in);
21 addr->sin_port = htons(port_host);
22 inet_addr_from_ip4addr(&addr->sin_addr, ip_2_ip4(ip));
23 return 0;
24 }
25#if LWIP_IPV6
26 if (family == AF_INET6) {
27 if (*addrlen < sizeof(struct sockaddr_in6)) {
28 errno = EINVAL;
29 return -1;
30 }
31 auto *addr = reinterpret_cast<struct sockaddr_in6 *>(name);
32 addr->sin6_family = AF_INET6;
33 *addrlen = addr->sin6_len = sizeof(struct sockaddr_in6);
34 addr->sin6_port = htons(port_host);
35 // AF_INET6 sockets may receive IPv4 packets; convert to IPv4-mapped IPv6.
36 if (IP_IS_V4(ip)) {
37 ip_addr_t mapped;
38 ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&mapped), ip_2_ip4(ip));
39 inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(&mapped));
40 } else {
41 inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(ip));
42 }
43 return 0;
44 }
45#endif
46 errno = EAFNOSUPPORT;
47 return -1;
48}
49
50bool sockaddr_to_lwip(const struct sockaddr *addr, socklen_t addrlen, ip_addr_t *ip, uint16_t *port) {
51 // headers.h defines sockaddr and sockaddr_in with the same size, so this covers AF_INET
52 if (addrlen < sizeof(struct sockaddr))
53 return false;
54 // Zero the whole struct — the IPv6 zone byte would otherwise be stack garbage
55 memset(ip, 0, sizeof(*ip));
56 if (addr->sa_family == AF_INET) {
57 auto *addr4 = reinterpret_cast<const sockaddr_in *>(addr);
58 *port = ntohs(addr4->sin_port);
59 IP_SET_TYPE_VAL(*ip, IPADDR_TYPE_V4);
60 ip_2_ip4(ip)->addr = addr4->sin_addr.s_addr;
61 return true;
62 }
63#if LWIP_IPV6
64 if (addr->sa_family == AF_INET6) {
65 if (addrlen < sizeof(sockaddr_in6))
66 return false;
67 auto *addr6 = reinterpret_cast<const sockaddr_in6 *>(addr);
68 *port = ntohs(addr6->sin6_port);
69 IP_SET_TYPE_VAL(*ip, IPADDR_TYPE_V6);
70 memcpy(&ip_2_ip6(ip)->addr, &addr6->sin6_addr.un.u8_addr, 16);
71 // Unmap ::ffff:a.b.c.d so replies to recvfrom addresses route as IPv4
72 if (ip6_addr_isipv4mappedipv6(ip_2_ip6(ip))) {
73 unmap_ipv4_mapped_ipv6(ip_2_ip4(ip), ip_2_ip6(ip));
74 IP_SET_TYPE_VAL(*ip, IPADDR_TYPE_V4);
75 }
76 return true;
77 }
78#endif
79 return false;
80}
81
82bool sockaddr_to_lwip_bind(sa_family_t family, const struct sockaddr *addr, socklen_t addrlen, ip_addr_t *ip,
83 uint16_t *port) {
84 const bool ok = sockaddr_to_lwip(addr, addrlen, ip, port);
85#if LWIP_IPV6
86 // Only promote wildcard binds — a specific address must keep filtering
87 if (ok && family == AF_INET6 && ip_addr_isany_val(*ip))
88 IP_SET_TYPE_VAL(*ip, IPADDR_TYPE_ANY);
89#endif
90 return ok;
91}
92
93int lwip_err_to_errno(err_t err) {
94 switch (err) {
95 case ERR_MEM:
96 return ENOMEM;
97 case ERR_BUF:
98 return EAGAIN; // transient, e.g. no free local port
99 case ERR_RTE:
100 return EHOSTUNREACH; // no route, e.g. no address yet
101 case ERR_VAL:
102 case ERR_ARG:
103 return EINVAL;
104 case ERR_USE:
105 return EADDRINUSE;
106 case ERR_ISCONN:
107 return EISCONN;
108 default:
109 return EIO;
110 }
111}
112
113} // namespace esphome::socket
114
115#endif // USE_SOCKET_IMPL_LWIP_TCP
uint32_t socklen_t
Definition headers.h:109
uint8_t sa_family_t
Definition headers.h:69
@ IPADDR_TYPE_V6
Definition ip_address.h:32
@ IPADDR_TYPE_V4
Definition ip_address.h:32
bool sockaddr_to_lwip(const struct sockaddr *addr, socklen_t addrlen, ip_addr_t *ip, uint16_t *port)
Convert sockaddr to lwip ip_addr_t and host-order port.
int lwip_err_to_errno(err_t err)
errno for a failed lwip call
bool sockaddr_to_lwip_bind(sa_family_t family, const struct sockaddr *addr, socklen_t addrlen, ip_addr_t *ip, uint16_t *port)
sockaddr_to_lwip variant for bind: promotes AF_INET6 sockets to IPADDR_TYPE_ANY so they accept both I...
int lwip_ip_to_sockaddr(sa_family_t family, const ip_addr_t *ip, uint16_t port_host, struct sockaddr *name, socklen_t *addrlen)
Convert lwip ip_addr_t + host-order port to sockaddr, based on the socket's address family.
sa_family_t sin6_family
Definition headers.h:86
sa_family_t sin_family
Definition headers.h:75
sa_family_t sa_family
Definition headers.h:97