On Wed, Mar 02, 2022 at 01:25:42PM +0100, Theo Buehler wrote:
> On Wed, Mar 02, 2022 at 01:07:09PM +0100, Claudio Jeker wrote:
> > On Wed, Mar 02, 2022 at 01:03:04PM +0100, Claudio Jeker wrote:
> > > This diff changes prefix_adjout_withdraw() to take a prefix pointer
> > > as argument. So instead of doing the lookup in the withdraw function the
> > > caller may need to do it.
> > > 
> > > With this one call to up_generate_updates() can be replaced with a direct
> > > call to prefix_adjout_withdraw(). rde_up_flush_upcall() tries to withdraw
> > > every prefix in the Adj-RIB-Out of a peer. The indirection via
> > > up_generate_updates() makes little sense here.
> > 
> > Forgot a hunk from the much bigger diff I wrestle with.
> > There is no need to pass the peer to rde_up_flush_upcall. This also fixes
> > a minor bug with rde_softreconfig_in_done() which expects arg to be
> > NULL.
> 
> I'm ok with this, though I have one question.
> 
> > @@ -1252,21 +1252,19 @@ prefix_adjout_update(struct rde_peer *pe
> >   * the prefix in the RIB linked to the peer withdraw list.
> >   */
> >  int
> > -prefix_adjout_withdraw(struct rde_peer *peer, struct bgpd_addr *prefix,
> > -    int prefixlen)
> > +prefix_adjout_withdraw(struct prefix *p)
> >  {
> > -   struct prefix *p;
> > -
> > -   p = prefix_adjout_get(peer, 0, prefix, prefixlen);
> > -   if (p == NULL)          /* Got a dummy withdrawn request. */
> > -           return (0);
> > +   struct rde_peer *peer = prefix_peer(p);
> >  
> >     if ((p->flags & PREFIX_FLAG_ADJOUT) == 0)
> >             fatalx("%s: prefix without PREFIX_FLAG_ADJOUT hit", __func__);
> >  
> > -   /* already a withdraw, error */
> > -   if (p->flags & PREFIX_FLAG_WITHDRAW)
> > -           log_warnx("%s: prefix already withdrawed", __func__);
> > +   /* already a withdraw, shortcut */
> > +   if (p->flags & PREFIX_FLAG_WITHDRAW) {
> > +           p->lastchange = getmonotime();
> > +           p->flags &= ~PREFIX_FLAG_STALE;
> > +           return (0);
> > +   }
> 
> I'm a bit confused by this part. You changed this to an error a few days
> ago, now you change it back to a shortcut. Why?
> 

I forgot to mention that. So during reconfigure rde_up_flush_upcall()
walks over the full adj_rib_out tree. This includes pending withdraws and
we need to properly handle them. I decided it is best to revert this back
to the way it was.

The current code is actually incorrect.  The RB_INSERT() at the end of
prefix_adjout_withdraw() would try re-insert the prefix that is already in
the tree.

Looking at this again, I realized that the accounting is not quite right.
When prefix_adjout_withdraw is called on a PREFIX_FLAG_DEAD prefix the
prefix_out_cnt is lowered but it should not. Again this is a very uncommon
case but it is wrong none the less.
up_wcnt and up_nlricnt need to be lowered where the corresponding RB_REMOVE()
calls are. The prefix_out_cnt() needs to be lowered when prefix_unlink()
is called. A similar change should be done for prefix_adjout_update() but
I will do that as a next step.

-- 
:wq Claudio

Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.538
diff -u -p -r1.538 rde.c
--- rde.c       28 Feb 2022 12:52:38 -0000      1.538
+++ rde.c       2 Mar 2022 13:32:38 -0000
@@ -3050,9 +3050,7 @@ rde_generate_updates(struct rib *rib, st
 static void
 rde_up_flush_upcall(struct prefix *p, void *ptr)
 {
-       struct rde_peer *peer = ptr;
-
-       up_generate_updates(out_rules, peer, NULL, p);
+       prefix_adjout_withdraw(p);
 }
 
 u_char queue_buf[4096];
@@ -3444,7 +3442,7 @@ rde_reload_done(void)
 
                if (peer->reconf_rib) {
                        if (prefix_dump_new(peer, AID_UNSPEC,
-                           RDE_RUNNER_ROUNDS, peer, rde_up_flush_upcall,
+                           RDE_RUNNER_ROUNDS, NULL, rde_up_flush_upcall,
                            rde_softreconfig_in_done, NULL) == -1)
                                fatal("%s: prefix_dump_new", __func__);
                        log_peer_info(&peer->conf, "flushing Adj-RIB-Out");
Index: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.244
diff -u -p -r1.244 rde.h
--- rde.h       25 Feb 2022 11:36:54 -0000      1.244
+++ rde.h       2 Mar 2022 13:32:12 -0000
@@ -596,8 +596,7 @@ int          prefix_withdraw(struct rib *, stru
 void            prefix_add_eor(struct rde_peer *, uint8_t);
 int             prefix_adjout_update(struct rde_peer *, struct filterstate *,
                    struct bgpd_addr *, int, uint8_t);
-int             prefix_adjout_withdraw(struct rde_peer *, struct bgpd_addr *,
-                   int);
+void            prefix_adjout_withdraw(struct prefix *);
 void            prefix_adjout_destroy(struct prefix *p);
 void            prefix_adjout_dump(struct rde_peer *, void *,
                    void (*)(struct prefix *, void *));
Index: rde_rib.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v
retrieving revision 1.230
diff -u -p -r1.230 rde_rib.c
--- rde_rib.c   1 Mar 2022 09:39:36 -0000       1.230
+++ rde_rib.c   2 Mar 2022 13:39:35 -0000
@@ -1251,37 +1251,40 @@ prefix_adjout_update(struct rde_peer *pe
  * Withdraw a prefix from the Adj-RIB-Out, this unlinks the aspath but leaves
  * the prefix in the RIB linked to the peer withdraw list.
  */
-int
-prefix_adjout_withdraw(struct rde_peer *peer, struct bgpd_addr *prefix,
-    int prefixlen)
+void
+prefix_adjout_withdraw(struct prefix *p)
 {
-       struct prefix *p;
-
-       p = prefix_adjout_get(peer, 0, prefix, prefixlen);
-       if (p == NULL)          /* Got a dummy withdrawn request. */
-               return (0);
+       struct rde_peer *peer = prefix_peer(p);
 
        if ((p->flags & PREFIX_FLAG_ADJOUT) == 0)
                fatalx("%s: prefix without PREFIX_FLAG_ADJOUT hit", __func__);
 
-       /* already a withdraw, error */
-       if (p->flags & PREFIX_FLAG_WITHDRAW)
-               log_warnx("%s: prefix already withdrawed", __func__);
+       /* already a withdraw, shortcut */
+       if (p->flags & PREFIX_FLAG_WITHDRAW) {
+               p->lastchange = getmonotime();
+               p->flags &= ~PREFIX_FLAG_STALE;
+               return;
+       }
        /* pending update just got withdrawn */
-       if (p->flags & PREFIX_FLAG_UPDATE)
+       if (p->flags & PREFIX_FLAG_UPDATE) {
                RB_REMOVE(prefix_tree, &peer->updates[p->pt->aid], p);
+               peer->up_nlricnt--;
+       }
        /* unlink prefix if it was linked (not a withdraw or dead) */
-       if ((p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD)) == 0)
+       if ((p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD)) == 0) {
                prefix_unlink(p);
+               peer->prefix_out_cnt--;
+       }
 
        /* nothing needs to be done for PREFIX_FLAG_DEAD and STALE */
        p->flags &= ~PREFIX_FLAG_MASK;
        p->lastchange = getmonotime();
 
        p->flags |= PREFIX_FLAG_WITHDRAW;
-       if (RB_INSERT(prefix_tree, &peer->withdraws[prefix->aid], p) != NULL)
+       if (RB_INSERT(prefix_tree, &peer->withdraws[p->pt->aid], p) != NULL)
                fatalx("%s: RB tree invariant violated", __func__);
-       return (1);
+
+       peer->up_wcnt++;
 }
 
 void
@@ -1298,13 +1301,19 @@ prefix_adjout_destroy(struct prefix *p)
                return;
        }
 
-       if (p->flags & PREFIX_FLAG_WITHDRAW)
+       if (p->flags & PREFIX_FLAG_WITHDRAW) {
                RB_REMOVE(prefix_tree, &peer->withdraws[p->pt->aid], p);
-       if (p->flags & PREFIX_FLAG_UPDATE)
+               peer->up_wcnt--;
+       }
+       if (p->flags & PREFIX_FLAG_UPDATE) {
                RB_REMOVE(prefix_tree, &peer->updates[p->pt->aid], p);
+               peer->up_nlricnt--;
+       }
        /* unlink prefix if it was linked (not a withdraw or dead) */
-       if ((p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD)) == 0)
+       if ((p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD)) == 0) {
                prefix_unlink(p);
+               peer->prefix_out_cnt--;
+       }
 
        /* nothing needs to be done for PREFIX_FLAG_DEAD and STALE */
        p->flags &= ~PREFIX_FLAG_MASK;
Index: rde_update.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v
retrieving revision 1.134
diff -u -p -r1.134 rde_update.c
--- rde_update.c        1 Mar 2022 09:53:42 -0000       1.134
+++ rde_update.c        2 Mar 2022 13:35:16 -0000
@@ -102,6 +102,7 @@ up_generate_updates(struct filter_head *
 {
        struct filterstate      state;
        struct bgpd_addr        addr;
+       struct prefix           *p;
        int                     need_withdraw;
        uint8_t                 prefixlen;
 
@@ -119,10 +120,8 @@ up_generate_updates(struct filter_head *
 again:
        if (new == NULL) {
                /* withdraw prefix */
-               if (prefix_adjout_withdraw(peer, &addr, prefixlen) == 1) {
-                       peer->prefix_out_cnt--;
-                       peer->up_wcnt++;
-               }
+               if ((p = prefix_adjout_get(peer, 0, &addr, prefixlen)) != NULL)
+                       prefix_adjout_withdraw(p);
        } else {
                need_withdraw = 0;
                /*
@@ -655,7 +654,6 @@ up_dump_prefix(u_char *buf, int len, str
                if (withdraw) {
                        /* prefix no longer needed, remove it */
                        prefix_adjout_destroy(p);
-                       peer->up_wcnt--;
                        peer->prefix_sent_withdraw++;
                } else {
                        /* prefix still in Adj-RIB-Out, keep it */

Reply via email to