Andrey,

can you try my patch and tell me if it works? If not, please try to debug
it, I was not able to test it at home.

TODO: Correctly unregister at socket close, try to fix code duplication
with IGMP.

Ciao
Dirk
diff --git a/src/api/sockets.c b/src/api/sockets.c
index a50faf5..21f6481 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -59,7 +59,9 @@
 #include "lwip/udp.h"
 #include "lwip/memp.h"
 #include "lwip/pbuf.h"
+#include "lwip/netif.h"
 #include "lwip/priv/tcpip_priv.h"
+#include "lwip/mld6.h"
 #if LWIP_CHECKSUM_ON_COPY
 #include "lwip/inet_chksum.h"
 #endif
@@ -234,12 +236,12 @@
 #endif /* LWIP_IPV4 */
 };
 
-#if LWIP_IGMP
 /* Define the number of IPv4 multicast memberships, default is one per socket */
 #ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
 #define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
 #endif
 
+#if LWIP_IGMP
 /* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
    a socket is closed */
 struct lwip_socket_multicast_pair {
@@ -256,6 +258,25 @@
 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 void lwip_socket_drop_registered_memberships(int s);
+#endif /* LWIP_IGMP */
+
+#if LWIP_IPV6_MLD
+/* This is to keep track of IP_JOIN_GROUP calls to drop the membership when
+   a socket is closed */
+struct lwip_socket_multicast_mld6_pair {
+  /** the socket */
+  struct lwip_sock* sock;
+  /** the interface index */
+  unsigned int if_idx;
+  /** the group address */
+  ip6_addr_t multi_addr;
+};
+
+struct lwip_socket_multicast_mld6_pair socket_ipv6_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+
+static int  lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
+static void lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
+static void lwip_socket_drop_registered_mld6_memberships(int s);
 #endif /* LWIP_IGMP */
 
 /** The global array of available sockets */
@@ -709,6 +730,10 @@
   /* drop all possibly joined IGMP memberships */
   lwip_socket_drop_registered_memberships(s);
 #endif /* LWIP_IGMP */
+#if LWIP_IPV6_MLD
+  /* drop all possibly joined MLD6 memberships */
+  lwip_socket_drop_registered_mld6_memberships(s);
+#endif /* LWIP_IPV6_MLD */
 
   err = netconn_delete(sock->conn);
   if (err != ERR_OK) {
@@ -3053,7 +3078,6 @@
     case IP_DROP_MEMBERSHIP:
       {
         /* If this is a TCP or a RAW socket, ignore these options. */
-        /* @todo: assign membership to this socket so that it is dropped when closing the socket */
         err_t igmp_err;
         const struct ip_mreq *imr = (const struct ip_mreq *)optval;
         ip4_addr_t if_addr;
@@ -3079,6 +3103,41 @@
       }
       break;
 #endif /* LWIP_IGMP */
+#if LWIP_IPV6_MLD
+    case IPV6_JOIN_GROUP:
+    case IPV6_LEAVE_GROUP:
+      {
+        /* If this is a TCP or a RAW socket, ignore these options. */
+        err_t mld6_err;
+        struct netif *netif;
+        ip6_addr_t multi_addr;
+        const struct ipv6_mreq *imr = (const struct ipv6_mreq *)optval;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ipv6_mreq, NETCONN_UDP);
+        inet6_addr_to_ip6addr(&multi_addr, &imr->ipv6mr_multiaddr);
+        netif = netif_get_by_index(imr->ipv6mr_interface);
+        if (netif == NULL) {
+          err = EADDRNOTAVAIL;
+          break;
+        }
+
+        if (optname == IPV6_JOIN_GROUP) {
+          if (!lwip_socket_register_mld6_membership(s, imr->ipv6mr_interface, &multi_addr)) {
+            /* cannot track membership (out of memory) */
+            err = ENOMEM;
+            mld6_err = ERR_OK;
+          } else {
+            mld6_err = mld6_joingroup_netif(netif, &multi_addr);
+          }
+        } else {
+          mld6_err = mld6_leavegroup_netif(netif, &multi_addr);
+          lwip_socket_unregister_mld6_membership(s, imr->ipv6mr_interface, &multi_addr);
+        }
+        if (mld6_err != ERR_OK) {
+          err = EADDRNOTAVAIL;
+        }
+      }
+      break;
+#endif /* LWIP_IPV6_MLD */
     default:
       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
                   s, optname));
@@ -3558,4 +3617,98 @@
   done_socket(sock);
 }
 #endif /* LWIP_IGMP */
+
+#if LWIP_IPV6_MLD
+/** Register a new MLD6 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_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return 0;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv6_multicast_memberships[i].sock == NULL) {
+      socket_ipv6_multicast_memberships[i].sock   = sock;
+      socket_ipv6_multicast_memberships[i].if_idx = if_idx;
+      ip6_addr_copy(socket_ipv6_multicast_memberships[i].multi_addr, *multi_addr);
+      done_socket(sock);
+      return 1;
+    }
+  }
+  done_socket(sock);
+  return 0;
+}
+
+/** Unregister a previously registered MLD6 membership. This prevents dropping the membership
+ * on socket close.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if ((socket_ipv6_multicast_memberships[i].sock   == sock) &&
+        (socket_ipv6_multicast_memberships[i].if_idx == if_idx) &&
+        ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) {
+      socket_ipv6_multicast_memberships[i].sock   = NULL;
+      socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
+      ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
+      break;
+    }
+  }
+  done_socket(sock);
+}
+
+/** Drop all MLD6 memberships of a socket that were not dropped explicitly via setsockopt.
+ *
+ * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_drop_registered_mld6_memberships(int s)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv6_multicast_memberships[i].sock == sock) {
+      ip_addr_t multi_addr;
+      struct netif *netif;
+      ip_addr_copy_from_ip6(multi_addr, socket_ipv6_multicast_memberships[i].multi_addr);
+      netif = netif_get_by_index(socket_ipv6_multicast_memberships[i].if_idx);
+      if (netif == NULL) {
+        return;
+      }
+      socket_ipv6_multicast_memberships[i].sock   = NULL;
+      socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
+      ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
+
+      /* fixme: need netconn_join_leave_group that takes netif as argument */
+      netconn_join_leave_group(sock->conn, &multi_addr, netif_ip_addr6(netif, 0), NETCONN_LEAVE);
+    }
+  }
+  done_socket(sock);
+}
+#endif /* LWIP_IPV6_MLD */
+
 #endif /* LWIP_SOCKET */
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index cb6a21c..a8d85fc 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -327,6 +327,19 @@
 };
 #endif /* LWIP_IPV4 */
 
+#if LWIP_IPV6_MLD
+/*
+ * Options and types related to IPv6 multicast membership
+ */
+#define IPV6_JOIN_GROUP   12
+#define IPV6_LEAVE_GROUP  13
+
+typedef struct ipv6_mreq {
+  struct in6_addr ipv6mr_multiaddr; /*  IPv6 multicast addr */
+  unsigned int    ipv6mr_interface; /*  interface index, or 0 */
+} ipv6_mreq;
+#endif /* LWIP_IPV6_MLD */
+
 /*
  * The Type of Service provides an indication of the abstract
  * parameters of the quality of service desired.  These parameters are
_______________________________________________
lwip-users mailing list
lwip-users@nongnu.org
https://lists.nongnu.org/mailman/listinfo/lwip-users

Reply via email to