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. If add-path send is not set then the system should behave as before. Which is the important bit of this. -- :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 %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 " + "'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. + */ + 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. + */ + 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. + * 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; }