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

Reply via email to