>From the start bgpd had prefix_link and prefix_unlink to link all the
various data objects together to build an actual prefix. Now prefix_move()
tries to be smart and reimplemented prefix_link and prefix_unlink as
inline versions (with minimal differences). Later the prefix_adjout_*
functions were added and again similar code to prefix_link / prefix_unlink
was added.

This diff removes the code duplication and uses prefix_link and
prefix_unlink in all those extra places. This removes a common error when
changing the object layout because one of the many codepaths was often
missed.

To make this happen prefix_link() gained an extra argument for the
pt_entry (the adjout has no rib_entry and so no way to get the pt from
there). It also moves the pftable handling and prefix_evaluate() call to
the currently only caller (prefix_add). Add an extra check in nexthop_link
to skip prefixes in the adj-rib-out since those are not linked up.
With this prefix_link can be used in all other places.

prefix_unlink can be almost used as is only the pt_unref() needs attention
(or actually setting p->pt to NULL). In the Adj-RIB-Out prefixes are
unlinked but remain on the RB tree to act as withdraw requests. For this
prefix_adjout_update() adds an extra reference to the pt_entry so that
after calling prefix_unlink() the pt_entry is still valid.
prefix_adjout_destroy() takes this last reference before freeing the
prefix again.

The result is a much cleaner handling of prefixes and far fewer places to
introduce new bugs.
-- 
:wq Claudio

Index: rde_rib.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v
retrieving revision 1.227
diff -u -p -r1.227 rde_rib.c
--- rde_rib.c   25 Feb 2022 12:56:12 -0000      1.227
+++ rde_rib.c   28 Feb 2022 09:10:40 -0000
@@ -848,9 +848,9 @@ static int  prefix_move(struct prefix *, 
                    struct nexthop *, uint8_t, uint8_t);
 
 static void    prefix_link(struct prefix *, struct rib_entry *,
-                    struct rde_peer *, uint32_t, struct rde_aspath *,
-                    struct rde_community *, struct nexthop *,
-                    uint8_t, uint8_t);
+                    struct pt_entry *, struct rde_peer *, 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);
@@ -941,18 +941,21 @@ prefix_adjout_lookup(struct rde_peer *pe
        xp.pt = pt_fill(prefix, prefixlen);
 
        np = RB_NFIND(prefix_index, &peer->adj_rib_out, &xp);
-       if (np != NULL && pt_prefix_cmp(np->pt, xp.pt) != 0)
+       if (np == NULL || pt_prefix_cmp(np->pt, xp.pt) != 0)
                return NULL;
        return np;
 }
 
+/*
+ * 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 *np;
 
        np = RB_NEXT(prefix_index, &peer->adj_rib_out, p);
-       if (np == NULL || p->pt != np->pt)
+       if (np == NULL || np->pt != p->pt)
                return NULL;
        return np;
 }
@@ -1060,7 +1063,14 @@ prefix_add(struct bgpd_addr *prefix, int
                re = rib_add(rib, prefix, prefixlen);
 
        p = prefix_alloc();
-       prefix_link(p, re, peer, path_id, asp, comm, nexthop, nhflags, vstate);
+       prefix_link(p, re, re->prefix, peer, path_id, asp, comm, nexthop,
+           nhflags, vstate);
+
+       /* add possible pftable reference form aspath */
+       if (asp && asp->pftableid)
+               rde_pftable_add(asp->pftableid, p);
+       /* make route decision */
+       prefix_evaluate(re, p, NULL);
        return (1);
 }
 
@@ -1082,18 +1092,8 @@ prefix_move(struct prefix *p, struct rde
 
        /* add new prefix node */
        np = prefix_alloc();
-       /* add reference to new AS path and communities */
-       np->aspath = path_ref(asp);
-       np->communities = communities_ref(comm);
-       np->peer = peer;
-       np->entry.list.re = prefix_re(p);
-       np->pt = p->pt; /* skip refcnt update since ref is moved */
-       np->path_id = p->path_id;
-       np->validation_state = vstate;
-       np->nhflags = nhflags;
-       np->nexthop = nexthop_ref(nexthop);
-       nexthop_link(np);
-       np->lastchange = getmonotime();
+       prefix_link(np, prefix_re(p), p->pt, peer, p->path_id, asp, comm,
+           nexthop, nhflags, vstate);
 
        /* add possible pftable reference from new aspath */
        if (asp && asp->pftableid)
@@ -1105,21 +1105,12 @@ prefix_move(struct prefix *p, struct rde
         */
        prefix_evaluate(prefix_re(np), np, p);
 
-       /* remove possible pftable reference from old path first */
+       /* remove possible pftable reference from old path */
        if (p->aspath && p->aspath->pftableid)
                rde_pftable_del(p->aspath->pftableid, p);
 
        /* remove old prefix node */
-       nexthop_unlink(p);
-       nexthop_unref(p->nexthop);
-       communities_unref(p->communities);
-       path_unref(p->aspath);
-       p->communities = NULL;
-       p->nexthop = NULL;
-       p->aspath = NULL;
-       p->peer = NULL;
-       p->pt = NULL;
-       p->entry.list.re = NULL;
+       prefix_unlink(p);
        prefix_free(p);
 
        return (0);
@@ -1213,14 +1204,10 @@ prefix_adjout_update(struct rde_peer *pe
                                prefix_head = &peer->updates[prefix->aid];
                                RB_REMOVE(prefix_tree, prefix_head, p);
                        }
+                       /* unlink prefix so it can be relinked below */
+                       prefix_unlink(p);
                }
-               /* unlink from aspath and remove nexthop ref */
-               nexthop_unref(p->nexthop);
-               communities_unref(p->communities);
-               path_unref(p->aspath);
                p->flags &= ~PREFIX_FLAG_MASK;
-
-               /* peer and pt remain */
        } else {
                p = prefix_alloc();
                p->flags |= PREFIX_FLAG_ADJOUT;
@@ -1248,13 +1235,8 @@ prefix_adjout_update(struct rde_peer *pe
                comm = communities_link(&state->communities);
        }
 
-       p->aspath = path_ref(asp);
-       p->communities = communities_ref(comm);
-       p->nexthop = nexthop_ref(state->nexthop);
-       p->nhflags = state->nhflags;
-
-       p->validation_state = vstate;
-       p->lastchange = getmonotime();
+       prefix_link(p, NULL, p->pt, peer, 0, asp, comm, state->nexthop,
+           state->nhflags, vstate);
 
        if (p->flags & PREFIX_FLAG_MASK)
                fatalx("%s: bad flags %x", __func__, p->flags);
@@ -1282,35 +1264,18 @@ prefix_adjout_withdraw(struct rde_peer *
        if ((p->flags & PREFIX_FLAG_ADJOUT) == 0)
                fatalx("%s: prefix without PREFIX_FLAG_ADJOUT hit", __func__);
 
-       /* already a withdraw, shortcut */
-       if (p->flags & PREFIX_FLAG_WITHDRAW) {
-               p->lastchange = getmonotime();
-               p->flags &= ~PREFIX_FLAG_STALE;
-               return (0);
-       }
+       /* already a withdraw, error */
+       if (p->flags & PREFIX_FLAG_WITHDRAW)
+               log_warnx("%s: prefix already withdrawed", __func__);
        /* pending update just got withdrawn */
        if (p->flags & PREFIX_FLAG_UPDATE)
                RB_REMOVE(prefix_tree, &peer->updates[p->pt->aid], 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);
+
        /* nothing needs to be done for PREFIX_FLAG_DEAD and STALE */
        p->flags &= ~PREFIX_FLAG_MASK;
-
-       /* remove nexthop ref ... */
-       nexthop_unref(p->nexthop);
-       p->nexthop = NULL;
-       p->nhflags = 0;
-
-       /* unlink from aspath ...*/
-       path_unref(p->aspath);
-       p->aspath = NULL;
-
-       /* ... communities ... */
-       communities_unref(p->communities);
-       p->communities = NULL;
-       /* and unlink from aspath */
-       path_unref(p->aspath);
-       p->aspath = NULL;
-       /* re already NULL */
-
        p->lastchange = getmonotime();
 
        p->flags |= PREFIX_FLAG_WITHDRAW;
@@ -1353,34 +1318,25 @@ prefix_adjout_destroy(struct prefix *p)
 
        if (p->flags & PREFIX_FLAG_WITHDRAW)
                RB_REMOVE(prefix_tree, &peer->withdraws[p->pt->aid], p);
-       else if (p->flags & PREFIX_FLAG_UPDATE)
+       if (p->flags & PREFIX_FLAG_UPDATE)
                RB_REMOVE(prefix_tree, &peer->updates[p->pt->aid], 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);
+
        /* nothing needs to be done for PREFIX_FLAG_DEAD and STALE */
        p->flags &= ~PREFIX_FLAG_MASK;
 
 
        if (prefix_is_locked(p)) {
-               /* remove nexthop ref ... */
-               nexthop_unref(p->nexthop);
-               p->nexthop = NULL;
-               /* ... communities ... */
-               communities_unref(p->communities);
-               p->communities = NULL;
-               /* and unlink from aspath */
-               path_unref(p->aspath);
-               p->aspath = NULL;
-               p->nhflags = 0;
-               /* re already NULL */
-
-               /* finally mark prefix dead */
+               /* mark prefix dead but leave it for prefix_restart */
                p->flags |= PREFIX_FLAG_DEAD;
-               return;
+       } else {
+               RB_REMOVE(prefix_index, &peer->adj_rib_out, p);
+               /* remove the last prefix reference before free */
+               pt_unref(p->pt);
+               prefix_free(p);
        }
-
-       RB_REMOVE(prefix_index, &peer->adj_rib_out, p);
-
-       prefix_unlink(p);
-       prefix_free(p);
 }
 
 static void
@@ -1588,31 +1544,24 @@ prefix_destroy(struct prefix *p)
  * Link a prefix into the different parent objects.
  */
 static void
-prefix_link(struct prefix *p, struct rib_entry *re, struct rde_peer *peer,
-    uint32_t path_id, struct rde_aspath *asp, struct rde_community *comm,
-    struct nexthop *nexthop, uint8_t nhflags, uint8_t vstate)
+prefix_link(struct prefix *p, struct rib_entry *re, struct pt_entry *pt,
+    struct rde_peer *peer, uint32_t path_id, struct rde_aspath *asp,
+    struct rde_community *comm, struct nexthop *nexthop, uint8_t nhflags,
+    uint8_t vstate)
 {
-       if (p->flags & PREFIX_FLAG_ADJOUT)
-               fatalx("%s: prefix with PREFIX_FLAG_ADJOUT hit", __func__);
 
-       p->entry.list.re = re;
+       if (re)
+               p->entry.list.re = re;
        p->aspath = path_ref(asp);
        p->communities = communities_ref(comm);
        p->peer = peer;
-       p->pt = pt_ref(re->prefix);
+       p->pt = pt_ref(pt);
        p->path_id = path_id;
        p->validation_state = vstate;
        p->nhflags = nhflags;
        p->nexthop = nexthop_ref(nexthop);
        nexthop_link(p);
        p->lastchange = getmonotime();
-
-       /* add possible pftable reference from aspath */
-       if (asp && asp->pftableid)
-               rde_pftable_add(asp->pftableid, p);
-
-       /* make route decision */
-       prefix_evaluate(re, p, NULL);
 }
 
 /*
@@ -1624,24 +1573,22 @@ prefix_unlink(struct prefix *p)
        struct rib_entry        *re = prefix_re(p);
 
        /* destroy all references to other objects */
+       /* remove nexthop ref ... */
        nexthop_unlink(p);
        nexthop_unref(p->nexthop);
+       p->nexthop = NULL;
+       p->nhflags = 0;
+       /* ... communities ... */
        communities_unref(p->communities);
-       path_unref(p->aspath);
-       pt_unref(p->pt);
        p->communities = NULL;
-       p->nexthop = NULL;
+       /* and unlink from aspath */
+       path_unref(p->aspath);
        p->aspath = NULL;
-       p->peer = NULL;
-       p->pt = NULL;
 
        if (re && rib_empty(re))
                rib_remove(re);
 
-       /*
-        * It's the caller's duty to do accounting and remove empty aspath
-        * structures. Also freeing the unlinked prefix is the caller's duty.
-        */
+       pt_unref(p->pt);
 }
 
 /* alloc and zero new entry. May not fail. */
@@ -1857,6 +1804,8 @@ void
 nexthop_link(struct prefix *p)
 {
        if (p->nexthop == NULL)
+               return;
+       if (p->flags & PREFIX_FLAG_ADJOUT)
                return;
 
        /* no need to link prefixes in RIBs that have no decision process */

Reply via email to