Possibility to treat /32 and /128 non-gateway routes as onlink on BSD?

Stefan Haller stefan.haller at stha.de
Wed Dec 15 22:01:57 CET 2021


Hi,

I am replying to this older email thread, because the context might be
appreciated. Hope that this is acceptable.

Thread in archive: <https://bird.network.cz/pipermail/bird-users/2021-April/015419.html>

Short summary: On FreeBSD bird will export onlink routes, but the kernel
does not support an onlink flag. So when bird is reading back the routes
from the kernel table there is some confusion, the route is dismissed
and bird tries to re-export the route again and again.

In my case I have a Wireguard interface with a local /32 address and two
peers:

  ifconfig wg0 192.168.0.10/32
  route add 192.168.0.4 -iface wg0
  route add 192.168.0.8 -iface wg0

If bird receives a route via Babel that looks for example like this:

  route 192.168.42.0/24 via 192.168.0.4%wg0 onlink

Then bird's log will be full of the following lines:

> KRT: Received route 192.168.42.0/24 with strange next-hop 192.168.0.4
> KRT: Error sending route 192.168.42.0/24 to kernel: File exists

bird installs the route, but when reading back the route from the kernel,
it will be dismissed, as the next-hop 192.168.0.4 is not part of any
interface subnet. Now bird wants to add the missing route. This fails,
because the route is in fact already present.

For some time now I am using a local patch (on top of bird 2.0.8) that
essentially implements the suggestion that was emerging from the
discussion:

On Mon, Apr 19, 2021 at 09:41:33PM +0200, Ondrej Zajicek wrote:
> I see the problem as BIRD internally support onlink flag, but BSD kernel
> does not support that flag, so onlink routes exported to BSD kernel are
> not read back properly. Seems to me that there is a simple woraround:
> 
> When i read a route (from kernel on BSD) that has gateway on iface, which
> has only /32 or /128 IP address(es), so no proper iface range, then i would
> assume onlink flag for the route (its nexthops).

My proposed patch is attached to end of the mail. What it does is that
for any route received by the kernel it checks if the iface only has /32
or /128 addresses configured. If this is the case, the RNF_ONLINK flag
will be set for this route. If any other address is found, the behaviour
is not changed. The neigh_find call was adapted in a way to mimic the
call in sysdep/linux/netlink.c.

The patch is working fine on my Wireguard/Babel mesh on FreeBSD. I tried
to keep the patch as non-intrusive as possible. The disadvantage is that
for each route the interface addresses are enumerated. However, for
normal use cases it will most likely return after the first address is
inspected (either it's not a /32 or /128 address, or there will be only
a single /32 or /128 address per interface per interface in normal
setups).

Another way would be store an 'assume_onlink_routes' flag per interface
on interface discovery.  Would probably touch more places in the code
base. One could also move the iface-addr check after the neigh_find
check, so it will only fire in rare corner cases before bailing out.

For symmetry I included the same logic for IPv6. Due to availability of
scoped link-local addresses I can't think of a real use case though.

I am looking forward to feedback or suggestions.

Best regards,
Stefan Haller


diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
index 5c905bc9..e9f1a82b 100644
--- a/sysdep/bsd/krt-sock.c
+++ b/sysdep/bsd/krt-sock.c
@@ -366,6 +366,27 @@ krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old)
   }
 }
 
+/**
+ * assume_onlink_for_iface - check if routes on interface are considered onlink
+ * @ipv6: Switch to only consider IPv6 or IPv4 addresses.
+ *
+ * The BSD kernel does not support an onlink flag. If the interface only has
+ * /32 or /128 addresses configured, all routes should be considered as onlink and
+ * the function returns 1.
+ */
+static int
+assume_onlink_for_iface(struct iface *iface, int ipv6)
+{
+  struct ifa *ifa;
+  const u8 type = ipv6 ? NET_IP6 : NET_IP4;
+  WALK_LIST(ifa, iface->addrs)
+  {
+    if (ifa->prefix.type == type && ifa->prefix.pxlen != net_max_prefix_length[type])
+      return 0;
+  }
+  return 1;
+}
+
 #define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0)
 
 static void
@@ -535,7 +556,17 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
     if (ipa_is_link_local(a.nh.gw))
       _I0(a.nh.gw) = 0xfe800000;
 
-    ng = neigh_find(&p->p, a.nh.gw, a.nh.iface, 0);
+    /* The BSD kernel does not support an onlink flag. We heuristically
+       set the onlink flag, if the iface only has /32 or /128 addresses
+       configured. */
+    if (assume_onlink_for_iface(a.nh.iface, ipv6))
+    {
+      a.nh.flags |= RNF_ONLINK;
+      goto done;
+    }
+
+    ng = neigh_find(&p->p, a.nh.gw, a.nh.iface,
+		    (a.nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
     if (!ng || (ng->scope == SCOPE_HOST))
       {
 	/* Ignore routes with next-hop 127.0.0.1, host routes with such


More information about the Bird-users mailing list