This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit fb50ff08264786acae393af351de8e4f40d73113
Author: Marco Casaroli <[email protected]>
AuthorDate: Wed Oct 28 22:07:00 2026 +0100

    net/inet: Support MCAST_JOIN_GROUP and MCAST_LEAVE_GROUP.
    
    Join and leave a multicast group with the RFC 3678 options.  At the
    level IPPROTO_IP the group is AF_INET and the options use the code of
    IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.  At the level IPPROTO_IPV6 the
    group is AF_INET6 and the options use the code of IPV6_JOIN_GROUP and
    IPV6_LEAVE_GROUP.  gr_interface is an interface index; 0 selects the
    default device.
    
    As on Linux, a short option length gives EINVAL, and a group of the
    other family gives EINVAL at IPPROTO_IP and EADDRNOTAVAIL at
    IPPROTO_IPV6.
    
    IGMP is version 2 and MLD keeps no source lists, so the source options
    MCAST_BLOCK_SOURCE, MCAST_UNBLOCK_SOURCE, MCAST_JOIN_SOURCE_GROUP and
    MCAST_LEAVE_SOURCE_GROUP give ENOSYS, as IP_BLOCK_SOURCE and the other
    IPv4 source options do.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 net/inet/ipv4_setsockopt.c | 94 ++++++++++++++++++++++++++++++++--------------
 net/inet/ipv6_setsockopt.c | 40 +++++++++++++++++++-
 2 files changed, 105 insertions(+), 29 deletions(-)

diff --git a/net/inet/ipv4_setsockopt.c b/net/inet/ipv4_setsockopt.c
index 5b2179a7cd2..13b2a654770 100644
--- a/net/inet/ipv4_setsockopt.c
+++ b/net/inet/ipv4_setsockopt.c
@@ -132,21 +132,55 @@ int ipv4_setsockopt(FAR struct socket *psock, int option,
 #ifdef NET_UDP_HAVE_STACK
       case IP_ADD_MEMBERSHIP:         /* Join a multicast group */
       case IP_DROP_MEMBERSHIP:        /* Leave a multicast group */
+      case MCAST_JOIN_GROUP:          /* Join a multicast group (RFC 3678) */
+      case MCAST_LEAVE_GROUP:         /* Leave a multicast group (RFC 3678) */
         {
-          FAR const struct ip_mreq *mrec;
+          FAR struct udp_conn_s *conn = psock->s_conn;
           FAR struct net_driver_s *dev;
+          struct in_addr grpaddr;
 
           /* REVISIT:  This is not a proper implementation of IP_MSGFILTER */
 
-          mrec = (FAR const struct ip_mreq *)value;
-          if (mrec == NULL || value_len < sizeof(struct ip_mreq))
+          if (option == MCAST_JOIN_GROUP || option == MCAST_LEAVE_GROUP)
             {
-              nerr("ERROR: Bad value or value_len\n");
-              ret = -EINVAL;
+              FAR const struct group_req *greq =
+                (FAR const struct group_req *)value;
+
+              if (greq == NULL || value_len < sizeof(struct group_req) ||
+                  greq->gr_group.ss_family != AF_INET)
+                {
+                  nerr("ERROR: Bad value or value_len\n");
+                  ret = -EINVAL;
+                  break;
+                }
+
+              grpaddr = ((FAR const struct sockaddr_in *)
+                         &greq->gr_group)->sin_addr;
+
+              /* Use the default network device if gr_interface is 0 */
+
+              if (greq->gr_interface == 0)
+                {
+                  dev = netdev_default();
+                }
+              else
+                {
+                  dev = netdev_findbyindex(greq->gr_interface);
+                }
             }
           else
             {
-              FAR struct udp_conn_s *conn = psock->s_conn;
+              FAR const struct ip_mreq *mrec =
+                (FAR const struct ip_mreq *)value;
+
+              if (mrec == NULL || value_len < sizeof(struct ip_mreq))
+                {
+                  nerr("ERROR: Bad value or value_len\n");
+                  ret = -EINVAL;
+                  break;
+                }
+
+              grpaddr = mrec->imr_multiaddr;
 
               /* Use the default network device is imr_interface is
                * INADDRY_ANY.
@@ -164,38 +198,38 @@ int ipv4_setsockopt(FAR struct socket *psock, int option,
 
                   dev = netdev_findby_lipv4addr(mrec->imr_interface.s_addr);
                 }
+            }
 
-              if (dev == NULL)
+          if (dev == NULL)
+            {
+              nwarn("WARNING: Could not find device\n");
+              ret = -ENODEV;
+            }
+          else if (option == IP_ADD_MEMBERSHIP || option == MCAST_JOIN_GROUP)
+            {
+              if (conn->mreq.imr_multiaddr.s_addr != 0)
                 {
-                  nwarn("WARNING: Could not find device\n");
-                  ret = -ENODEV;
-                }
-              else if (option == IP_ADD_MEMBERSHIP)
-                {
-                  if (conn->mreq.imr_multiaddr.s_addr != 0)
-                    {
-                      ret = -EADDRINUSE;
-                    }
-                  else
-                    {
-                      ret = igmp_joingroup(dev, &mrec->imr_multiaddr);
-                      if (ret == OK)
-                        {
-                          conn->mreq.imr_multiaddr = mrec->imr_multiaddr;
-                          conn->mreq.imr_ifindex   = dev->d_ifindex;
-                        }
-                    }
+                  ret = -EADDRINUSE;
                 }
               else
                 {
-                  ret = igmp_leavegroup(dev, &mrec->imr_multiaddr);
+                  ret = igmp_joingroup(dev, &grpaddr);
                   if (ret == OK)
                     {
-                      conn->mreq.imr_multiaddr.s_addr = 0;
-                      conn->mreq.imr_ifindex          = 0;
+                      conn->mreq.imr_multiaddr = grpaddr;
+                      conn->mreq.imr_ifindex   = dev->d_ifindex;
                     }
                 }
             }
+          else
+            {
+              ret = igmp_leavegroup(dev, &grpaddr);
+              if (ret == OK)
+                {
+                  conn->mreq.imr_multiaddr.s_addr = 0;
+                  conn->mreq.imr_ifindex          = 0;
+                }
+            }
         }
         break;
 
@@ -320,6 +354,10 @@ int ipv4_setsockopt(FAR struct socket *psock, int option,
       case IP_MULTICAST_ALL:          /* Modify the delivery policy of
                                        * multicast messages bound to
                                        * INADDR_ANY */
+      case MCAST_BLOCK_SOURCE:        /* Same as IP_BLOCK_SOURCE */
+      case MCAST_UNBLOCK_SOURCE:      /* Same as IP_UNBLOCK_SOURCE */
+      case MCAST_JOIN_SOURCE_GROUP:   /* Same as IP_ADD_SOURCE_MEMBERSHIP */
+      case MCAST_LEAVE_SOURCE_GROUP:  /* Same as IP_DROP_SOURCE_MEMBERSHIP */
 
         /* #warning Missing logic */
 
diff --git a/net/inet/ipv6_setsockopt.c b/net/inet/ipv6_setsockopt.c
index 881c508f1e2..d08958855ff 100644
--- a/net/inet/ipv6_setsockopt.c
+++ b/net/inet/ipv6_setsockopt.c
@@ -99,6 +99,40 @@ int ipv6_setsockopt(FAR struct socket *psock, int option,
         ret = mld_leavegroup(value);
         break;
 
+      case MCAST_JOIN_GROUP:      /* Join a multicast group (RFC 3678) */
+      case MCAST_LEAVE_GROUP:     /* Leave a multicast group (RFC 3678) */
+        {
+          FAR const struct group_req *greq =
+            (FAR const struct group_req *)value;
+          struct ipv6_mreq mrec;
+
+          if (value_len < sizeof(struct group_req))
+            {
+              ret = -EINVAL;
+              break;
+            }
+
+          if (greq->gr_group.ss_family != AF_INET6)
+            {
+              ret = -EADDRNOTAVAIL;
+              break;
+            }
+
+          mrec.ipv6mr_multiaddr = ((FAR const struct sockaddr_in6 *)
+                                   &greq->gr_group)->sin6_addr;
+          mrec.ipv6mr_interface = greq->gr_interface;
+
+          if (option == MCAST_JOIN_GROUP)
+            {
+              ret = mld_joingroup(&mrec);
+            }
+          else
+            {
+              ret = mld_leavegroup(&mrec);
+            }
+        }
+        break;
+
       case IPV6_MULTICAST_HOPS:   /* Multicast hop limit */
         {
           FAR struct socket_conn_s *conn = psock->s_conn;
@@ -142,10 +176,14 @@ int ipv6_setsockopt(FAR struct socket *psock, int option,
           break;
         }
 #endif /* NET_UDP_HAVE_STACK */
-#endif /* CONFIG_NET_MLD */
 
       /* The following IPv6 socket options are defined, but not implemented */
 
+      case MCAST_BLOCK_SOURCE:
+      case MCAST_UNBLOCK_SOURCE:
+      case MCAST_JOIN_SOURCE_GROUP:
+      case MCAST_LEAVE_SOURCE_GROUP:
+#endif /* CONFIG_NET_MLD */
       case IPV6_V6ONLY:           /* Restrict AF_INET6 socket to IPv6
                                    * communications only */
         nwarn("WARNING: Unimplemented IPv6 option: %d\n", option);

Reply via email to