From: Claudio Jeker Subject: bgpd: next round of adjout shuffling To: tech@openbsd.org Date: Wed, 19 Nov 2025 15:09:37 +0100 Duplicate struct prefix into struct prefix_adjout and adjust code to work with that. This is the next step to allow me to alter struct prefix_adjout in big ways. Most bits are ok but esp the dump code for the control socket is a somewhat horrible copy-paste job. I hope that over time this can be reduced but for now this gets us going :) -- :wq Claudio ? obj Index: rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v diff -u -p -r1.665 rde.c --- rde.c 18 Nov 2025 16:39:36 -0000 1.665 +++ rde.c 19 Nov 2025 13:49:20 -0000 @@ -109,8 +109,8 @@ static void flowspec_dump_upcall(struct static void flowspec_dump_done(void *, uint8_t); void rde_shutdown(void); -static int ovs_match(struct prefix *, uint32_t); -static int avs_match(struct prefix *, uint32_t); +static int ovs_match(uint8_t, uint32_t); +static int avs_match(uint8_t, uint32_t); static struct imsgbuf *ibuf_se; static struct imsgbuf *ibuf_se_ctl; @@ -2840,8 +2840,7 @@ rde_reflector(struct rde_peer *peer, str * control specific functions */ static void -rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags, - int adjout) +rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags) { struct ctl_show_rib rib; struct ibuf *wbuf; @@ -2880,7 +2879,7 @@ rde_dump_rib_as(struct prefix *p, struct rib.aspa_validation_state = prefix_aspa_vstate(p); rib.dmetric = p->dmetric; rib.flags = 0; - if (!adjout && prefix_eligible(p)) { + if (prefix_eligible(p)) { re = prefix_re(p); TAILQ_FOREACH(xp, &re->prefix_h, entry.list.rib) { switch (xp->dmetric) { @@ -2921,17 +2920,91 @@ rde_dump_rib_as(struct prefix *p, struct if (monotime_valid(staletime) && monotime_cmp(p->lastchange, staletime) <= 0) rib.flags |= F_PREF_STALE; - if (!adjout) { - if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_RECV)) { - rib.path_id = p->path_id; - rib.flags |= F_PREF_PATH_ID; + if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_RECV)) { + rib.path_id = p->path_id; + rib.flags |= F_PREF_PATH_ID; + } + aslen = aspath_length(asp->aspath); + + if ((wbuf = imsg_create(ibuf_se_ctl, IMSG_CTL_SHOW_RIB, 0, pid, + sizeof(rib) + aslen)) == NULL) + return; + if (imsg_add(wbuf, &rib, sizeof(rib)) == -1 || + imsg_add(wbuf, aspath_dump(asp->aspath), aslen) == -1) + return; + imsg_close(ibuf_se_ctl, wbuf); + + if (flags & F_CTL_DETAIL) { + struct rde_community *comm = prefix_communities(p); + size_t len = comm->nentries * sizeof(struct community); + if (comm->nentries > 0) { + if (imsg_compose(ibuf_se_ctl, + IMSG_CTL_SHOW_RIB_COMMUNITIES, 0, pid, -1, + comm->communities, len) == -1) + return; } - } else { - if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_SEND)) { - rib.path_id = p->path_id_tx; - rib.flags |= F_PREF_PATH_ID; + for (l = 0; l < asp->others_len; l++) { + if ((a = asp->others[l]) == NULL) + break; + if ((wbuf = imsg_create(ibuf_se_ctl, + IMSG_CTL_SHOW_RIB_ATTR, 0, pid, 0)) == NULL) + return; + if (attr_writebuf(wbuf, a->flags, a->type, a->data, + a->len) == -1) { + ibuf_free(wbuf); + return; + } + imsg_close(ibuf_se_ctl, wbuf); } } +} + +static void +rde_dump_adjout_as(struct prefix_adjout *p, struct rde_aspath *asp, pid_t pid, + int flags) +{ + struct ctl_show_rib rib; + struct ibuf *wbuf; + struct attr *a; + struct nexthop *nexthop; + struct rde_peer *peer; + size_t aslen; + uint8_t l; + + nexthop = prefix_adjout_nexthop(p); + peer = prefix_adjout_peer(p); + memset(&rib, 0, sizeof(rib)); + rib.lastchange = p->lastchange; + rib.local_pref = asp->lpref; + rib.med = asp->med; + rib.weight = asp->weight; + strlcpy(rib.descr, peer->conf.descr, sizeof(rib.descr)); + memcpy(&rib.remote_addr, &peer->remote_addr, + sizeof(rib.remote_addr)); + rib.remote_id = peer->remote_bgpid; + if (nexthop != NULL) { + rib.exit_nexthop = nexthop->exit_nexthop; + rib.true_nexthop = nexthop->true_nexthop; + } else { + /* announced network can have a NULL nexthop */ + rib.exit_nexthop.aid = p->pt->aid; + rib.true_nexthop.aid = p->pt->aid; + } + pt_getaddr(p->pt, &rib.prefix); + rib.prefixlen = p->pt->prefixlen; + rib.origin = asp->origin; + /* roa and aspa vstate skipped, they don't matter in adj-rib-out */ + rib.dmetric = p->dmetric; + rib.flags = 0; + rib.flags |= F_PREF_ELIGIBLE; + if (!peer->conf.ebgp) + rib.flags |= F_PREF_INTERNAL; + if (asp->flags & F_PREFIX_ANNOUNCED) + rib.flags |= F_PREF_ANNOUNCE; + if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_SEND)) { + rib.path_id = p->path_id_tx; + rib.flags |= F_PREF_PATH_ID; + } aslen = aspath_length(asp->aspath); if ((wbuf = imsg_create(ibuf_se_ctl, IMSG_CTL_SHOW_RIB, 0, pid, @@ -2943,7 +3016,7 @@ rde_dump_rib_as(struct prefix *p, struct imsg_close(ibuf_se_ctl, wbuf); if (flags & F_CTL_DETAIL) { - struct rde_community *comm = prefix_communities(p); + struct rde_community *comm = prefix_adjout_communities(p); size_t len = comm->nentries * sizeof(struct community); if (comm->nentries > 0) { if (imsg_compose(ibuf_se_ctl, @@ -2986,7 +3059,7 @@ rde_match_peer(struct rde_peer *p, struc } static void -rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req, int adjout) +rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req) { struct rde_aspath *asp; @@ -3007,14 +3080,8 @@ rde_dump_filter(struct prefix *p, struct (asp->flags & F_ATTR_OTC_LEAK) == 0) return; if ((req->flags & F_CTL_HAS_PATHID)) { - /* Match against the transmit path id if adjout is used. */ - if (adjout) { - if (req->path_id != p->path_id_tx) - return; - } else { - if (req->path_id != p->path_id) - return; - } + if (req->path_id != p->path_id) + return; } if (req->as.type != AS_UNDEF && !aspath_match(asp->aspath, &req->as, 0)) @@ -3024,11 +3091,38 @@ rde_dump_filter(struct prefix *p, struct NULL)) return; } - if (!ovs_match(p, req->flags)) + if (!ovs_match(prefix_roa_vstate(p), req->flags)) + return; + if (!avs_match(prefix_aspa_vstate(p), req->flags)) + return; + rde_dump_rib_as(p, asp, req->pid, req->flags); +} + +static void +rde_dump_adjout_filter(struct prefix_adjout *p, + struct ctl_show_rib_request *req) +{ + struct rde_aspath *asp; + + if (!rde_match_peer(prefix_adjout_peer(p), &req->neighbor)) return; - if (!avs_match(p, req->flags)) + + asp = prefix_adjout_aspath(p); + if ((req->flags & F_CTL_HAS_PATHID)) { + /* Match against the transmit path id if adjout is used. */ + if (req->path_id != p->path_id_tx) + return; + } + if (req->as.type != AS_UNDEF && + !aspath_match(asp->aspath, &req->as, 0)) return; - rde_dump_rib_as(p, asp, req->pid, req->flags, adjout); + if (req->community.flags != 0) { + if (!community_match(prefix_adjout_communities(p), + &req->community, NULL)) + return; + } + /* in the adj-rib-out, skip matching against roa and aspa state */ + rde_dump_adjout_as(p, asp, req->pid, req->flags); } static void @@ -3040,11 +3134,11 @@ rde_dump_upcall(struct rib_entry *re, vo if (re == NULL) return; TAILQ_FOREACH(p, &re->prefix_h, entry.list.rib) - rde_dump_filter(p, &ctx->req, 0); + rde_dump_filter(p, &ctx->req); } static void -rde_dump_adjout_upcall(struct prefix *p, void *ptr) +rde_dump_adjout_upcall(struct prefix_adjout *p, void *ptr) { struct rde_dump_ctx *ctx = ptr; @@ -3052,7 +3146,7 @@ rde_dump_adjout_upcall(struct prefix *p, fatalx("%s: prefix without PREFIX_FLAG_ADJOUT hit", __func__); if (p->flags & PREFIX_FLAG_WITHDRAW) return; - rde_dump_filter(p, &ctx->req, 1); + rde_dump_adjout_filter(p, &ctx->req); } static int @@ -3114,7 +3208,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req { struct rde_dump_ctx *ctx; struct rib_entry *re; - struct prefix *p; + struct prefix_adjout *p; u_int error; uint8_t hostplen, plen; uint16_t rid; @@ -3468,7 +3562,7 @@ rde_evaluate_all(void) /* flush Adj-RIB-Out by withdrawing all prefixes */ static void -rde_up_flush_upcall(struct prefix *p, void *ptr) +rde_up_flush_upcall(struct prefix_adjout *p, void *ptr) { prefix_adjout_withdraw(p); } @@ -4856,10 +4950,10 @@ rde_roa_validity(struct rde_prefixset *p } static int -ovs_match(struct prefix *p, uint32_t flag) +ovs_match(uint8_t roa_vstate, uint32_t flag) { if (flag & (F_CTL_OVS_VALID|F_CTL_OVS_INVALID|F_CTL_OVS_NOTFOUND)) { - switch (prefix_roa_vstate(p)) { + switch (roa_vstate) { case ROA_VALID: if (!(flag & F_CTL_OVS_VALID)) return 0; @@ -4881,10 +4975,10 @@ ovs_match(struct prefix *p, uint32_t fla } static int -avs_match(struct prefix *p, uint32_t flag) +avs_match(uint8_t aspa_vstate, uint32_t flag) { if (flag & (F_CTL_AVS_VALID|F_CTL_AVS_INVALID|F_CTL_AVS_UNKNOWN)) { - switch (prefix_aspa_vstate(p) & ASPA_MASK) { + switch (aspa_vstate & ASPA_MASK) { case ASPA_VALID: if (!(flag & F_CTL_AVS_VALID)) return 0; Index: rde.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v diff -u -p -r1.320 rde.h --- rde.h 19 Nov 2025 09:49:27 -0000 1.320 +++ rde.h 19 Nov 2025 13:49:21 -0000 @@ -71,8 +71,8 @@ struct rib { * Currently I assume that we can do that with the neighbor_ip... */ RB_HEAD(peer_tree, rde_peer); -RB_HEAD(prefix_tree, prefix); -RB_HEAD(prefix_index, prefix); +RB_HEAD(prefix_tree, prefix_adjout); +RB_HEAD(prefix_index, prefix_adjout); struct rde_peer { RB_ENTRY(rde_peer) entry; @@ -273,9 +273,6 @@ struct prefix { LIST_ENTRY(prefix) nexthop; struct rib_entry *re; } list; - struct { - RB_ENTRY(prefix) index, update; - } tree; } entry; struct pt_entry *pt; struct rde_aspath *aspath; @@ -316,6 +313,26 @@ struct prefix { #define NEXTHOP_MASK 0x0f #define NEXTHOP_VALID 0x80 +struct prefix_adjout { + union { + struct { + RB_ENTRY(prefix_adjout) index, update; + } tree; + } entry; + struct pt_entry *pt; + struct rde_aspath *aspath; + struct rde_community *communities; + struct rde_peer *peer; + struct nexthop *nexthop; /* may be NULL */ + monotime_t lastchange; + uint32_t path_id; + uint32_t path_id_tx; + uint16_t flags; + uint8_t validation_state; + uint8_t nhflags; + int8_t dmetric; /* decision metric */ +}; + struct filterstate { struct rde_aspath aspath; struct rde_community communities; @@ -333,10 +350,10 @@ enum eval_mode { struct rib_context { LIST_ENTRY(rib_context) entry; struct rib_entry *ctx_re; - struct prefix *ctx_p; + struct prefix_adjout *ctx_p; uint32_t ctx_id; void (*ctx_rib_call)(struct rib_entry *, void *); - void (*ctx_prefix_call)(struct prefix *, void *); + void (*ctx_prefix_call)(struct prefix_adjout *, void *); void (*ctx_done)(void *, uint8_t); int (*ctx_throttle)(void *); void *ctx_arg; @@ -597,6 +614,8 @@ re_rib(struct rib_entry *re) } void path_init(void); +struct rde_aspath *path_ref(struct rde_aspath *); +void path_unref(struct rde_aspath *); int path_equal(const struct rde_aspath *, const struct rde_aspath *); struct rde_aspath *path_getcache(struct rde_aspath *); @@ -622,17 +641,11 @@ void prefix_flowspec_dump(uint8_t, voi void (*)(struct rib_entry *, void *), void (*)(void *, uint8_t)); -void prefix_link(struct prefix *, struct rib_entry *, - struct pt_entry *, struct rde_peer *, uint32_t, uint32_t, - struct rde_aspath *, struct rde_community *, - struct nexthop *, uint8_t, uint8_t); -void prefix_unlink(struct prefix *); - struct prefix *prefix_bypeer(struct rib_entry *, struct rde_peer *, uint32_t); void prefix_destroy(struct prefix *); -RB_PROTOTYPE(prefix_tree, prefix, entry, prefix_cmp) +RB_PROTOTYPE(prefix_tree, prefix_adjout, entry, prefix_cmp) static inline struct rde_peer * prefix_peer(struct prefix *p) @@ -716,30 +729,64 @@ struct nexthop *nexthop_ref(struct nexth int nexthop_unref(struct nexthop *); /* rde_adjout.c */ -struct prefix *prefix_adjout_get(struct rde_peer *, uint32_t, - struct pt_entry *); -struct prefix *prefix_adjout_first(struct rde_peer *, struct pt_entry *); -struct prefix *prefix_adjout_next(struct rde_peer *, struct prefix *); -struct prefix *prefix_adjout_lookup(struct rde_peer *, struct bgpd_addr *, - int); -struct prefix *prefix_adjout_match(struct rde_peer *, struct bgpd_addr *); +struct prefix_adjout *prefix_adjout_get(struct rde_peer *, uint32_t, + struct pt_entry *); +struct prefix_adjout *prefix_adjout_first(struct rde_peer *, + struct pt_entry *); +struct prefix_adjout *prefix_adjout_next(struct rde_peer *, + struct prefix_adjout *); +struct prefix_adjout *prefix_adjout_lookup(struct rde_peer *, + struct bgpd_addr *, int); +struct prefix_adjout *prefix_adjout_match(struct rde_peer *, + struct bgpd_addr *); void prefix_add_eor(struct rde_peer *, uint8_t); -void prefix_adjout_update(struct prefix *, struct rde_peer *, +void prefix_adjout_update(struct prefix_adjout *, struct rde_peer *, struct filterstate *, struct pt_entry *, uint32_t); -void prefix_adjout_withdraw(struct prefix *); -void prefix_adjout_destroy(struct prefix *); +void prefix_adjout_withdraw(struct prefix_adjout *); +void prefix_adjout_destroy(struct prefix_adjout *); void prefix_adjout_flush_pending(struct rde_peer *); int prefix_adjout_reaper(struct rde_peer *); -void prefix_adjout_dump_cleanup(struct prefix *); +void prefix_adjout_dump_cleanup(struct prefix_adjout *); void prefix_adjout_dump_r(struct rib_context *); int prefix_adjout_dump_new(struct rde_peer *, uint8_t, - unsigned int, void *, void (*)(struct prefix *, void *), + unsigned int, void *, + void (*)(struct prefix_adjout *, void *), void (*)(void *, uint8_t), int (*)(void *)); int prefix_adjout_dump_subtree(struct rde_peer *, struct bgpd_addr *, uint8_t, unsigned int, void *, - void (*)(struct prefix *, void *), + void (*)(struct prefix_adjout *, void *), void (*)(void *, uint8_t), int (*)(void *)); + +static inline struct rde_peer * +prefix_adjout_peer(struct prefix_adjout *p) +{ + return (p->peer); +} + +static inline struct rde_aspath * +prefix_adjout_aspath(struct prefix_adjout *p) +{ + return (p->aspath); +} + +static inline struct rde_community * +prefix_adjout_communities(struct prefix_adjout *p) +{ + return (p->communities); +} + +static inline struct nexthop * +prefix_adjout_nexthop(struct prefix_adjout *p) +{ + return (p->nexthop); +} + +static inline uint8_t +prefix_adjout_nhflags(struct prefix_adjout *p) +{ + return (p->nhflags & NEXTHOP_MASK); +} /* rde_update.c */ void up_generate_updates(struct rde_peer *, struct rib_entry *); Index: rde_adjout.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_adjout.c,v diff -u -p -r1.2 rde_adjout.c --- rde_adjout.c 19 Nov 2025 09:49:27 -0000 1.2 +++ rde_adjout.c 19 Nov 2025 13:49:21 -0000 @@ -30,8 +30,8 @@ /* adj-rib-out specific functions */ -static inline struct prefix * -prefix_lock(struct prefix *p) +static inline struct prefix_adjout * +prefix_adjout_lock(struct prefix_adjout *p) { if (p->flags & PREFIX_FLAG_LOCKED) fatalx("%s: locking locked prefix", __func__); @@ -39,8 +39,8 @@ prefix_lock(struct prefix *p) return p; } -static inline struct prefix * -prefix_unlock(struct prefix *p) +static inline struct prefix_adjout * +prefix_adjout_unlock(struct prefix_adjout *p) { if ((p->flags & PREFIX_FLAG_LOCKED) == 0) fatalx("%s: unlocking unlocked prefix", __func__); @@ -49,23 +49,29 @@ prefix_unlock(struct prefix *p) } static inline int -prefix_is_locked(struct prefix *p) +prefix_is_locked(struct prefix_adjout *p) { return (p->flags & PREFIX_FLAG_LOCKED) != 0; } static inline int -prefix_is_dead(struct prefix *p) +prefix_is_dead(struct prefix_adjout *p) { return (p->flags & PREFIX_FLAG_DEAD) != 0; } -static struct prefix *prefix_alloc(void); -static void prefix_free(struct prefix *); +static void prefix_adjout_link(struct prefix_adjout *, struct pt_entry *, + struct rde_peer *, uint32_t, uint32_t, + struct rde_aspath *, struct rde_community *, + struct nexthop *, uint8_t, uint8_t); +static void prefix_adjout_unlink(struct prefix_adjout *); + +static struct prefix_adjout *prefix_adjout_alloc(void); +static void prefix_adjout_free(struct prefix_adjout *); /* RB tree comparison function */ static inline int -prefix_index_cmp(struct prefix *a, struct prefix *b) +prefix_index_cmp(struct prefix_adjout *a, struct prefix_adjout *b) { int r; r = pt_prefix_cmp(a->pt, b->pt); @@ -80,7 +86,7 @@ prefix_index_cmp(struct prefix *a, struc } static inline int -prefix_cmp(struct prefix *a, struct prefix *b) +prefix_cmp(struct prefix_adjout *a, struct prefix_adjout *b) { if ((a->flags & PREFIX_FLAG_EOR) != (b->flags & PREFIX_FLAG_EOR)) return (a->flags & PREFIX_FLAG_EOR) ? 1 : -1; @@ -99,18 +105,18 @@ prefix_cmp(struct prefix *a, struct pref return prefix_index_cmp(a, b); } -RB_GENERATE(prefix_tree, prefix, entry.tree.update, prefix_cmp) -RB_GENERATE_STATIC(prefix_index, prefix, entry.tree.index, prefix_index_cmp) +RB_GENERATE(prefix_tree, prefix_adjout, entry.tree.update, prefix_cmp) +RB_GENERATE_STATIC(prefix_index, prefix_adjout, entry.tree.index, prefix_index_cmp) /* * Search for specified prefix in the peer prefix_index. * Returns NULL if not found. */ -struct prefix * +struct prefix_adjout * prefix_adjout_get(struct rde_peer *peer, uint32_t path_id_tx, struct pt_entry *pte) { - struct prefix xp; + struct prefix_adjout xp; memset(&xp, 0, sizeof(xp)); xp.pt = pte; @@ -123,10 +129,10 @@ prefix_adjout_get(struct rde_peer *peer, * Lookup a prefix without considering path_id in the peer prefix_index. * Returns NULL if not found. */ -struct prefix * +struct prefix_adjout * prefix_adjout_first(struct rde_peer *peer, struct pt_entry *pte) { - struct prefix xp, *np; + struct prefix_adjout xp, *np; memset(&xp, 0, sizeof(xp)); xp.pt = pte; @@ -140,10 +146,10 @@ prefix_adjout_first(struct rde_peer *pee /* * Return next prefix after a lookup that is actually an update. */ -struct prefix * -prefix_adjout_next(struct rde_peer *peer, struct prefix *p) +struct prefix_adjout * +prefix_adjout_next(struct rde_peer *peer, struct prefix_adjout *p) { - struct prefix *np; + struct prefix_adjout *np; np = RB_NEXT(prefix_index, &peer->adj_rib_out, p); if (np == NULL || np->pt != p->pt) @@ -155,7 +161,7 @@ prefix_adjout_next(struct rde_peer *peer * Lookup addr/prefixlen in the peer prefix_index. Returns first match. * Returns NULL if not found. */ -struct prefix * +struct prefix_adjout * prefix_adjout_lookup(struct rde_peer *peer, struct bgpd_addr *addr, int plen) { return prefix_adjout_first(peer, pt_fill(addr, plen)); @@ -165,10 +171,10 @@ prefix_adjout_lookup(struct rde_peer *pe * Lookup addr in the peer prefix_index. Returns first match. * Returns NULL if not found. */ -struct prefix * +struct prefix_adjout * prefix_adjout_match(struct rde_peer *peer, struct bgpd_addr *addr) { - struct prefix *p; + struct prefix_adjout *p; int i; switch (addr->aid) { @@ -200,13 +206,13 @@ prefix_adjout_match(struct rde_peer *pee void prefix_add_eor(struct rde_peer *peer, uint8_t aid) { - struct prefix *p; + struct prefix_adjout *p; - p = prefix_alloc(); + p = prefix_adjout_alloc(); p->flags = PREFIX_FLAG_ADJOUT | PREFIX_FLAG_UPDATE | PREFIX_FLAG_EOR; if (RB_INSERT(prefix_tree, &peer->updates[aid], p) != NULL) /* no need to add if EoR marker already present */ - prefix_free(p); + prefix_adjout_free(p); /* EOR marker is not inserted into the adj_rib_out index */ } @@ -214,14 +220,14 @@ prefix_add_eor(struct rde_peer *peer, ui * Put a prefix from the Adj-RIB-Out onto the update queue. */ void -prefix_adjout_update(struct prefix *p, struct rde_peer *peer, +prefix_adjout_update(struct prefix_adjout *p, struct rde_peer *peer, struct filterstate *state, struct pt_entry *pte, uint32_t path_id_tx) { struct rde_aspath *asp; struct rde_community *comm; if (p == NULL) { - p = prefix_alloc(); + p = prefix_adjout_alloc(); /* initially mark DEAD so code below is skipped */ p->flags |= PREFIX_FLAG_ADJOUT | PREFIX_FLAG_DEAD; @@ -243,11 +249,11 @@ prefix_adjout_update(struct prefix *p, s * paths. */ if (p->path_id_tx == path_id_tx && - prefix_nhflags(p) == state->nhflags && - prefix_nexthop(p) == state->nexthop && + prefix_adjout_nhflags(p) == state->nhflags && + prefix_adjout_nexthop(p) == state->nexthop && communities_equal(&state->communities, - prefix_communities(p)) && - path_equal(&state->aspath, prefix_aspath(p))) { + prefix_adjout_communities(p)) && + path_equal(&state->aspath, prefix_adjout_aspath(p))) { /* nothing changed */ p->validation_state = state->vstate; p->lastchange = getmonotime(); @@ -262,7 +268,7 @@ prefix_adjout_update(struct prefix *p, s } /* unlink prefix so it can be relinked below */ - prefix_unlink(p); + prefix_adjout_unlink(p); peer->stats.prefix_out_cnt--; } if (p->flags & PREFIX_FLAG_WITHDRAW) { @@ -289,7 +295,7 @@ prefix_adjout_update(struct prefix *p, s comm = communities_link(&state->communities); } - prefix_link(p, NULL, p->pt, peer, 0, p->path_id_tx, asp, comm, + prefix_adjout_link(p, p->pt, peer, 0, p->path_id_tx, asp, comm, state->nexthop, state->nhflags, state->vstate); peer->stats.prefix_out_cnt++; @@ -308,9 +314,9 @@ prefix_adjout_update(struct prefix *p, s * the prefix in the RIB linked to the peer withdraw list. */ void -prefix_adjout_withdraw(struct prefix *p) +prefix_adjout_withdraw(struct prefix_adjout *p) { - struct rde_peer *peer = prefix_peer(p); + struct rde_peer *peer = prefix_adjout_peer(p); if ((p->flags & PREFIX_FLAG_ADJOUT) == 0) fatalx("%s: prefix without PREFIX_FLAG_ADJOUT hit", __func__); @@ -328,7 +334,7 @@ prefix_adjout_withdraw(struct prefix *p) } /* unlink prefix if it was linked (not a withdraw or dead) */ if ((p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD)) == 0) { - prefix_unlink(p); + prefix_adjout_unlink(p); peer->stats.prefix_out_cnt--; } @@ -350,16 +356,16 @@ prefix_adjout_withdraw(struct prefix *p) } void -prefix_adjout_destroy(struct prefix *p) +prefix_adjout_destroy(struct prefix_adjout *p) { - struct rde_peer *peer = prefix_peer(p); + struct rde_peer *peer = prefix_adjout_peer(p); if ((p->flags & PREFIX_FLAG_ADJOUT) == 0) fatalx("%s: prefix without PREFIX_FLAG_ADJOUT hit", __func__); if (p->flags & PREFIX_FLAG_EOR) { /* EOR marker is not linked in the index */ - prefix_free(p); + prefix_adjout_free(p); return; } @@ -373,7 +379,7 @@ prefix_adjout_destroy(struct prefix *p) } /* unlink prefix if it was linked (not a withdraw or dead) */ if ((p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD)) == 0) { - prefix_unlink(p); + prefix_adjout_unlink(p); peer->stats.prefix_out_cnt--; } @@ -387,14 +393,14 @@ prefix_adjout_destroy(struct prefix *p) RB_REMOVE(prefix_index, &peer->adj_rib_out, p); /* remove the last prefix reference before free */ pt_unref(p->pt); - prefix_free(p); + prefix_adjout_free(p); } } void prefix_adjout_flush_pending(struct rde_peer *peer) { - struct prefix *p, *np; + struct prefix_adjout *p, *np; uint8_t aid; for (aid = AID_MIN; aid < AID_MAX; aid++) { @@ -416,7 +422,7 @@ prefix_adjout_flush_pending(struct rde_p int prefix_adjout_reaper(struct rde_peer *peer) { - struct prefix *p, *np; + struct prefix_adjout *p, *np; int count = RDE_REAPER_ROUNDS; RB_FOREACH_SAFE(p, prefix_index, &peer->adj_rib_out, np) { @@ -427,16 +433,16 @@ prefix_adjout_reaper(struct rde_peer *pe return 1; } -static struct prefix * +static struct prefix_adjout * prefix_restart(struct rib_context *ctx) { - struct prefix *p = NULL; + struct prefix_adjout *p = NULL; if (ctx->ctx_p) - p = prefix_unlock(ctx->ctx_p); + p = prefix_adjout_unlock(ctx->ctx_p); - if (p && prefix_is_dead(p)) { - struct prefix *next; + while (p && prefix_is_dead(p)) { + struct prefix_adjout *next; next = RB_NEXT(prefix_index, unused, p); prefix_adjout_destroy(p); @@ -447,16 +453,16 @@ prefix_restart(struct rib_context *ctx) } void -prefix_adjout_dump_cleanup(struct prefix *p) +prefix_adjout_dump_cleanup(struct prefix_adjout *p) { - if (prefix_is_dead(prefix_unlock(p))) + if (prefix_is_dead(prefix_adjout_unlock(p))) prefix_adjout_destroy(p); } void prefix_adjout_dump_r(struct rib_context *ctx) { - struct prefix *p, *next; + struct prefix_adjout *p, *next; struct rde_peer *peer; unsigned int i; @@ -486,7 +492,7 @@ prefix_adjout_dump_r(struct rib_context if (ctx->ctx_count && i++ >= ctx->ctx_count && !prefix_is_locked(p)) { /* store and lock last element */ - ctx->ctx_p = prefix_lock(p); + ctx->ctx_p = prefix_adjout_lock(p); return; } ctx->ctx_prefix_call(p, ctx->ctx_arg); @@ -501,7 +507,7 @@ done: int prefix_adjout_dump_new(struct rde_peer *peer, uint8_t aid, unsigned int count, - void *arg, void (*upcall)(struct prefix *, void *), + void *arg, void (*upcall)(struct prefix_adjout *, void *), void (*done)(void *, uint8_t), int (*throttle)(void *)) { struct rib_context *ctx; @@ -528,11 +534,12 @@ prefix_adjout_dump_new(struct rde_peer * int prefix_adjout_dump_subtree(struct rde_peer *peer, struct bgpd_addr *subtree, uint8_t subtreelen, unsigned int count, void *arg, - void (*upcall)(struct prefix *, void *), void (*done)(void *, uint8_t), + void (*upcall)(struct prefix_adjout *, void *), + void (*done)(void *, uint8_t), int (*throttle)(void *)) { struct rib_context *ctx; - struct prefix xp; + struct prefix_adjout xp; if ((ctx = calloc(1, sizeof(*ctx))) == NULL) return -1; @@ -551,7 +558,7 @@ prefix_adjout_dump_subtree(struct rde_pe xp.pt = pt_fill(subtree, subtreelen); ctx->ctx_p = RB_NFIND(prefix_index, &peer->adj_rib_out, &xp); if (ctx->ctx_p) - prefix_lock(ctx->ctx_p); + prefix_adjout_lock(ctx->ctx_p); rib_dump_insert(ctx); @@ -562,11 +569,55 @@ prefix_adjout_dump_subtree(struct rde_pe return 0; } +/* + * Link a prefix into the different parent objects. + */ +static void +prefix_adjout_link(struct prefix_adjout *p, struct pt_entry *pt, + struct rde_peer *peer, uint32_t path_id, uint32_t path_id_tx, + struct rde_aspath *asp, struct rde_community *comm, + struct nexthop *nexthop, uint8_t nhflags, uint8_t vstate) +{ + p->aspath = path_ref(asp); + p->communities = communities_ref(comm); + p->peer = peer; + p->pt = pt_ref(pt); + p->path_id = path_id; + p->path_id_tx = path_id_tx; + p->validation_state = vstate; + p->nexthop = nexthop_ref(nexthop); + p->nhflags = nhflags; + /* All nexthops are valid in Adj-RIB-Out */ + p->nhflags |= NEXTHOP_VALID; + p->lastchange = getmonotime(); +} + +/* + * Unlink a prefix from the different parent objects. + */ +static void +prefix_adjout_unlink(struct prefix_adjout *p) +{ + /* destroy all references to other objects */ + /* remove nexthop ref ... */ + nexthop_unref(p->nexthop); + p->nexthop = NULL; + p->nhflags = 0; + /* ... communities ... */ + communities_unref(p->communities); + p->communities = NULL; + /* and unlink from aspath */ + path_unref(p->aspath); + p->aspath = NULL; + + pt_unref(p->pt); +} + /* alloc and zero new entry. May not fail. */ -static struct prefix * -prefix_alloc(void) +static struct prefix_adjout * +prefix_adjout_alloc(void) { - struct prefix *p; + struct prefix_adjout *p; p = calloc(1, sizeof(*p)); if (p == NULL) @@ -577,7 +628,7 @@ prefix_alloc(void) /* free a unlinked entry */ static void -prefix_free(struct prefix *p) +prefix_adjout_free(struct prefix_adjout *p) { rdemem.prefix_cnt--; free(p); Index: rde_peer.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v diff -u -p -r1.53 rde_peer.c --- rde_peer.c 18 Nov 2025 16:39:36 -0000 1.53 +++ rde_peer.c 19 Nov 2025 13:49:21 -0000 @@ -516,12 +516,12 @@ peer_stale(struct rde_peer *peer, uint8_ * Enqueue a prefix onto the update queue so it can be sent out. */ static void -peer_blast_upcall(struct prefix *p, void *ptr) +peer_blast_upcall(struct prefix_adjout *p, void *ptr) { struct rde_peer *peer; if ((p->flags & PREFIX_FLAG_MASK) == 0) { - peer = prefix_peer(p); + peer = prefix_adjout_peer(p); /* put entries on the update queue if not already on a queue */ p->flags |= PREFIX_FLAG_UPDATE; if (RB_INSERT(prefix_tree, &peer->updates[p->pt->aid], Index: rde_rib.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v diff -u -p -r1.278 rde_rib.c --- rde_rib.c 19 Nov 2025 13:30:59 -0000 1.278 +++ rde_rib.c 19 Nov 2025 13:49:21 -0000 @@ -624,7 +624,7 @@ CH_PROTOTYPE(path_tree, rde_aspath, path static struct path_tree pathtable = CH_INITIALIZER(&pathtable); -static inline struct rde_aspath * +struct rde_aspath * path_ref(struct rde_aspath *asp) { if ((asp->flags & F_ATTR_LINKED) == 0) @@ -635,7 +635,7 @@ path_ref(struct rde_aspath *asp) return asp; } -static inline void +void path_unref(struct rde_aspath *asp) { if (asp == NULL) @@ -788,6 +788,12 @@ static int prefix_move(struct prefix *, struct rde_aspath *, struct rde_community *, struct nexthop *, uint8_t, uint8_t, int); +static void prefix_link(struct prefix *, struct rib_entry *, + struct pt_entry *, struct rde_peer *, uint32_t, uint32_t, + struct rde_aspath *, struct rde_community *, + struct nexthop *, uint8_t, uint8_t); +static void prefix_unlink(struct prefix *); + static struct prefix *prefix_alloc(void); static void prefix_free(struct prefix *); @@ -1083,7 +1089,7 @@ prefix_destroy(struct prefix *p) /* * Link a prefix into the different parent objects. */ -void +static void prefix_link(struct prefix *p, struct rib_entry *re, struct pt_entry *pt, struct rde_peer *peer, uint32_t path_id, uint32_t path_id_tx, struct rde_aspath *asp, struct rde_community *comm, @@ -1107,7 +1113,7 @@ prefix_link(struct prefix *p, struct rib /* * Unlink a prefix from the different parent objects. */ -void +static void prefix_unlink(struct prefix *p) { struct rib_entry *re = prefix_re(p); Index: rde_update.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v diff -u -p -r1.179 rde_update.c --- rde_update.c 13 Nov 2025 21:43:12 -0000 1.179 +++ rde_update.c 19 Nov 2025 13:49:21 -0000 @@ -158,7 +158,8 @@ up_enforce_open_policy(struct rde_peer * * - UP_EXCLUDED if prefix was excluded because of up_test_update() */ static enum up_state -up_process_prefix(struct rde_peer *peer, struct prefix *new, struct prefix *p) +up_process_prefix(struct rde_peer *peer, struct prefix *new, + struct prefix_adjout *p) { struct filterstate state; struct bgpd_addr addr; @@ -217,7 +218,8 @@ up_process_prefix(struct rde_peer *peer, void up_generate_updates(struct rde_peer *peer, struct rib_entry *re) { - struct prefix *new, *p; + struct prefix *new; + struct prefix_adjout *p; p = prefix_adjout_first(peer, re->prefix); @@ -255,7 +257,8 @@ done: void up_generate_addpath(struct rde_peer *peer, struct rib_entry *re) { - struct prefix *head, *new, *p; + struct prefix *new; + struct prefix_adjout *head, *p; int maxpaths = 0, extrapaths = 0, extra; int checkmode = 1; @@ -343,7 +346,7 @@ void up_generate_addpath_all(struct rde_peer *peer, struct rib_entry *re, struct prefix *new, struct prefix *old) { - struct prefix *p; + struct prefix_adjout *p; /* * If old and new are NULL then re-insert all prefixes from re, @@ -387,7 +390,7 @@ up_generate_default(struct rde_peer *pee extern struct rde_peer *peerself; struct filterstate state; struct rde_aspath *asp; - struct prefix *p; + struct prefix_adjout *p; struct pt_entry *pte; struct bgpd_addr addr; @@ -791,7 +794,7 @@ up_generate_attr(struct ibuf *buf, struc int up_is_eor(struct rde_peer *peer, uint8_t aid) { - struct prefix *p; + struct prefix_adjout *p; p = RB_MIN(prefix_tree, &peer->updates[aid]); if (p != NULL && (p->flags & PREFIX_FLAG_EOR)) { @@ -811,7 +814,7 @@ up_is_eor(struct rde_peer *peer, uint8_t #define MIN_UPDATE_LEN 16 static void -up_prefix_free(struct prefix_tree *prefix_head, struct prefix *p, +up_prefix_free(struct prefix_tree *prefix_head, struct prefix_adjout *p, struct rde_peer *peer, int withdraw) { if (withdraw) { @@ -836,8 +839,8 @@ static int up_dump_prefix(struct ibuf *buf, struct prefix_tree *prefix_head, struct rde_peer *peer, int withdraw) { - struct prefix *p, *np; - int done = 0, has_ap = -1, rv = -1; + struct prefix_adjout *p, *np; + int done = 0, has_ap = -1, rv = -1; RB_FOREACH_SAFE(p, prefix_tree, prefix_head, np) { if (has_ap == -1) @@ -1079,7 +1082,8 @@ up_dump_withdraws(struct imsgbuf *imsg, * Withdraw a single prefix after an error. */ static int -up_dump_withdraw_one(struct rde_peer *peer, struct prefix *p, struct ibuf *buf) +up_dump_withdraw_one(struct rde_peer *peer, struct prefix_adjout *p, + struct ibuf *buf) { size_t off; int has_ap; @@ -1152,7 +1156,7 @@ up_dump_update(struct imsgbuf *imsg, str { struct ibuf *buf; struct bgpd_addr addr; - struct prefix *p; + struct prefix_adjout *p; size_t off, pkgsize = MAX_PKTSIZE; uint16_t len; int force_ip4mp = 0; @@ -1162,7 +1166,7 @@ up_dump_update(struct imsgbuf *imsg, str return; if (aid == AID_INET && peer_has_ext_nexthop(peer, AID_INET)) { - struct nexthop *nh = prefix_nexthop(p); + struct nexthop *nh = prefix_adjout_nexthop(p); if (nh != NULL && nh->exit_nexthop.aid == AID_INET6) force_ip4mp = 1; } @@ -1185,8 +1189,8 @@ up_dump_update(struct imsgbuf *imsg, str if (ibuf_add_zero(buf, sizeof(len)) == -1) goto fail; - if (up_generate_attr(buf, peer, prefix_aspath(p), - prefix_communities(p), prefix_nexthop(p), aid) == -1) + if (up_generate_attr(buf, peer, prefix_adjout_aspath(p), + prefix_adjout_communities(p), prefix_adjout_nexthop(p), aid) == -1) goto drop; if (aid != AID_INET || force_ip4mp) { @@ -1198,8 +1202,8 @@ up_dump_update(struct imsgbuf *imsg, str * merge the attributes together in reverse order of * creation. */ - if (up_generate_mp_reach(buf, peer, prefix_nexthop(p), aid) == - -1) + if (up_generate_mp_reach(buf, peer, prefix_adjout_nexthop(p), + aid) == -1) goto drop; }