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 /****************************************************************************