diff --git a/esphome/components/socket/lwip_raw_common_impl.cpp b/esphome/components/socket/lwip_raw_common_impl.cpp index 77e896f535..18850f6c12 100644 --- a/esphome/components/socket/lwip_raw_common_impl.cpp +++ b/esphome/components/socket/lwip_raw_common_impl.cpp @@ -68,6 +68,11 @@ bool sockaddr_to_lwip(const struct sockaddr *addr, socklen_t addrlen, ip_addr_t *port = ntohs(addr6->sin6_port); IP_SET_TYPE_VAL(*ip, IPADDR_TYPE_V6); memcpy(&ip_2_ip6(ip)->addr, &addr6->sin6_addr.un.u8_addr, 16); + // Unmap ::ffff:a.b.c.d so replies to recvfrom addresses route as IPv4 + if (ip6_addr_isipv4mappedipv6(ip_2_ip6(ip))) { + unmap_ipv4_mapped_ipv6(ip_2_ip4(ip), ip_2_ip6(ip)); + IP_SET_TYPE_VAL(*ip, IPADDR_TYPE_V4); + } return true; } #endif diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 69886e60df..b89cf10ff7 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -291,7 +291,7 @@ int LWIPRawCommon::setsockopt(int level, int optname, const void *optval, sockle } int LWIPRawCommon::ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen) { - // TCP pcb stores port in network byte order; convert to host order for the shared helper + // lwip pcb ports are host order; ntohs preserves historical byte-swapped sin_port output return lwip_ip_to_sockaddr(this->family_, ip, ntohs(port), name, addrlen); } diff --git a/esphome/components/socket/lwip_raw_udp_impl.cpp b/esphome/components/socket/lwip_raw_udp_impl.cpp index e232ae5215..4558b59759 100644 --- a/esphome/components/socket/lwip_raw_udp_impl.cpp +++ b/esphome/components/socket/lwip_raw_udp_impl.cpp @@ -122,8 +122,16 @@ int LWIPRawUDPSendImpl::setsockopt(int level, int optname, const void *optval, s return -1; } if (level == SOL_SOCKET && optname == SO_REUSEADDR) { - // lwip raw UDP doesn't enforce port exclusivity the same way, - // but we accept this silently for compatibility + if (optval == nullptr || optlen < sizeof(int)) { + errno = EINVAL; + return -1; + } + // Effective only where lwip is built with SO_REUSE=1 (ESP8266 yes, RP2040 currently no) + if (*reinterpret_cast(optval)) { + ip_set_option(this->pcb_, SOF_REUSEADDR); + } else { + ip_reset_option(this->pcb_, SOF_REUSEADDR); + } return 0; } if (level == SOL_SOCKET && optname == SO_BROADCAST) { @@ -170,7 +178,7 @@ int LWIPRawUDPSendImpl::getsockopt(int level, int optname, void *optval, socklen errno = EINVAL; return -1; } - *reinterpret_cast(optval) = 1; + *reinterpret_cast(optval) = ip_get_option(this->pcb_, SOF_REUSEADDR) ? 1 : 0; *optlen = sizeof(int); return 0; } diff --git a/esphome/components/socket/lwip_raw_udp_impl.h b/esphome/components/socket/lwip_raw_udp_impl.h index 1eda7786e6..268515106f 100644 --- a/esphome/components/socket/lwip_raw_udp_impl.h +++ b/esphome/components/socket/lwip_raw_udp_impl.h @@ -96,8 +96,7 @@ class LWIPRawUDPImpl : private LWIPRawUDPSendImpl { static constexpr uint8_t UDP_RX_QUEUE_SIZE = 4; static constexpr uint8_t UDP_RX_MASK = UDP_RX_QUEUE_SIZE - 1; static_assert((UDP_RX_QUEUE_SIZE & UDP_RX_MASK) == 0, "UDP_RX_QUEUE_SIZE must be power of 2"); - // Fields are written by recv_fn_ before rx_count_ makes a slot visible, - // so per-member initializers would only add dead zeroing of the array. + // Fields are written by recv_fn_ before rx_count_ makes a slot visible struct UDPRxPacket { ip_addr_t src_addr; struct pbuf *pb;