]> granicus.if.org Git - esp-idf/commitdiff
lwip: Enable IPV6_ONLY option for UDP sockets (BSD & netconn)
authorAngus Gratton <angus@espressif.com>
Fri, 7 Jul 2017 11:27:00 +0000 (19:27 +0800)
committerAngus Gratton <gus@projectgus.com>
Sun, 1 Oct 2017 23:50:27 +0000 (10:50 +1100)
* 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
components/lwip/api/netdb.c
components/lwip/api/sockets.c
components/lwip/core/udp.c
components/lwip/include/lwip/lwip/ip_addr.h

index bfb07b876ace16acde8a8f51109bf6f73f57b9e1..bdfc0a984a4754c74bebb0e15c1f85ef3e05de4d 100755 (executable)
@@ -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) {
index 65510f55e9174016585841a340e9aceb4f00db0f..66f602d268bda5e66e89c297d11aeb43e90016f4 100755 (executable)
@@ -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);
index a56775b0e84a86adaf9b867acf130944353cfc57..0da123839c098092c09fe4d91a7272fd5e3bd9cf 100755 (executable)
@@ -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 {
index 37ae2c1796f23da07b0c42428a4f6d166c6ab590..779dd9ad7f4ae2c07e993dc5e7d6f43f24ad5e1f 100755 (executable)
@@ -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
index 1e1ffb3f1ebf27a15d6a3eb9ece01eb2f1e06de7..74897a9ee9223fc60ee1eb4453e7715339c4751d 100755 (executable)
@@ -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