]> granicus.if.org Git - esp-idf/commitdiff
lwip: Add IPV6 multicast group membership socket options
authorAngus Gratton <angus@espressif.com>
Mon, 10 Jul 2017 08:32:20 +0000 (16:32 +0800)
committerAngus Gratton <gus@projectgus.com>
Sun, 1 Oct 2017 23:50:27 +0000 (10:50 +1100)
As described in RFC2133: IPV6_MULTICAST_IF, IPV6_MULTICAST_HOPS,
IPV6_MULTICAST_LOOP, IPV6_MULTICAST_LOOP, IPV6_DROP_MEMBERSHIP.

components/lwip/api/sockets.c
components/lwip/core/ipv6/ip6.c
components/lwip/include/lwip/lwip/sockets.h
components/lwip/include/lwip/lwip/udp.h

index 33d4a97e793b3d801a1c7835aec85f6c5431d9ae..a56775b0e84a86adaf9b867acf130944353cfc57 100755 (executable)
@@ -46,6 +46,7 @@
 #include "lwip/api.h"
 #include "lwip/sys.h"
 #include "lwip/igmp.h"
+#include "lwip/mld6.h"
 #include "lwip/inet.h"
 #include "lwip/tcp.h"
 #include "lwip/raw.h"
@@ -364,21 +365,21 @@ union sockaddr_aligned {
 #define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
 #endif
 
-/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
-   a socket is closed */
+/* This is to keep track of IP_ADD_MEMBERSHIP/IPV6_ADD_MEMBERSHIP calls to drop
+   the membership when a socket is closed */
 struct lwip_socket_multicast_pair {
   /** the socket (+1 to not require initialization) */
   int sa;
   /** the interface address */
-  ip4_addr_t if_addr;
+  ip_addr_t if_addr;
   /** the group address */
-  ip4_addr_t multi_addr;
+  ip_addr_t multi_addr;
 };
 
-struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+struct lwip_socket_multicast_pair socket_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
 
-static int  lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
-static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static int  lwip_socket_register_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr);
+static void lwip_socket_unregister_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr);
 static void lwip_socket_drop_registered_memberships(int s);
 #endif /* LWIP_IGMP */
 
@@ -852,7 +853,7 @@ lwip_close(int s)
     LWIP_ASSERT("lwip_close: sock->lastdata == NULL", sock->lastdata == NULL);
   }
 
-#if LWIP_IGMP
+#if (LWIP_IGMP) || (LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS)
   /* drop all possibly joined IGMP memberships */
   lwip_socket_drop_registered_memberships(s);
 #endif /* LWIP_IGMP */
@@ -2387,6 +2388,38 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
       break;
     }  /* switch (optname) */
     break;
+#if  LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS  /* Multicast options, similar to LWIP_IGMP options for IPV4 */
+    case IPV6_MULTICAST_IF: /* NB: like IP_MULTICAST_IF, this returns an IP not an index */
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in6_addr);
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+        return ENOPROTOOPT;
+      }
+      inet6_addr_from_ip6addr((struct in6_addr*)optval,
+                              udp_get_multicast_netif_ip6addr(sock->conn->pcb.udp));
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF) = 0x%"X32_F"\n",
+                                  s, *(u32_t *)optval));
+      break;
+    case IPV6_MULTICAST_HOPS:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+        return ENOPROTOOPT;
+      }
+      *(u8_t*)optval = sock->conn->pcb.udp->mcast_ttl;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IP_MULTICAST_LOOP) = %d\n",
+                                  s, *(int *)optval));
+      break;
+    case IPV6_MULTICAST_LOOP:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+      if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+        *(u8_t*)optval = 1;
+      } else {
+        *(u8_t*)optval = 0;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IP_MULTICAST_LOOP) = %d\n",
+                                  s, *(int *)optval));
+      break;
+
+#endif /* LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS */
 #endif /* LWIP_IPV6 */
 
 #if LWIP_UDP && LWIP_UDPLITE
@@ -2685,21 +2718,21 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
         /* @todo: assign membership to this socket so that it is dropped when state the socket */
         err_t igmp_err;
         const struct ip_mreq *imr = (const struct ip_mreq *)optval;
-        ip4_addr_t if_addr;
-        ip4_addr_t multi_addr;
+        ip_addr_t if_addr;
+        ip_addr_t multi_addr;
         LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
-        inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
-        inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
+        inet_addr_to_ipaddr(ip_2_ip4(&if_addr), &imr->imr_interface);
+        inet_addr_to_ipaddr(ip_2_ip4(&multi_addr), &imr->imr_multiaddr);
         if (optname == IP_ADD_MEMBERSHIP) {
           if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
             /* cannot track membership (out of memory) */
             err = ENOMEM;
             igmp_err = ERR_OK;
           } else {
-            igmp_err = igmp_joingroup(&if_addr, &multi_addr);
+            igmp_err = igmp_joingroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
           }
         } else {
-          igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
+          igmp_err = igmp_leavegroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
           lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
         }
         if (igmp_err != ERR_OK) {
@@ -2792,6 +2825,55 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
                   s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
       break;
+#if LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS  /* Multicast options, similar to LWIP_IGMP options for IPV4 */
+    case IPV6_MULTICAST_IF: /* NB: like IP_MULTICAST_IF, this takes an IP not an index */
+      {
+        ip6_addr_t if_addr;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in6_addr, NETCONN_UDP);
+        inet6_addr_to_ip6addr(&if_addr, (const struct in6_addr*)optval);
+        udp_set_multicast_netif_ip6addr(sock->conn->pcb.udp, &if_addr);
+      }
+      break;
+    case IPV6_MULTICAST_HOPS:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+      sock->conn->pcb.udp->mcast_ttl = (u8_t)(*(const u8_t*)optval);
+      break;
+    case IPV6_MULTICAST_LOOP:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+      if (*(const u8_t*)optval) {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
+      } else {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
+      }
+      break;
+    case IPV6_ADD_MEMBERSHIP:
+    case IPV6_DROP_MEMBERSHIP:
+      {
+        err_t mld_err;
+        const struct ip6_mreq *imr = (const struct ip6_mreq *)optval;
+        ip_addr_t if_addr;
+        ip_addr_t multi_addr;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip6_mreq, NETCONN_UDP);
+        inet6_addr_to_ip6addr(ip_2_ip6(&if_addr), &imr->ipv6mr_interface);
+        inet6_addr_to_ip6addr(ip_2_ip6(&multi_addr), &imr->ipv6mr_multiaddr);
+        if (optname == IPV6_ADD_MEMBERSHIP) {
+          if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
+            /* cannot track membership (out of memory) */
+            err = ENOMEM;
+            mld_err = ERR_OK;
+          } else {
+            mld_err = mld6_joingroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
+          }
+        } else {
+          mld_err = mld6_leavegroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
+          lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
+        }
+        if (mld_err != ERR_OK) {
+          err = EADDRNOTAVAIL;
+        }
+      }
+    break;
+#endif /* LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS */
     default:
       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
                   s, optname));
@@ -3005,15 +3087,15 @@ lwip_fcntl(int s, int cmd, int val)
   return ret;
 }
 
-#if LWIP_IGMP
-/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
+#if LWIP_IGMP || (LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS)
+/** Register a new multicast group membership. On socket close, the membership is dropped automatically.
  *
  * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
  *
  * @return 1 on success, 0 on failure
  */
 static int
-lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+lwip_socket_register_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr)
 {
   /* s+1 is stored in the array to prevent having to initialize the array
      (default initialization is to 0) */
@@ -3021,10 +3103,10 @@ lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr
   int i;
 
   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
-    if (socket_ipv4_multicast_memberships[i].sa == 0) {
-      socket_ipv4_multicast_memberships[i].sa = sa;
-      ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
-      ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
+    if (socket_multicast_memberships[i].sa == 0) {
+      socket_multicast_memberships[i].sa = sa;
+      ip_addr_copy(socket_multicast_memberships[i].if_addr, *if_addr);
+      ip_addr_copy(socket_multicast_memberships[i].multi_addr, *multi_addr);
       return 1;
     }
   }
@@ -3037,7 +3119,7 @@ lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr
  * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
  */
 static void
-lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+lwip_socket_unregister_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr)
 {
   /* s+1 is stored in the array to prevent having to initialize the array
      (default initialization is to 0) */
@@ -3045,12 +3127,12 @@ lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_ad
   int i;
 
   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
-    if ((socket_ipv4_multicast_memberships[i].sa == sa) &&
-        ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
-        ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
-      socket_ipv4_multicast_memberships[i].sa = 0;
-      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
-      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+    if ((socket_multicast_memberships[i].sa == sa) &&
+        ip_addr_cmp(&socket_multicast_memberships[i].if_addr, if_addr) &&
+        ip_addr_cmp(&socket_multicast_memberships[i].multi_addr, multi_addr)) {
+      socket_multicast_memberships[i].sa = 0;
+      ip_addr_set_zero(&socket_multicast_memberships[i].if_addr);
+      ip_addr_set_zero(&socket_multicast_memberships[i].multi_addr);
       return;
     }
   }
@@ -3070,19 +3152,18 @@ static void lwip_socket_drop_registered_memberships(int s)
   LWIP_ASSERT("socket has no netconn", sockets[s].conn != NULL);
 
   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
-    if (socket_ipv4_multicast_memberships[i].sa == sa) {
-      ip_addr_t multi_addr, if_addr;
-      ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
-      ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
-      socket_ipv4_multicast_memberships[i].sa = 0;
-      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
-      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
-
-      netconn_join_leave_group(sockets[s].conn, &multi_addr, &if_addr, NETCONN_LEAVE);
+    if (socket_multicast_memberships[i].sa == sa) {
+      socket_multicast_memberships[i].sa = 0;
+      netconn_join_leave_group(sockets[s].conn,
+                               &socket_multicast_memberships[i].multi_addr,
+                               &socket_multicast_memberships[i].if_addr,
+                               NETCONN_LEAVE);
+      ip_addr_set_zero(&socket_multicast_memberships[i].if_addr);
+      ip_addr_set_zero(&socket_multicast_memberships[i].multi_addr);
     }
   }
 }
-#endif /* LWIP_IGMP */
+#endif /* LWIP_IGMP || (LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS) */
 
 #if ESP_THREAD_SAFE
 
index 380bc290cdf7d0db41cde4f241c028f6c44d0b47..acd28fb5b8b3eb39bf4f7faab99570a8d49a9016 100755 (executable)
@@ -882,6 +882,11 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
       }
     }
   }
+#if LWIP_IPV6_MLD
+  if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+    netif_loop_output(netif, p);
+  }
+#endif /* LWIP_IPV6_MLD */
 #endif /* ENABLE_LOOPBACK */
 #if LWIP_IPV6_FRAG
   /* don't fragment if interface has mtu set to 0 [loopif] */
index d9622ea03d7c91b5ce191da20de60c36110fd84a..cb458988a6e21f8ce3153206b65a8ee95fb6e774 100755 (executable)
@@ -262,6 +262,27 @@ struct linger {
  */
 #define IPV6_CHECKSUM       7  /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */
 #define IPV6_V6ONLY         27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */
+
+#if LWIP_IPV6_MLD
+/* Socket options for IPV6 multicast, uses the MLD interface to manage group memberships. RFC2133. */
+#define IPV6_MULTICAST_IF 0x300
+#define IPV6_MULTICAST_HOPS 0x301
+#define IPV6_MULTICAST_LOOP 0x302
+#define IPV6_ADD_MEMBERSHIP 0x303
+#define IPV6_DROP_MEMBERSHIP 0x304
+
+/* Structure used for IPV6_ADD/DROP_MEMBERSHIP */
+typedef struct ip6_mreq {
+    struct in6_addr ipv6mr_multiaddr; /* IPv6 multicast addr */
+    struct in6_addr ipv6mr_interface; /* local IP address of interface */
+} ip6_mreq;
+
+/* Commonly used synonyms for these options */
+#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
+#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
+
+#endif /* LWIP_IPV6_MLD */
+
 #endif /* LWIP_IPV6 */
 
 #if LWIP_UDP && LWIP_UDPLITE
index a370d3f9b91e06ca50a9ea10643671b029321d3b..c2f6ed9df7ffdec50022484b05163f70fa62fea1 100755 (executable)
@@ -173,6 +173,12 @@ void             udp_init       (void);
 #define udp_get_multicast_netif_addr(pcb)          ip_2_ip4(&(pcb)->multicast_ip)
 #define udp_set_multicast_ttl(pcb, value)      do { (pcb)->mcast_ttl = value; } while(0)
 #define udp_get_multicast_ttl(pcb)                 ((pcb)->mcast_ttl)
+
+#if LWIP_IPV6_MLD
+#define udp_set_multicast_netif_ip6addr(pcb, ip6addr) ip_addr_copy_from_ip6((pcb)->multicast_ip, *(ip6addr))
+#define udp_get_multicast_netif_ip6addr(pcb)          ip_2_ip6(&(pcb)->multicast_ip)
+#endif
+
 #endif /* LWIP_MULTICAST_TX_OPTIONS */
 
 #if UDP_DEBUG