You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/11/07 11:30:42 UTC

(nuttx) branch master updated (85a375cd7d -> 6403965075)

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

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


    from 85a375cd7d drivers/serial: check the remote device before get name
     new c7845f5b25 netlink: Explicitly set ip address for netlink notify
     new 96233e0c42 net/netdev: Support multiple IPv6 addresses per device
     new 2b9633e652 net: Support multiple IPv6 address per netdev
     new 7f421a46ca net/procfs: Support printing multiple IPv6 address per netdev
     new 3e4d847f42 net/netdev: Support managing multiple IPv6 addresses by ioctl
     new 4c99ad1ba9 net/utils: Switch argument order of net_ipv6_pref2mask
     new 6403965075 Documentation: Add netdev description with multiple IPv6 addresses

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Documentation/reference/os/netdev.rst              | 187 +++++++++
 drivers/net/rpmsgdrv.c                             |   2 +-
 include/netinet/in.h                               |   7 +
 include/nuttx/compiler.h                           |   8 +
 include/nuttx/net/net.h                            |   4 +-
 include/nuttx/net/netdev.h                         | 171 +++++++-
 net/devif/devif_loopback.c                         |   2 +-
 net/devif/ipv6_input.c                             |   4 +-
 net/icmpv6/icmpv6.h                                |   1 +
 net/icmpv6/icmpv6_advertise.c                      |   5 +-
 net/icmpv6/icmpv6_autoconfig.c                     |  23 +-
 net/icmpv6/icmpv6_input.c                          |   9 +-
 net/icmpv6/icmpv6_neighbor.c                       |   3 +-
 net/icmpv6/icmpv6_radvertise.c                     |  35 +-
 net/icmpv6/icmpv6_reply.c                          |   3 +-
 net/icmpv6/icmpv6_rnotify.c                        |  35 +-
 net/icmpv6/icmpv6_rsolicit.c                       |   3 +-
 net/icmpv6/icmpv6_sendmsg.c                        |   6 +-
 net/icmpv6/icmpv6_solicit.c                        |   2 +-
 net/inet/ipv6_getsockname.c                        |   3 +-
 net/mld/mld_query.c                                |  14 +-
 net/mld/mld_send.c                                 |  13 +-
 net/neighbor/neighbor_ethernet_out.c               |   3 +-
 net/neighbor/neighbor_lookup.c                     |   2 +-
 net/netdev/CMakeLists.txt                          |   4 +
 net/netdev/Kconfig                                 |  18 +
 net/netdev/Make.defs                               |   4 +
 net/netdev/netdev_findbyaddr.c                     |   6 +-
 net/netdev/netdev_ifconf.c                         | 102 +++--
 net/netdev/netdev_ioctl.c                          | 135 ++++--
 net/netdev/netdev_ipv6.c                           | 466 +++++++++++++++++++++
 net/netlink/netlink.h                              |   5 +-
 net/netlink/netlink_route.c                        |  99 +++--
 net/procfs/netdev_statistics.c                     |  36 +-
 net/route/netdev_router.c                          |   3 +-
 net/sixlowpan/sixlowpan_tcpsend.c                  |   3 +-
 net/sixlowpan/sixlowpan_udpsend.c                  |   3 +-
 net/sixlowpan/sixlowpan_utils.c                    |   3 +-
 net/tcp/tcp_conn.c                                 |   3 +-
 net/tcp/tcp_connect.c                              |   3 +-
 net/tcp/tcp_send.c                                 |   8 +-
 net/udp/udp_conn.c                                 |   4 +-
 net/udp/udp_send.c                                 |   3 +-
 net/usrsock/usrsock_ioctl.c                        |   2 +-
 net/utils/CMakeLists.txt                           |   5 +-
 net/utils/Make.defs                                |   4 +-
 net/utils/net_ipv6_pref2mask.c                     |   4 +-
 .../{net_ipv6_mask2pref.c => net_mask2pref.c}      |  86 +++-
 net/utils/utils.h                                  |  51 ++-
 49 files changed, 1414 insertions(+), 191 deletions(-)
 create mode 100644 Documentation/reference/os/netdev.rst
 create mode 100644 net/netdev/netdev_ipv6.c
 rename net/utils/{net_ipv6_mask2pref.c => net_mask2pref.c} (67%)


(nuttx) 05/07: net/netdev: Support managing multiple IPv6 addresses by ioctl

Posted by xi...@apache.org.
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 <we...@xiaomi.com>
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 <we...@xiaomi.com>
---
 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;


(nuttx) 07/07: Documentation: Add netdev description with multiple IPv6 addresses

Posted by xi...@apache.org.
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 64039650751496fdc9d305f6bd58e4f30529987f
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Tue Oct 31 14:00:00 2023 +0800

    Documentation: Add netdev description with multiple IPv6 addresses
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 Documentation/reference/os/netdev.rst | 187 ++++++++++++++++++++++++++++++++++
 1 file changed, 187 insertions(+)

diff --git a/Documentation/reference/os/netdev.rst b/Documentation/reference/os/netdev.rst
new file mode 100644
index 0000000000..2bc0fe7391
--- /dev/null
+++ b/Documentation/reference/os/netdev.rst
@@ -0,0 +1,187 @@
+===============
+Network Devices
+===============
+
+-  ``include/nuttx/net/netdev.h``. All structures and APIs
+   needed to work with network drivers are provided in this
+   header file. The structure ``struct net_driver_s`` defines the
+   interface and is passed to the network via
+   ``netdev_register()``.
+
+IP Addresses
+============
+
+The structure ``struct net_driver_s`` now supports one IPv4 address and
+multiple IPv6 addresses. Multiple IPv6 addresses is common in modern
+network devices. For example, a network device may have a link-local
+address and a global address. The link-local address is used for
+neighbor discovery protocol and the global address is used for
+communication with the Internet.
+
+Configuration Options
+---------------------
+
+``CONFIG_NETDEV_MULTIPLE_IPv6``
+  Enable support for multiple IPv6 addresses per network device.
+  Depends on ``CONFIG_NET_IPv6``.
+``CONFIG_NETDEV_MAX_IPv6_ADDR``
+  Maximum number of IPv6 addresses that can be assigned to a single
+  network device. Normally a link-local address and a global address
+  are needed.
+
+IPv4 Interfaces
+---------------
+
+Now we only support one IPv4 address per network device, and directly
+use the :c:member:`d_ipaddr`, :c:member:`d_draddr` and :c:member:`d_netmask`
+in :c:struct:`net_driver_s`.
+
+.. c:struct:: net_driver_s
+
+  .. code-block:: c
+
+    struct net_driver_s
+    {
+    #ifdef CONFIG_NET_IPv4
+      in_addr_t      d_ipaddr;      /* Host IPv4 address assigned to the network interface */
+      in_addr_t      d_draddr;      /* Default router IP address */
+      in_addr_t      d_netmask;     /* Network subnet mask */
+    #endif
+    };
+
+IPv6 Interfaces
+---------------
+
+Now we support multiple IPv6 addresses per network device, and use
+the :c:member:`d_ipv6` in :c:struct:`net_driver_s` to store the IPv6
+addresses. For historical reason, we keep the old name :c:member:`d_ipv6addr`
+and :c:member:`d_ipv6netmask` for backward compatibility. Please use
+:c:member:`d_ipv6` for new drivers.
+
+.. c:struct:: net_driver_s
+
+  .. code-block:: c
+
+    struct net_driver_s
+    {
+    #ifdef CONFIG_NET_IPv6
+      struct netdev_ifaddr6_s d_ipv6[CONFIG_NETDEV_MAX_IPv6_ADDR];
+    #endif
+    };
+
+Managing the IPv6 addresses by provided APIs would be more flexible:
+
+  - :c:func:`netdev_ipv6_add()`
+  - :c:func:`netdev_ipv6_del()`
+  - :c:func:`netdev_ipv6_srcaddr()`
+  - :c:func:`netdev_ipv6_lladdr()`
+  - :c:func:`netdev_ipv6_lookup()`
+  - :c:func:`netdev_ipv6_foreach()`
+
+.. c:function:: int netdev_ipv6_add(FAR struct net_driver_s *dev, const net_ipv6addr_t addr, \
+                                    unsigned int preflen);
+.. c:function:: int netdev_ipv6_del(FAR struct net_driver_s *dev, const net_ipv6addr_t addr, \
+                                    unsigned int preflen);
+
+  Add or delete an IPv6 address on the network device
+
+  :return: Zero is returned if the operation is successfully applied on
+    the device; A negated errno value is returned if failed.
+
+.. c:function:: FAR const uint16_t *netdev_ipv6_srcaddr(FAR struct net_driver_s *dev, \
+                                                        const net_ipv6addr_t dst);
+
+  Get the source IPv6 address (RFC6724).
+
+  :return: A pointer to the IPv6 address is returned on success.  It will never be
+    NULL, but can be an address containing g_ipv6_unspecaddr.
+
+.. c:function:: FAR const uint16_t *netdev_ipv6_lladdr(FAR struct net_driver_s *dev);
+
+  Get the link-local address of the network device.
+
+  :return: A pointer to the link-local address is returned on success.
+    NULL is returned if the address is not found on the device.
+
+.. c:function:: FAR struct netdev_ifaddr6_s *netdev_ipv6_lookup(FAR struct net_driver_s *dev, \
+                                                    const net_ipv6addr_t addr, bool maskcmp);
+
+  Look up an IPv6 address in the network device's IPv6 addresses
+
+  :return: A pointer to the matching IPv6 address entry is returned on success.
+    NULL is returned if the IPv6 address is not found in the device.
+
+.. c:function:: int netdev_ipv6_foreach(FAR struct net_driver_s *dev, \
+                      devif_ipv6_callback_t callback, FAR void *arg);
+
+  Enumerate each IPv6 address on a network device.  This function will
+  terminate when either (1) all addresses have been enumerated or (2) when
+  a callback returns any non-zero value.
+
+  :return: Zero is returned if the enumeration is successfully completed;
+    Non-zero value is returned if enumeration is terminated early by callback.
+
+Ioctls for IP Addresses
+-----------------------
+
+  - :c:macro:`SIOCGIFADDR`
+  - :c:macro:`SIOCSIFADDR`
+  - :c:macro:`SIOCDIFADDR`
+  - :c:macro:`SIOCGLIFADDR`
+  - :c:macro:`SIOCSLIFADDR`
+  - :c:macro:`SIOCGIFNETMASK`
+  - :c:macro:`SIOCSIFNETMASK`
+  - :c:macro:`SIOCGLIFNETMASK`
+  - :c:macro:`SIOCSLIFNETMASK`
+
+.. c:macro:: SIOCGIFADDR
+.. c:macro:: SIOCSIFADDR
+.. c:macro:: SIOCDIFADDR
+
+  We just follow the Linux convention[1]:
+
+    Get, set, or delete the address of the device using :c:member:`ifr_addr`,
+    or :c:member:`ifr6_addr` with :c:member:`ifr6_prefixlen`.
+    For compatibility, :c:macro:`SIOCGIFADDR` returns only :c:macro:`AF_INET`
+    addresses, :c:macro:`SIOCSIFADDR` accepts :c:macro:`AF_INET` and
+    :c:macro:`AF_INET6` addresses, and :c:macro:`SIOCDIFADDR` deletes
+    only :c:macro:`AF_INET6` addresses.  A :c:macro:`AF_INET` address
+    can be deleted by setting it to zero via :c:macro:`SIOCSIFADDR`.
+
+  Note: Unlike Linux, the maximum number of IPv6 addresses is limited on
+  NuttX.  If you add more IPv6 addresses when we have already reached the
+  limit, the new addresses will replace addresses with same scope.
+
+  [1]: https://man7.org/linux/man-pages/man7/netdevice.7.html
+
+.. c:macro:: SIOCGLIFADDR
+.. c:macro:: SIOCSLIFADDR
+
+  Get or set the IPv6 address of the device using :c:member:`lifr_addr`.
+
+  We follow the Linux convention[1] to allow interface name to be
+  <eth>:<num>[2], to keep working with multiple IPv6 addresses.
+
+  Note: Recommend to use :c:macro:`SIOCSIFADDR` and :c:macro:`SIOCDIFADDR`
+  to manage IPv6 addresses, by which you don't need to care about the
+  slot it stored.
+
+  [1]: https://man7.org/linux/man-pages/man7/netdevice.7.html
+  [2]: e.g. 'eth0:0' stands for the secondary address on eth0
+
+.. c:macro:: SIOCGIFNETMASK
+.. c:macro:: SIOCSIFNETMASK
+
+  Get or set the IPv4 network mask for a device using :c:member:`ifr_netmask`.
+
+.. c:macro:: SIOCGLIFNETMASK
+.. c:macro:: SIOCSLIFNETMASK
+
+  Get or set the IPv6 network mask for a device using :c:member:`lifr_netmask`.
+
+  We follow the Linux convention to allow interface name to be <eth>:<num>,
+  to keep working with multiple IPv6 addresses.
+
+  Note: Recommend to use :c:macro:`SIOCSIFADDR` and :c:macro:`SIOCDIFADDR`
+  to manage IPv6 addresses, by which you don't need to care about the
+  slot it stored.


(nuttx) 06/07: net/utils: Switch argument order of net_ipv6_pref2mask

Posted by xi...@apache.org.
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 4c99ad1ba9576f59267135211b5ff6cc3477315c
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Fri Nov 3 21:22:03 2023 +0800

    net/utils: Switch argument order of net_ipv6_pref2mask
    
    When implementing IPv6-related logic, we found the `net_ipv6_pref2mask`
    and `net_ipv6addr_copy` are using different argument order:
    ```
    net_ipv6addr_copy(ifaddr->addr, addr);
    net_ipv6_pref2mask(preflen, ifaddr->mask);
    ```
    Change the order to:
    ```
    net_ipv6addr_copy(ifaddr->addr, addr);
    net_ipv6_pref2mask(ifaddr->mask, preflen);
    ```
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 net/icmpv6/icmpv6_rnotify.c    | 2 +-
 net/netdev/netdev_ipv6.c       | 4 ++--
 net/utils/net_ipv6_pref2mask.c | 4 ++--
 net/utils/utils.h              | 4 ++--
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/net/icmpv6/icmpv6_rnotify.c b/net/icmpv6/icmpv6_rnotify.c
index 9ea4a1683f..24516da857 100644
--- a/net/icmpv6/icmpv6_rnotify.c
+++ b/net/icmpv6/icmpv6_rnotify.c
@@ -103,7 +103,7 @@ void icmpv6_setaddresses(FAR struct net_driver_s *dev,
       preflen = 128;
     }
 
-  net_ipv6_pref2mask(preflen, mask);
+  net_ipv6_pref2mask(mask, preflen);
 
   ninfo("preflen=%d netmask=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
         preflen,
diff --git a/net/netdev/netdev_ipv6.c b/net/netdev/netdev_ipv6.c
index 0692d32305..67e51e871f 100644
--- a/net/netdev/netdev_ipv6.c
+++ b/net/netdev/netdev_ipv6.c
@@ -142,7 +142,7 @@ int netdev_ipv6_add(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
        *          allows to override the address. But not sure if it's good.
        */
 
-      net_ipv6_pref2mask(preflen, ifaddr->mask);
+      net_ipv6_pref2mask(ifaddr->mask, preflen);
       return OK;
     }
 
@@ -174,7 +174,7 @@ int netdev_ipv6_add(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
 #endif /* CONFIG_NETDEV_MULTIPLE_IPv6 */
 
   net_ipv6addr_copy(ifaddr->addr, addr);
-  net_ipv6_pref2mask(preflen, ifaddr->mask);
+  net_ipv6_pref2mask(ifaddr->mask, preflen);
 
   return OK;
 }
diff --git a/net/utils/net_ipv6_pref2mask.c b/net/utils/net_ipv6_pref2mask.c
index 4f016b57c8..880abc5985 100644
--- a/net/utils/net_ipv6_pref2mask.c
+++ b/net/utils/net_ipv6_pref2mask.c
@@ -42,15 +42,15 @@
  *   specifies the number of MS bits under mask (0-128)
  *
  * Input Parameters:
+ *   mask     - The location to return the netmask.
  *   preflen  - Determines the width of the netmask (in bits).  Range 0-128
- *   mask  - The location to return the netmask.
  *
  * Returned Value:
  *   None
  *
  ****************************************************************************/
 
-void net_ipv6_pref2mask(uint8_t preflen, net_ipv6addr_t mask)
+void net_ipv6_pref2mask(net_ipv6addr_t mask, uint8_t preflen)
 {
   unsigned int bit;
   unsigned int i;
diff --git a/net/utils/utils.h b/net/utils/utils.h
index 757e0580b9..04fe60a442 100644
--- a/net/utils/utils.h
+++ b/net/utils/utils.h
@@ -242,8 +242,8 @@ uint8_t net_ipv6_mask2pref(FAR const uint16_t *mask);
  *   specifies the number of MS bits under mask (0-128)
  *
  * Input Parameters:
+ *   mask     - The location to return the netmask.
  *   preflen  - Determines the width of the netmask (in bits).  Range 0-128
- *   mask  - The location to return the netmask.
  *
  * Returned Value:
  *   None
@@ -251,7 +251,7 @@ uint8_t net_ipv6_mask2pref(FAR const uint16_t *mask);
  ****************************************************************************/
 
 #ifdef CONFIG_NET_IPv6
-void net_ipv6_pref2mask(uint8_t preflen, net_ipv6addr_t mask);
+void net_ipv6_pref2mask(net_ipv6addr_t mask, uint8_t preflen);
 #endif
 
 /****************************************************************************


(nuttx) 01/07: netlink: Explicitly set ip address for netlink notify

Posted by xi...@apache.org.
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 c7845f5b257a41c0b7cb2b8b1b5fae2f763c24bc
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Thu Oct 19 15:04:23 2023 +0800

    netlink: Explicitly set ip address for netlink notify
    
    Prepare for multiple IPv6 addresses per net device, then we can notify
    add/remove of a single address later.
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 net/netdev/netdev_ioctl.c                          | 13 +++--
 net/netlink/netlink.h                              |  5 +-
 net/netlink/netlink_route.c                        | 67 ++++++++++++++--------
 net/utils/CMakeLists.txt                           |  5 +-
 net/utils/Make.defs                                |  4 +-
 .../{net_ipv6_mask2pref.c => net_mask2pref.c}      | 48 +++++++++++++++-
 net/utils/utils.h                                  | 28 +++++++++
 7 files changed, 134 insertions(+), 36 deletions(-)

diff --git a/net/netdev/netdev_ioctl.c b/net/netdev/netdev_ioctl.c
index d1eea0c40e..9a89f96f8f 100644
--- a/net/netdev/netdev_ioctl.c
+++ b/net/netdev/netdev_ioctl.c
@@ -83,6 +83,7 @@
 #include "icmpv6/icmpv6.h"
 #include "route/route.h"
 #include "netlink/netlink.h"
+#include "utils/utils.h"
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -858,7 +859,8 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
 
       case SIOCSIFADDR:  /* Set IP address */
         ioctl_set_ipv4addr(&dev->d_ipaddr, &req->ifr_addr);
-        netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET);
+        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 */
@@ -899,7 +901,8 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
         {
           FAR struct lifreq *lreq = (FAR struct lifreq *)req;
           ioctl_set_ipv6addr(dev->d_ipv6addr, &lreq->lifr_addr);
-          netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6);
+          netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6,
+                    dev->d_ipv6addr, net_ipv6_mask2pref(dev->d_ipv6netmask));
         }
         break;
 
@@ -1056,12 +1059,14 @@ static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
 
       case SIOCDIFADDR:  /* Delete IP address */
 #ifdef CONFIG_NET_IPv4
+        netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET,
+                         &dev->d_ipaddr, net_ipv4_mask2pref(dev->d_netmask));
         dev->d_ipaddr = 0;
-        netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET);
 #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));
-        netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6);
 #endif
         break;
 
diff --git a/net/netlink/netlink.h b/net/netlink/netlink.h
index c14514f0f2..c86882266f 100644
--- a/net/netlink/netlink.h
+++ b/net/netlink/netlink.h
@@ -45,7 +45,7 @@
 
 #ifndef CONFIG_NETLINK_ROUTE
 #  define netlink_device_notify(dev)
-#  define netlink_device_notify_ipaddr(dev, type, domain)
+#  define netlink_device_notify_ipaddr(dev, type, domain, addr, preflen)
 #endif
 
 #ifdef CONFIG_NET_NETLINK
@@ -502,7 +502,8 @@ void netlink_device_notify(FAR struct net_driver_s *dev);
  ****************************************************************************/
 
 void netlink_device_notify_ipaddr(FAR struct net_driver_s *dev,
-                                  int type, int domain);
+                                  int type, int domain,
+                                  FAR const void *addr, uint8_t preflen);
 
 /****************************************************************************
  * Name: nla_next
diff --git a/net/netlink/netlink_route.c b/net/netlink/netlink_route.c
index 8ed9caa4c4..4409016da9 100644
--- a/net/netlink/netlink_route.c
+++ b/net/netlink/netlink_route.c
@@ -369,16 +369,6 @@ static uint32_t make_mask(int prefixlen)
 
   return 0;
 }
-
-static uint8_t mask_len(uint32_t nmask)
-{
-  uint8_t prefixlen = 0;
-  uint32_t hmask = NTOHL(nmask);
-
-  for (; hmask; ++prefixlen, hmask <<= 1);
-
-  return prefixlen;
-}
 #endif
 
 /****************************************************************************
@@ -392,6 +382,7 @@ static uint8_t mask_len(uint32_t nmask)
 #ifndef CONFIG_NETLINK_DISABLE_GETLINK
 static FAR struct netlink_response_s *
 netlink_get_ifaddr(FAR struct net_driver_s *dev, int domain, int type,
+                   FAR const void *local_addr, uint8_t prefixlen,
                    FAR const struct nlroute_sendto_request_s *req)
 {
   FAR struct getaddr_recvfrom_rsplist_s *alloc;
@@ -429,8 +420,8 @@ netlink_get_ifaddr(FAR struct net_driver_s *dev, int domain, int type,
   if (domain == AF_INET)
     {
       resp->attr.rta_len = RTA_LENGTH(sizeof(struct in_addr));
-      memcpy(&resp->local_addr, &dev->d_ipaddr, sizeof(struct in_addr));
-      resp->ifaddr.ifa_prefixlen = mask_len(dev->d_netmask);
+      memcpy(&resp->local_addr, local_addr, sizeof(struct in_addr));
+      resp->ifaddr.ifa_prefixlen = prefixlen;
     }
 #endif
 
@@ -438,8 +429,8 @@ netlink_get_ifaddr(FAR struct net_driver_s *dev, int domain, int type,
   if (domain == AF_INET6)
     {
       resp->attr.rta_len = RTA_LENGTH(sizeof(struct in6_addr));
-      memcpy(&resp->local_addr, dev->d_ipv6addr, sizeof(struct in6_addr));
-      resp->ifaddr.ifa_prefixlen = net_ipv6_mask2pref(dev->d_ipv6netmask);
+      memcpy(&resp->local_addr, local_addr, sizeof(struct in6_addr));
+      resp->ifaddr.ifa_prefixlen = prefixlen;
     }
 #endif
 
@@ -951,7 +942,8 @@ static int netlink_new_ipv4addr(NETLINK_HANDLE handle,
   dev->d_ipaddr  = nla_get_in_addr(tb[IFA_LOCAL]);
   dev->d_netmask = make_mask(ifm->ifa_prefixlen);
 
-  netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET);
+  netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET, &dev->d_ipaddr,
+                               ifm->ifa_prefixlen);
   net_unlock();
 
   return OK;
@@ -1000,7 +992,8 @@ static int netlink_new_ipv6addr(NETLINK_HANDLE handle,
   memcpy(dev->d_ipv6addr, nla_data(tb[IFA_LOCAL]), 16);
   net_ipv6_pref2mask(ifm->ifa_prefixlen, dev->d_ipv6netmask);
 
-  netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6);
+  netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6, dev->d_ipv6addr,
+                               ifm->ifa_prefixlen);
   net_unlock();
 
   return OK;
@@ -1047,9 +1040,10 @@ static int netlink_del_ipv4addr(NETLINK_HANDLE handle,
       return -EADDRNOTAVAIL;
     }
 
+  netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET, &dev->d_ipaddr,
+                               net_ipv4_mask2pref(dev->d_netmask));
   dev->d_ipaddr  = 0;
 
-  netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET);
   net_unlock();
 
   return OK;
@@ -1090,15 +1084,17 @@ static int netlink_del_ipv6addr(NETLINK_HANDLE handle,
       return -ENODEV;
     }
 
-  if (tb[IFA_LOCAL] && dev->d_ipaddr != nla_get_in_addr(tb[IFA_LOCAL]))
+  if (tb[IFA_LOCAL] && !net_ipv6addr_cmp(dev->d_ipv6addr,
+                                         nla_data(tb[IFA_LOCAL])))
     {
       net_unlock();
       return -EADDRNOTAVAIL;
     }
 
+  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));
 
-  netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6);
   net_unlock();
 
   return OK;
@@ -1114,14 +1110,36 @@ static int netlink_del_ipv6addr(NETLINK_HANDLE handle,
  ****************************************************************************/
 
 #ifndef CONFIG_NETLINK_DISABLE_GETADDR
-static int netlink_addr_callback(FAR struct net_driver_s *dev,
-                                   FAR void *arg)
+static int netlink_addr_callback(FAR struct net_driver_s *dev, FAR void *arg)
 {
   FAR struct nlroute_info_s *info = arg;
   FAR struct netlink_response_s *resp;
+  FAR void *addr = NULL;
+  uint8_t preflen;
+
+#ifdef CONFIG_NET_IPv4
+  if (info->req->gen.rtgen_family == AF_INET)
+    {
+      addr = &dev->d_ipaddr;
+      preflen = net_ipv4_mask2pref(dev->d_netmask);
+    }
+#endif
+
+#ifdef CONFIG_NET_IPv6
+  if (info->req->gen.rtgen_family == AF_INET6)
+    {
+      addr = &dev->d_ipv6addr;
+      preflen = net_ipv6_mask2pref(dev->d_ipv6netmask);
+    }
+#endif
+
+  if (addr == NULL)
+    {
+      return OK;
+    }
 
   resp = netlink_get_ifaddr(dev, info->req->gen.rtgen_family, RTM_NEWADDR,
-                            info->req);
+                            addr, preflen, info->req);
   if (resp == NULL)
     {
       return -ENOMEM;
@@ -1378,14 +1396,15 @@ void netlink_device_notify(FAR struct net_driver_s *dev)
     !defined(CONFIG_NETLINK_DISABLE_DELADDR) || \
     !defined(CONFIG_NETLINK_DISABLE_GETADDR)
 void netlink_device_notify_ipaddr(FAR struct net_driver_s *dev,
-                                  int type, int domain)
+                                  int type, int domain,
+                                  FAR const void *addr, uint8_t preflen)
 {
   FAR struct netlink_response_s *resp;
   int group;
 
   DEBUGASSERT(dev != NULL);
 
-  resp = netlink_get_ifaddr(dev, domain, type, NULL);
+  resp = netlink_get_ifaddr(dev, domain, type, addr, preflen, NULL);
   if (resp != NULL)
     {
 #ifdef CONFIG_NET_IPv4
diff --git a/net/utils/CMakeLists.txt b/net/utils/CMakeLists.txt
index 8c779bfe0b..27868b2fcd 100644
--- a/net/utils/CMakeLists.txt
+++ b/net/utils/CMakeLists.txt
@@ -31,12 +31,13 @@ set(SRCS
     net_snoop.c
     net_cmsg.c
     net_iob_concat.c
-    net_getrandom.c)
+    net_getrandom.c
+    net_mask2pref.c)
 
 # IPv6 utilities
 
 if(CONFIG_NET_IPv6)
-  list(APPEND SRCS net_ipv6_maskcmp.c net_ipv6_mask2pref.c net_ipv6_pref2mask.c)
+  list(APPEND SRCS net_ipv6_maskcmp.c net_ipv6_pref2mask.c)
 endif()
 
 # TCP utilities
diff --git a/net/utils/Make.defs b/net/utils/Make.defs
index 400d0367fb..764dee1481 100644
--- a/net/utils/Make.defs
+++ b/net/utils/Make.defs
@@ -22,12 +22,12 @@
 
 NET_CSRCS += net_dsec2tick.c net_dsec2timeval.c net_timeval2dsec.c
 NET_CSRCS += net_chksum.c net_ipchksum.c net_incr32.c net_lock.c net_snoop.c
-NET_CSRCS += net_cmsg.c net_iob_concat.c net_getrandom.c
+NET_CSRCS += net_cmsg.c net_iob_concat.c net_getrandom.c net_mask2pref.c
 
 # IPv6 utilities
 
 ifeq ($(CONFIG_NET_IPv6),y)
-NET_CSRCS += net_ipv6_maskcmp.c net_ipv6_mask2pref.c net_ipv6_pref2mask.c
+NET_CSRCS += net_ipv6_maskcmp.c net_ipv6_pref2mask.c
 endif
 
 # TCP utilities
diff --git a/net/utils/net_ipv6_mask2pref.c b/net/utils/net_mask2pref.c
similarity index 78%
rename from net/utils/net_ipv6_mask2pref.c
rename to net/utils/net_mask2pref.c
index 4c5de09173..2c06a6607e 100644
--- a/net/utils/net_ipv6_mask2pref.c
+++ b/net/utils/net_mask2pref.c
@@ -1,5 +1,5 @@
 /****************************************************************************
- * net/utils/net_ipv6_mask2pref.c
+ * net/utils/net_mask2pref.c
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -26,7 +26,7 @@
 
 #include "utils/utils.h"
 
-#ifdef CONFIG_NET_IPv6
+#if defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)
 
 /****************************************************************************
  * Private Data
@@ -112,10 +112,52 @@ static inline uint8_t net_msbits16(uint16_t hword)
   return ones;
 }
 
+#endif /* CONFIG_NET_IPv4 || CONFIG_NET_IPv6 */
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: net_ipv4_mask2pref
+ *
+ * Description:
+ *   Convert a 32-bit netmask to a prefix length.  The NuttX IPv4
+ *   networking uses 32-bit network masks internally.  This function
+ *   converts the IPv4 netmask to a prefix length.
+ *
+ *   The prefix length is the number of MS '1' bits on in the netmask.
+ *   This, of course, assumes that all MS bits are '1' and all LS bits are
+ *   '0' with no intermixed 1's and 0's.  This function searches from the MS
+ *   bit until the first '0' is found (this does not necessary mean that
+ *   there might not be additional '1' bits following the firs '0', but that
+ *   will be a malformed netmask.
+ *
+ * Input Parameters:
+ *   mask   An IPv4 netmask in the form of in_addr_t
+ *
+ * Returned Value:
+ *   The prefix length, range 0-32 on success;  This function will not
+ *   fail.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv4
+
+uint8_t net_ipv4_mask2pref(in_addr_t mask)
+{
+  uint32_t hmask = NTOHL(mask);
+  uint8_t ones = net_msbits16((uint16_t)(hmask >> 16));
+  if (ones == 16)
+    {
+      ones += net_msbits16((uint16_t)(hmask & 0xffff));
+    }
+
+  return ones;
+}
+
+#endif /* CONFIG_NET_IPv4 */
+
 /****************************************************************************
  * Name: net_ipv6_mask2pref
  *
@@ -140,6 +182,8 @@ static inline uint8_t net_msbits16(uint16_t hword)
  *
  ****************************************************************************/
 
+#ifdef CONFIG_NET_IPv6
+
 uint8_t net_ipv6_mask2pref(FAR const uint16_t *mask)
 {
   uint8_t preflen;
diff --git a/net/utils/utils.h b/net/utils/utils.h
index 401f2e1161..9688cac7fd 100644
--- a/net/utils/utils.h
+++ b/net/utils/utils.h
@@ -159,6 +159,34 @@ unsigned int net_timeval2dsec(FAR struct timeval *tv,
 
 void net_getrandom(FAR void *bytes, size_t nbytes);
 
+/****************************************************************************
+ * Name: net_ipv4_mask2pref
+ *
+ * Description:
+ *   Convert a 32-bit netmask to a prefix length.  The NuttX IPv4
+ *   networking uses 32-bit network masks internally.  This function
+ *   converts the IPv4 netmask to a prefix length.
+ *
+ *   The prefix length is the number of MS '1' bits on in the netmask.
+ *   This, of course, assumes that all MS bits are '1' and all LS bits are
+ *   '0' with no intermixed 1's and 0's.  This function searches from the MS
+ *   bit until the first '0' is found (this does not necessary mean that
+ *   there might not be additional '1' bits following the firs '0', but that
+ *   will be a malformed netmask.
+ *
+ * Input Parameters:
+ *   mask   An IPv4 netmask in the form of in_addr_t
+ *
+ * Returned Value:
+ *   The prefix length, range 0-32 on success;  This function will not
+ *   fail.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv4
+uint8_t net_ipv4_mask2pref(in_addr_t mask);
+#endif
+
 /****************************************************************************
  * Name: net_ipv6_mask2pref
  *


(nuttx) 04/07: net/procfs: Support printing multiple IPv6 address per netdev

Posted by xi...@apache.org.
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 7f421a46ca8871d70f0f76f30a1b98e3a53cb1d5
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Mon Oct 23 17:18:09 2023 +0800

    net/procfs: Support printing multiple IPv6 address per netdev
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 include/nuttx/compiler.h       |  8 ++++++++
 net/procfs/netdev_statistics.c | 36 ++++++++++++++++++++++++++++--------
 2 files changed, 36 insertions(+), 8 deletions(-)

diff --git a/include/nuttx/compiler.h b/include/nuttx/compiler.h
index a71b34d2d8..74edd9f212 100644
--- a/include/nuttx/compiler.h
+++ b/include/nuttx/compiler.h
@@ -61,6 +61,14 @@
 #  define CONFIG_C99_BOOL 1
 #endif
 
+/* ISO C99 supports Designated initializers */
+
+#undef CONFIG_DESIGNATED_INITIALIZERS
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#  define CONFIG_DESIGNATED_INITIALIZERS 1
+#endif
+
 /* ISO C/C++11 atomic types support */
 
 #undef CONFIG_HAVE_ATOMICS
diff --git a/net/procfs/netdev_statistics.c b/net/procfs/netdev_statistics.c
index fdc6ea5ab8..e62fd59830 100644
--- a/net/procfs/netdev_statistics.c
+++ b/net/procfs/netdev_statistics.c
@@ -36,6 +36,7 @@
 #include <nuttx/net/netdev.h>
 #include <nuttx/net/sixlowpan.h>
 
+#include "inet/inet.h"
 #include "netdev/netdev.h"
 #include "utils/utils.h"
 #include "procfs/procfs.h"
@@ -43,6 +44,16 @@
 #if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) && \
     !defined(CONFIG_FS_PROCFS_EXCLUDE_NET)
 
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv4
+#  define NETSTAT_IPv6_IDX 2
+#else
+#  define NETSTAT_IPv6_IDX 1
+#endif
+
 /****************************************************************************
  * Private Function Prototypes
  ****************************************************************************/
@@ -83,7 +94,13 @@ static const linegen_t g_netstat_linegen[] =
   , netprocfs_inet4addresses
 #endif
 #ifdef CONFIG_NET_IPv6
+#  if defined(CONFIG_NETDEV_MULTIPLE_IPv6) && \
+      defined(CONFIG_DESIGNATED_INITIALIZERS)
+  , [NETSTAT_IPv6_IDX ... NETSTAT_IPv6_IDX + CONFIG_NETDEV_MAX_IPv6_ADDR - 1]
+  = netprocfs_inet6address
+#  else
   , netprocfs_inet6address
+#  endif
   , netprocfs_inet6draddress
 #endif
 #if !defined(CONFIG_NET_IPv4) && !defined(CONFIG_NET_IPv6)
@@ -309,18 +326,26 @@ static int netprocfs_inet6address(FAR struct netprocfs_file_s *netfile)
   FAR struct net_driver_s *dev;
   char addrstr[INET6_ADDRSTRLEN];
   uint8_t preflen;
+  int idx = netfile->lineno - NETSTAT_IPv6_IDX;
   int len = 0;
 
   DEBUGASSERT(netfile != NULL && netfile->dev != NULL);
   dev = netfile->dev;
 
+#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
+  if (net_ipv6addr_cmp(dev->d_ipv6[idx].addr, g_ipv6_unspecaddr))
+    {
+      return 0;
+    }
+#endif
+
   /* Convert the 128 network mask to a human friendly prefix length */
 
-  preflen = net_ipv6_mask2pref(dev->d_ipv6netmask);
+  preflen = net_ipv6_mask2pref(dev->d_ipv6[idx].mask);
 
   /* Show the assigned IPv6 address */
 
-  if (inet_ntop(AF_INET6, dev->d_ipv6addr, addrstr, INET6_ADDRSTRLEN))
+  if (inet_ntop(AF_INET6, dev->d_ipv6[idx].addr, addrstr, INET6_ADDRSTRLEN))
     {
       len += snprintf(&netfile->line[len], NET_LINELEN - len,
                       "\tinet6 addr: %s/%d\n", addrstr, preflen);
@@ -339,22 +364,17 @@ static int netprocfs_inet6draddress(FAR struct netprocfs_file_s *netfile)
 {
   FAR struct net_driver_s *dev;
   char addrstr[INET6_ADDRSTRLEN];
-  uint8_t preflen;
   int len = 0;
 
   DEBUGASSERT(netfile != NULL && netfile->dev != NULL);
   dev = netfile->dev;
 
-  /* Convert the 128 network mask to a human friendly prefix length */
-
-  preflen = net_ipv6_mask2pref(dev->d_ipv6netmask);
-
   /* Show the IPv6 default router address */
 
   if (inet_ntop(AF_INET6, dev->d_ipv6draddr, addrstr, INET6_ADDRSTRLEN))
     {
       len += snprintf(&netfile->line[len], NET_LINELEN - len,
-                      "\tinet6 DRaddr: %s/%d\n\n", addrstr, preflen);
+                      "\tinet6 DRaddr: %s\n\n", addrstr);
     }
 
   return len;


(nuttx) 03/07: net: Support multiple IPv6 address per netdev

Posted by xi...@apache.org.
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 2b9633e652f616de7121c303bfba4b3913b4660e
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Fri Aug 25 17:37:11 2023 +0800

    net: Support multiple IPv6 address per netdev
    
    Note that user-space related code, like procfs and lifreq related ioctl commands, are not touched in this commit.
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 include/nuttx/net/netdev.h           |  7 ++++
 net/devif/devif_loopback.c           |  2 +-
 net/devif/ipv6_input.c               |  4 +-
 net/icmpv6/icmpv6.h                  |  1 +
 net/icmpv6/icmpv6_advertise.c        |  5 ++-
 net/icmpv6/icmpv6_autoconfig.c       | 23 +++++++----
 net/icmpv6/icmpv6_input.c            |  9 ++--
 net/icmpv6/icmpv6_neighbor.c         |  3 +-
 net/icmpv6/icmpv6_radvertise.c       | 35 ++++++++++++----
 net/icmpv6/icmpv6_reply.c            |  3 +-
 net/icmpv6/icmpv6_rnotify.c          | 35 ++++++++++------
 net/icmpv6/icmpv6_rsolicit.c         |  3 +-
 net/icmpv6/icmpv6_sendmsg.c          |  6 +--
 net/icmpv6/icmpv6_solicit.c          |  2 +-
 net/inet/ipv6_getsockname.c          |  3 +-
 net/mld/mld_query.c                  | 14 ++++++-
 net/mld/mld_send.c                   | 13 +++++-
 net/neighbor/neighbor_ethernet_out.c |  3 +-
 net/neighbor/neighbor_lookup.c       |  2 +-
 net/netdev/netdev_findbyaddr.c       |  6 +--
 net/netdev/netdev_ifconf.c           |  3 +-
 net/netlink/netlink_route.c          | 80 ++++++++++++++++++++++--------------
 net/route/netdev_router.c            |  3 +-
 net/sixlowpan/sixlowpan_tcpsend.c    |  3 +-
 net/sixlowpan/sixlowpan_udpsend.c    |  3 +-
 net/sixlowpan/sixlowpan_utils.c      |  3 +-
 net/tcp/tcp_conn.c                   |  3 +-
 net/tcp/tcp_connect.c                |  3 +-
 net/tcp/tcp_send.c                   |  8 +++-
 net/udp/udp_conn.c                   |  4 +-
 net/udp/udp_send.c                   |  3 +-
 31 files changed, 195 insertions(+), 100 deletions(-)

diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h
index cbfa19fb44..df9ecefc22 100644
--- a/include/nuttx/net/netdev.h
+++ b/include/nuttx/net/netdev.h
@@ -172,6 +172,13 @@
 #  ifndef CONFIG_NETDEV_MAX_IPv6_ADDR
 #    define CONFIG_NETDEV_MAX_IPv6_ADDR 1
 #  endif
+#  define NETDEV_HAS_V6ADDR(dev) \
+     (!net_ipv6addr_cmp(netdev_ipv6_srcaddr(dev, g_ipv6_unspecaddr), \
+                        g_ipv6_unspecaddr))
+#  define NETDEV_IS_MY_V6ADDR(dev,addr) \
+     (netdev_ipv6_lookup(dev, addr, false) != NULL)
+#  define NETDEV_V6ADDR_ONLINK(dev,addr) \
+     (netdev_ipv6_lookup(dev, addr, true) != NULL)
 #endif
 
 /****************************************************************************
diff --git a/net/devif/devif_loopback.c b/net/devif/devif_loopback.c
index a7379aa4e5..6fa8e89adc 100644
--- a/net/devif/devif_loopback.c
+++ b/net/devif/devif_loopback.c
@@ -62,7 +62,7 @@ bool devif_is_loopback(FAR struct net_driver_s *dev)
 
 #ifdef CONFIG_NET_IPv6
       if ((IPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION &&
-          net_ipv6addr_hdrcmp(IPv6BUF->destipaddr, dev->d_ipv6addr))
+          NETDEV_IS_MY_V6ADDR(dev, IPv6BUF->destipaddr))
         {
           return true;
         }
diff --git a/net/devif/ipv6_input.c b/net/devif/ipv6_input.c
index 4565261c5d..b29daea10d 100644
--- a/net/devif/ipv6_input.c
+++ b/net/devif/ipv6_input.c
@@ -78,7 +78,7 @@ static int check_dev_destipaddr(FAR struct net_driver_s *dev, FAR void *arg)
    * to this device.
    */
 
-  if (net_ipv6addr_cmp(ipv6->destipaddr, dev->d_ipv6addr))
+  if (NETDEV_IS_MY_V6ADDR(dev, ipv6->destipaddr))
     {
       return 1;
     }
@@ -389,7 +389,7 @@ static int ipv6_in(FAR struct net_driver_s *dev)
    * (the all zero address is the "unspecified" address.
    */
 
-  if (net_ipv6addr_cmp(dev->d_ipv6addr, g_ipv6_unspecaddr))
+  if (!NETDEV_HAS_V6ADDR(dev))
     {
       nwarn("WARNING: No IP address assigned\n");
       goto drop;
diff --git a/net/icmpv6/icmpv6.h b/net/icmpv6/icmpv6.h
index c90c3722b1..ec11dbb0bb 100644
--- a/net/icmpv6/icmpv6.h
+++ b/net/icmpv6/icmpv6.h
@@ -313,6 +313,7 @@ void icmpv6_rsolicit(FAR struct net_driver_s *dev);
  ****************************************************************************/
 
 void icmpv6_advertise(FAR struct net_driver_s *dev,
+                      const net_ipv6addr_t tgtaddr,
                       const net_ipv6addr_t destipaddr);
 
 /****************************************************************************
diff --git a/net/icmpv6/icmpv6_advertise.c b/net/icmpv6/icmpv6_advertise.c
index 583522ac1b..8b27a8f012 100644
--- a/net/icmpv6/icmpv6_advertise.c
+++ b/net/icmpv6/icmpv6_advertise.c
@@ -65,6 +65,7 @@
  ****************************************************************************/
 
 void icmpv6_advertise(FAR struct net_driver_s *dev,
+                      const net_ipv6addr_t tgtaddr,
                       const net_ipv6addr_t destipaddr)
 {
   FAR struct icmpv6_neighbor_advertise_s *adv;
@@ -77,7 +78,7 @@ void icmpv6_advertise(FAR struct net_driver_s *dev,
   l3size       = SIZEOF_ICMPV6_NEIGHBOR_ADVERTISE_S(lladdrsize);
 
   ipv6_build_header(IPv6BUF, l3size, IP_PROTO_ICMP6,
-                    dev->d_ipv6addr, destipaddr, 255, 0);
+                    tgtaddr, destipaddr, 255, 0);
 
   /* Set up the ICMPv6 Neighbor Advertise response */
 
@@ -92,7 +93,7 @@ void icmpv6_advertise(FAR struct net_driver_s *dev,
 
   /* Copy the target address into the Neighbor Advertisement message */
 
-  net_ipv6addr_copy(adv->tgtaddr, dev->d_ipv6addr);
+  net_ipv6addr_copy(adv->tgtaddr, tgtaddr);
 
   /* Set up the options */
 
diff --git a/net/icmpv6/icmpv6_autoconfig.c b/net/icmpv6/icmpv6_autoconfig.c
index dfc87e6777..ec8adecaad 100644
--- a/net/icmpv6/icmpv6_autoconfig.c
+++ b/net/icmpv6/icmpv6_autoconfig.c
@@ -40,6 +40,7 @@
 #include "netdev/netdev.h"
 #include "inet/inet.h"
 #include "icmpv6/icmpv6.h"
+#include "utils/utils.h"
 
 #ifdef CONFIG_NET_ICMPv6_AUTOCONF
 
@@ -139,9 +140,11 @@ static uint16_t icmpv6_router_eventhandler(FAR struct net_driver_s *dev,
 
       if (state->snd_advertise)
         {
-          /* Send the ICMPv6 Neighbor Advertisement message */
+          /* Send the ICMPv6 Neighbor Advertisement message, we should
+           * already have link-local address by previous logic.
+           */
 
-          icmpv6_advertise(dev, g_ipv6_allnodes);
+          icmpv6_advertise(dev, netdev_ipv6_lladdr(dev), g_ipv6_allnodes);
         }
       else
         {
@@ -311,6 +314,11 @@ int icmpv6_autoconfig(FAR struct net_driver_s *dev)
    *    will be derived from the link layer (MAC) address.
    */
 
+  if (netdev_ipv6_lladdr(dev) != NULL)
+    {
+      goto got_lladdr;
+    }
+
   icmpv6_linkipaddr(dev, lladdr);
 
   ninfo("lladdr=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
@@ -352,8 +360,13 @@ int icmpv6_autoconfig(FAR struct net_driver_s *dev)
    *    on the wider Internet (since link-local addresses are not routed).
    */
 
-  net_ipv6addr_copy(dev->d_ipv6addr, lladdr);
+  ret = netdev_ipv6_add(dev, lladdr, net_ipv6_mask2pref(g_ipv6_llnetmask));
+  if (ret < 0)
+    {
+      return ret;
+    }
 
+got_lladdr:
   /* 4. Router Contact: The node next attempts to contact a local router for
    *    more information on continuing the configuration. This is done either
    *    by listening for Router Advertisement messages sent periodically by
@@ -416,10 +429,6 @@ int icmpv6_autoconfig(FAR struct net_driver_s *dev)
       /* No off-link communications; No router address. */
 
       net_ipv6addr_copy(dev->d_ipv6draddr, g_ipv6_unspecaddr);
-
-      /* Set a netmask for the local link address */
-
-      net_ipv6addr_copy(dev->d_ipv6netmask, g_ipv6_llnetmask);
     }
 
   /* 5. Router Direction: The router provides direction to the node on how
diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c
index f072e70d3d..79339b3e3a 100644
--- a/net/icmpv6/icmpv6_input.c
+++ b/net/icmpv6/icmpv6_input.c
@@ -299,7 +299,7 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen)
         /* Check if we are the target of the solicitation */
 
         sol = ICMPv6SOLICIT;
-        if (net_ipv6addr_cmp(sol->tgtaddr, dev->d_ipv6addr))
+        if (NETDEV_IS_MY_V6ADDR(dev, sol->tgtaddr))
           {
             if (sol->opttype == ICMPv6_OPT_SRCLLADDR)
               {
@@ -312,7 +312,7 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen)
              * solicitation came from.
              */
 
-            icmpv6_advertise(dev, ipv6->srcipaddr);
+            icmpv6_advertise(dev, sol->tgtaddr, ipv6->srcipaddr);
 
             /* All statistics have been updated.  Nothing to do but exit. */
 
@@ -341,7 +341,7 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen)
          */
 
         adv = ICMPv6ADVERTISE;
-        if (net_ipv6addr_cmp(ipv6->destipaddr, dev->d_ipv6addr))
+        if (NETDEV_IS_MY_V6ADDR(dev, ipv6->destipaddr))
           {
             /* This message is required to support the Target link-layer
              * address option.
@@ -544,7 +544,8 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen)
         icmpv6->type = ICMPv6_ECHO_REPLY;
 
         net_ipv6addr_copy(ipv6->destipaddr, ipv6->srcipaddr);
-        net_ipv6addr_copy(ipv6->srcipaddr, dev->d_ipv6addr);
+        net_ipv6addr_copy(ipv6->srcipaddr,
+                          netdev_ipv6_srcaddr(dev, ipv6->srcipaddr));
 
         icmpv6->chksum = 0;
         icmpv6->chksum = ~icmpv6_chksum(dev, iplen);
diff --git a/net/icmpv6/icmpv6_neighbor.c b/net/icmpv6/icmpv6_neighbor.c
index c7274d4fa8..ac2c7b4a94 100644
--- a/net/icmpv6/icmpv6_neighbor.c
+++ b/net/icmpv6/icmpv6_neighbor.c
@@ -218,8 +218,7 @@ int icmpv6_neighbor(FAR struct net_driver_s *dev,
 
   /* Check if the destination address is on the local network. */
 
-  if (net_ipv6addr_maskcmp(ipaddr, dev->d_ipv6addr, dev->d_ipv6netmask) ||
-      net_is_addr_linklocal(ipaddr))
+  if (NETDEV_V6ADDR_ONLINK(dev, ipaddr) || net_is_addr_linklocal(ipaddr))
     {
       /* Yes.. use the input address for the lookup */
 
diff --git a/net/icmpv6/icmpv6_radvertise.c b/net/icmpv6/icmpv6_radvertise.c
index f8926403f2..52cb4864ed 100644
--- a/net/icmpv6/icmpv6_radvertise.c
+++ b/net/icmpv6/icmpv6_radvertise.c
@@ -170,6 +170,9 @@ void icmpv6_radvertise(FAR struct net_driver_s *dev)
   FAR struct icmpv6_srclladdr_s *srcaddr;
   FAR struct icmpv6_mtu_s *mtu;
   FAR struct icmpv6_prefixinfo_s *prefix;
+#ifndef CONFIG_NET_ICMPv6_ROUTER_MANUAL
+  FAR const struct netdev_ifaddr6_s *ifaddr;
+#endif
 #ifdef CONFIG_NET_ICMPv6_ROUTER_RDNSS
   FAR struct icmpv6_rdnss_s *rdnss;
   struct rdnss_add_dns_nameserver_s rndss_context;
@@ -183,12 +186,18 @@ void icmpv6_radvertise(FAR struct net_driver_s *dev)
   lladdrsize   = netdev_lladdrsize(dev);
   l3size       = sizeof(struct icmpv6_router_advertise_s) +
                  SIZEOF_ICMPV6_SRCLLADDR_S(lladdrsize) +
-                 sizeof(struct icmpv6_mtu_s) +
-                 sizeof(struct icmpv6_prefixinfo_s);
+                 sizeof(struct icmpv6_mtu_s);
 
   /* Source IP address must be set to link-local IP */
 
-  icmpv6_linkipaddr(dev, srcv6addr);
+  if (netdev_ipv6_lladdr(dev) == NULL)
+    {
+      icmpv6_linkipaddr(dev, srcv6addr);
+    }
+  else
+    {
+      net_ipv6addr_copy(srcv6addr, netdev_ipv6_lladdr(dev));
+    }
 
   /* Set up the ICMPv6 Router Advertise response */
 
@@ -224,6 +233,16 @@ void icmpv6_radvertise(FAR struct net_driver_s *dev)
   mtu->mtu[0]       = 0;
   mtu->mtu[1]       = HTONS(dev->d_pktsize - dev->d_llhdrlen);
 
+#ifndef CONFIG_NET_ICMPv6_ROUTER_MANUAL
+  /* We only anounce a prefix when we have one. */
+
+  ifaddr = netdev_ipv6_srcifaddr(dev, g_ipv6_unspecaddr);
+  if (net_ipv6addr_cmp(ifaddr->addr, g_ipv6_unspecaddr))
+    {
+      goto skip_prefix;
+    }
+#endif
+
   /* Set up the prefix option */
 
   prefix               = (FAR struct icmpv6_prefixinfo_s *)
@@ -238,6 +257,8 @@ void icmpv6_radvertise(FAR struct net_driver_s *dev)
   prefix->reserved[0]  = 0;
   prefix->reserved[1]  = 0;
 
+  l3size              += sizeof(struct icmpv6_prefixinfo_s);
+
 #ifdef CONFIG_NET_ICMPv6_ROUTER_MANUAL
   /* Copy the configured prefex */
 
@@ -246,14 +267,14 @@ void icmpv6_radvertise(FAR struct net_driver_s *dev)
 #else
   /* Set the prefix and prefix length based on net driver IP and netmask */
 
-  prefix->preflen     = net_ipv6_mask2pref(dev->d_ipv6netmask);
-  ipv6addr_mask(prefix->prefix, dev->d_ipv6addr, dev->d_ipv6netmask);
+  prefix->preflen     = net_ipv6_mask2pref(ifaddr->mask);
+  ipv6addr_mask(prefix->prefix, ifaddr->addr, ifaddr->mask);
+skip_prefix:
 #endif /* CONFIG_NET_ICMPv6_ROUTER_MANUAL */
 
 #ifdef CONFIG_NET_ICMPv6_ROUTER_RDNSS
   rdnss                  = (FAR struct icmpv6_rdnss_s *)
-                           ((FAR uint8_t *)prefix +
-                           sizeof(struct icmpv6_prefixinfo_s));
+                           ((FAR uint8_t *)adv + l3size);
   rndss_context.rdnss    = rdnss;
   rndss_context.nservers = 0;
 
diff --git a/net/icmpv6/icmpv6_reply.c b/net/icmpv6/icmpv6_reply.c
index 9aa0745983..726b499ca4 100644
--- a/net/icmpv6/icmpv6_reply.c
+++ b/net/icmpv6/icmpv6_reply.c
@@ -172,7 +172,8 @@ void icmpv6_reply(FAR struct net_driver_s *dev, int type, int code, int data)
   dev->d_len = ipicmplen + datalen;
 
   ipv6_build_header(IPv6BUF, dev->d_len - IPv6_HDRLEN, IP_PROTO_ICMP6,
-                    dev->d_ipv6addr, ipv6->srcipaddr, 255, 0);
+                    netdev_ipv6_srcaddr(dev, ipv6->srcipaddr),
+                    ipv6->srcipaddr, 255, 0);
 
   /* Initialize the ICMPv6 header */
 
diff --git a/net/icmpv6/icmpv6_rnotify.c b/net/icmpv6/icmpv6_rnotify.c
index fca0317750..9ea4a1683f 100644
--- a/net/icmpv6/icmpv6_rnotify.c
+++ b/net/icmpv6/icmpv6_rnotify.c
@@ -75,6 +75,9 @@ void icmpv6_setaddresses(FAR struct net_driver_s *dev,
                          const net_ipv6addr_t prefix,
                          unsigned int preflen)
 {
+  FAR const uint16_t *curaddr;
+  net_ipv6addr_t addr;
+  net_ipv6addr_t mask;
   unsigned int i;
 
   /* Lock the network.
@@ -100,20 +103,28 @@ void icmpv6_setaddresses(FAR struct net_driver_s *dev,
       preflen = 128;
     }
 
-  net_ipv6_pref2mask(preflen, dev->d_ipv6netmask);
+  net_ipv6_pref2mask(preflen, mask);
 
   ninfo("preflen=%d netmask=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
-        preflen, NTOHS(dev->d_ipv6netmask[0]), NTOHS(dev->d_ipv6netmask[1]),
-        NTOHS(dev->d_ipv6netmask[2]), NTOHS(dev->d_ipv6netmask[3]),
-        NTOHS(dev->d_ipv6netmask[4]), NTOHS(dev->d_ipv6netmask[5]),
-        NTOHS(dev->d_ipv6netmask[6]), NTOHS(dev->d_ipv6netmask[7]));
+        preflen,
+        NTOHS(mask[0]), NTOHS(mask[1]), NTOHS(mask[2]), NTOHS(mask[3]),
+        NTOHS(mask[4]), NTOHS(mask[5]), NTOHS(mask[6]), NTOHS(mask[7]));
 
-  /* Copy prefix to the current IPv6 address, applying the mask */
+  /* Copy prefix to the current link local address, applying the mask.
+   * According to RFC4862, Section 5.5.3, Page 18, global address is formed
+   * by prefix + IID, and the IID is normally the link-local suffix.
+   */
+
+  curaddr = netdev_ipv6_lladdr(dev);
+  if (curaddr == NULL)
+    {
+      icmpv6_linkipaddr(dev, addr);
+      curaddr = addr;
+    }
 
   for (i = 0; i < 8; i++)
     {
-      dev->d_ipv6addr[i] = (dev->d_ipv6addr[i] & ~dev->d_ipv6netmask[i]) |
-                           (prefix[i] & dev->d_ipv6netmask[i]);
+      addr[i] = (curaddr[i] & ~mask[i]) | (prefix[i] & mask[i]);
     }
 
   ninfo("prefix=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
@@ -121,10 +132,10 @@ void icmpv6_setaddresses(FAR struct net_driver_s *dev,
         NTOHS(prefix[3]), NTOHS(prefix[4]), NTOHS(prefix[5]),
         NTOHS(prefix[6]), NTOHS(prefix[7]));
   ninfo("IP address=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
-        NTOHS(dev->d_ipv6addr[0]), NTOHS(dev->d_ipv6addr[1]),
-        NTOHS(dev->d_ipv6addr[2]), NTOHS(dev->d_ipv6addr[3]),
-        NTOHS(dev->d_ipv6addr[4]), NTOHS(dev->d_ipv6addr[5]),
-        NTOHS(dev->d_ipv6addr[6]), NTOHS(dev->d_ipv6addr[7]));
+        NTOHS(addr[0]), NTOHS(addr[1]), NTOHS(addr[2]), NTOHS(addr[3]),
+        NTOHS(addr[4]), NTOHS(addr[5]), NTOHS(addr[6]), NTOHS(addr[7]));
+
+  netdev_ipv6_add(dev, addr, preflen);
 
   /* Finally, copy the router address */
 
diff --git a/net/icmpv6/icmpv6_rsolicit.c b/net/icmpv6/icmpv6_rsolicit.c
index 40f7f024b5..ea26f4ccc0 100644
--- a/net/icmpv6/icmpv6_rsolicit.c
+++ b/net/icmpv6/icmpv6_rsolicit.c
@@ -76,7 +76,8 @@ void icmpv6_rsolicit(FAR struct net_driver_s *dev)
   l3size        = SIZEOF_ICMPV6_ROUTER_SOLICIT_S(lladdrsize);
 
   ipv6_build_header(IPv6BUF, l3size, IP_PROTO_ICMP6,
-                    dev->d_ipv6addr, g_ipv6_allrouters, 255, 0);
+                    netdev_ipv6_srcaddr(dev, g_ipv6_allrouters),
+                    g_ipv6_allrouters, 255, 0);
 
   /* Set up the ICMPv6 Router Solicitation message */
 
diff --git a/net/icmpv6/icmpv6_sendmsg.c b/net/icmpv6/icmpv6_sendmsg.c
index e238e82fcb..7a73cab173 100644
--- a/net/icmpv6/icmpv6_sendmsg.c
+++ b/net/icmpv6/icmpv6_sendmsg.c
@@ -115,7 +115,8 @@ static void sendto_request(FAR struct net_driver_s *dev,
   dev->d_len = IPv6_HDRLEN + pstate->snd_buflen;
 
   ipv6_build_header(IPv6BUF, pstate->snd_buflen, IP_PROTO_ICMP6,
-                    dev->d_ipv6addr, pstate->snd_toaddr.s6_addr16, 255, 0);
+                    netdev_ipv6_srcaddr(dev, pstate->snd_toaddr.s6_addr16),
+                    pstate->snd_toaddr.s6_addr16, 255, 0);
 
   /* Copy the ICMPv6 request and payload into place after the IPv6 header */
 
@@ -405,8 +406,7 @@ ssize_t icmpv6_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
                * destination device.
                */
 
-              if (!net_ipv6addr_maskcmp(state.snd_toaddr.s6_addr16,
-                                        dev->d_ipv6addr, dev->d_ipv6netmask))
+              if (!NETDEV_V6ADDR_ONLINK(dev, state.snd_toaddr.s6_addr16))
                 {
                   /* Destination address was not on the local network served
                    * by this device.  If a timeout occurs, then the most
diff --git a/net/icmpv6/icmpv6_solicit.c b/net/icmpv6/icmpv6_solicit.c
index 8519abd933..3c85d443d8 100644
--- a/net/icmpv6/icmpv6_solicit.c
+++ b/net/icmpv6/icmpv6_solicit.c
@@ -95,7 +95,7 @@ void icmpv6_solicit(FAR struct net_driver_s *dev,
   dstaddr[7] = ipaddr[7];
 
   ipv6_build_header(IPv6BUF, l3size, IP_PROTO_ICMP6,
-                    dev->d_ipv6addr, dstaddr, 255, 0);
+                    netdev_ipv6_srcaddr(dev, ipaddr), dstaddr, 255, 0);
 
   /* Set up the ICMPv6 Neighbor Solicitation message */
 
diff --git a/net/inet/ipv6_getsockname.c b/net/inet/ipv6_getsockname.c
index 771b0046e1..7ea4667e91 100644
--- a/net/inet/ipv6_getsockname.c
+++ b/net/inet/ipv6_getsockname.c
@@ -150,7 +150,8 @@ int ipv6_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr,
   /* Set the address family and the IP address */
 
   outaddr->sin6_family = AF_INET6;
-  memcpy(outaddr->sin6_addr.in6_u.u6_addr8, dev->d_ipv6addr, 16);
+  net_ipv6addr_copy(outaddr->sin6_addr.in6_u.u6_addr8,
+                    netdev_ipv6_srcaddr(dev, *ripaddr));
   *addrlen = sizeof(struct sockaddr_in6);
 
   net_unlock();
diff --git a/net/mld/mld_query.c b/net/mld/mld_query.c
index fd144a4032..4014ab875a 100644
--- a/net/mld/mld_query.c
+++ b/net/mld/mld_query.c
@@ -139,11 +139,21 @@ static clock_t mld_mrc2mrd(uint16_t mrc)
 static bool mld_cmpaddr(FAR struct net_driver_s *dev,
                         const net_ipv6addr_t srcaddr)
 {
+  FAR const uint16_t *lladdr = netdev_ipv6_lladdr(dev);
   int i;
 
+  if (lladdr == NULL)
+    {
+      /* If no link-local address presents, regard address as ::, then nobody
+       * can be less than it.
+       */
+
+      return false;
+    }
+
   for (i = 0; i < 8; i++)
     {
-      if (srcaddr[i] < dev->d_ipv6addr[i])
+      if (srcaddr[i] < lladdr[i])
         {
           return true;
         }
@@ -439,7 +449,7 @@ int mld_query(FAR struct net_driver_s *dev,
 
   /* Not sent to all systems.  Check for Unicast General Query */
 
-  else if (net_ipv6addr_cmp(ipv6->destipaddr, dev->d_ipv6addr))
+  else if (NETDEV_IS_MY_V6ADDR(dev, ipv6->destipaddr))
     {
       mldinfo("Unicast query\n");
       MLD_STATINCR(g_netstats.mld.ucast_query_received);
diff --git a/net/mld/mld_send.c b/net/mld/mld_send.c
index 541156de60..a291b85d1d 100644
--- a/net/mld/mld_send.c
+++ b/net/mld/mld_send.c
@@ -89,6 +89,7 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
 {
   FAR struct ipv6_router_alert_s *ra;
   FAR const uint16_t *destipaddr;
+  FAR const uint16_t *srcipaddr;
   unsigned int mldsize;
 
   /* Only a general query message can have a NULL group */
@@ -210,8 +211,18 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
           NTOHS(destipaddr[3]), NTOHS(destipaddr[4]), NTOHS(destipaddr[5]),
           NTOHS(destipaddr[6]), NTOHS(destipaddr[7]));
 
+  srcipaddr = netdev_ipv6_lladdr(dev);
+  if (srcipaddr == NULL)
+    {
+      /* Unspecified address is used when link-local address is not
+       * available, as described in RFC 3590, Section 4, Page 2.
+       */
+
+      srcipaddr = g_ipv6_unspecaddr;
+    }
+
   ipv6_build_header(IPv6BUF, dev->d_sndlen, NEXT_HOPBYBOT_EH,
-                    dev->d_ipv6addr, destipaddr, MLD_TTL, 0);
+                    srcipaddr, destipaddr, MLD_TTL, 0);
 
   /* Add the router alert IP header option.
    *
diff --git a/net/neighbor/neighbor_ethernet_out.c b/net/neighbor/neighbor_ethernet_out.c
index ba6e462cb9..00b641e63a 100644
--- a/net/neighbor/neighbor_ethernet_out.c
+++ b/net/neighbor/neighbor_ethernet_out.c
@@ -126,8 +126,7 @@ void neighbor_ethernet_out(FAR struct net_driver_s *dev)
 
       /* Check if the destination address is on the local network. */
 
-      if (!net_ipv6addr_maskcmp(ip->destipaddr, dev->d_ipv6addr,
-                                dev->d_ipv6netmask))
+      if (!NETDEV_V6ADDR_ONLINK(dev, ip->destipaddr))
         {
           /* Destination address is not on the local network */
 
diff --git a/net/neighbor/neighbor_lookup.c b/net/neighbor/neighbor_lookup.c
index f6f3ce9fc0..e1e194f546 100644
--- a/net/neighbor/neighbor_lookup.c
+++ b/net/neighbor/neighbor_lookup.c
@@ -66,7 +66,7 @@ static int neighbor_match(FAR struct net_driver_s *dev, FAR void *arg)
    * lookup.
    */
 
-  if (!net_ipv6addr_cmp(dev->d_ipv6addr, info->ni_ipaddr))
+  if (!NETDEV_IS_MY_V6ADDR(dev, info->ni_ipaddr))
     {
       return 0;
     }
diff --git a/net/netdev/netdev_findbyaddr.c b/net/netdev/netdev_findbyaddr.c
index c89488bf09..35cb6c7054 100644
--- a/net/netdev/netdev_findbyaddr.c
+++ b/net/netdev/netdev_findbyaddr.c
@@ -126,13 +126,11 @@ FAR struct net_driver_s *netdev_findby_lipv6addr(
     {
       /* Is the interface in the "up" state? */
 
-      if ((dev->d_flags & IFF_UP) != 0 &&
-          !net_ipv6addr_cmp(dev->d_ipv6addr, g_ipv6_unspecaddr))
+      if ((dev->d_flags & IFF_UP) != 0 && NETDEV_HAS_V6ADDR(dev))
         {
           /* Yes.. check for an address match (under the netmask) */
 
-          if (net_ipv6addr_maskcmp(dev->d_ipv6addr, lipaddr,
-                                   dev->d_ipv6netmask))
+          if (NETDEV_V6ADDR_ONLINK(dev, lipaddr))
             {
               /* Its a match */
 
diff --git a/net/netdev/netdev_ifconf.c b/net/netdev/netdev_ifconf.c
index 323cc0811f..1e30f3c208 100644
--- a/net/netdev/netdev_ifconf.c
+++ b/net/netdev/netdev_ifconf.c
@@ -169,8 +169,7 @@ static int ifconf_ipv6_callback(FAR struct net_driver_s *dev, FAR void *arg)
    * state.
    */
 
-  if (!net_ipv6addr_cmp(dev->d_ipv6addr, g_ipv6_unspecaddr) &&
-      (dev->d_flags & IFF_UP) != 0)
+  if (NETDEV_HAS_V6ADDR(dev) && IFF_IS_UP(dev->d_flags))
     {
       /* Check if we would exceed the buffer space provided by the caller.
        * NOTE: A common usage model is:
diff --git a/net/netlink/netlink_route.c b/net/netlink/netlink_route.c
index 4409016da9..82192203ef 100644
--- a/net/netlink/netlink_route.c
+++ b/net/netlink/netlink_route.c
@@ -989,14 +989,16 @@ static int netlink_new_ipv6addr(NETLINK_HANDLE handle,
       return -ENODEV;
     }
 
-  memcpy(dev->d_ipv6addr, nla_data(tb[IFA_LOCAL]), 16);
-  net_ipv6_pref2mask(ifm->ifa_prefixlen, dev->d_ipv6netmask);
+  ret = netdev_ipv6_add(dev, nla_data(tb[IFA_LOCAL]), ifm->ifa_prefixlen);
+  if (ret == OK)
+    {
+      netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6,
+                                nla_data(tb[IFA_LOCAL]), ifm->ifa_prefixlen);
+    }
 
-  netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6, dev->d_ipv6addr,
-                               ifm->ifa_prefixlen);
   net_unlock();
 
-  return OK;
+  return ret;
 }
 #endif
 
@@ -1075,6 +1077,11 @@ static int netlink_del_ipv6addr(NETLINK_HANDLE handle,
       return ret;
     }
 
+  if (!tb[IFA_LOCAL] || ifm->ifa_prefixlen > 128)
+    {
+      return -EINVAL;
+    }
+
   net_lock();
   dev = netdev_findbyindex(ifm->ifa_index);
 
@@ -1084,20 +1091,22 @@ static int netlink_del_ipv6addr(NETLINK_HANDLE handle,
       return -ENODEV;
     }
 
-  if (tb[IFA_LOCAL] && !net_ipv6addr_cmp(dev->d_ipv6addr,
-                                         nla_data(tb[IFA_LOCAL])))
+  if (!NETDEV_IS_MY_V6ADDR(dev, nla_data(tb[IFA_LOCAL])))
     {
       net_unlock();
       return -EADDRNOTAVAIL;
     }
 
-  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));
+  ret = netdev_ipv6_del(dev, nla_data(tb[IFA_LOCAL]), ifm->ifa_prefixlen);
+  if (ret == OK)
+    {
+      netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6,
+                                nla_data(tb[IFA_LOCAL]), ifm->ifa_prefixlen);
+    }
 
   net_unlock();
 
-  return OK;
+  return ret;
 }
 #endif
 
@@ -1110,42 +1119,53 @@ static int netlink_del_ipv6addr(NETLINK_HANDLE handle,
  ****************************************************************************/
 
 #ifndef CONFIG_NETLINK_DISABLE_GETADDR
+#ifdef CONFIG_NET_IPv6
+static int netlink_ipv6_addr_callback(FAR struct net_driver_s *dev,
+                                      FAR struct netdev_ifaddr6_s *addr,
+                                      FAR void *arg)
+{
+  FAR struct nlroute_info_s *info = arg;
+  FAR struct netlink_response_s *resp;
+
+  resp = netlink_get_ifaddr(dev, AF_INET6, RTM_NEWADDR, addr->addr,
+                            net_ipv6_mask2pref(addr->mask), info->req);
+  if (resp == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  netlink_add_response(info->handle, resp);
+  return OK;
+}
+#endif
+
 static int netlink_addr_callback(FAR struct net_driver_s *dev, FAR void *arg)
 {
   FAR struct nlroute_info_s *info = arg;
   FAR struct netlink_response_s *resp;
-  FAR void *addr = NULL;
-  uint8_t preflen;
 
 #ifdef CONFIG_NET_IPv4
   if (info->req->gen.rtgen_family == AF_INET)
     {
-      addr = &dev->d_ipaddr;
-      preflen = net_ipv4_mask2pref(dev->d_netmask);
+      resp = netlink_get_ifaddr(dev, AF_INET, RTM_NEWADDR, &dev->d_ipaddr,
+                                net_ipv4_mask2pref(dev->d_netmask),
+                                info->req);
+      if (resp == NULL)
+        {
+          return -ENOMEM;
+        }
+
+      netlink_add_response(info->handle, resp);
     }
 #endif
 
 #ifdef CONFIG_NET_IPv6
   if (info->req->gen.rtgen_family == AF_INET6)
     {
-      addr = &dev->d_ipv6addr;
-      preflen = net_ipv6_mask2pref(dev->d_ipv6netmask);
+      return netdev_ipv6_foreach(dev, netlink_ipv6_addr_callback, arg);
     }
 #endif
 
-  if (addr == NULL)
-    {
-      return OK;
-    }
-
-  resp = netlink_get_ifaddr(dev, info->req->gen.rtgen_family, RTM_NEWADDR,
-                            addr, preflen, info->req);
-  if (resp == NULL)
-    {
-      return -ENOMEM;
-    }
-
-  netlink_add_response(info->handle, resp);
   return OK;
 }
 
diff --git a/net/route/netdev_router.c b/net/route/netdev_router.c
index cffd847a08..348e702281 100644
--- a/net/route/netdev_router.c
+++ b/net/route/netdev_router.c
@@ -167,8 +167,7 @@ static int net_ipv6_devmatch(FAR struct net_route_ipv6_s *route,
    */
 
   if (net_ipv6addr_maskcmp(route->target, match->target, route->netmask) &&
-      net_ipv6addr_maskcmp(route->router, dev->d_ipv6addr,
-                           dev->d_ipv6netmask))
+      NETDEV_V6ADDR_ONLINK(dev, route->router))
     {
 #ifdef CONFIG_ROUTE_IPv6_CACHEROUTE
       /* They match.. Copy the entire routing table entry */
diff --git a/net/sixlowpan/sixlowpan_tcpsend.c b/net/sixlowpan/sixlowpan_tcpsend.c
index 3fd90d4a5f..e8f534c700 100644
--- a/net/sixlowpan/sixlowpan_tcpsend.c
+++ b/net/sixlowpan/sixlowpan_tcpsend.c
@@ -213,7 +213,8 @@ static int sixlowpan_tcp_header(FAR struct tcp_conn_s *conn,
     }
   else
     {
-      net_ipv6addr_hdrcopy(ipv6tcp->ipv6.srcipaddr, dev->d_ipv6addr);
+      net_ipv6addr_hdrcopy(ipv6tcp->ipv6.srcipaddr,
+                           netdev_ipv6_srcaddr(dev, conn->u.ipv6.raddr));
     }
 
   ninfo("IPv6 length: %d\n",
diff --git a/net/sixlowpan/sixlowpan_udpsend.c b/net/sixlowpan/sixlowpan_udpsend.c
index 203e4561df..c28174c3ae 100644
--- a/net/sixlowpan/sixlowpan_udpsend.c
+++ b/net/sixlowpan/sixlowpan_udpsend.c
@@ -247,7 +247,8 @@ ssize_t psock_6lowpan_udp_sendto(FAR struct socket *psock,
     }
   else
     {
-      net_ipv6addr_hdrcopy(ipv6udp.ipv6.srcipaddr, dev->d_ipv6addr);
+      net_ipv6addr_hdrcopy(ipv6udp.ipv6.srcipaddr,
+                          netdev_ipv6_srcaddr(dev, ipv6udp.ipv6.destipaddr));
     }
 
   ninfo("IPv6 length: %d\n",
diff --git a/net/sixlowpan/sixlowpan_utils.c b/net/sixlowpan/sixlowpan_utils.c
index 0b49c2682b..a31efb8aa5 100644
--- a/net/sixlowpan/sixlowpan_utils.c
+++ b/net/sixlowpan/sixlowpan_utils.c
@@ -319,8 +319,7 @@ int sixlowpan_destaddrfromip(FAR struct radio_driver_s *radio,
    */
 
   if (!sixlowpan_islinklocal(ipaddr) &&
-      !net_ipv6addr_maskcmp(radio->r_dev.d_ipv6addr, ipaddr,
-                            radio->r_dev.d_ipv6netmask))
+      !NETDEV_V6ADDR_ONLINK(&radio->r_dev, ipaddr))
     {
       return -EADDRNOTAVAIL;
     }
diff --git a/net/tcp/tcp_conn.c b/net/tcp/tcp_conn.c
index 566a7b16da..8b721a2edf 100644
--- a/net/tcp/tcp_conn.c
+++ b/net/tcp/tcp_conn.c
@@ -440,8 +440,7 @@ static inline int tcp_ipv6_bind(FAR struct tcp_conn_s *conn,
 
       for (dev = g_netdevices; dev; dev = dev->flink)
         {
-          if (net_ipv6addr_cmp(addr->sin6_addr.in6_u.u6_addr16,
-                              dev->d_ipv6addr))
+          if (NETDEV_IS_MY_V6ADDR(dev, addr->sin6_addr.in6_u.u6_addr16))
             {
               ret = 0;
               break;
diff --git a/net/tcp/tcp_connect.c b/net/tcp/tcp_connect.c
index 1a78134c8c..1cbe5910f2 100644
--- a/net/tcp/tcp_connect.c
+++ b/net/tcp/tcp_connect.c
@@ -322,7 +322,8 @@ int psock_tcp_connect(FAR struct socket *psock,
 #endif
           if (net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_unspecaddr))
             {
-              net_ipv6addr_copy(conn->u.ipv6.laddr, conn->dev->d_ipv6addr);
+              net_ipv6addr_copy(conn->u.ipv6.laddr,
+                        netdev_ipv6_srcaddr(conn->dev, conn->u.ipv6.raddr));
             }
 #endif /* CONFIG_NET_IPv6 */
 
diff --git a/net/tcp/tcp_send.c b/net/tcp/tcp_send.c
index 7651839a46..025994e731 100644
--- a/net/tcp/tcp_send.c
+++ b/net/tcp/tcp_send.c
@@ -181,7 +181,9 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev,
     {
       ninfo("do IPv6 IP header build!\n");
       ipv6_build_header(IPv6BUF, dev->d_len - IPv6_HDRLEN,
-                        IP_PROTO_TCP, dev->d_ipv6addr, conn->u.ipv6.raddr,
+                        IP_PROTO_TCP,
+                        netdev_ipv6_srcaddr(dev, conn->u.ipv6.raddr),
+                        conn->u.ipv6.raddr,
                         conn->sconn.ttl, conn->sconn.s_tclass);
 
       /* Calculate TCP checksum. */
@@ -476,7 +478,9 @@ void tcp_reset(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn)
       FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
 
       ipv6_build_header(ipv6, dev->d_len - IPv6_HDRLEN,
-                        IP_PROTO_TCP, dev->d_ipv6addr, ipv6->srcipaddr,
+                        IP_PROTO_TCP,
+                        netdev_ipv6_srcaddr(dev, ipv6->srcipaddr),
+                        ipv6->srcipaddr,
                         conn ? conn->sconn.ttl : IP_TTL_DEFAULT,
                         conn ? conn->sconn.s_tos : 0);
       tcp->tcpchksum = 0;
diff --git a/net/udp/udp_conn.c b/net/udp/udp_conn.c
index 2bb4f64b22..49fa9c3f54 100644
--- a/net/udp/udp_conn.c
+++ b/net/udp/udp_conn.c
@@ -882,8 +882,8 @@ int udp_bind(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr)
 
           for (dev = g_netdevices; dev; dev = dev->flink)
             {
-              if (net_ipv6addr_cmp(inaddr->sin6_addr.in6_u.u6_addr16,
-                                  dev->d_ipv6addr))
+              if (NETDEV_IS_MY_V6ADDR(dev,
+                                      inaddr->sin6_addr.in6_u.u6_addr16))
                 {
                   ret = 0;
                   break;
diff --git a/net/udp/udp_send.c b/net/udp/udp_send.c
index c3d5228f2e..b9c6d4d7e8 100644
--- a/net/udp/udp_send.c
+++ b/net/udp/udp_send.c
@@ -149,7 +149,8 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn)
           dev->d_len        = dev->d_sndlen + UDP_HDRLEN;
 
           ipv6_build_header(IPv6BUF, dev->d_len, IP_PROTO_UDP,
-                            dev->d_ipv6addr, conn->u.ipv6.raddr,
+                            netdev_ipv6_srcaddr(dev, conn->u.ipv6.raddr),
+                            conn->u.ipv6.raddr,
                             conn->sconn.ttl, conn->sconn.s_tclass);
 
           /* The total length to send is the size of the application data


(nuttx) 02/07: net/netdev: Support multiple IPv6 addresses per device

Posted by xi...@apache.org.
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 96233e0c4262ae4a46c6a79c3a050301e2ddb59c
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Fri Aug 25 15:55:53 2023 +0800

    net/netdev: Support multiple IPv6 addresses per device
    
    Compatible with previous usage, because may network drivers are using old member name to print logs, and there's no significant need to change them now.
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 include/nuttx/net/netdev.h | 164 +++++++++++++++-
 net/netdev/CMakeLists.txt  |   4 +
 net/netdev/Kconfig         |  18 ++
 net/netdev/Make.defs       |   4 +
 net/netdev/netdev_ipv6.c   | 466 +++++++++++++++++++++++++++++++++++++++++++++
 net/utils/net_mask2pref.c  |  38 ++++
 net/utils/utils.h          |  19 ++
 7 files changed, 710 insertions(+), 3 deletions(-)

diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h
index 9e279a0fe0..cbfa19fb44 100644
--- a/include/nuttx/net/netdev.h
+++ b/include/nuttx/net/netdev.h
@@ -168,6 +168,12 @@
 #define IPv4BUF ((FAR struct ipv4_hdr_s *)IPBUF(0))
 #define IPv6BUF ((FAR struct ipv6_hdr_s *)IPBUF(0))
 
+#ifdef CONFIG_NET_IPv6
+#  ifndef CONFIG_NETDEV_MAX_IPv6_ADDR
+#    define CONFIG_NETDEV_MAX_IPv6_ADDR 1
+#  endif
+#endif
+
 /****************************************************************************
  * Public Types
  ****************************************************************************/
@@ -230,6 +236,14 @@ struct netdev_varaddr_s
 };
 #endif
 
+#ifdef CONFIG_NET_IPv6
+struct netdev_ifaddr6_s
+{
+  net_ipv6addr_t addr; /* Host IPv6 address */
+  net_ipv6addr_t mask; /* Network IPv6 subnet mask */
+};
+#endif
+
 /* This structure collects information that is specific to a specific network
  * interface driver.  If the hardware platform supports only a single
  * instance of this structure.
@@ -302,10 +316,30 @@ struct net_driver_s
 #endif
 
 #ifdef CONFIG_NET_IPv6
-  net_ipv6addr_t d_ipv6addr;    /* Host IPv6 address assigned to the network interface */
+  /* Host IPv6 addresses assigned to the network interface.
+   * For historical reason, we keep the old name d_ipv6addr and d_ipv6netmask
+   * for backward compatibility. Please use d_ipv6 for new drivers.
+   */
+
+#  if defined(CONFIG_HAVE_ANONYMOUS_STRUCT) && \
+      defined(CONFIG_HAVE_ANONYMOUS_UNION)
+  union /* Try to limit the scope of backward compatibility alias. */
+  {
+    struct netdev_ifaddr6_s d_ipv6[CONFIG_NETDEV_MAX_IPv6_ADDR];
+    struct
+    {
+      net_ipv6addr_t d_ipv6addr;    /* Compatible with previous usage */
+      net_ipv6addr_t d_ipv6netmask; /* Compatible with previous usage */
+    };
+  };
+#  else /* Without anonymous union/struct support, we can only use macros. */
+  struct netdev_ifaddr6_s d_ipv6[CONFIG_NETDEV_MAX_IPv6_ADDR];
+#    define d_ipv6addr    d_ipv6[0].addr /* Compatible with previous usage */
+#    define d_ipv6netmask d_ipv6[0].mask /* Compatible with previous usage */
+#  endif /* CONFIG_HAVE_ANONYMOUS_STRUCT && CONFIG_HAVE_ANONYMOUS_UNION */
+
   net_ipv6addr_t d_ipv6draddr;  /* Default router IPv6 address */
-  net_ipv6addr_t d_ipv6netmask; /* Network IPv6 subnet mask */
-#endif
+#endif /* CONFIG_NET_IPv6 */
   /* This is a new design that uses d_iob as packets input and output
    * buffer which used by some NICs such as celluler net driver. Case for
    * data input, note that d_iob maybe a linked chain only when using
@@ -445,6 +479,9 @@ struct net_driver_s
 };
 
 typedef CODE int (*devif_poll_callback_t)(FAR struct net_driver_s *dev);
+typedef CODE int (*devif_ipv6_callback_t)(FAR struct net_driver_s *dev,
+                                          FAR struct netdev_ifaddr6_s *addr,
+                                          FAR void *arg);
 
 /****************************************************************************
  * Public Function Prototypes
@@ -979,4 +1016,125 @@ void netdev_iob_clear(FAR struct net_driver_s *dev);
 
 void netdev_iob_release(FAR struct net_driver_s *dev);
 
+/****************************************************************************
+ * Name: netdev_ipv6_add/del
+ *
+ * Description:
+ *   Add or delete an IPv6 address on the network device
+ *
+ * Returned Value:
+ *   OK             - Success
+ *   -EINVAL        - Invalid prefix length
+ *   -EADDRNOTAVAIL - Delete on non-existent address
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv6
+int netdev_ipv6_add(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
+                    unsigned int preflen);
+int netdev_ipv6_del(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
+                    unsigned int preflen);
+#endif
+
+/****************************************************************************
+ * Name: netdev_ipv6_srcaddr/srcifaddr
+ *
+ * Description:
+ *   Get the source IPv6 address (RFC6724).
+ *
+ * Returned Value:
+ *   A pointer to the IPv6 address is returned on success.  It will never be
+ *   NULL, but can be an address containing g_ipv6_unspecaddr.
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv6
+FAR const uint16_t *netdev_ipv6_srcaddr(FAR struct net_driver_s *dev,
+                                        const net_ipv6addr_t dst);
+FAR const struct netdev_ifaddr6_s *
+netdev_ipv6_srcifaddr(FAR struct net_driver_s *dev,
+                      const net_ipv6addr_t dst);
+#endif
+
+/****************************************************************************
+ * Name: netdev_ipv6_lladdr
+ *
+ * Description:
+ *   Get the link-local address of the network device.
+ *
+ * Returned Value:
+ *   A pointer to the link-local address is returned on success.
+ *   NULL is returned if the address is not found on the device.
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv6
+FAR const uint16_t *netdev_ipv6_lladdr(FAR struct net_driver_s *dev);
+#endif
+
+/****************************************************************************
+ * Name: netdev_ipv6_lookup
+ *
+ * Description:
+ *   Look up an IPv6 address in the network device's IPv6 addresses
+ *
+ * Input Parameters:
+ *   dev     - The network device to use in the lookup
+ *   addr    - The IPv6 address to be looked up
+ *   maskcmp - If true, then the IPv6 address is compared to the network
+ *             device's IPv6 addresses with mask compare.
+ *             If false, then the IPv6 address should be exactly the same as
+ *             the network device's IPv6 address.
+ *
+ * Returned Value:
+ *   A pointer to the matching IPv6 address entry is returned on success.
+ *   NULL is returned if the IPv6 address is not found in the device.
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv6
+FAR struct netdev_ifaddr6_s *
+netdev_ipv6_lookup(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
+                   bool maskcmp);
+#endif
+
+/****************************************************************************
+ * Name: netdev_ipv6_foreach
+ *
+ * Description:
+ *   Enumerate each IPv6 address on a network device.  This function will
+ *   terminate when either (1) all addresses have been enumerated or (2) when
+ *   a callback returns any non-zero value.
+ *
+ * Input Parameters:
+ *   dev      - The network device
+ *   callback - Will be called for each IPv6 address
+ *   arg      - Opaque user argument passed to callback()
+ *
+ * Returned Value:
+ *  Zero:     Enumeration completed
+ *  Non-zero: Enumeration terminated early by callback
+ *
+ * Assumptions:
+ *  The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv6
+int netdev_ipv6_foreach(FAR struct net_driver_s *dev,
+                        devif_ipv6_callback_t callback, FAR void *arg);
+#endif
+
 #endif /* __INCLUDE_NUTTX_NET_NETDEV_H */
diff --git a/net/netdev/CMakeLists.txt b/net/netdev/CMakeLists.txt
index a20df4d684..53c9254bac 100644
--- a/net/netdev/CMakeLists.txt
+++ b/net/netdev/CMakeLists.txt
@@ -42,4 +42,8 @@ if(CONFIG_NETDOWN_NOTIFIER)
   list(APPEND SRCS netdown_notifier.c)
 endif()
 
+if(CONFIG_NET_IPv6)
+  list(APPEND SRCS netdev_ipv6.c)
+endif()
+
 target_sources(net PRIVATE ${SRCS})
diff --git a/net/netdev/Kconfig b/net/netdev/Kconfig
index 6aa4699b51..27ba60c5e8 100644
--- a/net/netdev/Kconfig
+++ b/net/netdev/Kconfig
@@ -61,6 +61,24 @@ config NETDEV_IFINDEX
 		When enabled, these option also enables the user interfaces:
 		if_nametoindex() and if_indextoname().
 
+config NETDEV_MULTIPLE_IPv6
+	bool "Enable multiple IPv6 addresses support"
+	default n
+	select NETDEV_IFINDEX
+	depends on NET_IPv6
+	---help---
+		Enable support for multiple IPv6 addresses per network device.
+
+config NETDEV_MAX_IPv6_ADDR
+	int "Maximum number of IPv6 addresses per network device"
+	range 2 255
+	default 2
+	depends on NETDEV_MULTIPLE_IPv6
+	---help---
+		Maximum number of IPv6 addresses that can be assigned to a single
+		network device. Normally a link-local address and a global address
+		are needed.
+
 config NETDOWN_NOTIFIER
 	bool "Support network down notifications"
 	default n
diff --git a/net/netdev/Make.defs b/net/netdev/Make.defs
index ace44637da..a273c5d6a9 100644
--- a/net/netdev/Make.defs
+++ b/net/netdev/Make.defs
@@ -34,6 +34,10 @@ ifeq ($(CONFIG_NETDOWN_NOTIFIER),y)
 SOCK_CSRCS += netdown_notifier.c
 endif
 
+ifeq ($(CONFIG_NET_IPv6),y)
+NETDEV_CSRCS += netdev_ipv6.c
+endif
+
 # Include netdev build support
 
 DEPPATH += --dep-path netdev
diff --git a/net/netdev/netdev_ipv6.c b/net/netdev/netdev_ipv6.c
new file mode 100644
index 0000000000..0692d32305
--- /dev/null
+++ b/net/netdev/netdev_ipv6.c
@@ -0,0 +1,466 @@
+/****************************************************************************
+ * net/netdev/netdev_ipv6.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <debug.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <nuttx/net/netdev.h>
+
+#include "inet/inet.h"
+#include "utils/utils.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Defined in Section 2.7 of RFC4291 */
+
+#define IPv6_SCOPE_INTERFACE_LOCAL    0x1
+#define IPv6_SCOPE_LINK_LOCAL         0x2
+#define IPv6_SCOPE_ADMIN_LOCAL        0x4
+#define IPv6_SCOPE_SITE_LOCAL         0x5
+#define IPv6_SCOPE_ORGANIZATION_LOCAL 0x8
+#define IPv6_SCOPE_GLOBAL             0xe
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netdev_ipv6_get_scope
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
+static uint8_t netdev_ipv6_get_scope(const net_ipv6addr_t addr)
+{
+  if (net_is_addr_mcast(addr))
+    {
+      /* As defined in Section 2.7 of RFC4291:
+       * |   8    |  4 |  4 |                  112 bits                   |
+       * +------ -+----+----+---------------------------------------------+
+       * |11111111|flgs|scop|                  group ID                   |
+       * +--------+----+----+---------------------------------------------+
+       */
+
+      return NTOHS(addr[0]) & 0x000f;
+    }
+
+  if (net_is_addr_linklocal(addr))
+    {
+      return IPv6_SCOPE_LINK_LOCAL;
+    }
+
+  if (net_is_addr_sitelocal(addr))
+    {
+      return IPv6_SCOPE_SITE_LOCAL;
+    }
+
+  return IPv6_SCOPE_GLOBAL;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netdev_ipv6_add/del
+ *
+ * Description:
+ *   Add or delete an IPv6 address on the network device
+ *
+ * Returned Value:
+ *   OK             - Success
+ *   -EINVAL        - Invalid prefix length
+ *   -EADDRNOTAVAIL - Delete on non-existent address
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+int netdev_ipv6_add(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
+                    unsigned int preflen)
+{
+  FAR struct netdev_ifaddr6_s *ifaddr = &dev->d_ipv6[0];
+#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
+  uint8_t scope;
+  int i;
+#endif
+
+  /* Verify the prefix length */
+
+  if (preflen > 128)
+    {
+      return -EINVAL;
+    }
+
+#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
+  /* Avoid duplicate address. */
+
+  ifaddr = netdev_ipv6_lookup(dev, addr, false);
+  if (ifaddr != NULL)
+    {
+      /* Check if net mask is the same. */
+
+      if (net_ipv6_mask2pref(ifaddr->mask) == preflen)
+        {
+          nwarn("WARNING: Trying to add same IPv6 address on net device! "
+                "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%d\n",
+                NTOHS(addr[0]), NTOHS(addr[1]), NTOHS(addr[2]),
+                NTOHS(addr[3]), NTOHS(addr[4]), NTOHS(addr[5]),
+                NTOHS(addr[6]), NTOHS(addr[7]), preflen);
+          return -EEXIST;
+        }
+
+      /* Not exactly the same, update the net mask.
+       * REVISIT: Currently try to keep logic same as previous, which always
+       *          allows to override the address. But not sure if it's good.
+       */
+
+      net_ipv6_pref2mask(preflen, ifaddr->mask);
+      return OK;
+    }
+
+  /* Now we start to find a proper slot to put this address. */
+
+  ifaddr = &dev->d_ipv6[0]; /* Set default to a valid address. */
+  scope = netdev_ipv6_get_scope(addr);
+
+  for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
+    {
+      FAR struct netdev_ifaddr6_s *current = &dev->d_ipv6[i];
+
+      /* Select empty address. */
+
+      if (net_ipv6addr_cmp(current->addr, g_ipv6_unspecaddr))
+        {
+          ifaddr = current;
+          break;
+        }
+
+      /* Select address with same scope. */
+
+      if (netdev_ipv6_get_scope(current->addr) == scope)
+        {
+          ifaddr = current;
+          continue; /* Good slot, but maybe we have empty slot later. */
+        }
+    }
+#endif /* CONFIG_NETDEV_MULTIPLE_IPv6 */
+
+  net_ipv6addr_copy(ifaddr->addr, addr);
+  net_ipv6_pref2mask(preflen, ifaddr->mask);
+
+  return OK;
+}
+
+int netdev_ipv6_del(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
+                    unsigned int preflen)
+{
+  FAR struct netdev_ifaddr6_s *ifaddr;
+
+  /* Verify the prefix length */
+
+  if (preflen > 128)
+    {
+      return -EINVAL;
+    }
+
+  /* Find the matching address entry */
+
+  ifaddr = netdev_ipv6_lookup(dev, addr, false);
+  if (ifaddr == NULL)
+    {
+      /* The address does not exist on the device */
+
+      return -EADDRNOTAVAIL;
+    }
+
+  if (net_ipv6_mask2pref(ifaddr->mask) != preflen)
+    {
+      /* Prefix length does not match, regard as not found (same as Linux) */
+
+      return -EADDRNOTAVAIL;
+    }
+
+  /* Delete the address */
+
+  net_ipv6addr_copy(ifaddr->addr, g_ipv6_unspecaddr);
+  net_ipv6addr_copy(ifaddr->mask, g_ipv6_unspecaddr);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: netdev_ipv6_srcaddr/srcifaddr
+ *
+ * Description:
+ *   Get the source IPv6 address (RFC6724).
+ *
+ * Returned Value:
+ *   A pointer to the IPv6 address is returned on success.  It will never be
+ *   NULL, but can be an address containing g_ipv6_unspecaddr.
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+FAR const uint16_t *netdev_ipv6_srcaddr(FAR struct net_driver_s *dev,
+                                        const net_ipv6addr_t dst)
+{
+  return netdev_ipv6_srcifaddr(dev, dst)->addr;
+}
+
+FAR const struct netdev_ifaddr6_s *
+netdev_ipv6_srcifaddr(FAR struct net_driver_s *dev, const net_ipv6addr_t dst)
+{
+  FAR struct netdev_ifaddr6_s *best = &dev->d_ipv6[0]; /* Don't be NULL */
+#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
+  uint8_t scope_dst  = netdev_ipv6_get_scope(dst);
+  uint8_t scope_best = 0; /* All scope is larget than 0 */
+  uint8_t pref_best  = 0;
+  int i;
+
+  for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
+    {
+      FAR struct netdev_ifaddr6_s *current = &dev->d_ipv6[i];
+      uint8_t scope_cur;
+      uint8_t pref_cur;
+
+      /* Skip empty address */
+
+      if (net_ipv6addr_cmp(current->addr, g_ipv6_unspecaddr))
+        {
+          continue;
+        }
+
+      /* Rule 1: Prefer same address */
+
+      if (net_ipv6addr_cmp(dst, current->addr))
+        {
+          best = current;
+          break;
+        }
+
+      scope_cur = netdev_ipv6_get_scope(current->addr);
+      pref_cur  = net_ipv6_common_pref(current->addr, dst);
+
+      /* Rule 2: Prefer appropriate scope */
+
+      if (scope_cur != scope_best)
+        {
+          /* According to RFC6724:
+           * If Scope(SA) < Scope(SB):
+           *  If Scope(SA) < Scope(D), then prefer SB and otherwise prefer SA
+           * If Scope(SB) < Scope(SA):
+           *  If Scope(SB) < Scope(D), then prefer SA and otherwise prefer SB
+           * Let Scope(SA)->Scope(cur), Scope(SB)->Scope(best) in our case.
+           */
+
+          if ((scope_cur < scope_best && scope_cur >= scope_dst) ||
+              (scope_best < scope_cur && scope_best < scope_dst))
+            {
+              best       = current;
+              scope_best = scope_cur;
+              pref_best  = pref_cur;
+            }
+
+          continue;
+        }
+
+      /* Rule 3: Avoid deprecated and optimistic addresses
+       *         [Not implemented: Need DAD & address type support]
+       * Rule 4: Prefer home address
+       *         [Not implemented: Need MIP6]
+       * Rule 5: Prefer outgoing interface
+       *         [Already satisfied: We already have the device]
+       * Rule 6: Prefer matching label
+       *         [Not implemented: Need policy table support]
+       *         [Note: Neither lwIP nor Zephyr supports policy table yet]
+       * Rule 7: Prefer temporary addresses
+       *         [Not implemented: Need DAD & temporary addresses support]
+       */
+
+      /* Rule 8: Use longest matching prefix */
+
+      if (pref_cur > pref_best)
+        {
+          best       = current;
+          scope_best = scope_cur;
+          pref_best  = pref_cur;
+        }
+    }
+#endif /* CONFIG_NETDEV_MULTIPLE_IPv6 */
+
+  return best;
+}
+
+/****************************************************************************
+ * Name: netdev_ipv6_lladdr
+ *
+ * Description:
+ *   Get the link-local address of the network device.
+ *
+ * Returned Value:
+ *   A pointer to the link-local address is returned on success.
+ *   NULL is returned if the address is not found on the device.
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+FAR const uint16_t *netdev_ipv6_lladdr(FAR struct net_driver_s *dev)
+{
+  int i;
+
+  for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
+    {
+      FAR struct netdev_ifaddr6_s *ifaddr = &dev->d_ipv6[i];
+
+      if (net_is_addr_linklocal(ifaddr->addr))
+        {
+          return ifaddr->addr;
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: netdev_ipv6_lookup
+ *
+ * Description:
+ *   Look up an IPv6 address in the network device's IPv6 addresses
+ *
+ * Input Parameters:
+ *   dev     - The network device to use in the lookup
+ *   addr    - The IPv6 address to be looked up
+ *   maskcmp - If true, then the IPv6 address is compared to the network
+ *             device's IPv6 addresses with mask compare.
+ *             If false, then the IPv6 address should be exactly the same as
+ *             the network device's IPv6 address.
+ *
+ * Returned Value:
+ *   A pointer to the matching IPv6 address entry is returned on success.
+ *   NULL is returned if the IPv6 address is not found in the device.
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+FAR struct netdev_ifaddr6_s *
+netdev_ipv6_lookup(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
+                   bool maskcmp)
+{
+  int i;
+
+  for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
+    {
+      FAR struct netdev_ifaddr6_s *ifaddr = &dev->d_ipv6[i];
+
+      /* Skip empty address */
+
+      if (net_ipv6addr_cmp(ifaddr->addr, g_ipv6_unspecaddr))
+        {
+          continue;
+        }
+
+      /* Check if the address matches */
+
+      if (maskcmp)
+        {
+          if (net_ipv6addr_maskcmp(addr, ifaddr->addr, ifaddr->mask))
+            {
+              return ifaddr;
+            }
+        }
+      else
+        {
+          if (net_ipv6addr_cmp(addr, ifaddr->addr))
+            {
+              return ifaddr;
+            }
+        }
+    }
+
+  /* No match found */
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: netdev_ipv6_foreach
+ *
+ * Description:
+ *   Enumerate each IPv6 address on a network device.  This function will
+ *   terminate when either (1) all addresses have been enumerated or (2) when
+ *   a callback returns any non-zero value.
+ *
+ * Input Parameters:
+ *   dev      - The network device
+ *   callback - Will be called for each IPv6 address
+ *   arg      - Opaque user argument passed to callback()
+ *
+ * Returned Value:
+ *  Zero:     Enumeration completed
+ *  Non-zero: Enumeration terminated early by callback
+ *
+ * Assumptions:
+ *  The network is locked.
+ *
+ ****************************************************************************/
+
+int netdev_ipv6_foreach(FAR struct net_driver_s *dev,
+                        devif_ipv6_callback_t callback, FAR void *arg)
+{
+  int i;
+
+  if (callback == NULL)
+    {
+      return OK;
+    }
+
+  for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
+    {
+      FAR struct netdev_ifaddr6_s *ifaddr = &dev->d_ipv6[i];
+
+      if (!net_ipv6addr_cmp(ifaddr->addr, g_ipv6_unspecaddr))
+        {
+          int ret = callback(dev, ifaddr, arg);
+          if (ret != 0) /* Stop on any error and return it */
+            {
+              return ret;
+            }
+        }
+    }
+
+  return OK;
+}
diff --git a/net/utils/net_mask2pref.c b/net/utils/net_mask2pref.c
index 2c06a6607e..252c1f6b72 100644
--- a/net/utils/net_mask2pref.c
+++ b/net/utils/net_mask2pref.c
@@ -207,4 +207,42 @@ uint8_t net_ipv6_mask2pref(FAR const uint16_t *mask)
   return preflen;
 }
 
+/****************************************************************************
+ * Name: net_ipv6_common_pref
+ *
+ * Description:
+ *   Calculate the common prefix length of two IPv6 addresses.
+ *
+ * Input Parameters:
+ *   a1,a2   Points to IPv6 addresses in the form of uint16_t[8]
+ *
+ * Returned Value:
+ *   The common prefix length, range 0-128 on success;  This function will
+ *   not fail.
+ *
+ ****************************************************************************/
+
+uint8_t net_ipv6_common_pref(FAR const uint16_t *a1, FAR const uint16_t *a2)
+{
+  uint8_t preflen;
+  int i;
+
+  /* Count the leading same 16-bit groups */
+
+  for (i = 0, preflen = 0; i < 8 && a1[i] == a2[i]; i++, preflen += 16);
+
+  /* Now i either, (1) indexes past the end of the mask, or (2) is the index
+   * to the first half-word that is not equal between the addresses.
+   */
+
+  if (i < 8)
+    {
+      preflen += net_msbits16(NTOHS(~(a1[i] ^ a2[i])));
+    }
+
+  /* Return the prefix length */
+
+  return preflen;
+}
+
 #endif /* CONFIG_NET_IPv6 */
diff --git a/net/utils/utils.h b/net/utils/utils.h
index 9688cac7fd..757e0580b9 100644
--- a/net/utils/utils.h
+++ b/net/utils/utils.h
@@ -187,6 +187,25 @@ void net_getrandom(FAR void *bytes, size_t nbytes);
 uint8_t net_ipv4_mask2pref(in_addr_t mask);
 #endif
 
+/****************************************************************************
+ * Name: net_ipv6_common_pref
+ *
+ * Description:
+ *   Calculate the common prefix length of two IPv6 addresses.
+ *
+ * Input Parameters:
+ *   a1,a2   Points to IPv6 addresses in the form of uint16_t[8]
+ *
+ * Returned Value:
+ *   The common prefix length, range 0-128 on success;  This function will
+ *   not fail.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv6
+uint8_t net_ipv6_common_pref(FAR const uint16_t *a1, FAR const uint16_t *a2);
+#endif
+
 /****************************************************************************
  * Name: net_ipv6_mask2pref
  *