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

Reply via email to