Re: bgpd: refactor update generation a bit

2022-07-06 Thread Theo Buehler
On Wed, Jul 06, 2022 at 06:56:28PM +0200, Claudio Jeker wrote:
> On Wed, Jul 06, 2022 at 06:15:45PM +0200, Theo Buehler wrote:
> > On Wed, Jul 06, 2022 at 05:07:45PM +0200, Claudio Jeker wrote:
> > > This diff changes various loops which call into up_generate_update() so
> > > that all these loops call the same function peer_generate_update() which
> > > then calls up_generate_update(). This is a step to add an alternative path
> > > to generate updates for add-path send support without altering many
> > > code-paths.
> > > 
> > > If I did not fool myself this should not alter current behaviour.
> > > rde_up_dump_upcall() gets a few more checks but all those checks should be
> > > false in this case (e.g. peer_dump checks the export_type already).
> > 
> > This looks good and I agree that the behavior is mostly preserved. I
> > noticed are two changes of behavior and I have a small question for
> > rde_up_dump_upcall():
> > 
> > > @@ -317,20 +374,10 @@ rde_up_dump_upcall(struct rib_entry *re,
> > >   struct rde_peer *peer = ptr;
> > >   struct prefix   *p;
> > >  
> > > - if (peer->state != PEER_UP)
> > > - return;
> > > - if (re->rib_id != peer->loc_rib_id)
> > > - fatalx("%s: Unexpected RIB %u != %u.", __func__, re->rib_id,
> > > - peer->loc_rib_id);
> > 
> > Failure of this check will now be silent.
> 
> This is fine, that code path was acting as an assert().
> rde_up_dump_upcall() is called by a RIB table walk for the
> peer->loc_rib_id RIB (see peer_dump()) so there should be no way that
> if (re->rib_id != peer->loc_rib_id) is true.

Indeed.

>  
> > > - if (peer->capa.mp[re->prefix->aid] == 0)
> > > - fatalx("%s: Unexpected %s prefix", __func__,
> > > - aid2str(re->prefix->aid));
> > 
> > Same here.
> 
> Similar reason. The rib walks each address family independent:
> for (i = 0; i < AID_MAX; i++) {
> if (peer->capa.mp[i])
> peer_dump(peer, i);
> }
> 
> Now this check is missing in the IMSG_REFRESH case in rde.c but other than
> that there should be no way to reach this code with a wrong aid.
> Skipping routes with non-negotiated address families should not matter
> here but it will make the code more robust.
> 

Makes sense.

>  
> > I got lost when checking that re->prefix->aid == p->pt->aid. I assume
> > that follows from the definition of the rib_entry's prefix queue?
> 
> If the prefix p is on the list of prefixes in re (part of re->prefix_h)
> then re->prefix == p->pt and so the aid check also needs to match.
> We cache the struct pt_entry * in a few places to simplify access.
> Since the code uses prefix_best(re) to get the p we know that p is part of
> the re list of prefixes.

All these comments made things a lot clearer. Thanks

> 
> > > -
> > >   /* no eligible prefix, not even for 'evaluate all' */
> > >   if ((p = prefix_best(re)) == NULL)
> > >   return;
> > > -
> > > - up_generate_updates(out_rules, peer, p, NULL);
> > > + peer_generate_update(peer, re->rib_id, p, NULL, 0);
> > >  }
> > >  
> > >  static void
> 
> Updated diff below that adds the extra aid check in the IMSG_REFRESH case. 

ok tb


> 
> -- 
> :wq Claudio
> 
> Index: rde.c
> ===
> RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
> retrieving revision 1.547
> diff -u -p -r1.547 rde.c
> --- rde.c 27 Jun 2022 13:26:51 -  1.547
> +++ rde.c 6 Jul 2022 16:43:22 -
> @@ -1123,12 +1123,19 @@ rde_dispatch_imsg_peer(struct rde_peer *
>   break;
>   case IMSG_REFRESH:
>   if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rr)) {
> - log_warnx("%s: wrong imsg len", __func__);
> + log_warnx("route refresh: wrong imsg len");
>   break;
>   }
>   memcpy(, imsg.data, sizeof(rr));
>   if (rr.aid >= AID_MAX) {
> - log_warnx("%s: bad AID", __func__);
> + log_peer_warnx(>conf,
> + "route refresh: bad AID %d", rr.aid);
> + break;
> + }
> + if (peer->capa.mp[rr.aid]) {
> + log_peer_warnx(>conf,
> + "route refresh: AID %s not negotiated",
> + aid2str(rr.aid));
>   break;
>   }
>   switch (rr.subtype) {
> @@ -1156,7 +1163,8 @@ rde_dispatch_imsg_peer(struct rde_peer *
>   aid2str(rr.aid));
>   break;
>   default:
> - log_warnx("%s: bad subtype %d", __func__, rr.subtype);
> + log_peer_warnx(>conf,
> + "route refresh: bad subtype %d", rr.subtype);
>   break;
>   }
>   break;
> @@ -3037,59 +3045,6 @@ rde_evaluate_all(void)
>   return 

Re: bgpd: refactor update generation a bit

2022-07-06 Thread Claudio Jeker
On Wed, Jul 06, 2022 at 06:15:45PM +0200, Theo Buehler wrote:
> On Wed, Jul 06, 2022 at 05:07:45PM +0200, Claudio Jeker wrote:
> > This diff changes various loops which call into up_generate_update() so
> > that all these loops call the same function peer_generate_update() which
> > then calls up_generate_update(). This is a step to add an alternative path
> > to generate updates for add-path send support without altering many
> > code-paths.
> > 
> > If I did not fool myself this should not alter current behaviour.
> > rde_up_dump_upcall() gets a few more checks but all those checks should be
> > false in this case (e.g. peer_dump checks the export_type already).
> 
> This looks good and I agree that the behavior is mostly preserved. I
> noticed are two changes of behavior and I have a small question for
> rde_up_dump_upcall():
> 
> > @@ -317,20 +374,10 @@ rde_up_dump_upcall(struct rib_entry *re,
> > struct rde_peer *peer = ptr;
> > struct prefix   *p;
> >  
> > -   if (peer->state != PEER_UP)
> > -   return;
> > -   if (re->rib_id != peer->loc_rib_id)
> > -   fatalx("%s: Unexpected RIB %u != %u.", __func__, re->rib_id,
> > -   peer->loc_rib_id);
> 
> Failure of this check will now be silent.

This is fine, that code path was acting as an assert().
rde_up_dump_upcall() is called by a RIB table walk for the
peer->loc_rib_id RIB (see peer_dump()) so there should be no way that
if (re->rib_id != peer->loc_rib_id) is true.
 
> > -   if (peer->capa.mp[re->prefix->aid] == 0)
> > -   fatalx("%s: Unexpected %s prefix", __func__,
> > -   aid2str(re->prefix->aid));
> 
> Same here.

Similar reason. The rib walks each address family independent:
for (i = 0; i < AID_MAX; i++) {
if (peer->capa.mp[i])
peer_dump(peer, i);
}

Now this check is missing in the IMSG_REFRESH case in rde.c but other than
that there should be no way to reach this code with a wrong aid.
Skipping routes with non-negotiated address families should not matter
here but it will make the code more robust.

 
> I got lost when checking that re->prefix->aid == p->pt->aid. I assume
> that follows from the definition of the rib_entry's prefix queue?

If the prefix p is on the list of prefixes in re (part of re->prefix_h)
then re->prefix == p->pt and so the aid check also needs to match.
We cache the struct pt_entry * in a few places to simplify access.
Since the code uses prefix_best(re) to get the p we know that p is part of
the re list of prefixes.

> > -
> > /* no eligible prefix, not even for 'evaluate all' */
> > if ((p = prefix_best(re)) == NULL)
> > return;
> > -
> > -   up_generate_updates(out_rules, peer, p, NULL);
> > +   peer_generate_update(peer, re->rib_id, p, NULL, 0);
> >  }
> >  
> >  static void

Updated diff below that adds the extra aid check in the IMSG_REFRESH case. 

-- 
:wq Claudio

Index: rde.c
===
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.547
diff -u -p -r1.547 rde.c
--- rde.c   27 Jun 2022 13:26:51 -  1.547
+++ rde.c   6 Jul 2022 16:43:22 -
@@ -1123,12 +1123,19 @@ rde_dispatch_imsg_peer(struct rde_peer *
break;
case IMSG_REFRESH:
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rr)) {
-   log_warnx("%s: wrong imsg len", __func__);
+   log_warnx("route refresh: wrong imsg len");
break;
}
memcpy(, imsg.data, sizeof(rr));
if (rr.aid >= AID_MAX) {
-   log_warnx("%s: bad AID", __func__);
+   log_peer_warnx(>conf,
+   "route refresh: bad AID %d", rr.aid);
+   break;
+   }
+   if (peer->capa.mp[rr.aid]) {
+   log_peer_warnx(>conf,
+   "route refresh: AID %s not negotiated",
+   aid2str(rr.aid));
break;
}
switch (rr.subtype) {
@@ -1156,7 +1163,8 @@ rde_dispatch_imsg_peer(struct rde_peer *
aid2str(rr.aid));
break;
default:
-   log_warnx("%s: bad subtype %d", __func__, rr.subtype);
+   log_peer_warnx(>conf,
+   "route refresh: bad subtype %d", rr.subtype);
break;
}
break;
@@ -3037,59 +3045,6 @@ rde_evaluate_all(void)
return rde_eval_all;
 }
 
-static int
-rde_skip_peer(struct rde_peer *peer, uint16_t rib_id, uint8_t aid)
-{
-   /* skip ourself */
-   if (peer == peerself)
-   return 1;
-   if (peer->state != PEER_UP)
-   return 1;
-   /* skip peers using a different rib */
-

Re: bgpd: refactor update generation a bit

2022-07-06 Thread Theo Buehler
On Wed, Jul 06, 2022 at 05:07:45PM +0200, Claudio Jeker wrote:
> This diff changes various loops which call into up_generate_update() so
> that all these loops call the same function peer_generate_update() which
> then calls up_generate_update(). This is a step to add an alternative path
> to generate updates for add-path send support without altering many
> code-paths.
> 
> If I did not fool myself this should not alter current behaviour.
> rde_up_dump_upcall() gets a few more checks but all those checks should be
> false in this case (e.g. peer_dump checks the export_type already).

This looks good and I agree that the behavior is mostly preserved. I
noticed are two changes of behavior and I have a small question for
rde_up_dump_upcall():

> @@ -317,20 +374,10 @@ rde_up_dump_upcall(struct rib_entry *re,
>   struct rde_peer *peer = ptr;
>   struct prefix   *p;
>  
> - if (peer->state != PEER_UP)
> - return;
> - if (re->rib_id != peer->loc_rib_id)
> - fatalx("%s: Unexpected RIB %u != %u.", __func__, re->rib_id,
> - peer->loc_rib_id);

Failure of this check will now be silent.

> - if (peer->capa.mp[re->prefix->aid] == 0)
> - fatalx("%s: Unexpected %s prefix", __func__,
> - aid2str(re->prefix->aid));

Same here.

I got lost when checking that re->prefix->aid == p->pt->aid. I assume
that follows from the definition of the rib_entry's prefix queue?

> -
>   /* no eligible prefix, not even for 'evaluate all' */
>   if ((p = prefix_best(re)) == NULL)
>   return;
> -
> - up_generate_updates(out_rules, peer, p, NULL);
> + peer_generate_update(peer, re->rib_id, p, NULL, 0);
>  }
>  
>  static void



bgpd: refactor update generation a bit

2022-07-06 Thread Claudio Jeker
This diff changes various loops which call into up_generate_update() so
that all these loops call the same function peer_generate_update() which
then calls up_generate_update(). This is a step to add an alternative path
to generate updates for add-path send support without altering many
code-paths.

If I did not fool myself this should not alter current behaviour.
rde_up_dump_upcall() gets a few more checks but all those checks should be
false in this case (e.g. peer_dump checks the export_type already).

-- 
:wq Claudio

Index: rde.c
===
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.547
diff -u -p -r1.547 rde.c
--- rde.c   27 Jun 2022 13:26:51 -  1.547
+++ rde.c   28 Jun 2022 19:38:42 -
@@ -3037,59 +3037,6 @@ rde_evaluate_all(void)
return rde_eval_all;
 }
 
-static int
-rde_skip_peer(struct rde_peer *peer, uint16_t rib_id, uint8_t aid)
-{
-   /* skip ourself */
-   if (peer == peerself)
-   return 1;
-   if (peer->state != PEER_UP)
-   return 1;
-   /* skip peers using a different rib */
-   if (peer->loc_rib_id != rib_id)
-   return 1;
-   /* check if peer actually supports the address family */
-   if (peer->capa.mp[aid] == 0)
-   return 1;
-   /* skip peers with special export types */
-   if (peer->export_type == EXPORT_NONE ||
-   peer->export_type == EXPORT_DEFAULT_ROUTE)
-   return 1;
-
-   return 0;
-}
-
-void
-rde_generate_updates(struct rib *rib, struct prefix *new, struct prefix *old,
-int eval_all)
-{
-   struct rde_peer *peer;
-   uint8_t  aid;
-
-   /*
-* If old is != NULL we know it was active and should be removed.
-* If new is != NULL we know it is reachable and then we should
-* generate an update.
-*/
-   if (old == NULL && new == NULL)
-   return;
-
-   if (new)
-   aid = new->pt->aid;
-   else
-   aid = old->pt->aid;
-
-   LIST_FOREACH(peer, , peer_l) {
-   if (rde_skip_peer(peer, rib->id, aid))
-   continue;
-   /* skip regular peers if the best path didn't change */
-   if ((peer->flags & PEERFLAG_EVALUATE_ALL) == 0 && eval_all)
-   continue;
-
-   up_generate_updates(out_rules, peer, new, old);
-   }
-}
-
 /* flush Adj-RIB-Out by withdrawing all prefixes */
 static void
 rde_up_flush_upcall(struct prefix *p, void *ptr)
@@ -3760,26 +3707,16 @@ rde_softreconfig_in(struct rib_entry *re
 }
 
 static void
-rde_softreconfig_out(struct rib_entry *re, void *bula)
+rde_softreconfig_out(struct rib_entry *re, void *arg)
 {
-   struct prefix   *p;
-   struct rde_peer *peer;
-   uint8_t  aid = re->prefix->aid;
+   struct rib  *rib = arg;
+   struct prefix   *p;
 
if ((p = prefix_best(re)) == NULL)
/* no valid path for prefix */
return;
 
-   LIST_FOREACH(peer, , peer_l) {
-   if (rde_skip_peer(peer, re->rib_id, aid))
-   continue;
-   /* skip peers which don't need to reconfigure */
-   if (peer->reconf_out == 0)
-   continue;
-
-   /* Regenerate all updates. */
-   up_generate_updates(out_rules, peer, p, p);
-   }
+   rde_generate_updates(rib, p, NULL, EVAL_RECONF);
 }
 
 static void
Index: rde.h
===
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.254
diff -u -p -r1.254 rde.h
--- rde.h   27 Jun 2022 13:26:51 -  1.254
+++ rde.h   6 Jul 2022 14:53:36 -
@@ -362,6 +362,12 @@ struct filterstate {
uint8_t  nhflags;
 };
 
+enum eval_mode {
+   EVAL_DEFAULT,
+   EVAL_ALL,
+   EVAL_RECONF,
+};
+
 extern struct rde_memstats rdemem;
 
 /* prototypes */
@@ -384,7 +390,7 @@ voidrde_pftable_del(uint16_t, struct p
 
 intrde_evaluate_all(void);
 void   rde_generate_updates(struct rib *, struct prefix *,
-   struct prefix *, int);
+   struct prefix *, enum eval_mode);
 uint32_t   rde_local_as(void);
 intrde_decisionflags(void);
 void   rde_peer_send_rrefresh(struct rde_peer *, uint8_t, uint8_t);
Index: rde_decide.c
===
RCS file: /cvs/src/usr.sbin/bgpd/rde_decide.c,v
retrieving revision 1.91
diff -u -p -r1.91 rde_decide.c
--- rde_decide.c22 Mar 2022 10:53:08 -  1.91
+++ rde_decide.c6 Jul 2022 14:54:58 -
@@ -493,7 +493,7 @@ prefix_evaluate(struct rib_entry *re, st
 * but remember that xp may be NULL aka ineligible.
 * Additional decision may be made by