From 04a2cefb263678aa42b668ba1fb39c59e9c7296a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 7 Jul 2017 19:27:00 +0800 Subject: [PATCH] lwip: Enable IPV6_ONLY option for UDP sockets (BSD & netconn) * setsockopt(s, IPV6_ONLY, &one, sizeof(int)) will disable IPV6-only mode. Incoming/outgoing IPV4 packets are dropped. * Otherwise, sockets bound to IPV6_ANY_ADDR can receive unicast packets for IPV4 or IPV6. * sendto() a IPV6-mapped-IPV4 address on a UDP socket works correctly (not supported for RAW or TCP sockets.) * getaddrinfo() option AI_V4MAPPED is implemented. As well as extending support to TCP & RAW, there is some potential improvement to dropping incoming packets - the drop happens a bit late in the process and there is no "ICMP port unreachable" response sent. --- components/lwip/api/api_msg.c | 16 +++++++++++++ components/lwip/api/netdb.c | 13 ++++++++++- components/lwip/api/sockets.c | 11 +-------- components/lwip/core/udp.c | 13 +++++++++++ components/lwip/include/lwip/lwip/ip_addr.h | 25 +++++++++++++++++++++ 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/components/lwip/api/api_msg.c b/components/lwip/api/api_msg.c index bfb07b876a..bdfc0a984a 100755 --- a/components/lwip/api/api_msg.c +++ b/components/lwip/api/api_msg.c @@ -180,6 +180,16 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, return; } +#if LWIP_IPV6 + /* This should be eventually moved to a flag on the UDP PCB, and this drop can happen + more correctly in udp_input(). This will also allow icmp_dest_unreach() to be called. */ + if (conn->flags & NETCONN_FLAG_IPV6_V6ONLY && !ip_current_is_v6()) { + LWIP_DEBUGF(API_MSG_DEBUG, ("recv_udp: Dropping IPv4 UDP packet (IPv6-only socket)")); + pbuf_free(p); + return; + } +#endif + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); if (buf == NULL) { pbuf_free(p); @@ -1388,6 +1398,12 @@ lwip_netconn_do_send(void *m) if (ERR_IS_FATAL(msg->conn->last_err)) { msg->err = msg->conn->last_err; +#if LWIP_IPV4 && LWIP_IPV6 + } else if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) && + IP_IS_V4MAPPEDV6(&msg->msg.b->addr)) { + LWIP_DEBUGF(API_MSG_DEBUG, ("lwip_netconn_do_send: Dropping IPv4 packet on IPv6-only socket")); + msg->err = ERR_VAL; +#endif /* LWIP_IPV4 && LWIP_IPV6 */ } else { msg->err = ERR_CONN; if (msg->conn->pcb.tcp != NULL) { diff --git a/components/lwip/api/netdb.c b/components/lwip/api/netdb.c index 65510f55e9..66f602d268 100755 --- a/components/lwip/api/netdb.c +++ b/components/lwip/api/netdb.c @@ -261,7 +261,7 @@ lwip_freeaddrinfo(struct addrinfo *ai) * @param res pointer to a pointer where to store the result (set to NULL on failure) * @return 0 on success, non-zero on failure * - * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG + * @todo: implement AI_ADDRCONFIG */ int lwip_getaddrinfo(const char *nodename, const char *servname, @@ -330,6 +330,9 @@ lwip_getaddrinfo(const char *nodename, const char *servname, type = NETCONN_DNS_IPV4; } else if (ai_family == AF_INET6) { type = NETCONN_DNS_IPV6; + if (hints->ai_flags & AI_V4MAPPED) { + type = NETCONN_DNS_IPV6_IPV4; + } } #endif /* LWIP_IPV4 && LWIP_IPV6 */ err = netconn_gethostbyname_addrtype(nodename, &addr, type); @@ -346,6 +349,14 @@ lwip_getaddrinfo(const char *nodename, const char *servname, } } +#if LWIP_IPV4 && LWIP_IPV6 + if (ai_family == AF_INET6 && (hints->ai_flags & AI_V4MAPPED) + && IP_GET_TYPE(&addr) == IPADDR_TYPE_V4) { + /* Convert native V4 address to a V4-mapped IPV6 address */ + ip_addr_make_ip4_mapped_ip6(&addr, &addr); + } +#endif + total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage); if (nodename != NULL) { namelen = strlen(nodename); diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index a56775b0e8..0da123839c 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -2373,10 +2373,6 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt switch (optname) { case IPV6_V6ONLY: LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); - /* @todo: this does not work for datagram sockets, yet */ - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { - return ENOPROTOOPT; - } *(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n", s, *(int *)optval)); @@ -2811,12 +2807,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ case IPPROTO_IPV6: switch (optname) { case IPV6_V6ONLY: - /* @todo: this does not work for datagram sockets, yet */ -#if CONFIG_MDNS - //LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP); -#else - LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP); -#endif + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int); if (*(const int*)optval) { netconn_set_ipv6only(sock->conn, 1); } else { diff --git a/components/lwip/core/udp.c b/components/lwip/core/udp.c index 37ae2c1796..779dd9ad7f 100755 --- a/components/lwip/core/udp.c +++ b/components/lwip/core/udp.c @@ -542,6 +542,19 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); +#if LWIP_IPV4 && LWIP_IPV6 + /* Unwrap IPV4-mapped IPV6 addresses and convert to native IPV4 here */ + if (IP_IS_V4MAPPEDV6(dst_ip)) { + ip_addr_t dest_ipv4; + ip_addr_ip4_from_mapped_ip6(&dest_ipv4, dst_ip); +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UP + return udp_sendto_chksum(pcb, p, &dest_ipv4, dst_port, have_chksum, chksum); +#else + return udp_sendto(pcb, p, &dest_ipv4, dst_port); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UP */ + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + #if LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) if (ip_addr_ismulticast(dst_ip_route)) { #if LWIP_IPV6 diff --git a/components/lwip/include/lwip/lwip/ip_addr.h b/components/lwip/include/lwip/lwip/ip_addr.h index 1e1ffb3f1e..74897a9ee9 100755 --- a/components/lwip/include/lwip/lwip/ip_addr.h +++ b/components/lwip/include/lwip/lwip/ip_addr.h @@ -69,6 +69,10 @@ extern const ip_addr_t ip_addr_any_type; #define IP_IS_V6_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V6) #define IP_IS_V6(ipaddr) (((ipaddr) != NULL) && IP_IS_V6_VAL(*(ipaddr))) + +#define IP_V6_EQ_PART(ipaddr, WORD, VAL) (ip_2_ip6(ipaddr)->addr[WORD] == htonl(VAL)) +#define IP_IS_V4MAPPEDV6(ipaddr) (IP_IS_V6(ipaddr) && IP_V6_EQ_PART(ipaddr, 0, 0) && IP_V6_EQ_PART(ipaddr, 1, 0) && IP_V6_EQ_PART(ipaddr, 2, 0x0000FFFF)) + #define IP_SET_TYPE_VAL(ipaddr, iptype) do { (ipaddr).type = (iptype); }while(0) #define IP_SET_TYPE(ipaddr, iptype) do { if((ipaddr) != NULL) { IP_SET_TYPE_VAL(*(ipaddr), iptype); }}while(0) #define IP_GET_TYPE(ipaddr) ((ipaddr)->type) @@ -156,6 +160,27 @@ extern const ip_addr_t ip_addr_any_type; ((IP_IS_V6(addr)) ? ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen) : ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen))) int ipaddr_aton(const char *cp, ip_addr_t *addr); +/* Map an IPv4 ip_addr into an IPV6 ip_addr, using format + defined in RFC4291 2.5.5.2. + + Safe to call when dest==src. +*/ +#define ip_addr_make_ip4_mapped_ip6(dest, src) do { \ + u32_t tmp = ip_2_ip4(src)->addr; \ + IP_ADDR6((dest), 0x0, 0x0, htonl(0x0000FFFF), tmp); \ + } while(0) + +/* Convert an IPv4 mapped V6 address to an IPV4 address. + + Check IP_IS_V4MAPPEDV6(src) before using this. + + Safe to call when dest == src. +*/ +#define ip_addr_ip4_from_mapped_ip6(dest, src) do { \ + ip_2_ip4(dest)->addr = ip_2_ip6(src)->addr[3]; \ + IP_SET_TYPE(dest, IPADDR_TYPE_V4); \ + } while(0) + #else /* LWIP_IPV4 && LWIP_IPV6 */ #define IP_ADDR_PCB_VERSION_MATCH(addr, pcb) 1 -- 2.40.0