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; > } >