On Fri, Jul 08, 2022 at 06:43:17PM +0200, Claudio Jeker wrote:
> Add the missing bits for add-path send support.
> The config options allows for a fair amount of configuration and not all
> have been tested:
> announce add-path send best [ plus X ]
> announce add-path send ecmp [ plus X ] [ max Y ]
> announce add-path send as-wide-best [ plus X ] [ max Y ]
> announce add-path send all
>
> Right now ECMP and as-wide-best are the same. This is because of missing
> nexthop metrics. The heavy lifting is done by up_generate_addpath()
> and is based on the same trick as 'rde evaluate all'. This is not optimal
> but it should work and the code is somewhat easy to follow.
> Also the reload behaviour is a bit special. You may want to tune the
> settings on an active session but removing the option should keep
> the last settings until session reset. Still need to think about this
> a bit more.
>
> A few bits like the minor cleanup in rde_decide.c can be committed
> independently.
Please do.
>
> If add-path send is not set then the system should behave as before.
> Which is the important bit of this.
The diff reads fine to me and I agree that without setting add-path send
nothing changes. I have a couple of nits inline, but I'm fine with the
diff as it is.
ok tb
> --
> :wq Claudio
>
> Index: bgpd.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
> retrieving revision 1.440
> diff -u -p -r1.440 bgpd.h
> --- bgpd.h 7 Jul 2022 12:16:04 -0000 1.440
> +++ bgpd.h 8 Jul 2022 12:32:49 -0000
> @@ -307,6 +307,20 @@ struct bgpd_config {
>
> extern int cmd_opts;
>
> +enum addpath_mode {
> + ADDPATH_EVAL_NONE,
> + ADDPATH_EVAL_BEST,
> + ADDPATH_EVAL_ECMP,
> + ADDPATH_EVAL_AS_WIDE,
> + ADDPATH_EVAL_ALL,
> +};
> +
> +struct addpath_eval {
> + enum addpath_mode mode;
> + int extrapaths;
> + int maxpaths;
> +};
> +
> enum export_type {
> EXPORT_UNSET,
> EXPORT_NONE,
> @@ -402,6 +416,7 @@ struct peer_config {
> struct bgpd_addr local_addr_v6;
> struct peer_auth auth;
> struct capabilities capabilities;
> + struct addpath_eval eval;
> char group[PEER_DESCR_LEN];
> char descr[PEER_DESCR_LEN];
> char reason[REASON_LEN];
> Index: parse.y
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
> retrieving revision 1.431
> diff -u -p -r1.431 parse.y
> --- parse.y 27 Jun 2022 13:26:51 -0000 1.431
> +++ parse.y 8 Jul 2022 12:41:23 -0000
> @@ -210,7 +210,7 @@ typedef struct {
> %token EBGP IBGP
> %token LOCALAS REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX
> RESTART
> %token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY ENHANCED
> ADDPATH
> -%token SEND RECV POLICY
> +%token SEND RECV PLUS POLICY
> %token DEMOTE ENFORCE NEIGHBORAS ASOVERRIDE REFLECTOR DEPEND DOWN
> %token DUMP IN OUT SOCKET RESTRICTED
> %token LOG TRANSPARENT
> @@ -230,12 +230,13 @@ typedef struct {
> %token IPSEC ESP AH SPI IKE
> %token IPV4 IPV6
> %token QUALIFY VIA
> -%token NE LE GE XRANGE LONGER MAXLEN
> +%token NE LE GE XRANGE LONGER MAXLEN MAX
> %token <v.string> STRING
> %token <v.number> NUMBER
> %type <v.number> asnumber as4number as4number_any
> optnumber
> %type <v.number> espah family safi restart origincode
> nettype
> %type <v.number> yesno inout restricted validity expires
> enforce
> +%type <v.number> addpathopt addpathmax
Maybe addpathextra would match the 'extra' used everywhere else better than
addpathopt. Or even addextrapaths and addmaxpaths.
> %type <v.string> string
> %type <v.addr> address
> %type <v.prefix> prefix addrspec
> @@ -1356,6 +1357,28 @@ groupopts_l : /* empty */
> | groupopts_l error '\n'
> ;
>
> +addpathopt : /* empty */ { $$ = 0; }
> + | PLUS NUMBER {
> + if ($2 < 1 || $2 > USHRT_MAX) {
> + yyerror("additional paths must be between "
> + "%u and %u", 1, USHRT_MAX);
> + YYERROR;
> + }
> + $$ = $2;
> + }
> + ;
> +
> +addpathmax : /* empty */ { $$ = 0; }
> + | MAX NUMBER {
> + if ($2 < 1 || $2 > USHRT_MAX) {
> + yyerror("maximum additional paths must be "
> + "between %u and %u", 1, USHRT_MAX);
> + YYERROR;
> + }
> + $$ = $2;
> + }
> + ;
> +
> peeropts_h : '{' '\n' peeropts_l '}'
> | '{' peeropts '}'
> | /* empty */
> @@ -1515,6 +1538,50 @@ peeropts : REMOTEAS as4number {
> else
> *ap++ &= ~CAPA_AP_RECV;
> }
> + | ANNOUNCE ADDPATH SEND STRING addpathopt addpathmax {
> + int8_t *ap = curpeer->conf.capabilities.add_path;
> + enum addpath_mode mode;
> + u_int8_t i;
> +
> + if (!strcmp($4, "no")) {
> + free($4);
> + if ($5 != 0 || $6 != 0) {
> + yyerror("no additional option allowed "
> + "for 'add-path send no'");
> + YYERROR;
> + }
> + for (i = 0; i < AID_MAX; i++)
> + *ap++ &= ~CAPA_AP_SEND;
> + break;
> + } else if (!strcmp($4, "all")) {
> + free($4);
> + if ($5 != 0 || $6 != 0) {
> + yyerror("no additional option allowed "
> + "for 'add-path send all'");
> + YYERROR;
> + }
> + mode = ADDPATH_EVAL_ALL;
> + } else if (!strcmp($4, "best")) {
> + free($4);
> + mode = ADDPATH_EVAL_BEST;
> + } else if (!strcmp($4, "ecmp")) {
> + free($4);
> + mode = ADDPATH_EVAL_ECMP;
> + } else if (!strcmp($4, "as-wide-best")) {
> + free($4);
> + mode = ADDPATH_EVAL_AS_WIDE;
> + } else {
> + yyerror("announce add-path send: "
> + "unknown mode \"%s\"", $4);
> + free($4);
> + YYERROR;
> + }
> + for (i = 0; i < AID_MAX; i++)
> + *ap++ |= CAPA_AP_SEND;
> + curpeer->conf.eval.mode = mode;
> + curpeer->conf.eval.extrapaths = $5;
> + curpeer->conf.eval.maxpaths = $6;
> + }
> | ANNOUNCE POLICY STRING enforce {
> curpeer->conf.capabilities.role_ena = $4;
> if (strcmp($3, "no") == 0) {
> @@ -3070,6 +3137,7 @@ lookup(char *s)
> { "localpref", LOCALPREF},
> { "log", LOG},
> { "match", MATCH},
> + { "max", MAX},
> { "max-as-len", MAXASLEN},
> { "max-as-seq", MAXASSEQ},
> { "max-communities", MAXCOMMUNITIES},
> @@ -3098,6 +3166,7 @@ lookup(char *s)
> { "password", PASSWORD},
> { "peer-as", PEERAS},
> { "pftable", PFTABLE},
> + { "plus", PLUS},
> { "policy", POLICY},
> { "port", PORT},
> { "prefix", PREFIX},
> @@ -4593,6 +4662,14 @@ neighbor_consistent(struct peer *p)
> char *descr = log_fmt_peer(&p->conf);
> yyerror("duplicate %s", descr);
> free(descr);
> + return (-1);
> + }
> +
> + /* bail if add-path send and rde evaluate all is used together */
> + if ((p->conf.flags & PEERFLAG_EVALUATE_ALL) &&
> + (p->conf.capabilities.add_path[0] & CAPA_AP_SEND)) {
> + yyerror("neighbors with add-path send can not use "
typo: cannot
> + "'rde evaluate all'");
> return (-1);
> }
>
> Index: printconf.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
> retrieving revision 1.155
> diff -u -p -r1.155 printconf.c
> --- printconf.c 28 Jun 2022 11:46:05 -0000 1.155
> +++ printconf.c 8 Jul 2022 12:42:42 -0000
> @@ -768,6 +768,23 @@ print_enc_alg(enum auth_enc_alg alg)
> }
> }
>
> +static const char *
> +print_addpath_mode(enum addpath_mode mode)
> +{
> + switch (mode) {
> + case ADDPATH_EVAL_NONE:
> + return "none";
> + case ADDPATH_EVAL_BEST:
> + return "best";
> + case ADDPATH_EVAL_ECMP:
> + return "ecmp";
> + case ADDPATH_EVAL_AS_WIDE:
> + return "as-wide-best";
> + case ADDPATH_EVAL_ALL:
> + return "all";
> + }
> +}
> +
> void
> print_announce(struct peer_config *p, const char *c)
> {
> @@ -790,6 +807,15 @@ print_announce(struct peer_config *p, co
> printf("%s\tannounce as4byte no\n", c);
> if (p->capabilities.add_path[0] & CAPA_AP_RECV)
> printf("%s\tannounce add-path recv yes\n", c);
> + if (p->capabilities.add_path[0] & CAPA_AP_SEND) {
> + printf("%s\tannounce add-path send %s", c,
> + print_addpath_mode(p->eval.mode));
> + if (p->eval.extrapaths != 0)
> + printf(" plus %d", p->eval.extrapaths);
> + if (p->eval.maxpaths != 0)
> + printf(" max %d", p->eval.maxpaths);
> + printf("\n");
> + }
> if (p->capabilities.role_ena) {
> printf("%s\tannounce policy %s%s\n", c,
> log_policy(p->capabilities.role),
> Index: rde.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
> retrieving revision 1.552
> diff -u -p -r1.552 rde.c
> --- rde.c 8 Jul 2022 08:11:25 -0000 1.552
> +++ rde.c 8 Jul 2022 16:37:08 -0000
> @@ -1091,6 +1091,9 @@ rde_dispatch_imsg_peer(struct rde_peer *
> imsg_compose(ibuf_se, IMSG_SESSION_DOWN, peer->conf.id,
> 0, -1, NULL, 0);
> }
> + /* make sure rde_eval_all is on if needed. */
> + if (peer_has_add_path(peer, AID_UNSPEC, CAPA_AP_SEND))
> + rde_eval_all = 1;
> break;
> case IMSG_SESSION_DOWN:
> peer_down(peer, NULL);
> @@ -2526,10 +2529,8 @@ rde_dump_rib_as(struct prefix *p, struct
> rib.flags |= F_PREF_INTERNAL;
> if (asp->flags & F_PREFIX_ANNOUNCED)
> rib.flags |= F_PREF_ANNOUNCE;
> - if (nexthop == NULL || nexthop->state == NEXTHOP_REACH)
> + if (prefix_eligible(p))
> rib.flags |= F_PREF_ELIGIBLE;
> - if (asp->flags & F_ATTR_LOOP)
> - rib.flags &= ~F_PREF_ELIGIBLE;
> /* otc loop includes parse err so skip the latter if the first is set */
> if (asp->flags & F_ATTR_OTC_LOOP)
> rib.flags |= F_PREF_OTC_LOOP;
> @@ -3495,6 +3496,23 @@ rde_reload_done(void)
> if (peer->loc_rib_id == RIB_NOTFOUND)
> fatalx("King Bula's peer met an unknown RIB");
> peer->reconf_rib = 1;
> + }
> + /*
> + * Update add-path settings but only if the session is
> + * running with add-path and the config uses add-path
> + * as well.
> + */
> + if (peer_has_add_path(peer, AID_UNSPEC, CAPA_AP_SEND)) {
> + if (peer->conf.eval.mode != ADDPATH_EVAL_NONE &&
> + memcmp(&peer->eval, &peer->conf.eval,
> + sizeof(peer->eval)) != 0) {
> + log_peer_info(&peer->conf,
> + "addpath eval change, reloading");
> + peer->reconf_out = 1;
> + peer->eval = peer->conf.eval;
> + }
> + /* add-path send needs rde_eval_all */
> + rde_eval_all = 1;
> }
> peer->export_type = peer->conf.export_type;
> peer->flags = peer->conf.flags;
> Index: rde.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
> retrieving revision 1.258
> diff -u -p -r1.258 rde.h
> --- rde.h 8 Jul 2022 10:01:52 -0000 1.258
> +++ rde.h 8 Jul 2022 11:00:05 -0000
> @@ -87,6 +87,7 @@ struct rde_peer {
> struct bgpd_addr local_v4_addr;
> struct bgpd_addr local_v6_addr;
> struct capabilities capa;
> + struct addpath_eval eval;
> struct prefix_index adj_rib_out;
> struct prefix_tree updates[AID_MAX];
> struct prefix_tree withdraws[AID_MAX];
> @@ -696,6 +697,8 @@ int nexthop_compare(struct nexthop *,
> /* rde_update.c */
> void up_init(struct rde_peer *);
> void up_generate_updates(struct filter_head *, struct rde_peer *,
> + struct prefix *, struct prefix *);
> +void up_generate_addpath(struct filter_head *, struct rde_peer *,
> struct prefix *, struct prefix *);
> void up_generate_default(struct filter_head *, struct rde_peer *,
> uint8_t);
> Index: rde_decide.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde_decide.c,v
> retrieving revision 1.94
> diff -u -p -r1.94 rde_decide.c
> --- rde_decide.c 7 Jul 2022 19:46:38 -0000 1.94
> +++ rde_decide.c 8 Jul 2022 15:05:09 -0000
> @@ -541,7 +541,7 @@ prefix_best(struct rib_entry *re)
> void
> prefix_evaluate(struct rib_entry *re, struct prefix *new, struct prefix *old)
> {
> - struct prefix *xp, *active;
> + struct prefix *newbest, *oldbest;
> struct rib *rib;
>
> rib = re_rib(re);
> @@ -556,7 +556,7 @@ prefix_evaluate(struct rib_entry *re, st
> return;
> }
>
> - active = prefix_best(re);
> + oldbest = prefix_best(re);
>
> if (old != NULL)
> prefix_remove(old, re);
> @@ -564,23 +564,23 @@ prefix_evaluate(struct rib_entry *re, st
> if (new != NULL)
> prefix_insert(new, NULL, re);
>
> - xp = TAILQ_FIRST(&re->prefix_h);
> - if (xp != NULL && !prefix_eligible(xp))
> - xp = NULL;
> + newbest = TAILQ_FIRST(&re->prefix_h);
> + if (newbest != NULL && !prefix_eligible(newbest))
> + newbest = NULL;
>
> /*
> * If the active prefix changed or the active prefix was removed
> * and added again then generate an update.
> */
> - if (active != xp || (old != NULL && xp == old)) {
> + if (oldbest != newbest || (old != NULL && newbest == old)) {
> /*
> - * Send update withdrawing re->active and adding xp
> - * but remember that xp may be NULL aka ineligible.
> + * Send update withdrawing oldbest and adding newbest
> + * but remember that newbest may be NULL aka ineligible.
> * Additional decision may be made by the called functions.
> */
> - rde_generate_updates(rib, xp, active, EVAL_DEFAULT);
> + rde_generate_updates(rib, newbest, oldbest, EVAL_DEFAULT);
> if ((rib->flags & F_RIB_NOFIB) == 0)
> - rde_send_kroute(rib, xp, active);
> + rde_send_kroute(rib, newbest, oldbest);
> return;
> }
>
> @@ -591,6 +591,5 @@ prefix_evaluate(struct rib_entry *re, st
> */
> if (rde_evaluate_all())
> if ((new != NULL && prefix_eligible(new)) || old != NULL)
> - rde_generate_updates(rib, prefix_best(re), NULL,
> - EVAL_ALL);
> + rde_generate_updates(rib, newbest, NULL, EVAL_ALL);
> }
> Index: rde_peer.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v
> retrieving revision 1.18
> diff -u -p -r1.18 rde_peer.c
> --- rde_peer.c 7 Jul 2022 10:46:54 -0000 1.18
> +++ rde_peer.c 8 Jul 2022 16:32:07 -0000
> @@ -58,6 +58,13 @@ peer_has_add_path(struct rde_peer *peer,
> {
> if (aid > AID_MAX)
> return 0;
> + if (aid == AID_UNSPEC) {
> + /* check if at capability is set for at least one AID */
> + for (aid = AID_MIN; aid < AID_MAX; aid++)
> + if (peer->capa.add_path[aid] & mode)
> + return 1;
> + return 0;
> + }
> return (peer->capa.add_path[aid] & mode);
> }
>
> @@ -200,6 +207,7 @@ peer_add(uint32_t id, struct peer_config
> if (peer->loc_rib_id == RIB_NOTFOUND)
> fatalx("King Bula's new peer met an unknown RIB");
> peer->state = PEER_NONE;
> + peer->eval = peer->conf.eval;
> peer->export_type = peer->conf.export_type;
> peer->flags = peer->conf.flags;
> SIMPLEQ_INIT(&peer->imsg_queue);
> @@ -244,10 +252,16 @@ peer_generate_update(struct rde_peer *pe
> /* if reconf skip peers which don't need to reconfigure */
> if (mode == EVAL_RECONF && peer->reconf_out == 0)
> return;
> +
> + /* handle peers with add-path */
> + if (peer_has_add_path(peer, aid, CAPA_AP_SEND)) {
> + up_generate_addpath(out_rules, peer, new, old);
> + return;
> + }
> +
> /* skip regular peers if the best path didn't change */
> if (mode == EVAL_ALL && (peer->flags & PEERFLAG_EVALUATE_ALL) == 0)
> return;
> -
> up_generate_updates(out_rules, peer, new, old);
> }
>
> Index: rde_update.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v
> retrieving revision 1.142
> diff -u -p -r1.142 rde_update.c
> --- rde_update.c 8 Jul 2022 10:01:52 -0000 1.142
> +++ rde_update.c 8 Jul 2022 14:41:29 -0000
> @@ -96,6 +96,46 @@ up_test_update(struct rde_peer *peer, st
> return (1);
> }
>
> +/* RFC9234 open policy handling */
> +static int
> +up_enforce_open_policy(struct rde_peer *peer, struct filterstate *state)
> +{
> + uint8_t role;
> +
> + if (!peer_has_open_policy(peer, &role))
> + return 0;
> +
> + /*
> + * do not propagate (consider it filtered) if
> + * OTC is present and neighbor role is peer,
> + * provider or rs.
Could rewrap this comment
> + */
> + if (role == CAPA_ROLE_PEER || role == CAPA_ROLE_PROVIDER ||
> + role == CAPA_ROLE_RS)
> + if (state->aspath.flags & F_ATTR_OTC)
> + return (1);
> +
> + /*
> + * add OTC attribute if not present for peers,
> + * customers and rs-clients.
ditto
> + */
> + if (role == CAPA_ROLE_PEER || role == CAPA_ROLE_CUSTOMER ||
> + role == CAPA_ROLE_RS_CLIENT)
> + if ((state->aspath.flags & F_ATTR_OTC) == 0) {
> + uint32_t tmp;
> +
> + tmp = htonl(peer->conf.local_as);
> + if (attr_optadd(&state->aspath,
> + ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_OTC,
> + &tmp, sizeof(tmp)) == -1)
> + log_peer_warnx(&peer->conf,
> + "failed to add OTC attribute");
> + state->aspath.flags |= F_ATTR_OTC;
> + }
> +
> + return 0;
> +}
> +
> void
> up_generate_updates(struct filter_head *rules, struct rde_peer *peer,
> struct prefix *new, struct prefix *old)
> @@ -104,12 +144,9 @@ up_generate_updates(struct filter_head *
> struct bgpd_addr addr;
> struct prefix *p;
> int need_withdraw;
> - uint8_t prefixlen, role;
> + uint8_t prefixlen;
>
> if (new == NULL) {
> - if (old == NULL)
> - /* no prefix to update or withdraw */
> - return;
> pt_getaddr(old->pt, &addr);
> prefixlen = old->pt->prefixlen;
> } else {
> @@ -156,43 +193,14 @@ up_generate_updates(struct filter_head *
> break;
> }
>
> - /* RFC9234 open policy handling */
> - if (peer_has_open_policy(peer, &role)) {
> - /*
> - * do not propagate (consider it filtered) if
> - * OTC is present and neighbor role is peer,
> - * provider or rs.
> - */
> - if ((role == CAPA_ROLE_PEER ||
> - role == CAPA_ROLE_PROVIDER ||
> - role == CAPA_ROLE_RS) &&
> - state.aspath.flags & F_ATTR_OTC) {
> - rde_filterstate_clean(&state);
> - if (peer->flags & PEERFLAG_EVALUATE_ALL) {
> - new = TAILQ_NEXT(new, entry.list.rib);
> - if (new != NULL && prefix_eligible(new))
> - continue;
> - }
> - break;
> - }
> - /*
> - * add OTC attribute if not present for peers,
> - * customers and rs-clients.
> - */
> - if ((role == CAPA_ROLE_PEER ||
> - role == CAPA_ROLE_CUSTOMER ||
> - role == CAPA_ROLE_RS_CLIENT) &&
> - (state.aspath.flags & F_ATTR_OTC) == 0) {
> - uint32_t tmp;
> -
> - tmp = htonl(peer->conf.local_as);
> - if (attr_optadd(&state.aspath,
> - ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_OTC,
> - &tmp, sizeof(tmp)) == -1)
> - log_peer_warnx(&peer->conf,
> - "failed to add OTC attribute");
> - state.aspath.flags |= F_ATTR_OTC;
> + if (up_enforce_open_policy(peer, &state)) {
> + rde_filterstate_clean(&state);
> + if (peer->flags & PEERFLAG_EVALUATE_ALL) {
> + new = TAILQ_NEXT(new, entry.list.rib);
> + if (new != NULL && prefix_eligible(new))
> + continue;
> }
> + break;
> }
>
> /* check if this was actually a withdraw */
> @@ -224,6 +232,138 @@ up_generate_updates(struct filter_head *
> prefix_adjout_withdraw(p);
> }
>
> +/*
> + * Generate updates for the add-path send case. Depending on the
> + * peer eval settings prefixes are selected and distributed.
> + * This highly depends on the Adj-RIB-Out to handle prefixes with no
> + * changes gracefully. It may be possible to improve the API so that
> + * less churn is needed.
> + */
> +void
> +up_generate_addpath(struct filter_head *rules, struct rde_peer *peer,
> + struct prefix *new, struct prefix *old)
> +{
> + struct filterstate state;
> + struct bgpd_addr addr;
> + struct prefix *head, *p;
> + uint8_t prefixlen;
> + int maxpaths = 0, extrapaths = 0, extra;
> + int checkmode = 1;
> +
> + if (new == NULL) {
> + pt_getaddr(old->pt, &addr);
> + prefixlen = old->pt->prefixlen;
> + } else {
> + pt_getaddr(new->pt, &addr);
> + prefixlen = new->pt->prefixlen;
> + }
> +
> + head = prefix_adjout_lookup(peer, &addr, prefixlen);
> +
> + /* mark all paths as stale */
> + for (p = head; p != NULL; p = prefix_adjout_next(peer, p))
> + p->flags |= PREFIX_FLAG_STALE;
> +
> + /* update paths */
> + for ( ; new != NULL; new = TAILQ_NEXT(new, entry.list.rib)) {
> + /* since list is sorted, stop at first invalid prefix */
> + if (!prefix_eligible(new))
> + break;
> +
> + /* check limits and stop when a limit is reached */
> + if (peer->eval.maxpaths != 0 &&
> + maxpaths >= peer->eval.maxpaths)
> + break;
> + if (peer->eval.extrapaths != 0 &&
> + extrapaths >= peer->eval.extrapaths)
> + break;
> +
> + extra = 1;
> + if (checkmode) {
> + switch (peer->eval.mode) {
> + case ADDPATH_EVAL_BEST:
> + if (new->dmetric == PREFIX_DMETRIC_BEST)
> + extra = 0;
> + else
> + checkmode = 0;
> + break;
> + case ADDPATH_EVAL_ECMP:
> + if (new->dmetric == PREFIX_DMETRIC_BEST ||
> + new->dmetric == PREFIX_DMETRIC_ECMP)
> + extra = 0;
> + else
> + checkmode = 0;
> + break;
> + case ADDPATH_EVAL_AS_WIDE:
> + if (new->dmetric == PREFIX_DMETRIC_BEST ||
> + new->dmetric == PREFIX_DMETRIC_ECMP ||
> + new->dmetric == PREFIX_DMETRIC_AS_WIDE)
> + extra = 0;
> + else
> + checkmode = 0;
> + break;
> + case ADDPATH_EVAL_ALL:
> + /* nothing to check */
> + checkmode = 0;
> + break;
> + default:
> + fatalx("unknown add-path eval mode");
> + }
> + }
> +
> + /*
> + * up_test_update() needs to run before the output filters
> + * else the well known communities wont work properly.
typo: won't
> + * The output filters would not be able to add well known
> + * communities.
> + */
> + if (!up_test_update(peer, new))
> + continue;
> +
> + rde_filterstate_prep(&state, prefix_aspath(new),
> + prefix_communities(new), prefix_nexthop(new),
> + prefix_nhflags(new));
> + if (rde_filter(rules, peer, prefix_peer(new), &addr,
> + prefixlen, prefix_vstate(new), &state) == ACTION_DENY) {
> + rde_filterstate_clean(&state);
> + continue;
> + }
> +
> + if (up_enforce_open_policy(peer, &state)) {
> + rde_filterstate_clean(&state);
> + continue;
> + }
> +
> + /* from here on we know this is an update */
> + maxpaths++;
> + extrapaths += extra;
> +
> + p = prefix_adjout_get(peer, new->path_id_tx, &addr,
> + new->pt->prefixlen);
> +
> + up_prep_adjout(peer, &state, addr.aid);
> + prefix_adjout_update(p, peer, &state, &addr,
> + new->pt->prefixlen, new->path_id_tx, prefix_vstate(new));
> + rde_filterstate_clean(&state);
> +
> + /* max prefix checker outbound */
> + if (peer->conf.max_out_prefix &&
> + peer->prefix_out_cnt > peer->conf.max_out_prefix) {
> + log_peer_warnx(&peer->conf,
> + "outbound prefix limit reached (>%u/%u)",
> + peer->prefix_out_cnt, peer->conf.max_out_prefix);
> + rde_update_err(peer, ERR_CEASE,
> + ERR_CEASE_MAX_SENT_PREFIX, NULL, 0);
> + }
> + }
> +
> + /* withdraw stale paths */
> + for (p = head; p != NULL; p = prefix_adjout_next(peer, p)) {
> + if (p->flags & PREFIX_FLAG_STALE)
> + prefix_adjout_withdraw(p);
> + }
> +}
> +
> struct rib_entry *rib_add(struct rib *, struct bgpd_addr *, int);
> void rib_remove(struct rib_entry *);
> int rib_empty(struct rib_entry *);
> @@ -669,8 +809,11 @@ up_dump_prefix(u_char *buf, int len, str
> }
> pt_getaddr(p->pt, &addr);
> if ((r = prefix_write(buf + wpos, len - wpos,
> - &addr, p->pt->prefixlen, withdraw)) == -1)
> + &addr, p->pt->prefixlen, withdraw)) == -1) {
> + if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_SEND))
> + wpos -= sizeof(pathid);
> break;
> + }
> wpos += r;
>
> /* make sure we only dump prefixes which belong together */
> @@ -682,7 +825,6 @@ up_dump_prefix(u_char *buf, int len, str
> (np->flags & PREFIX_FLAG_EOR))
> done = 1;
>
> -
> if (withdraw) {
> /* prefix no longer needed, remove it */
> prefix_adjout_destroy(p);
> @@ -694,6 +836,7 @@ up_dump_prefix(u_char *buf, int len, str
> peer->up_nlricnt--;
> peer->prefix_sent_update++;
> }
> +
> if (done)
> break;
> }
>