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

commit 3e4d847f42b69d333dbde6bf6410284c6745392e
Author: Zhe Weng <[email protected]>
AuthorDate: Fri Oct 20 17:08:10 2023 +0800

    net/netdev: Support managing multiple IPv6 addresses by ioctl
    
    1. Supporting `SIOCSIFADDR` and `SIOCDIFADDR` with Linux in6_ifreq struct 
to manage ipv6 addresses.
       Ref: https://man7.org/linux/man-pages/man7/netdevice.7.html
    2. Supporting alias like 'eth0:0' for multiple IPv6 addresses, to keep 
previous ioctl `SIOCGLIFADDR`, `SIOCSLIFADDR`, `SIOCGLIFNETMASK` and 
`SIOCSLIFNETMASK` working.
       Ref: https://man7.org/linux/man-pages/man8/ifconfig.8.html
    
    Signed-off-by: Zhe Weng <[email protected]>
---
 drivers/net/rpmsgdrv.c      |   2 +-
 include/netinet/in.h        |   7 +++
 include/nuttx/net/net.h     |   4 +-
 net/netdev/netdev_ifconf.c  |  99 +++++++++++++++++++++++---------
 net/netdev/netdev_ioctl.c   | 134 ++++++++++++++++++++++++++++++--------------
 net/usrsock/usrsock_ioctl.c |   2 +-
 6 files changed, 178 insertions(+), 70 deletions(-)

diff --git a/drivers/net/rpmsgdrv.c b/drivers/net/rpmsgdrv.c
index 67cf6286f8..12a829be0b 100644
--- a/drivers/net/rpmsgdrv.c
+++ b/drivers/net/rpmsgdrv.c
@@ -1057,7 +1057,7 @@ static int net_rpmsg_drv_ioctl(FAR struct net_driver_s 
*dev, int cmd,
   ssize_t len;
   int ret;
 
-  len = net_ioctl_arglen(cmd);
+  len = net_ioctl_arglen(PF_RPMSG, cmd);
   if (len >= 0)
     {
       FAR struct net_rpmsg_ioctl_s *msg;
diff --git a/include/netinet/in.h b/include/netinet/in.h
index a6038a474d..6901e44e2d 100644
--- a/include/netinet/in.h
+++ b/include/netinet/in.h
@@ -335,6 +335,13 @@ struct in6_pktinfo
   int             ipi6_ifindex;     /* send/recv interface index */
 };
 
+struct in6_ifreq
+{
+  struct in6_addr ifr6_addr;        /* The IPv6 address of the request */
+  uint32_t        ifr6_prefixlen;   /* The IPv6 prefix length */
+  int             ifr6_ifindex;     /* The interface index of the request */
+};
+
 /****************************************************************************
  * Public Data
  ****************************************************************************/
diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h
index f92ea91ca8..4c2452f0c0 100644
--- a/include/nuttx/net/net.h
+++ b/include/nuttx/net/net.h
@@ -301,7 +301,7 @@ void net_initialize(void);
  *   Calculate the ioctl argument buffer length.
  *
  * Input Parameters:
- *
+ *   domain   The socket domain
  *   cmd      The ioctl command
  *
  * Returned Value:
@@ -309,7 +309,7 @@ void net_initialize(void);
  *
  ****************************************************************************/
 
-ssize_t net_ioctl_arglen(int cmd);
+ssize_t net_ioctl_arglen(uint8_t domain, int cmd);
 
 /****************************************************************************
  * Critical section management.
diff --git a/net/netdev/netdev_ifconf.c b/net/netdev/netdev_ifconf.c
index 1e30f3c208..362c926b07 100644
--- a/net/netdev/netdev_ifconf.c
+++ b/net/netdev/netdev_ifconf.c
@@ -26,7 +26,9 @@
 
 #include <string.h>
 #include <assert.h>
+#include <debug.h>
 #include <errno.h>
+#include <stdio.h>
 
 #include <net/if.h>
 #include <nuttx/net/netdev.h>
@@ -139,6 +141,77 @@ static int ifconf_ipv4_callback(FAR struct net_driver_s 
*dev, FAR void *arg)
 }
 #endif
 
+/****************************************************************************
+ * Name: ifconf_ipv6_addr_callback
+ *
+ * Description:
+ *   Callback from netdev_ipv6_foreach() that does the real implementation of
+ *   netdev_ipv6_ifconf().
+ *
+ * Input Parameters:
+ *   dev  - The network device for this callback.
+ *   addr - The IPv6 address.
+ *   arg  - User callback argument
+ *
+ * Returned Value:
+ *   Zero is returned on success; a negated errno value is returned on any
+ *   failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv6
+static int ifconf_ipv6_addr_callback(FAR struct net_driver_s *dev,
+                                     FAR struct netdev_ifaddr6_s *addr,
+                                     FAR void *arg)
+{
+  FAR struct ifconf_ipv6_info_s *info = (FAR struct ifconf_ipv6_info_s *)arg;
+  FAR struct lifconf *lifc = info->lifc;
+
+  if (lifc->lifc_len + sizeof(struct lifreq) <= info->bufsize)
+    {
+      FAR struct lifreq *req =
+        (FAR struct lifreq *)&lifc->lifc_buf[lifc->lifc_len];
+      FAR struct sockaddr_in6 *inaddr =
+        (FAR struct sockaddr_in6 *)&req->lifr_addr;
+#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
+      int addr_idx = addr - dev->d_ipv6;
+
+      /* There is space for information about another adapter.  Within
+       * each ifreq structure, lifr_name will receive the interface
+       * name and lifr_addr the address.  The actual number of bytes
+       * transferred is returned in lifc_len.
+       */
+
+      if (addr_idx > 0)
+        {
+          /* eth0:0 represents the second addr on eth0 */
+
+          if (snprintf(req->lifr_name, IFNAMSIZ,
+                       "%s:%d", dev->d_ifname, addr_idx - 1) >= IFNAMSIZ)
+            {
+              nwarn("WARNING: ifname too long to print %s:%d\n",
+                    dev->d_ifname, addr_idx - 1);
+            }
+        }
+      else
+#endif
+        {
+          strlcpy(req->lifr_name, dev->d_ifname, IFNAMSIZ);
+        }
+
+      inaddr->sin6_family = AF_INET6;
+      inaddr->sin6_port   = 0;
+      net_ipv6addr_copy(inaddr->sin6_addr.s6_addr16, addr->addr);
+    }
+
+  /* Increment the size of the buffer in any event */
+
+  lifc->lifc_len += sizeof(struct lifreq);
+
+  return OK;
+}
+#endif
+
 /****************************************************************************
  * Name: ifconf_ipv6_callback
  *
@@ -160,10 +233,8 @@ static int ifconf_ipv4_callback(FAR struct net_driver_s 
*dev, FAR void *arg)
 static int ifconf_ipv6_callback(FAR struct net_driver_s *dev, FAR void *arg)
 {
   FAR struct ifconf_ipv6_info_s *info = (FAR struct ifconf_ipv6_info_s *)arg;
-  FAR struct lifconf *lifc;
 
   DEBUGASSERT(dev != NULL && info != NULL && info->lifc != NULL);
-  lifc = info->lifc;
 
   /* Check if this adapter has an IPv6 address assigned and is in the UP
    * state.
@@ -186,29 +257,7 @@ static int ifconf_ipv6_callback(FAR struct net_driver_s 
*dev, FAR void *arg)
        * cases.
        */
 
-      if (lifc->lifc_len + sizeof(struct lifreq) <= info->bufsize)
-        {
-          FAR struct lifreq *req =
-            (FAR struct lifreq *)&lifc->lifc_buf[lifc->lifc_len];
-          FAR struct sockaddr_in6 *inaddr =
-            (FAR struct sockaddr_in6 *)&req->lifr_addr;
-
-          /* There is space for information about another adapter.  Within
-           * each ifreq structure, lifr_name will receive the interface
-           * name and lifr_addr the address.  The actual number of bytes
-           * transferred is returned in lifc_len.
-           */
-
-          strlcpy(req->lifr_name, dev->d_ifname, IFNAMSIZ);
-
-          inaddr->sin6_family = AF_INET6;
-          inaddr->sin6_port   = 0;
-          net_ipv6addr_copy(inaddr->sin6_addr.s6_addr16, dev->d_ipv6addr);
-        }
-
-      /* Increment the size of the buffer in any event */
-
-      lifc->lifc_len += sizeof(struct lifreq);
+      return netdev_ipv6_foreach(dev, ifconf_ipv6_addr_callback, arg);
     }
 
   return 0;
diff --git a/net/netdev/netdev_ioctl.c b/net/netdev/netdev_ioctl.c
index 9a89f96f8f..1b8e0cebe2 100644
--- a/net/netdev/netdev_ioctl.c
+++ b/net/netdev/netdev_ioctl.c
@@ -652,31 +652,32 @@ static int netdev_wifr_ioctl(FAR struct socket *psock, 
int cmd,
 #endif
 
 /****************************************************************************
- * Name: netdev_ifr_dev
+ * Name: netdev_ifr_split_idx
  *
  * Description:
- *   Verify the struct ifreq and get the Ethernet device.
+ *   Split the address index from device name like 'eth0:0'.
  *
  * Input Parameters:
  *   req - The argument of the ioctl cmd
  *
  * Returned Value:
- *  A pointer to the driver structure on success; NULL on failure.
+ *   The address index from device name.
  *
  ****************************************************************************/
 
-static FAR struct net_driver_s *netdev_ifr_dev(FAR struct ifreq *req)
+static unsigned int netdev_ifr_split_idx(FAR struct ifreq *req)
 {
-  if (req != NULL)
-    {
-      /* Find the network device associated with the device name
-       * in the request data.
-       */
+  FAR char *colon = strchr(req->ifr_name, ':');
+  int idx;
 
-      return netdev_findbyname(req->ifr_name);
+  if (colon)
+    {
+      *colon++ = '\0'; /* Remove suffix from device name */
+      idx = atoi(colon);
+      return idx >= 0 ? idx + 1 : 0; /* eth0:0 represents the second addr */
     }
 
-  return NULL;
+  return 0;
 }
 
 /****************************************************************************
@@ -686,7 +687,7 @@ static FAR struct net_driver_s *netdev_ifr_dev(FAR struct 
ifreq *req)
  *   Calculate the ioctl argument buffer length of ifreq.
  *
  * Input Parameters:
- *
+ *   domain   The socket domain
  *   cmd      The ioctl command
  *
  * Returned Value:
@@ -694,12 +695,11 @@ static FAR struct net_driver_s *netdev_ifr_dev(FAR struct 
ifreq *req)
  *
  ****************************************************************************/
 
-static ssize_t net_ioctl_ifreq_arglen(int cmd)
+static ssize_t net_ioctl_ifreq_arglen(uint8_t domain, int cmd)
 {
   switch (cmd)
     {
       case SIOCGIFADDR:
-      case SIOCSIFADDR:
       case SIOCGIFDSTADDR:
       case SIOCSIFDSTADDR:
       case SIOCGIFBRDADDR:
@@ -710,7 +710,6 @@ static ssize_t net_ioctl_ifreq_arglen(int cmd)
       case SIOCGIFMTU:
       case SIOCGIFHWADDR:
       case SIOCSIFHWADDR:
-      case SIOCDIFADDR:
       case SIOCGIFCOUNT:
       case SIOCSIFFLAGS:
       case SIOCGIFFLAGS:
@@ -730,6 +729,11 @@ static ssize_t net_ioctl_ifreq_arglen(int cmd)
       case SIOCGIFINDEX:
         return sizeof(struct ifreq);
 
+      case SIOCSIFADDR:
+      case SIOCDIFADDR:
+        return domain == PF_INET6 ?
+                 sizeof(struct in6_ifreq) : sizeof(struct ifreq);
+
       case SIOCGLIFADDR:
       case SIOCSLIFADDR:
       case SIOCGLIFDSTADDR:
@@ -769,6 +773,7 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int 
cmd,
                             FAR struct ifreq *req)
 {
   FAR struct net_driver_s *dev = NULL;
+  unsigned int idx = 0;
   int ret = OK;
 
   ninfo("cmd: %d\n", cmd);
@@ -827,15 +832,27 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int 
cmd,
         break;
 #endif
       default:
-        if (net_ioctl_ifreq_arglen(cmd) > 0)
+        if (req == NULL)
           {
-            dev = netdev_ifr_dev(req);
-            if (dev == NULL)
-              {
-                ret = -ENODEV;
-              }
+            net_unlock();
+            return -ENOTTY;
           }
-        else
+
+        if (net_ioctl_ifreq_arglen(psock->s_domain, cmd)
+            >= (ssize_t)sizeof(struct ifreq))
+          {
+            idx = netdev_ifr_split_idx(req);
+            UNUSED(idx);
+            dev = netdev_findbyname(req->ifr_name);
+          }
+        else if (net_ioctl_ifreq_arglen(psock->s_domain, cmd)
+                 == (ssize_t)sizeof(struct in6_ifreq))
+          {
+            FAR struct in6_ifreq *ifr6 = (FAR struct in6_ifreq *)req;
+            dev = netdev_findbyindex(ifr6->ifr6_ifindex);
+          }
+
+        if (dev == NULL)
           {
             ret = -ENOTTY;
           }
@@ -857,12 +874,6 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int 
cmd,
         ioctl_get_ipv4addr(&req->ifr_addr, dev->d_ipaddr);
         break;
 
-      case SIOCSIFADDR:  /* Set IP address */
-        ioctl_set_ipv4addr(&dev->d_ipaddr, &req->ifr_addr);
-        netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET,
-                         &dev->d_ipaddr, net_ipv4_mask2pref(dev->d_netmask));
-        break;
-
       case SIOCGIFDSTADDR:  /* Get P-to-P address */
         ioctl_get_ipv4addr(&req->ifr_dstaddr, dev->d_draddr);
         break;
@@ -893,16 +904,18 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int 
cmd,
       case SIOCGLIFADDR:  /* Get IP address */
         {
           FAR struct lifreq *lreq = (FAR struct lifreq *)req;
-          ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6addr);
+          idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
+          ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6[idx].addr);
         }
         break;
 
       case SIOCSLIFADDR:  /* Set IP address */
         {
           FAR struct lifreq *lreq = (FAR struct lifreq *)req;
-          ioctl_set_ipv6addr(dev->d_ipv6addr, &lreq->lifr_addr);
+          idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
+          ioctl_set_ipv6addr(dev->d_ipv6[idx].addr, &lreq->lifr_addr);
           netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6,
-                    dev->d_ipv6addr, net_ipv6_mask2pref(dev->d_ipv6netmask));
+           dev->d_ipv6[idx].addr, net_ipv6_mask2pref(dev->d_ipv6[idx].mask));
         }
         break;
 
@@ -928,14 +941,16 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int 
cmd,
       case SIOCGLIFNETMASK:  /* Get network mask */
         {
           FAR struct lifreq *lreq = (FAR struct lifreq *)req;
-          ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6netmask);
+          idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
+          ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6[idx].mask);
         }
         break;
 
       case SIOCSLIFNETMASK:  /* Set network mask */
         {
           FAR struct lifreq *lreq = (FAR struct lifreq *)req;
-          ioctl_set_ipv6addr(dev->d_ipv6netmask, &lreq->lifr_addr);
+          idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
+          ioctl_set_ipv6addr(dev->d_ipv6[idx].mask, &lreq->lifr_addr);
         }
         break;
 #endif
@@ -1057,16 +1072,53 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, 
int cmd,
         break;
 #endif
 
+      case SIOCSIFADDR:  /* Set IP address */
+#ifdef CONFIG_NET_IPv4
+        if (psock->s_domain != PF_INET6)
+          {
+            ioctl_set_ipv4addr(&dev->d_ipaddr, &req->ifr_addr);
+            netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET,
+                         &dev->d_ipaddr, net_ipv4_mask2pref(dev->d_netmask));
+          }
+#endif
+
+#ifdef CONFIG_NET_IPv6
+        if (psock->s_domain == PF_INET6)
+          {
+            FAR struct in6_ifreq *ifr6 = (FAR struct in6_ifreq *)req;
+            ret = netdev_ipv6_add(dev, ifr6->ifr6_addr.in6_u.u6_addr16,
+                                  ifr6->ifr6_prefixlen);
+            if (ret == OK)
+              {
+                netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6,
+                      ifr6->ifr6_addr.in6_u.u6_addr16, ifr6->ifr6_prefixlen);
+              }
+          }
+#endif
+        break;
+
       case SIOCDIFADDR:  /* Delete IP address */
 #ifdef CONFIG_NET_IPv4
-        netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET,
+        if (psock->s_domain != PF_INET6)
+          {
+            netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET,
                          &dev->d_ipaddr, net_ipv4_mask2pref(dev->d_netmask));
-        dev->d_ipaddr = 0;
+            dev->d_ipaddr = 0;
+          }
 #endif
+
 #ifdef CONFIG_NET_IPv6
-        netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6,
-                    dev->d_ipv6addr, net_ipv6_mask2pref(dev->d_ipv6netmask));
-        memset(&dev->d_ipv6addr, 0, sizeof(net_ipv6addr_t));
+        if (psock->s_domain == PF_INET6)
+          {
+            FAR struct in6_ifreq *ifr6 = (FAR struct in6_ifreq *)req;
+            ret = netdev_ipv6_del(dev, ifr6->ifr6_addr.in6_u.u6_addr16,
+                                  ifr6->ifr6_prefixlen);
+            if (ret == OK)
+              {
+                netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6,
+                      ifr6->ifr6_addr.in6_u.u6_addr16, ifr6->ifr6_prefixlen);
+              }
+          }
 #endif
         break;
 
@@ -1589,7 +1641,7 @@ static int netdev_ioctl(FAR struct socket *psock, int cmd,
  *   Calculate the ioctl argument buffer length.
  *
  * Input Parameters:
- *
+ *   domain   The socket domain
  *   cmd      The ioctl command
  *
  * Returned Value:
@@ -1597,11 +1649,11 @@ static int netdev_ioctl(FAR struct socket *psock, int 
cmd,
  *
  ****************************************************************************/
 
-ssize_t net_ioctl_arglen(int cmd)
+ssize_t net_ioctl_arglen(uint8_t domain, int cmd)
 {
   ssize_t arglen;
 
-  arglen = net_ioctl_ifreq_arglen(cmd);
+  arglen = net_ioctl_ifreq_arglen(domain, cmd);
   if (arglen > 0)
     {
       return arglen;
diff --git a/net/usrsock/usrsock_ioctl.c b/net/usrsock/usrsock_ioctl.c
index 4d9e264562..d102f17d20 100644
--- a/net/usrsock/usrsock_ioctl.c
+++ b/net/usrsock/usrsock_ioctl.c
@@ -183,7 +183,7 @@ int usrsock_ioctl(FAR struct socket *psock, int cmd, 
unsigned long arg_)
       return -ENOTTY;
     }
 
-  arglen = net_ioctl_arglen(cmd);
+  arglen = net_ioctl_arglen(psock->s_domain, cmd);
   if (arglen < 0)
     {
       return arglen;

Reply via email to