Some people noticed that routes get stuck or to be more precise that
withdraws are not sent to peers in some cases. Until now bgpd did not
really track what was announced and what not (there is no real
Adj-RIB-Out) and instead tried to figure out if it should or should not
send the withdraw. With the increased filtering we try to push this fails
more often and the result is rather terrible.
Instead now introduce minimal tracking of announced prefixes. This adds a
per peer RB tree that tracks what was sent out as UPDATE. This can be
considered a minimal Adj-RIB-Out.

This is an important fix and needs to be tested.
-- 
:wq Claudio


Index: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.193
diff -u -p -r1.193 rde.h
--- rde.h       20 Sep 2018 11:45:59 -0000      1.193
+++ rde.h       27 Sep 2018 11:56:43 -0000
@@ -55,6 +55,7 @@ LIST_HEAD(aspath_head, rde_aspath);
 TAILQ_HEAD(aspath_queue, rde_aspath);
 RB_HEAD(uptree_prefix, update_prefix);
 RB_HEAD(uptree_attr, update_attr);
+RB_HEAD(uptree_rib, update_rib);
 
 struct rib_desc;
 struct rib;
@@ -70,6 +71,7 @@ struct rde_peer {
        struct bgpd_addr                 remote_addr;
        struct bgpd_addr                 local_v4_addr;
        struct bgpd_addr                 local_v6_addr;
+       struct uptree_rib                up_rib;
        struct uptree_prefix             up_prefix;
        struct uptree_attr               up_attrs;
        struct uplist_attr               updates[AID_MAX];
Index: rde_decide.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_decide.c,v
retrieving revision 1.71
diff -u -p -r1.71 rde_decide.c
--- rde_decide.c        29 Aug 2018 08:51:49 -0000      1.71
+++ rde_decide.c        27 Sep 2018 09:25:52 -0000
@@ -264,7 +264,7 @@ prefix_evaluate(struct prefix *p, struct
                if (LIST_EMPTY(&re->prefix_h))
                        LIST_INSERT_HEAD(&re->prefix_h, p, rib_l);
                else {
-                       LIST_FOREACH(xp, &re->prefix_h, rib_l)
+                       LIST_FOREACH(xp, &re->prefix_h, rib_l) {
                                if (prefix_cmp(p, xp) > 0) {
                                        LIST_INSERT_BEFORE(xp, p, rib_l);
                                        break;
@@ -273,6 +273,7 @@ prefix_evaluate(struct prefix *p, struct
                                        LIST_INSERT_AFTER(xp, p, rib_l);
                                        break;
                                }
+                       }
                }
        }
 
Index: rde_update.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v
retrieving revision 1.99
diff -u -p -r1.99 rde_update.c
--- rde_update.c        18 Sep 2018 16:54:01 -0000      1.99
+++ rde_update.c        27 Sep 2018 13:34:56 -0000
@@ -53,9 +53,17 @@ struct update_attr {
        u_int16_t                        mpattr_len;
 };
 
+struct update_rib {
+       RB_ENTRY(update_rib)             entry;
+       struct rib_entry                *re;
+};
+
 void   up_clear(struct uplist_attr *, struct uplist_prefix *);
 int    up_prefix_cmp(struct update_prefix *, struct update_prefix *);
 int    up_attr_cmp(struct update_attr *, struct update_attr *);
+int    up_rib_cmp(struct update_rib *, struct update_rib *);
+int    up_rib_remove(struct rde_peer *, struct rib_entry *);
+void   up_rib_add(struct rde_peer *, struct rib_entry *);
 int    up_add(struct rde_peer *, struct update_prefix *, struct update_attr *);
 
 RB_PROTOTYPE(uptree_prefix, update_prefix, entry, up_prefix_cmp)
@@ -64,6 +72,9 @@ RB_GENERATE(uptree_prefix, update_prefix
 RB_PROTOTYPE(uptree_attr, update_attr, entry, up_attr_cmp)
 RB_GENERATE(uptree_attr, update_attr, entry, up_attr_cmp)
 
+RB_PROTOTYPE(uptree_rib, update_rib, entry, up_rib_cmp)
+RB_GENERATE(uptree_rib, update_rib, entry, up_rib_cmp)
+
 SIPHASH_KEY uptree_key;
 
 void
@@ -77,6 +88,7 @@ up_init(struct rde_peer *peer)
        }
        RB_INIT(&peer->up_prefix);
        RB_INIT(&peer->up_attrs);
+       RB_INIT(&peer->up_rib);
        peer->up_pcnt = 0;
        peer->up_acnt = 0;
        peer->up_nlricnt = 0;
@@ -110,13 +122,18 @@ up_clear(struct uplist_attr *updates, st
 void
 up_down(struct rde_peer *peer)
 {
-       u_int8_t        i;
+       struct update_rib       *ur, *nur;
+       u_int8_t                i;
 
        for (i = 0; i < AID_MAX; i++)
                up_clear(&peer->updates[i], &peer->withdraws[i]);
 
        RB_INIT(&peer->up_prefix);
        RB_INIT(&peer->up_attrs);
+       RB_FOREACH_SAFE(ur, uptree_rib, &peer->up_rib, nur) {
+               RB_REMOVE(uptree_rib, &peer->up_rib, ur);
+               free(ur);
+       }
 
        peer->up_pcnt = 0;
        peer->up_acnt = 0;
@@ -195,6 +212,42 @@ up_attr_cmp(struct update_attr *a, struc
 }
 
 int
+up_rib_cmp(struct update_rib *a, struct update_rib *b)
+{
+       if (a->re != b->re)
+               return (a->re > b->re ? 1 : -1);
+       return 0;
+}
+
+int
+up_rib_remove(struct rde_peer *peer, struct rib_entry *re)
+{
+       struct update_rib *ur, u;
+       u.re = re;
+
+       ur = RB_FIND(uptree_rib, &peer->up_rib, &u);
+       if (ur != NULL) {
+               RB_REMOVE(uptree_rib, &peer->up_rib, ur);
+               free(ur);
+               return 1;
+       } else
+               return 0;
+}
+
+void
+up_rib_add(struct rde_peer *peer, struct rib_entry *re)
+{
+       struct update_rib *ur;
+
+       if ((ur = calloc(1, sizeof(*ur))) == NULL)
+               fatal("%s", __func__);
+       ur->re = re;
+
+       if (RB_INSERT(uptree_rib, &peer->up_rib, ur) != NULL)
+               free(ur);
+}
+
+int
 up_add(struct rde_peer *peer, struct update_prefix *p, struct update_attr *a)
 {
        struct update_attr      *na = NULL;
@@ -409,11 +462,11 @@ withdraw:
                if (up_test_update(peer, old) != 1)
                        return;
 
-               pt_getaddr(old->re->prefix, &addr);
-               if (rde_filter(rules, peer, old, NULL) == ACTION_DENY)
+               if (!up_rib_remove(peer, old->re))
                        return;
 
                /* withdraw prefix */
+               pt_getaddr(old->re->prefix, &addr);
                up_generate(peer, NULL, &addr, old->re->prefix->prefixlen);
        } else {
                switch (up_test_update(peer, new)) {
@@ -433,8 +486,8 @@ withdraw:
                }
 
                pt_getaddr(new->re->prefix, &addr);
-               up_generate(peer, &state, &addr,
-                   new->re->prefix->prefixlen);
+               up_generate(peer, &state, &addr, new->re->prefix->prefixlen);
+               up_rib_add(peer, new->re);
 
                rde_filterstate_clean(&state);
        }

Reply via email to