Index | Thread | Search

From:
Claudio Jeker <cjeker@diehard.n-r-g.com>
Subject:
bgpd: fix for bgpctl show rib out <IP>
To:
tech@openbsd.org
Date:
Wed, 20 May 2026 11:44:18 +0200

Download raw body.

Thread
There is an error when it comes to handling
	bgpctl show rib out <IP>
queries. If the IP to lookup up is covered by a not exported route but
instead by a less specific one the lookup fails.

For example in this setup where the /26 route is filtered:
bgpctl show rib 
flags  vs destination          gateway          lpref   med aspath origin
AI*>  N-? 192.0.2.0/24         0.0.0.0           100     0 i
AI*>  N-? 192.0.2.0/26         0.0.0.0           100     0 i
bgpctl show rib out
flags  vs destination          gateway          lpref   med aspath origin
AI*   N-? 192.0.2.0/24         10.83.0.138       100     0 i
bgpctl show rib out 192.0.2.0/24
flags  vs destination          gateway          lpref   med aspath origin
AI*   N-? 192.0.2.0/24         10.83.0.138       100     0 i

but
bgpctl show rib out 192.0.2.1
flags  vs destination          gateway          lpref   med aspath origin

The last command fails even though there is a route for 192.0.2.1 it just
hides behind 192.0.2.0/26.

The diff below fixes this. In the pt_lookup() case one needs to walk up
all possible prefixes to see if a covering route is actually exported.

Not super fond of this diff since it adds yet another loop into a nested
mess. This should all be refactored but I'm not doing that right now.
-- 
:wq Claudio

Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
diff -u -p -r1.700 rde.c
--- rde.c	13 May 2026 14:01:29 -0000	1.700
+++ rde.c	18 May 2026 11:36:40 -0000
@@ -3276,7 +3276,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
 	struct rib_entry	*re;
 	struct adjout_prefix	*p;
 	u_int			 error;
-	uint8_t			 hostplen, plen;
+	int			 hostplen, plen;
 	uint16_t		 rid;
 
 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
@@ -3342,6 +3342,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
 
 			do {
 				struct pt_entry *pte;
+				int found;
 
 				if (req->flags & F_SHORTER) {
 					for (plen = 0; plen <= req->prefixlen;
@@ -3370,13 +3371,28 @@ rde_dump_ctx_new(struct ctl_show_rib_req
 				if (pte == NULL)
 					continue;
 
-				/* dump all matching paths */
-				for (p = adjout_prefix_first(peer, pte);
-				    p != NULL;
-				    p = adjout_prefix_next(peer, pte, p)) {
-					rde_dump_adjout_upcall(peer, pte, p,
-					    ctx);
-				}
+				do {
+					/* dump all matching paths */
+					found = 0;
+					for (p = adjout_prefix_first(peer, pte);
+					    p != NULL;
+					    p = adjout_prefix_next(peer, pte,
+					    p)) {
+						rde_dump_adjout_upcall(peer,
+						    pte, p, ctx);
+						found = 1;
+					}
+					if (!found &&
+					    req->prefixlen == hostplen) {
+						for (plen = pte->prefixlen - 1;
+						    plen >= 0; plen--) {
+							pte = pt_get(
+							    &req->prefix, plen);
+							if (pte != NULL)
+								break;
+						}
+					}
+				} while (!found && pte != NULL);
 			} while ((peer = peer_match(&req->neighbor,
 			    peer->conf.id)));