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 3b74cfecc250e61bc7704b9d7db90a902e71bd89
Author: Zhe Weng <weng...@xiaomi.com>
AuthorDate: Fri Dec 29 16:12:11 2023 +0800

    net/route: Support longest prefix match for routing
    
    Support longest prefix match routing described as "Longest Match" in
    RFC 1812, Section 5.2.4.3, Page 75.
    
    Introduced `prefixlen` to indicate the prefix length of currently
    founded route, and only looks up for longer prefix in all later steps.
    
    Signed-off-by: Zhe Weng <weng...@xiaomi.com>
---
 include/nuttx/net/neighbor.h   |   7 +-
 net/neighbor/neighbor_add.c    |   1 +
 net/netdev/netdev_findbyaddr.c | 254 +++++++++++++++++++++++++++++++++--------
 net/route/Kconfig              |   9 +-
 net/route/net_router.c         | 101 ++++++++++++++--
 net/route/netdev_router.c      |  73 +++++++++++-
 net/route/route.h              |  24 ++--
 7 files changed, 394 insertions(+), 75 deletions(-)

diff --git a/include/nuttx/net/neighbor.h b/include/nuttx/net/neighbor.h
index b1d4a8adc7..9dc43144b5 100644
--- a/include/nuttx/net/neighbor.h
+++ b/include/nuttx/net/neighbor.h
@@ -79,9 +79,10 @@ struct neighbor_addr_s
 
 struct neighbor_entry_s
 {
-  net_ipv6addr_t         ne_ipaddr;  /* IPv6 address of the Neighbor */
-  struct neighbor_addr_s ne_addr;    /* Link layer address of the Neighbor */
-  clock_t                ne_time;    /* For aging, units of tick */
+  net_ipv6addr_t           ne_ipaddr;  /* IPv6 address of the Neighbor */
+  struct neighbor_addr_s   ne_addr;    /* Link layer address of the Neighbor */
+  clock_t                  ne_time;    /* For aging, units of tick */
+  FAR struct net_driver_s *ne_dev;     /* The device driver structure */
 };
 
 #ifdef __cplusplus
diff --git a/net/neighbor/neighbor_add.c b/net/neighbor/neighbor_add.c
index c66185c0b8..49801c5541 100644
--- a/net/neighbor/neighbor_add.c
+++ b/net/neighbor/neighbor_add.c
@@ -99,6 +99,7 @@ void neighbor_add(FAR struct net_driver_s *dev, FAR 
net_ipv6addr_t ipaddr,
    * "oldest_ndx" variable).
    */
 
+  g_neighbors[oldest_ndx].ne_dev  = dev;
   g_neighbors[oldest_ndx].ne_time = clock_systime_ticks();
   net_ipv6addr_copy(g_neighbors[oldest_ndx].ne_ipaddr, ipaddr);
 
diff --git a/net/netdev/netdev_findbyaddr.c b/net/netdev/netdev_findbyaddr.c
index 35cb6c7054..bfe5745d9b 100644
--- a/net/netdev/netdev_findbyaddr.c
+++ b/net/netdev/netdev_findbyaddr.c
@@ -25,6 +25,7 @@
 #include <nuttx/config.h>
 
 #include <stdbool.h>
+#include <stdint.h>
 #include <string.h>
 #include <errno.h>
 #include <debug.h>
@@ -34,6 +35,8 @@
 #include <nuttx/net/netdev.h>
 #include <nuttx/net/ip.h>
 
+#include "arp/arp.h"
+#include "neighbor/neighbor.h"
 #include "utils/utils.h"
 #include "devif/devif.h"
 #include "inet/inet.h"
@@ -41,11 +44,11 @@
 #include "netdev/netdev.h"
 
 /****************************************************************************
- * Public Functions
+ * Private Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: netdev_findby_lipv4addr
+ * Name: netdev_prefixlen_findby_lipv4addr
  *
  * Description:
  *   Find a previously registered network device by matching a local address
@@ -55,6 +58,7 @@
  * Input Parameters:
  *   lipaddr - Local, IPv4 address assigned to the network device.  Or any
  *             IPv4 address on the sub-net served by the network device.
+ *   prefixlen - The length of matching prefix. Range: -1(no match) ~ 32
  *
  * Returned Value:
  *   Pointer to driver on success; null on failure
@@ -62,9 +66,15 @@
  ****************************************************************************/
 
 #ifdef CONFIG_NET_IPv4
-FAR struct net_driver_s *netdev_findby_lipv4addr(in_addr_t lipaddr)
+static FAR struct net_driver_s *
+netdev_prefixlen_findby_lipv4addr(in_addr_t lipaddr, FAR int8_t *prefixlen)
 {
   FAR struct net_driver_s *dev;
+  FAR struct net_driver_s *bestdev  = NULL;
+  int8_t                   bestpref = -1;
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  int8_t len;
+#endif
 
   /* Examine each registered network device */
 
@@ -76,6 +86,7 @@ FAR struct net_driver_s *netdev_findby_lipv4addr(in_addr_t 
lipaddr)
       if ((dev->d_flags & IFF_UP) != 0 &&
           !net_ipv4addr_cmp(dev->d_ipaddr, INADDR_ANY))
         {
+#ifndef CONFIG_ROUTE_LONGEST_MATCH
           /* Yes.. check for an address match (under the netmask) */
 
           if (net_ipv4addr_maskcmp(dev->d_ipaddr, lipaddr,
@@ -83,21 +94,58 @@ FAR struct net_driver_s *netdev_findby_lipv4addr(in_addr_t 
lipaddr)
             {
               /* Its a match */
 
-              net_unlock();
-              return dev;
+              bestdev  = dev;
+              bestpref = 32; /* Regard as best (exact) match */
+              break;
+            }
+#else
+          /* Longest prefix flow: First, check for an exact address match */
+
+          if (net_ipv4addr_cmp(dev->d_ipaddr, lipaddr))
+            {
+              /* It's an exact match */
+
+              bestdev  = dev;
+              bestpref = 32;
+              break;
+            }
+
+          /* Then, check for an address match (under the netmask) */
+
+          if (net_ipv4addr_maskcmp(dev->d_ipaddr, lipaddr,
+                                   dev->d_netmask))
+            {
+              len = (int8_t)net_ipv4_mask2pref(dev->d_netmask);
+
+              /* Regard current device as better if:
+               * 1. It has longer prefix length
+               * 2. It has the same prefix length but it has target address
+               *    in the ARP cache (We don't have other information
+               *    for the precedence of networks)
+               */
+
+              if (len > bestpref
+#ifdef CONFIG_NET_ARP
+                  || (len == bestpref && arp_find(lipaddr, NULL, dev) == OK)
+#endif
+                  )
+                {
+                  bestdev  = dev;
+                  bestpref = len;
+                }
             }
+#endif /* CONFIG_ROUTE_LONGEST_MATCH */
         }
     }
 
-  /* No device with the matching address found */
-
   net_unlock();
-  return NULL;
+  *prefixlen = bestpref;
+  return bestdev;
 }
 #endif /* CONFIG_NET_IPv4 */
 
 /****************************************************************************
- * Name: netdev_findby_lipv6addr
+ * Name: netdev_prefixlen_findby_lipv6addr
  *
  * Description:
  *   Find a previously registered network device by matching a local address
@@ -107,6 +155,7 @@ FAR struct net_driver_s *netdev_findby_lipv4addr(in_addr_t 
lipaddr)
  * Input Parameters:
  *   lipaddr - Local, IPv6 address assigned to the network device.  Or any
  *             IPv6 address on the sub-net served by the network device.
+ *   prefixlen - The length of matching prefix. Range: -1(no match) ~ 128
  *
  * Returned Value:
  *   Pointer to driver on success; null on failure
@@ -114,36 +163,143 @@ FAR struct net_driver_s 
*netdev_findby_lipv4addr(in_addr_t lipaddr)
  ****************************************************************************/
 
 #ifdef CONFIG_NET_IPv6
-FAR struct net_driver_s *netdev_findby_lipv6addr(
-                                      const net_ipv6addr_t lipaddr)
+static FAR struct net_driver_s *
+netdev_prefixlen_findby_lipv6addr(const net_ipv6addr_t lipaddr,
+                                  FAR int16_t *prefixlen)
 {
   FAR struct net_driver_s *dev;
+  FAR struct net_driver_s *bestdev  = NULL;
+  int16_t                  bestpref = -1;
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  FAR struct netdev_ifaddr6_s *ifaddr6;
+  FAR struct neighbor_entry_s *ne;
+  FAR struct net_driver_s     *hint;
+  int16_t len;
+#endif
+
+  net_lock();
+
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  /* Find a hint from neighbor table in case same prefix length exists on
+   * multiple devices.
+   */
+
+  ne   = neighbor_findentry(lipaddr);
+  hint = ne ? ne->ne_dev : NULL;
+#endif
 
   /* Examine each registered network device */
 
-  net_lock();
   for (dev = g_netdevices; dev; dev = dev->flink)
     {
       /* Is the interface in the "up" state? */
 
       if ((dev->d_flags & IFF_UP) != 0 && NETDEV_HAS_V6ADDR(dev))
         {
+#ifndef CONFIG_ROUTE_LONGEST_MATCH
           /* Yes.. check for an address match (under the netmask) */
 
           if (NETDEV_V6ADDR_ONLINK(dev, lipaddr))
             {
               /* Its a match */
 
-              net_unlock();
-              return dev;
+              bestdev  = dev;
+              bestpref = 128; /* Regard as best (exact) match */
+              break;
             }
+#else
+          /* Longest prefix flow: First, check for an exact address match */
+
+          if (NETDEV_IS_MY_V6ADDR(dev, lipaddr))
+            {
+              /* It's an exact match */
+
+              bestdev  = dev;
+              bestpref = 128;
+              break;
+            }
+
+          /* Then, check for an address match (under the netmask) */
+
+          if ((ifaddr6 = netdev_ipv6_lookup(dev, lipaddr, true)) != NULL)
+            {
+              len = (int16_t)net_ipv6_mask2pref(ifaddr6->mask);
+
+              /* Regard current device as better if:
+               * 1. It has longer prefix length
+               * 2. It has the same prefix length but it has target address
+               *    in the neighbor cache (We don't have other information
+               *    for the precedence of networks)
+               */
+
+              if (len > bestpref || (len == bestpref && hint == dev))
+                {
+                  bestdev  = dev;
+                  bestpref = len;
+                }
+            }
+#endif /* CONFIG_ROUTE_LONGEST_MATCH */
         }
     }
 
-  /* No device with the matching address found */
-
   net_unlock();
-  return NULL;
+  *prefixlen = bestpref;
+  return bestdev;
+}
+#endif /* CONFIG_NET_IPv6 */
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netdev_findby_lipv4addr
+ *
+ * Description:
+ *   Find a previously registered network device by matching a local address
+ *   with the subnet served by the device.  Only "up" devices are considered
+ *   (since a "down" device has no meaningful address).
+ *
+ * Input Parameters:
+ *   lipaddr - Local, IPv4 address assigned to the network device.  Or any
+ *             IPv4 address on the sub-net served by the network device.
+ *
+ * Returned Value:
+ *   Pointer to driver on success; null on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv4
+FAR struct net_driver_s *netdev_findby_lipv4addr(in_addr_t lipaddr)
+{
+  int8_t prefixlen;
+  return netdev_prefixlen_findby_lipv4addr(lipaddr, &prefixlen);
+}
+#endif /* CONFIG_NET_IPv4 */
+
+/****************************************************************************
+ * Name: netdev_findby_lipv6addr
+ *
+ * Description:
+ *   Find a previously registered network device by matching a local address
+ *   with the subnet served by the device.  Only "up" devices are considered
+ *   (since a "down" device has no meaningful address).
+ *
+ * Input Parameters:
+ *   lipaddr - Local, IPv6 address assigned to the network device.  Or any
+ *             IPv6 address on the sub-net served by the network device.
+ *
+ * Returned Value:
+ *   Pointer to driver on success; null on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IPv6
+FAR struct net_driver_s *netdev_findby_lipv6addr(
+                                      const net_ipv6addr_t lipaddr)
+{
+  int16_t prefixlen;
+  return netdev_prefixlen_findby_lipv6addr(lipaddr, &prefixlen);
 }
 #endif /* CONFIG_NET_IPv6 */
 
@@ -168,7 +324,8 @@ FAR struct net_driver_s *netdev_findby_lipv6addr(
 FAR struct net_driver_s *netdev_findby_ripv4addr(in_addr_t lipaddr,
                                                  in_addr_t ripaddr)
 {
-  struct net_driver_s *dev;
+  FAR struct net_driver_s *dev;
+  int8_t prefixlen;
 #ifdef CONFIG_NET_ROUTE
   in_addr_t router;
   int ret;
@@ -198,22 +355,20 @@ FAR struct net_driver_s 
*netdev_findby_ripv4addr(in_addr_t lipaddr,
         }
     }
 
-  /* Check if the address maps to a locally available network */
-
-  dev = netdev_findby_lipv4addr(ripaddr);
-  if (dev)
-    {
-      return dev;
-    }
+  /* Check if the address maps to a locally available network
+   * Note: If longest prefix match is not enabled, prefixlen will be 32 if
+   *       matched and it will disable further routing lookup.
+   */
 
-  /* No.. The address lies on an external network */
+  dev = netdev_prefixlen_findby_lipv4addr(ripaddr, &prefixlen);
 
 #ifdef CONFIG_NET_ROUTE
   /* If we have a routing table, then perhaps we can find the local
-   * address of a router that can forward packets to the external network.
+   * address of a router that can forward packets to the external network
+   * with longer prefix.
    */
 
-  ret = net_ipv4_router(ripaddr, &router);
+  ret = net_ipv4_router(ripaddr, &router, prefixlen);
   if (ret >= 0)
     {
       /* Success... try to find the network device associated with the local
@@ -221,13 +376,16 @@ FAR struct net_driver_s 
*netdev_findby_ripv4addr(in_addr_t lipaddr,
        */
 
       dev = netdev_findby_lipv4addr(router);
-      if (dev)
-        {
-          return dev;
-        }
     }
 #endif /* CONFIG_NET_ROUTE */
 
+  /* Return the device we found. */
+
+  if (dev)
+    {
+      return dev;
+    }
+
   /* The above lookup will fail if the packet is being sent out of our
    * out subnet to a router and there is no routing information. Let's
    * try the default network device.
@@ -259,7 +417,8 @@ FAR struct net_driver_s *netdev_findby_ripv6addr(
                                  const net_ipv6addr_t lipaddr,
                                  const net_ipv6addr_t ripaddr)
 {
-  struct net_driver_s *dev;
+  FAR struct net_driver_s *dev;
+  int16_t prefixlen;
 #ifdef CONFIG_NET_ROUTE
   net_ipv6addr_t router;
   int ret;
@@ -291,22 +450,20 @@ FAR struct net_driver_s *netdev_findby_ripv6addr(
         }
     }
 
-  /* Check if the address maps to a locally available network */
-
-  dev = netdev_findby_lipv6addr(ripaddr);
-  if (dev)
-    {
-      return dev;
-    }
+  /* Check if the address maps to a locally available network
+   * Note: If longest prefix match is not enabled, prefixlen will be 128 if
+   *       matched and it will disable further routing lookup.
+   */
 
-  /* No.. The address lies on an external network */
+  dev = netdev_prefixlen_findby_lipv6addr(ripaddr, &prefixlen);
 
 #ifdef CONFIG_NET_ROUTE
   /* If we have a routing table, then perhaps we can find the local
-   * address of a router that can forward packets to the external network.
+   * address of a router that can forward packets to the external network
+   * with longer prefix.
    */
 
-  ret = net_ipv6_router(ripaddr, router);
+  ret = net_ipv6_router(ripaddr, router, prefixlen);
   if (ret >= 0)
     {
       /* Success... try to find the network device associated with the local
@@ -314,13 +471,16 @@ FAR struct net_driver_s *netdev_findby_ripv6addr(
        */
 
       dev = netdev_findby_lipv6addr(router);
-      if (dev)
-        {
-          return dev;
-        }
     }
 #endif /* CONFIG_NET_ROUTE */
 
+  /* Return the device we found. */
+
+  if (dev)
+    {
+      return dev;
+    }
+
   /* The above lookup will fail if the packet is being sent out of our
    * out subnet to a router and there is no routing information. Let's
    * try the default network device.
diff --git a/net/route/Kconfig b/net/route/Kconfig
index 03cf002778..a7191e9130 100644
--- a/net/route/Kconfig
+++ b/net/route/Kconfig
@@ -137,5 +137,12 @@ config ROUTE_MAX_IPv6_CACHEROUTES
                This determines the maximum number of routes that can be cached 
in
                memory.
 
+config ROUTE_LONGEST_MATCH
+       bool "Enable longest prefix match support"
+       default y
+       ---help---
+               Enable support for longest prefix match routing.
+               ("Longest Match" in RFC 1812, Section 5.2.4.3, Page 75)
+
 endif # NET_ROUTE
-endmenu # ARP Configuration
+endmenu # Routing Table Configuration
diff --git a/net/route/net_router.c b/net/route/net_router.c
index 1ccea8fa49..a3922d042c 100644
--- a/net/route/net_router.c
+++ b/net/route/net_router.c
@@ -35,6 +35,7 @@
 #include "devif/devif.h"
 #include "route/cacheroute.h"
 #include "route/route.h"
+#include "utils/utils.h"
 
 #if defined(CONFIG_NET) && defined(CONFIG_NET_ROUTE)
 
@@ -67,6 +68,14 @@ struct route_ipv4_match_s
 #else
   in_addr_t router;              /* IPv4 address of router a local networks */
 #endif
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  /* Only match prefix longer than prefixlen, equals to entry.netmask if we
+   * have got a match (then we only find longer prefix later).
+   * Range: -1 ~ 32
+   */
+
+  int8_t prefixlen;
+#endif
 };
 #endif
 
@@ -79,6 +88,14 @@ struct route_ipv6_match_s
 #else
   net_ipv6addr_t router;         /* IPv6 address of router a local networks */
 #endif
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  /* Only match prefix longer than prefixlen, equals to entry.netmask if we
+   * have got a match (then we only find longer prefix later).
+   * Range: -1 ~ 128
+   */
+
+  int16_t prefixlen;
+#endif
 };
 #endif
 
@@ -106,13 +123,20 @@ static int net_ipv4_match(FAR struct net_route_ipv4_s 
*route, FAR void *arg)
 {
   FAR struct route_ipv4_match_s *match =
                                (FAR struct route_ipv4_match_s *)arg;
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  int8_t prefixlen = (int8_t)net_ipv4_mask2pref(route->netmask);
+#endif
 
   /* To match, the masked target addresses must be the same.  In the event
    * of multiple matches, only the first is returned.  There is not (yet) any
    * concept for the precedence of networks.
    */
 
-  if (net_ipv4addr_maskcmp(route->target, match->target, route->netmask))
+  if (net_ipv4addr_maskcmp(route->target, match->target, route->netmask)
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+      && prefixlen > match->prefixlen
+#endif
+     )
     {
 #ifdef CONFIG_ROUTE_IPv4_CACHEROUTE
       /* They match.. Copy the entire routing table entry */
@@ -123,7 +147,13 @@ static int net_ipv4_match(FAR struct net_route_ipv4_s 
*route, FAR void *arg)
 
       net_ipv4addr_copy(match->router, route->router);
 #endif
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+      /* Cache the prefix length */
+
+      match->prefixlen = prefixlen;
+#else
       return 1;
+#endif
     }
 
   return 0;
@@ -150,13 +180,20 @@ static int net_ipv6_match(FAR struct net_route_ipv6_s 
*route, FAR void *arg)
 {
   FAR struct route_ipv6_match_s *match =
                                 (FAR struct route_ipv6_match_s *)arg;
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  int16_t prefixlen = (int16_t)net_ipv6_mask2pref(route->netmask);
+#endif
 
   /* To match, the masked target addresses must be the same.  In the event
    * of multiple matches, only the first is returned.  There is not (yet) any
    * concept for the precedence of networks.
    */
 
-  if (net_ipv6addr_maskcmp(route->target, match->target, route->netmask))
+  if (net_ipv6addr_maskcmp(route->target, match->target, route->netmask)
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+      && prefixlen > match->prefixlen
+#endif
+     )
     {
 #ifdef CONFIG_ROUTE_IPv6_CACHEROUTE
       /* They match.. Copy the entire routing table entry */
@@ -167,7 +204,13 @@ static int net_ipv6_match(FAR struct net_route_ipv6_s 
*route, FAR void *arg)
 
       net_ipv6addr_copy(match->router, route->router);
 #endif
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+      /* Cache the prefix length */
+
+      match->prefixlen = prefixlen;
+#else
       return 1;
+#endif
     }
 
   return 0;
@@ -186,9 +229,12 @@ static int net_ipv6_match(FAR struct net_route_ipv6_s 
*route, FAR void *arg)
  *   router on a local network that can forward to the external network.
  *
  * Input Parameters:
- *   target - An IPv4 address on a remote network to use in the lookup.
- *   router - The address of router on a local network that can forward our
- *     packets to the target.
+ *   target    - An IPv4 address on a remote network to use in the lookup.
+ *   router    - The address of router on a local network that can forward
+ *               our packets to the target.
+ *   prefixlen - The prefix length of previously matched routes (maybe on
+ *               device), will only match prefix longer than prefixlen.
+ *               Range: -1(match all) ~ 32(match none)
  *
  * Returned Value:
  *   OK on success; Negated errno on failure.
@@ -196,11 +242,19 @@ static int net_ipv6_match(FAR struct net_route_ipv6_s 
*route, FAR void *arg)
  ****************************************************************************/
 
 #ifdef CONFIG_NET_IPv4
-int net_ipv4_router(in_addr_t target, FAR in_addr_t *router)
+int net_ipv4_router(in_addr_t target, FAR in_addr_t *router,
+                    int8_t prefixlen)
 {
   struct route_ipv4_match_s match;
   int ret;
 
+  /* Just early return for long prefix, maybe already got exact match. */
+
+  if (prefixlen >= 32)
+    {
+      return -ENOENT;
+    }
+
   /* Do not route the special broadcast IP address */
 
   if (net_ipv4addr_cmp(target, INADDR_BROADCAST))
@@ -212,6 +266,9 @@ int net_ipv4_router(in_addr_t target, FAR in_addr_t *router)
 
   memset(&match, 0, sizeof(struct route_ipv4_match_s));
   net_ipv4addr_copy(match.target, target);
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  match.prefixlen = prefixlen;
+#endif
 
 #ifdef CONFIG_ROUTE_IPv4_CACHEROUTE
   /* First see if we can find a router entry in the cache */
@@ -229,7 +286,12 @@ int net_ipv4_router(in_addr_t target, FAR in_addr_t 
*router)
 
   /* Did we find a route? */
 
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  UNUSED(ret);
+  if (match.prefixlen <= prefixlen)
+#else
   if (ret <= 0)
+#endif
     {
       /* No.. there is no route for this address */
 
@@ -261,9 +323,12 @@ int net_ipv4_router(in_addr_t target, FAR in_addr_t 
*router)
  *   router on a local network that can forward to the external network.
  *
  * Input Parameters:
- *   target - An IPv6 address on a remote network to use in the lookup.
- *   router - The address of router on a local network that can forward our
- *     packets to the target.
+ *   target    - An IPv6 address on a remote network to use in the lookup.
+ *   router    - The address of router on a local network that can forward
+ *               our packets to the target.
+ *   prefixlen - The prefix length of previously matched routes (maybe on
+ *               device), will only match prefix longer than prefixlen.
+ *               Range: -1(match all) ~ 128(match none)
  *
  * Returned Value:
  *   OK on success; Negated errno on failure.
@@ -271,11 +336,19 @@ int net_ipv4_router(in_addr_t target, FAR in_addr_t 
*router)
  ****************************************************************************/
 
 #ifdef CONFIG_NET_IPv6
-int net_ipv6_router(const net_ipv6addr_t target, net_ipv6addr_t router)
+int net_ipv6_router(const net_ipv6addr_t target, net_ipv6addr_t router,
+                    int16_t prefixlen)
 {
   struct route_ipv6_match_s match;
   int ret;
 
+  /* Just early return for long prefix, maybe already got exact match. */
+
+  if (prefixlen >= 128)
+    {
+      return -ENOENT;
+    }
+
   /* Do not route to any the special IPv6 multicast addresses */
 
   if (target[0] == HTONS(0xff02))
@@ -287,6 +360,9 @@ int net_ipv6_router(const net_ipv6addr_t target, 
net_ipv6addr_t router)
 
   memset(&match, 0, sizeof(struct route_ipv6_match_s));
   net_ipv6addr_copy(match.target, target);
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  match.prefixlen = prefixlen;
+#endif
 
 #ifdef CONFIG_ROUTE_IPv6_CACHEROUTE
   /* First see if we can find a router entry in the cache */
@@ -304,7 +380,12 @@ int net_ipv6_router(const net_ipv6addr_t target, 
net_ipv6addr_t router)
 
   /* Did we find a route? */
 
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  UNUSED(ret);
+  if (match.prefixlen <= prefixlen)
+#else
   if (ret <= 0)
+#endif
     {
       /* No.. there is no route for this address */
 
diff --git a/net/route/netdev_router.c b/net/route/netdev_router.c
index 348e702281..d186802c1f 100644
--- a/net/route/netdev_router.c
+++ b/net/route/netdev_router.c
@@ -34,6 +34,7 @@
 #include "netdev/netdev.h"
 #include "route/cacheroute.h"
 #include "route/route.h"
+#include "utils/utils.h"
 
 #if defined(CONFIG_NET) && defined(CONFIG_NET_ROUTE)
 
@@ -67,6 +68,14 @@ struct route_ipv4_devmatch_s
 #else
   in_addr_t router;              /* IPv4 address of router a local networks */
 #endif
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  /* Only match prefix longer than prefixlen, equals to entry.netmask if we
+   * have got a match (then we only find longer prefix later).
+   * Range: -1 ~ 32
+   */
+
+  int8_t prefixlen;
+#endif
 };
 #endif
 
@@ -80,6 +89,14 @@ struct route_ipv6_devmatch_s
 #else
   net_ipv6addr_t router;         /* IPv6 address of router a local networks */
 #endif
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  /* Only match prefix longer than prefixlen, equals to entry.netmask if we
+   * have got a match (then we only find longer prefix later).
+   * Range: -1 ~ 128
+   */
+
+  int16_t prefixlen;
+#endif
 };
 #endif
 
@@ -109,16 +126,24 @@ static int net_ipv4_devmatch(FAR struct net_route_ipv4_s 
*route,
   FAR struct route_ipv4_devmatch_s *match =
     (FAR struct route_ipv4_devmatch_s *)arg;
   FAR struct net_driver_s *dev = match->dev;
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  int8_t prefixlen = (int8_t)net_ipv4_mask2pref(route->netmask);
+#endif
 
   /* To match, (1) the masked target addresses must be the same, and (2) the
    * router address must like on the network provided by the device.
    *
-   * In the event of multiple matches, only the first is returned.  There
-   * not (yet) any concept for the precedence of networks.
+   * In the event of multiple matches, we try to get the longest prefix if
+   * CONFIG_ROUTE_LONGEST_MATCH is set, otherwise only the first match is
+   * returned.
    */
 
   if (net_ipv4addr_maskcmp(route->target, match->target, route->netmask) &&
-      net_ipv4addr_maskcmp(route->router, dev->d_ipaddr, dev->d_netmask))
+      net_ipv4addr_maskcmp(route->router, dev->d_ipaddr, dev->d_netmask)
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+      && prefixlen > match->prefixlen
+#endif
+      )
     {
 #ifdef CONFIG_ROUTE_IPv4_CACHEROUTE
       /* They match.. Copy the entire routing table entry */
@@ -129,7 +154,13 @@ static int net_ipv4_devmatch(FAR struct net_route_ipv4_s 
*route,
 
       net_ipv4addr_copy(match->router, route->router);
 #endif
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+      /* Cache the prefix length */
+
+      match->prefixlen = prefixlen;
+#else
       return 1;
+#endif
     }
 
   return 0;
@@ -158,16 +189,24 @@ static int net_ipv6_devmatch(FAR struct net_route_ipv6_s 
*route,
   FAR struct route_ipv6_devmatch_s *match =
     (FAR struct route_ipv6_devmatch_s *)arg;
   FAR struct net_driver_s *dev = match->dev;
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  int16_t prefixlen = (int16_t)net_ipv6_mask2pref(route->netmask);
+#endif
 
   /* To match, (1) the masked target addresses must be the same, and (2) the
    * router address must like on the network provided by the device.
    *
-   * In the event of multiple matches, only the first is returned.  There
-   * not (yet) any concept for the precedence of networks.
+   * In the event of multiple matches, we try to get the longest prefix if
+   * CONFIG_ROUTE_LONGEST_MATCH is set, otherwise only the first match is
+   * returned.
    */
 
   if (net_ipv6addr_maskcmp(route->target, match->target, route->netmask) &&
-      NETDEV_V6ADDR_ONLINK(dev, route->router))
+      NETDEV_V6ADDR_ONLINK(dev, route->router)
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+      && prefixlen > match->prefixlen
+#endif
+      )
     {
 #ifdef CONFIG_ROUTE_IPv6_CACHEROUTE
       /* They match.. Copy the entire routing table entry */
@@ -178,7 +217,13 @@ static int net_ipv6_devmatch(FAR struct net_route_ipv6_s 
*route,
 
       net_ipv6addr_copy(match->router, route->router);
 #endif
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+      /* Cache the prefix length */
+
+      match->prefixlen = prefixlen;
+#else
       return 1;
+#endif
     }
 
   return 0;
@@ -222,6 +267,9 @@ void netdev_ipv4_router(FAR struct net_driver_s *dev, 
in_addr_t target,
   memset(&match, 0, sizeof(struct route_ipv4_devmatch_s));
   match.dev = dev;
   net_ipv4addr_copy(match.target, target);
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  match.prefixlen = -1;
+#endif
 
 #ifdef CONFIG_ROUTE_IPv4_CACHEROUTE
   /* First see if we can find a router entry in the cache */
@@ -239,7 +287,12 @@ void netdev_ipv4_router(FAR struct net_driver_s *dev, 
in_addr_t target,
 
   /* Did we find a route? */
 
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  UNUSED(ret);
+  if (match.prefixlen >= 0)
+#else
   if (ret > 0)
+#endif
     {
       /* We found a route. */
 
@@ -301,6 +354,9 @@ void netdev_ipv6_router(FAR struct net_driver_s *dev,
   memset(&match, 0, sizeof(struct route_ipv6_devmatch_s));
   match.dev = dev;
   net_ipv6addr_copy(match.target, target);
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  match.prefixlen = -1;
+#endif
 
 #ifdef CONFIG_ROUTE_IPv6_CACHEROUTE
   /* First see if we can find a router entry in the cache */
@@ -318,7 +374,12 @@ void netdev_ipv6_router(FAR struct net_driver_s *dev,
 
   /* Did we find a route? */
 
+#ifdef CONFIG_ROUTE_LONGEST_MATCH
+  UNUSED(ret);
+  if (match.prefixlen >= 0)
+#else
   if (ret > 0)
+#endif
     {
       /* We found a route. */
 
diff --git a/net/route/route.h b/net/route/route.h
index 7548c216c9..d018b16650 100644
--- a/net/route/route.h
+++ b/net/route/route.h
@@ -159,9 +159,12 @@ int net_delroute_ipv6(net_ipv6addr_t target, 
net_ipv6addr_t netmask);
  *   router on a local network that can forward to the external network.
  *
  * Input Parameters:
- *   target - An IPv4 address on a remote network to use in the lookup.
- *   router - The address of router on a local network that can forward our
- *     packets to the target.
+ *   target    - An IPv4 address on a remote network to use in the lookup.
+ *   router    - The address of router on a local network that can forward
+ *               our packets to the target.
+ *   prefixlen - The prefix length of previously matched routes (maybe on
+ *               device), will only match prefix longer than prefixlen.
+ *               Range: -1(match all) ~ 32(match none)
  *
  * Returned Value:
  *   OK on success; Negated errno on failure.
@@ -169,7 +172,8 @@ int net_delroute_ipv6(net_ipv6addr_t target, net_ipv6addr_t 
netmask);
  ****************************************************************************/
 
 #ifdef CONFIG_NET_IPv4
-int net_ipv4_router(in_addr_t target, FAR in_addr_t *router);
+int net_ipv4_router(in_addr_t target, FAR in_addr_t *router,
+                    int8_t prefixlen);
 #endif
 
 /****************************************************************************
@@ -180,9 +184,12 @@ int net_ipv4_router(in_addr_t target, FAR in_addr_t 
*router);
  *   router on a local network that can forward to the external network.
  *
  * Input Parameters:
- *   target - An IPv6 address on a remote network to use in the lookup.
- *   router - The address of router on a local network that can forward our
- *     packets to the target.
+ *   target    - An IPv6 address on a remote network to use in the lookup.
+ *   router    - The address of router on a local network that can forward
+ *               our packets to the target.
+ *   prefixlen - The prefix length of previously matched routes (maybe on
+ *               device), will only match prefix longer than prefixlen.
+ *               Range: -1(match all) ~ 128(match none)
  *
  * Returned Value:
  *   OK on success; Negated errno on failure.
@@ -190,7 +197,8 @@ int net_ipv4_router(in_addr_t target, FAR in_addr_t 
*router);
  ****************************************************************************/
 
 #ifdef CONFIG_NET_IPv6
-int net_ipv6_router(const net_ipv6addr_t target, net_ipv6addr_t router);
+int net_ipv6_router(const net_ipv6addr_t target, net_ipv6addr_t router,
+                    int16_t prefixlen);
 #endif
 
 /****************************************************************************

Reply via email to