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

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


The following commit(s) were added to refs/heads/master by this push:
     new 99ee94728a net:add IP_MULTICAST_IF & IPV6_MULTICAST_IF function 
implementation
99ee94728a is described below

commit 99ee94728a68c018768e4b9d6d5d6e3e37f47258
Author: wangchen <[email protected]>
AuthorDate: Mon Aug 21 10:49:02 2023 +0800

    net:add IP_MULTICAST_IF & IPV6_MULTICAST_IF function implementation
    
    refer to https://man7.org/linux/man-pages/man7/ip.7.html
    IP_MULTICAST_IF (since Linux 1.2)
                  Set the local device for a multicast socket.  The argument
                  for setsockopt(2) is an ip_mreqn or (since Linux 3.5)
                  ip_mreq structure similar to IP_ADD_MEMBERSHIP, or an
                  in_addr structure.  (The kernel determines which structure
                  is being passed based on the size passed in optlen.)  For
                  getsockopt(2), the argument is an in_addr structure.
    refer to https://man7.org/linux/man-pages/man7/ipv6.7.html
    IPV6_MULTICAST_IF
                  Set the device for outgoing multicast packets on the
                  socket.  This is allowed only for SOCK_DGRAM and SOCK_RAW
                  socket.  The argument is a pointer to an interface index
                  (see netdevice(7)) in an integer.
    testcase1:
    TEST_IMPL(udp_multicast_interface) {
    /* TODO(gengjiawen): Fix test on QEMU. */
      RETURN_SKIP("Test does not currently work in QEMU");
    
      int r;
      uv_udp_send_t req;
      uv_buf_t buf;
      struct sockaddr_in addr;
      struct sockaddr_in baddr;
    
      close_cb_called = 0;
      sv_send_cb_called = 0;
      ASSERT(0 == uv_ip4_addr("239.255.0.1", TEST_PORT, &addr));
    
      r = uv_udp_init(uv_default_loop(), &server);
      ASSERT(r == 0);
    
      ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &baddr));
      r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0);
      ASSERT(r == 0);
    
      r = uv_udp_set_multicast_interface(&server, "0.0.0.0");
      ASSERT(r == 0);
    
      /* server sends "PING" */
      buf = uv_buf_init("PING", 4);
      r = uv_udp_send(&req,
                      &server,
                      &buf,
                      1,
                      (const struct sockaddr*)&addr,
                      sv_send_cb);
      ASSERT(r == 0);
    
      ASSERT(close_cb_called == 0);
      ASSERT(sv_send_cb_called == 0);
    
      /* run the loop till all events are processed */
      uv_run(uv_default_loop(), UV_RUN_DEFAULT);
    
      ASSERT(sv_send_cb_called == 1);
      ASSERT(close_cb_called == 1);
    
      ASSERT(client.send_queue_size == 0);
      ASSERT(server.send_queue_size == 0);
    
      MAKE_VALGRIND_HAPPY();
      return 0;
    }
    testcase2:
    TEST_IMPL(udp_multicast_interface6) {
    /* TODO(gengjiawen): Fix test on QEMU. */
      RETURN_SKIP("Test does not currently work in QEMU");
    
      int r;
      uv_udp_send_t req;
      uv_buf_t buf;
      struct sockaddr_in6 addr;
      struct sockaddr_in6 baddr;
    
      if (!can_ipv6())
        RETURN_SKIP("IPv6 not supported");
    
      close_cb_called = 0;
      sv_send_cb_called = 0;
    
      ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr));
    
      r = uv_udp_init(uv_default_loop(), &server);
      ASSERT(r == 0);
    
      ASSERT(0 == uv_ip6_addr("::", 0, &baddr));
      r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0);
      ASSERT(r == 0);
    
      r = uv_udp_set_multicast_interface(&server, "::1%lo0");
      r = uv_udp_set_multicast_interface(&server, NULL);
      ASSERT(r == 0);
    
      /* server sends "PING" */
      buf = uv_buf_init("PING", 4);
      r = uv_udp_send(&req,
                      &server,
                      &buf,
                      1,
                      (const struct sockaddr*)&addr,
                      sv_send_cb);
      ASSERT(r == 0);
    
      ASSERT(close_cb_called == 0);
      ASSERT(sv_send_cb_called == 0);
    
      /* run the loop till all events are processed */
      uv_run(uv_default_loop(), UV_RUN_DEFAULT);
    
      ASSERT(sv_send_cb_called == 1);
      ASSERT(close_cb_called == 1);
    
      MAKE_VALGRIND_HAPPY();
      return 0;
    }
    
    Signed-off-by: wangchen <[email protected]>
---
 net/inet/ipv4_setsockopt.c |  78 +++++++++++++++++++++++++++++-
 net/inet/ipv6_setsockopt.c |  38 ++++++++++++++-
 net/udp/udp.h              |   4 ++
 net/udp/udp_finddev.c      | 117 ++++++++++++++++++++++++++++-----------------
 4 files changed, 188 insertions(+), 49 deletions(-)

diff --git a/net/inet/ipv4_setsockopt.c b/net/inet/ipv4_setsockopt.c
index 5cfc853a09..7553cdf134 100644
--- a/net/inet/ipv4_setsockopt.c
+++ b/net/inet/ipv4_setsockopt.c
@@ -208,10 +208,84 @@ int ipv4_setsockopt(FAR struct socket *psock, int option,
         }
         break;
 
-      /* The following IPv4 socket options are defined, but not implemented */
-
       case IP_MULTICAST_IF:           /* Set local device for a multicast
                                        * socket */
+#ifdef NET_UDP_HAVE_STACK
+        {
+          FAR struct udp_conn_s *conn;
+          FAR struct net_driver_s *dev;
+          struct ip_mreqn mreq;
+
+          conn = psock->s_conn;
+          if (value == NULL || value_len == 0)
+            {
+              ret = -EINVAL;
+              break;
+            }
+
+          if (value_len >= sizeof(struct ip_mreqn))
+            {
+              memcpy(&mreq, value, sizeof(mreq));
+            }
+          else
+            {
+              memset(&mreq, 0, sizeof(mreq));
+              if (value_len >= sizeof(struct ip_mreq))
+                {
+                  memcpy(&mreq, value, sizeof(struct ip_mreq));
+                }
+              else if (value_len >= sizeof(struct in_addr))
+                {
+                  memcpy(&mreq.imr_multiaddr,
+                         value, sizeof(struct in_addr));
+                }
+            }
+
+          if (!mreq.imr_ifindex)
+            {
+              if (net_ipv4addr_cmp(mreq.imr_multiaddr.s_addr, INADDR_ANY))
+                {
+                  conn->mreq.imr_interface.s_addr = 0;
+                  conn->mreq.imr_ifindex = 0;
+                  ret = OK;
+                  break;
+                }
+
+              dev = netdev_findby_lipv4addr(mreq.imr_multiaddr.s_addr);
+              if (dev)
+                {
+                  mreq.imr_ifindex = dev->d_ifindex;
+                }
+            }
+          else
+            {
+              dev = netdev_findbyindex(mreq.imr_ifindex);
+            }
+
+          if (!dev)
+            {
+              ret = -EADDRNOTAVAIL;
+              break;
+            }
+
+#ifdef CONFIG_NET_BINDTODEVICE
+          if (conn->sconn.s_boundto &&
+              mreq.imr_ifindex != conn->sconn.s_boundto)
+            {
+              ret = -EINVAL;
+              break;
+            }
+#endif
+
+          conn->mreq.imr_interface.s_addr = mreq.imr_multiaddr.s_addr;
+          conn->mreq.imr_ifindex = mreq.imr_ifindex;
+          ret = OK;
+          break;
+        }
+#endif
+
+      /* The following IPv4 socket options are defined, but not implemented */
+
       case IP_MULTICAST_LOOP:         /* Set/read boolean that determines
                                        * whether sent multicast packets
                                        * should be looped back to local
diff --git a/net/inet/ipv6_setsockopt.c b/net/inet/ipv6_setsockopt.c
index 2df0b6c8a1..2fc08de040 100644
--- a/net/inet/ipv6_setsockopt.c
+++ b/net/inet/ipv6_setsockopt.c
@@ -32,9 +32,11 @@
 
 #include <nuttx/net/net.h>
 
+#include "netdev/netdev.h"
 #include "mld/mld.h"
 #include "inet/inet.h"
 #include "socket/socket.h"
+#include "udp/udp.h"
 
 #if defined(CONFIG_NET_IPv6) && defined(CONFIG_NET_SOCKOPTS)
 
@@ -103,10 +105,42 @@ int ipv6_setsockopt(FAR struct socket *psock, int option,
         }
         break;
 
-      /* The following IPv6 socket options are defined, but not implemented */
-
       case IPV6_MULTICAST_IF:     /* Interface to use for outgoing multicast
                                    * packets */
+#ifdef NET_UDP_HAVE_STACK
+      {
+        FAR struct net_driver_s *dev;
+        FAR struct udp_conn_s *conn = psock->s_conn;
+        int ifindex = *(FAR int *)value;
+
+        if (ifindex > 0)
+          {
+            dev = netdev_findbyindex(ifindex);
+            if (dev == NULL)
+              {
+                ret = -ENODEV;
+                break;
+              }
+
+#ifdef CONFIG_NET_BINDTODEVICE
+            if (conn->sconn.s_boundto &&
+                ifindex != conn->sconn.s_boundto)
+              {
+                ret = -EINVAL;
+                break;
+              }
+#endif
+          }
+
+        conn->mreq.imr_ifindex = ifindex;
+
+        ret = OK;
+        break;
+      }
+#endif
+
+      /* The following IPv6 socket options are defined, but not implemented */
+
       case IPV6_MULTICAST_LOOP:   /* Multicast packets are delivered back to
                                    * the local application */
 #endif
diff --git a/net/udp/udp.h b/net/udp/udp.h
index 622897cd0b..354aefb8ea 100644
--- a/net/udp/udp.h
+++ b/net/udp/udp.h
@@ -147,6 +147,10 @@ struct udp_conn_s
   FAR struct devif_callback_s *sndcb;
 #endif
 
+#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_MLD)
+  struct ip_mreqn mreq;
+#endif
+
   /* The following is a list of poll structures of threads waiting for
    * socket events.
    */
diff --git a/net/udp/udp_finddev.c b/net/udp/udp_finddev.c
index 93c5d4b7ee..2240ac05ba 100644
--- a/net/udp/udp_finddev.c
+++ b/net/udp/udp_finddev.c
@@ -143,27 +143,6 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
         {
           in_addr_t raddr;
 
-          if (conn->u.ipv4.laddr != INADDR_ANY)
-            {
-              /* If the socket is bound to some non-zero, local address.
-               * Normal lookup using the verified local address.
-               */
-
-              return netdev_findby_lipv4addr(conn->u.ipv4.laddr);
-            }
-
-#ifdef CONFIG_NET_BINDTODEVICE
-          if (conn->sconn.s_boundto != 0)
-            {
-              /* If the socket is bound to a local network device.
-               * Select the network device that has been bound.
-               * If the index is invalid, return NULL.
-               */
-
-              return netdev_findbyindex(conn->sconn.s_boundto);
-            }
-#endif
-
           if (remote)
             {
               FAR const struct sockaddr_in *inaddr =
@@ -175,6 +154,40 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
               net_ipv4addr_copy(raddr, conn->u.ipv4.raddr);
             }
 
+#if defined(CONFIG_NET_IGMP) && defined(CONFIG_NET_BINDTODEVICE)
+          if (IN_MULTICAST(NTOHL(raddr)))
+            {
+              if ((conn->sconn.s_boundto == 0) &&
+                  (conn->mreq.imr_ifindex != 0))
+                {
+                  return netdev_findbyindex(conn->mreq.imr_ifindex);
+                }
+            }
+          else
+#endif
+            {
+              if (conn->u.ipv4.laddr != INADDR_ANY)
+                {
+                  /* If the socket is bound to some non-zero, local address.
+                   * Normal lookup using the verified local address.
+                   */
+
+                  return netdev_findby_lipv4addr(conn->u.ipv4.laddr);
+                }
+
+#ifdef CONFIG_NET_BINDTODEVICE
+              if (conn->sconn.s_boundto != 0)
+                {
+                  /* If the socket is bound to a local network device.
+                   * Select the network device that has been bound.
+                   * If the index is invalid, return NULL.
+                   */
+
+                  return netdev_findbyindex(conn->sconn.s_boundto);
+                }
+#endif
+            }
+
           /* Normal lookup using the verified remote address */
 
           return netdev_findby_ripv4addr(conn->u.ipv4.laddr, raddr);
@@ -186,43 +199,57 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
       else
 #endif
         {
-          net_ipv6addr_t raddr;
-
-          if (!net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_unspecaddr))
+          struct in6_addr raddr;
+          if (remote)
             {
-              /* If the socket is bound to some non-zero, local address.
-               * Normal lookup using the verified local address.
-               */
-
-              return netdev_findby_lipv6addr(conn->u.ipv6.laddr);
+              FAR const struct sockaddr_in6 *inaddr =
+                (FAR const struct sockaddr_in6 *)remote;
+              net_ipv6addr_copy(raddr.in6_u.u6_addr16,
+                                inaddr->sin6_addr.s6_addr16);
             }
-
-#ifdef CONFIG_NET_BINDTODEVICE
-          if (conn->sconn.s_boundto != 0)
+          else
             {
-              /* If the socket is bound to a local network device.
-               * Select the network device that has been bound.
-               * If the index is invalid, return NULL.
-               */
-
-              return netdev_findbyindex(conn->sconn.s_boundto);
+              net_ipv6addr_copy(raddr.in6_u.u6_addr16, conn->u.ipv6.raddr);
             }
-#endif
 
-          if (remote)
+#if defined(CONFIG_NET_MLD) && defined(CONFIG_NET_BINDTODEVICE)
+          if (IN6_IS_ADDR_MULTICAST(&raddr))
             {
-              FAR const struct sockaddr_in6 *inaddr =
-                (FAR const struct sockaddr_in6 *)remote;
-              net_ipv6addr_copy(raddr, inaddr->sin6_addr.s6_addr16);
+              if ((conn->sconn.s_boundto == 0) &&
+                  (conn->mreq.imr_ifindex != 0))
+                {
+                  return netdev_findbyindex(conn->mreq.imr_ifindex);
+                }
             }
           else
+#endif
             {
-              net_ipv6addr_copy(raddr, conn->u.ipv6.raddr);
+              if (!net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_unspecaddr))
+                {
+                  /* If the socket is bound to some non-zero, local address.
+                   * Normal lookup using the verified local address.
+                   */
+
+                  return netdev_findby_lipv6addr(conn->u.ipv6.laddr);
+                }
+
+#ifdef CONFIG_NET_BINDTODEVICE
+              if (conn->sconn.s_boundto != 0)
+                {
+                  /* If the socket is bound to a local network device.
+                   * Select the network device that has been bound.
+                   * If the index is invalid, return NULL.
+                   */
+
+                  return netdev_findbyindex(conn->sconn.s_boundto);
+                }
+#endif
             }
 
           /* Normal lookup using the verified remote address */
 
-          return netdev_findby_ripv6addr(conn->u.ipv6.laddr, raddr);
+          return netdev_findby_ripv6addr(conn->u.ipv6.laddr,
+                                         raddr.in6_u.u6_addr16);
         }
 #endif
 }

Reply via email to