Skip to content

Commit

Permalink
route: properly handle multiple kernel prefix routes with same dst
Browse files Browse the repository at this point in the history
When configuring IP(v6) addresses on interfaces, the kernel will install
appropriate prefix routes (except when using noprefixroute).

When configuring addresses from the same subnet on multiple interfaces,
the kernel will happily configure multiple prefix routes. Most prominent
example are per interface IPv6 link local fe80::/64 routes.

Currently libnl treats these routes as identical, and thus will only
keep the newest one of the routes in the cache (as each additional one
is treated as an update to the previous route).

Therefore we need to extend route_id_attrs_get() to properly treat these
routes as different:

* For IPv4, these routes will have a preferred source set.
* For IPv6, these routes will have a single nexthop pointing to the
  interface (OIF).

Also, only kernel may create these routes, attempts from userspace will
be rejected when trying to add a second route for the same prefix.

ip route output:
$ ip r show table all
...
10.0.0.0/24 dev tun0 proto kernel scope link src 10.0.0.1
10.0.0.0/24 dev enp0s31f6 proto kernel scope link src 10.0.0.2
...
broadcast 10.0.0.255 dev tun0 table local proto kernel scope link src 10.0.0.1
broadcast 10.0.0.255 dev enp0s31f6 table local proto kernel scope link src 10.0.0.2
...
fe80::/64 dev tun0 proto kernel metric 256 pref medium
fe80::/64 dev enp0s31f6 proto kernel metric 1024 pref medium
...
multicast ff00::/8 dev enp0s31f6 table local proto kernel metric 256 pref medium
multicast ff00::/8 dev tun0 table local proto kernel metric 256 pref medium

$ ip r show table all | wc -l
37

Before:
$ ./src/nl-route-list
...
inet 10.0.0.0/24 table main type unicast via dev tun0
...
inet 10.0.0.255 table local type broadcast via dev tun0
...
inet6 fe80::/64 table main type unicast via dev tun0
inet6 fe80::/64 table main type unicast via dev enp0s31f6
...
inet6 ff00::/8 table local type multicast via dev enp0s31f6

$ ./src/nl-route-list | grep -v cache | wc -l
34

After:
$ ./src/nl-route-list
...
inet 10.0.0.0/24 table main type unicast via dev tun0
inet 10.0.0.0/24 table main type unicast via dev enp0s31f6
...
inet6 fe80::/64 table main type unicast via dev tun0
inet6 fe80::/64 table main type unicast via dev enp0s31f6
...
inet6 ff00::/8 table local type multicast via dev enp0s31f6
inet6 ff00::/8 table local type multicast via dev tun0

$ ./src/nl-route-list | grep -v cache | wc -l
37

(ip route doesn't show cache routes)

Signed-off-by: Jonas Gorski <[email protected]>
  • Loading branch information
KanjiMonster committed May 14, 2024
1 parent 7cc72d1 commit f02e6c7
Showing 1 changed file with 33 additions and 0 deletions.
33 changes: 33 additions & 0 deletions lib/route/route_obj.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,39 @@ static uint32_t route_id_attrs_get(struct nl_object *obj)
if (route->rt_family == AF_MPLS)
rv &= ~ROUTE_ATTR_PRIO;

if (route->rt_protocol == RTPROT_KERNEL) {
/*
* If configuring Ip(v4) addresses for the same prefix on
* different interfaces, the kernel will install a
* prefix route for each interface with the ip address
* as preferred source.
*/
if (route->rt_family == AF_INET &&
route->rt_scope == RT_SCOPE_LINK)
rv |= ROUTE_ATTR_PREF_SRC;

/*
* For IPv6 addresses, the prefix routes will have
* a single dev nexthop.
*/
if (route->rt_family == AF_INET6 && route->rt_nr_nh == 1) {
struct rtnl_nexthop *first;

first = nl_list_first_entry(&route->rt_nexthops,
struct rtnl_nexthop,
rtnh_list);

/*
* Only interface, no other values, so force
* nexthop comparison.
*/
if (!rtnl_route_nh_get_gateway(first) &&
!rtnl_route_nh_get_via(first) &&
!rtnl_route_nh_get_newdst(first))
rv |= ROUTE_ATTR_MULTIPATH;
}
}

return rv;
}

Expand Down

0 comments on commit f02e6c7

Please sign in to comment.