Claudio Jeker([email protected]) on 2021.05.05 14:20:58 +0200:
> The peer flags (mainly rde evaluate all but also transparent-as) and the
> export options (none, default) are not properly handled on a config
> reload. In both cases a full session restart is needed after the config
> reload (with a bit of extra wait time to ensure that the peer config is
> actually up to date).
>
> The following diff should fix this.
>
> Add an extra reload barrier (IMSG_RECONF_DRAIN) to the sync of the peer
> config from the session engine to the rde. This way it can be ensured that
> the peer config is up to date in the RDE before hitting reconfiguration
> function.
>
> Store the export_type and the peer flags outside of peer->conf. Adjust all
> users of these two fields so they only look at the copies in peer.
> This way the RDE is able to notice that a value has changed during reload.
> Flush the Adj-RIB-Out for a peer where either the export_type changed or
> where rde evaluate or transparent-as is changed. After the flush the RIB
> is rebuilt in a 2nd step.
>
> Fix multiple issues in the rde_softreconfig_in_done handler that resulted
> in multiple runs of the out stage of the softreconfig pipeline.
>
> OK?
yes, ok.
> --
> :wq Claudio
>
> Index: rde.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
> retrieving revision 1.519
> diff -u -p -r1.519 rde.c
> --- rde.c 27 Apr 2021 09:07:10 -0000 1.519
> +++ rde.c 5 May 2021 12:04:55 -0000
> @@ -666,6 +666,10 @@ badnetdel:
> rde_dump_ctx_throttle(imsg.hdr.pid, 1);
> }
> break;
> + case IMSG_RECONF_DRAIN:
> + imsg_compose(ibuf_se, IMSG_RECONF_DRAIN, 0, 0,
> + -1, NULL, 0);
> + break;
> default:
> break;
> }
> @@ -2136,7 +2140,7 @@ rde_update_log(const char *message, u_in
> char *p = NULL;
>
> if (!((conf->log & BGPD_LOG_UPDATES) ||
> - (peer->conf.flags & PEERFLAG_LOG_UPDATES)))
> + (peer->flags & PEERFLAG_LOG_UPDATES)))
> return;
>
> if (next != NULL)
> @@ -2919,7 +2923,7 @@ rde_generate_updates(struct rib *rib, st
> if (peer->state != PEER_UP)
> continue;
> /* handle evaluate all, keep track if it is needed */
> - if (peer->conf.flags & PEERFLAG_EVALUATE_ALL)
> + if (peer->flags & PEERFLAG_EVALUATE_ALL)
> rde_eval_all = 1;
> else if (eval_all)
> /* skip default peers if the best path didn't change */
> @@ -2931,8 +2935,8 @@ rde_generate_updates(struct rib *rib, st
> if (peer->capa.mp[aid] == 0)
> continue;
> /* skip peers with special export types */
> - if (peer->conf.export_type == EXPORT_NONE ||
> - peer->conf.export_type == EXPORT_DEFAULT_ROUTE)
> + if (peer->export_type == EXPORT_NONE ||
> + peer->export_type == EXPORT_DEFAULT_ROUTE)
> continue;
>
> up_generate_updates(out_rules, peer, new, old);
> @@ -3289,13 +3293,34 @@ rde_reload_done(void)
> continue;
> peer->reconf_out = 0;
> peer->reconf_rib = 0;
> + if (peer->export_type != peer->conf.export_type) {
> + log_peer_info(&peer->conf, "export type change, "
> + "reloading");
> + peer->reconf_rib = 1;
> + }
> + if ((peer->flags & PEERFLAG_EVALUATE_ALL) !=
> + (peer->conf.flags & PEERFLAG_EVALUATE_ALL)) {
> + log_peer_info(&peer->conf, "rde evaluate change, "
> + "reloading");
> + peer->reconf_rib = 1;
> + }
> + if ((peer->flags & PEERFLAG_TRANS_AS) !=
> + (peer->conf.flags & PEERFLAG_TRANS_AS)) {
> + log_peer_info(&peer->conf, "transparent-as change, "
> + "reloading");
> + peer->reconf_rib = 1;
> + }
> if (peer->loc_rib_id != rib_find(peer->conf.rib)) {
> log_peer_info(&peer->conf, "rib change, reloading");
> peer->loc_rib_id = rib_find(peer->conf.rib);
> if (peer->loc_rib_id == RIB_NOTFOUND)
> fatalx("King Bula's peer met an unknown RIB");
> peer->reconf_rib = 1;
> - softreconfig++;
> + }
> + peer->export_type = peer->conf.export_type;
> + peer->flags = peer->conf.flags;
> +
> + if (peer->reconf_rib) {
> if (prefix_dump_new(peer, AID_UNSPEC,
> RDE_RUNNER_ROUNDS, NULL, rde_up_flush_upcall,
> rde_softreconfig_in_done, NULL) == -1)
> @@ -3362,15 +3387,15 @@ rde_reload_done(void)
>
> log_info("RDE reconfigured");
>
> + softreconfig++;
> if (reload > 0) {
> - softreconfig++;
> if (rib_dump_new(RIB_ADJ_IN, AID_UNSPEC, RDE_RUNNER_ROUNDS,
> - rib_byid(RIB_ADJ_IN), rde_softreconfig_in,
> - rde_softreconfig_in_done, NULL) == -1)
> + NULL, rde_softreconfig_in, rde_softreconfig_in_done,
> + NULL) == -1)
> fatal("%s: rib_dump_new", __func__);
> log_info("running softreconfig in");
> } else {
> - rde_softreconfig_in_done(NULL, AID_UNSPEC);
> + rde_softreconfig_in_done((void *)1, AID_UNSPEC);
> }
> }
>
> @@ -3380,14 +3405,13 @@ rde_softreconfig_in_done(void *arg, u_in
> struct rde_peer *peer;
> u_int16_t i;
>
> - if (arg != NULL) {
> - softreconfig--;
> - /* one guy done but other dumps are still running */
> - if (softreconfig > 0)
> - return;
> + softreconfig--;
> + /* one guy done but other dumps are still running */
> + if (softreconfig > 0)
> + return;
>
> + if (arg == NULL)
> log_info("softreconfig in done");
> - }
>
> /* now do the Adj-RIB-Out sync and a possible FIB sync */
> softreconfig = 0;
> @@ -3419,11 +3443,10 @@ rde_softreconfig_in_done(void *arg, u_in
> u_int8_t aid;
>
> if (peer->reconf_out) {
> - if (peer->conf.export_type == EXPORT_NONE) {
> + if (peer->export_type == EXPORT_NONE) {
> /* nothing to do here */
> peer->reconf_out = 0;
> - } else if (peer->conf.export_type ==
> - EXPORT_DEFAULT_ROUTE) {
> + } else if (peer->export_type == EXPORT_DEFAULT_ROUTE) {
> /* just resend the default route */
> for (aid = 0; aid < AID_MAX; aid++) {
> if (peer->capa.mp[aid])
> @@ -3739,7 +3762,7 @@ rde_as4byte(struct rde_peer *peer)
> static int
> rde_no_as_set(struct rde_peer *peer)
> {
> - return (peer->conf.flags & PEERFLAG_NO_AS_SET);
> + return (peer->flags & PEERFLAG_NO_AS_SET);
> }
>
> /* End-of-RIB marker, RFC 4724 */
> Index: rde.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
> retrieving revision 1.237
> diff -u -p -r1.237 rde.h
> --- rde.h 2 Mar 2021 09:45:07 -0000 1.237
> +++ rde.h 8 Mar 2021 11:41:28 -0000
> @@ -103,12 +103,14 @@ struct rde_peer {
> u_int32_t up_nlricnt;
> u_int32_t up_wcnt;
> enum peer_state state;
> + enum export_type export_type;
> u_int16_t loc_rib_id;
> u_int16_t short_as;
> u_int16_t mrt_idx;
> u_int8_t reconf_out; /* out filter changed */
> u_int8_t reconf_rib; /* rib changed */
> u_int8_t throttled;
> + u_int8_t flags;
> };
>
> #define AS_SET 1
> Index: rde_peer.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v
> retrieving revision 1.6
> diff -u -p -r1.6 rde_peer.c
> --- rde_peer.c 4 Dec 2020 11:57:13 -0000 1.6
> +++ rde_peer.c 5 May 2021 09:26:26 -0000
> @@ -170,6 +170,8 @@ peer_add(u_int32_t id, struct peer_confi
> if (peer->loc_rib_id == RIB_NOTFOUND)
> fatalx("King Bula's new peer met an unknown RIB");
> peer->state = PEER_NONE;
> + peer->export_type = peer->conf.export_type;
> + peer->flags = peer->conf.flags;
> SIMPLEQ_INIT(&peer->imsg_queue);
>
> head = PEER_HASH(id);
> @@ -429,11 +431,11 @@ peer_stale(struct rde_peer *peer, u_int8
> void
> peer_dump(struct rde_peer *peer, u_int8_t aid)
> {
> - if (peer->conf.export_type == EXPORT_NONE) {
> + if (peer->export_type == EXPORT_NONE) {
> /* nothing to send apart from the marker */
> if (peer->capa.grestart.restart)
> prefix_add_eor(peer, aid);
> - } else if (peer->conf.export_type == EXPORT_DEFAULT_ROUTE) {
> + } else if (peer->export_type == EXPORT_DEFAULT_ROUTE) {
> up_generate_default(out_rules, peer, aid);
> rde_up_dump_done(peer, aid);
> } else {
> Index: rde_update.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v
> retrieving revision 1.126
> diff -u -p -r1.126 rde_update.c
> --- rde_update.c 20 Apr 2021 11:19:56 -0000 1.126
> +++ rde_update.c 5 May 2021 09:29:34 -0000
> @@ -135,7 +135,7 @@ again:
> * skip the filters.
> */
> if (need_withdraw &&
> - !(peer->conf.flags & PEERFLAG_EVALUATE_ALL)) {
> + !(peer->flags & PEERFLAG_EVALUATE_ALL)) {
> new = NULL;
> goto again;
> }
> @@ -148,7 +148,7 @@ again:
> new->pt->prefixlen, prefix_vstate(new), &state) ==
> ACTION_DENY) {
> rde_filterstate_clean(&state);
> - if (peer->conf.flags & PEERFLAG_EVALUATE_ALL)
> + if (peer->flags & PEERFLAG_EVALUATE_ALL)
> new = LIST_NEXT(new, entry.list.rib);
> else
> new = NULL;
> @@ -360,7 +360,7 @@ up_generate_attr(u_char *buf, int len, s
> break;
> case ATTR_ASPATH:
> if (!peer->conf.ebgp ||
> - peer->conf.flags & PEERFLAG_TRANS_AS)
> + peer->flags & PEERFLAG_TRANS_AS)
> pdata = aspath_prepend(asp->aspath,
> peer->conf.local_as, 0, &plen);
> else
> @@ -399,7 +399,7 @@ up_generate_attr(u_char *buf, int len, s
> */
> if (asp->flags & F_ATTR_MED && (!peer->conf.ebgp ||
> asp->flags & F_ATTR_MED_ANNOUNCE ||
> - peer->conf.flags & PEERFLAG_TRANS_AS)) {
> + peer->flags & PEERFLAG_TRANS_AS)) {
> tmp32 = htonl(asp->med);
> if ((r = attr_write(buf + wlen, len,
> ATTR_OPTIONAL, ATTR_MED, &tmp32, 4)) == -1)
> @@ -439,7 +439,7 @@ up_generate_attr(u_char *buf, int len, s
> case ATTR_AS4_PATH:
> if (neednewpath) {
> if (!peer->conf.ebgp ||
> - peer->conf.flags & PEERFLAG_TRANS_AS)
> + peer->flags & PEERFLAG_TRANS_AS)
> pdata = aspath_prepend(asp->aspath,
> peer->conf.local_as, 0, &plen);
> else
> Index: session.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
> retrieving revision 1.413
> diff -u -p -r1.413 session.c
> --- session.c 3 May 2021 14:08:09 -0000 1.413
> +++ session.c 5 May 2021 09:13:41 -0000
> @@ -2734,10 +2734,24 @@ session_dispatch_imsg(struct imsgbuf *ib
> }
> break;
> case IMSG_RECONF_DRAIN:
> - if (idx != PFD_PIPE_MAIN)
> - fatalx("reconf request not from parent");
> - imsg_compose(ibuf_main, IMSG_RECONF_DRAIN, 0, 0,
> - -1, NULL, 0);
> + switch (idx) {
> + case PFD_PIPE_ROUTE:
> + if (nconf != NULL)
> + fatalx("got unexpected %s from RDE",
> + "IMSG_RECONF_DONE");
> + imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0,
> + -1, NULL, 0);
> + break;
> + case PFD_PIPE_MAIN:
> + if (nconf == NULL)
> + fatalx("got unexpected %s from parent",
> + "IMSG_RECONF_DONE");
> + imsg_compose(ibuf_main, IMSG_RECONF_DRAIN, 0, 0,
> + -1, NULL, 0);
> + break;
> + default:
> + fatalx("reconf request not from parent or RDE");
> + }
> break;
> case IMSG_RECONF_DONE:
> if (idx != PFD_PIPE_MAIN)
> @@ -2771,8 +2785,10 @@ session_dispatch_imsg(struct imsgbuf *ib
> nconf = NULL;
> pending_reconf = 0;
> log_info("SE reconfigured");
> - imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0,
> - -1, NULL, 0);
> + /*
> + * IMSG_RECONF_DONE is sent when the RDE drained
> + * the peer config sent in merge_peers().
> + */
> break;
> case IMSG_IFINFO:
> if (idx != PFD_PIPE_MAIN)
> @@ -3342,6 +3358,9 @@ merge_peers(struct bgpd_config *c, struc
> }
> }
> }
> +
> + if (imsg_rde(IMSG_RECONF_DRAIN, 0, NULL, 0) == -1)
> + fatalx("imsg_compose error");
>
> /* pfkeys of new peers already loaded by the parent process */
> RB_FOREACH_SAFE(np, peer_head, &nc->peers, next) {
>