"Fossies" - the Fresh Open Source Software Archive

Member "openbgpd-6.5p0/src/bgpd/rde_update.c" (13 Feb 2019, 25564 Bytes) of package /linux/privat/openbgpd-6.5p0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "rde_update.c" see the Fossies "Dox" file reference documentation.

    1 /*  $OpenBSD: rde_update.c,v 1.108 2019/01/21 02:07:56 claudio Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 #include <sys/types.h>
   19 #include <sys/queue.h>
   20 #include <sys/tree.h>
   21 
   22 #include <limits.h>
   23 #include <stdlib.h>
   24 #include <string.h>
   25 #include <siphash.h>
   26 
   27 #include "bgpd.h"
   28 #include "rde.h"
   29 #include "log.h"
   30 
   31 static struct filter_community  comm_no_advertise = {
   32     .type = COMMUNITY_TYPE_BASIC,
   33     .c.b.data1 = COMMUNITY_WELLKNOWN,
   34     .c.b.data2 = COMMUNITY_NO_ADVERTISE
   35 };
   36 static struct filter_community  comm_no_export = {
   37     .type = COMMUNITY_TYPE_BASIC,
   38     .c.b.data1 = COMMUNITY_WELLKNOWN,
   39     .c.b.data2 = COMMUNITY_NO_EXPORT
   40 };
   41 static struct filter_community  comm_no_expsubconfed = {
   42     .type = COMMUNITY_TYPE_BASIC,
   43     .c.b.data1 = COMMUNITY_WELLKNOWN,
   44     .c.b.data2 = COMMUNITY_NO_EXPSUBCONFED
   45 };
   46 
   47 static int
   48 up_test_update(struct rde_peer *peer, struct prefix *p)
   49 {
   50     struct bgpd_addr     addr;
   51     struct rde_aspath   *asp;
   52     struct rde_peer     *prefp;
   53     struct attr     *attr;
   54 
   55     if (p == NULL)
   56         /* no prefix available */
   57         return (0);
   58 
   59     prefp = prefix_peer(p);
   60     asp = prefix_aspath(p);
   61 
   62     if (peer == prefp)
   63         /* Do not send routes back to sender */
   64         return (0);
   65 
   66     if (asp == NULL || asp->flags & F_ATTR_PARSE_ERR)
   67         fatalx("try to send out a botched path");
   68     if (asp->flags & F_ATTR_LOOP)
   69         fatalx("try to send out a looped path");
   70 
   71     pt_getaddr(p->re->prefix, &addr);
   72     if (peer->capa.mp[addr.aid] == 0)
   73         return (-1);
   74 
   75     if (!prefp->conf.ebgp && !peer->conf.ebgp) {
   76         /*
   77          * route reflector redistribution rules:
   78          * 1. if announce is set                -> announce
   79          * 2. old non-client, new non-client    -> no
   80          * 3. old client, new non-client        -> yes
   81          * 4. old non-client, new client        -> yes
   82          * 5. old client, new client            -> yes
   83          */
   84         if (prefp->conf.reflector_client == 0 &&
   85             peer->conf.reflector_client == 0 &&
   86             (asp->flags & F_PREFIX_ANNOUNCED) == 0)
   87             /* Do not redistribute updates to ibgp peers */
   88             return (0);
   89     }
   90 
   91     /* export type handling */
   92     if (peer->conf.export_type == EXPORT_NONE ||
   93         peer->conf.export_type == EXPORT_DEFAULT_ROUTE) {
   94         /*
   95          * no need to withdraw old prefix as this will be
   96          * filtered out as well.
   97          */
   98         return (-1);
   99     }
  100 
  101     /* well known communities */
  102     if (community_match(asp, &comm_no_advertise, NULL))
  103         return (0);
  104     if (peer->conf.ebgp) {
  105         if (community_match(asp, &comm_no_export, NULL))
  106             return (0);
  107         if (community_match(asp, &comm_no_expsubconfed, NULL))
  108             return (0);
  109     }
  110 
  111     /*
  112      * Don't send messages back to originator
  113      * this is not specified in the RFC but seems logical.
  114      */
  115     if ((attr = attr_optget(asp, ATTR_ORIGINATOR_ID)) != NULL) {
  116         if (memcmp(attr->data, &peer->remote_bgpid,
  117             sizeof(peer->remote_bgpid)) == 0) {
  118             /* would cause loop don't send */
  119             return (-1);
  120         }
  121     }
  122 
  123     return (1);
  124 }
  125 
  126 void
  127 up_generate_updates(struct filter_head *rules, struct rde_peer *peer,
  128     struct prefix *new, struct prefix *old)
  129 {
  130     struct filterstate      state;
  131     struct bgpd_addr        addr;
  132 
  133     if (peer->state != PEER_UP)
  134         return;
  135 
  136     if (new == NULL) {
  137 withdraw:
  138         if (old == NULL)
  139             /* no prefix to withdraw */
  140             return;
  141 
  142         /* withdraw prefix */
  143         pt_getaddr(old->re->prefix, &addr);
  144         if (prefix_withdraw(&ribs[RIB_ADJ_OUT].rib, peer, &addr,
  145             old->re->prefix->prefixlen) == 1)
  146             peer->up_wcnt++;
  147     } else {
  148         switch (up_test_update(peer, new)) {
  149         case 1:
  150             break;
  151         case 0:
  152             goto withdraw;
  153         case -1:
  154             return;
  155         }
  156 
  157         rde_filterstate_prep(&state, prefix_aspath(new),
  158             prefix_nexthop(new), prefix_nhflags(new));
  159         if (rde_filter(rules, peer, new, &state) == ACTION_DENY) {
  160             rde_filterstate_clean(&state);
  161             goto withdraw;
  162         }
  163 
  164         pt_getaddr(new->re->prefix, &addr);
  165         if (path_update(&ribs[RIB_ADJ_OUT].rib, peer, &state, &addr,
  166             new->re->prefix->prefixlen, prefix_vstate(new)) != 2) {
  167             /* only send update if path changed */
  168             prefix_update(&ribs[RIB_ADJ_OUT].rib, peer, &addr,
  169                 new->re->prefix->prefixlen);
  170             peer->up_nlricnt++;
  171         }
  172 
  173         rde_filterstate_clean(&state);
  174     }
  175 }
  176 
  177 struct rib_entry *rib_add(struct rib *, struct bgpd_addr *, int);
  178 void rib_remove(struct rib_entry *);
  179 int rib_empty(struct rib_entry *);
  180 
  181 /* send a default route to the specified peer */
  182 void
  183 up_generate_default(struct filter_head *rules, struct rde_peer *peer,
  184     u_int8_t aid)
  185 {
  186     struct filterstate   state;
  187     struct rde_aspath   *asp;
  188     struct prefix        p;
  189     struct rib_entry    *re;
  190     struct bgpd_addr     addr;
  191 
  192     if (peer->capa.mp[aid] == 0)
  193         return;
  194 
  195     rde_filterstate_prep(&state, NULL, NULL, 0);
  196     asp = &state.aspath;
  197     asp->aspath = aspath_get(NULL, 0);
  198     asp->origin = ORIGIN_IGP;
  199     /* the other default values are OK, nexthop is once again NULL */
  200 
  201     /*
  202      * XXX apply default overrides. Not yet possible, mainly a parse.y
  203      * problem.
  204      */
  205     /* rde_apply_set(asp, set, af, NULL ???, DIR_IN); */
  206 
  207     /*
  208      * XXX this is ugly because we need to have a prefix for rde_filter()
  209      * but it will be added after filtering. So fake it till we make it.
  210      */
  211     bzero(&p, sizeof(p));
  212     bzero(&addr, sizeof(addr));
  213     addr.aid = aid;
  214     re = rib_get(rib_byid(peer->loc_rib_id), &addr, 0);
  215     if (re == NULL)
  216         re = rib_add(rib_byid(peer->loc_rib_id), &addr, 0);
  217     p.re = re;
  218     p.aspath = asp;
  219     p.peer = peer; /* XXX should be peerself */
  220 
  221     /* filter as usual */
  222     if (rde_filter(rules, peer, &p, &state) == ACTION_DENY) {
  223         rde_filterstate_clean(&state);
  224         return;
  225     }
  226 
  227     path_update(&ribs[RIB_ADJ_OUT].rib, peer, &state, &addr, 0,
  228         ROA_NOTFOUND);
  229 
  230     /* no longer needed */
  231     rde_filterstate_clean(&state);
  232 
  233     if (rib_empty(re))
  234         rib_remove(re);
  235 }
  236 
  237 /* only for IPv4 */
  238 static in_addr_t
  239 up_get_nexthop(struct rde_peer *peer, struct filterstate *state)
  240 {
  241     in_addr_t   mask;
  242 
  243     /* nexthop, already network byte order */
  244     if (state->nhflags & NEXTHOP_NOMODIFY) {
  245         /* no modify flag set */
  246         if (state->nexthop == NULL)
  247             return (peer->local_v4_addr.v4.s_addr);
  248         else
  249             return (state->nexthop->exit_nexthop.v4.s_addr);
  250     } else if (state->nhflags & NEXTHOP_SELF)
  251         return (peer->local_v4_addr.v4.s_addr);
  252     else if (!peer->conf.ebgp) {
  253         /*
  254          * If directly connected use peer->local_v4_addr
  255          * this is only true for announced networks.
  256          */
  257         if (state->nexthop == NULL)
  258             return (peer->local_v4_addr.v4.s_addr);
  259         else if (state->nexthop->exit_nexthop.v4.s_addr ==
  260             peer->remote_addr.v4.s_addr)
  261             /*
  262              * per RFC: if remote peer address is equal to
  263              * the nexthop set the nexthop to our local address.
  264              * This reduces the risk of routing loops.
  265              */
  266             return (peer->local_v4_addr.v4.s_addr);
  267         else
  268             return (state->nexthop->exit_nexthop.v4.s_addr);
  269     } else if (peer->conf.distance == 1) {
  270         /* ebgp directly connected */
  271         if (state->nexthop != NULL &&
  272             state->nexthop->flags & NEXTHOP_CONNECTED) {
  273             mask = htonl(
  274                 prefixlen2mask(state->nexthop->nexthop_netlen));
  275             if ((peer->remote_addr.v4.s_addr & mask) ==
  276                 (state->nexthop->nexthop_net.v4.s_addr & mask))
  277                 /* nexthop and peer are in the same net */
  278                 return (state->nexthop->exit_nexthop.v4.s_addr);
  279             else
  280                 return (peer->local_v4_addr.v4.s_addr);
  281         } else
  282             return (peer->local_v4_addr.v4.s_addr);
  283     } else
  284         /* ebgp multihop */
  285         /*
  286          * For ebgp multihop nh->flags should never have
  287          * NEXTHOP_CONNECTED set so it should be possible to unify the
  288          * two ebgp cases. But this is safe and RFC compliant.
  289          */
  290         return (peer->local_v4_addr.v4.s_addr);
  291 }
  292 
  293 static int
  294 up_generate_attr(u_char *buf, int len, struct rde_peer *peer,
  295     struct filterstate *state, u_int8_t aid)
  296 {
  297     struct rde_aspath *asp = &state->aspath;
  298     struct attr *oa, *newaggr = NULL;
  299     u_char      *pdata;
  300     u_int32_t    tmp32;
  301     in_addr_t    nexthop;
  302     int      flags, r, neednewpath = 0;
  303     u_int16_t    wlen = 0, plen;
  304     u_int8_t     l;
  305     u_int16_t    nlen = 0;
  306     u_char      *ndata;
  307 
  308     /* origin */
  309     if ((r = attr_write(buf + wlen, len, ATTR_WELL_KNOWN,
  310         ATTR_ORIGIN, &asp->origin, 1)) == -1)
  311         return (-1);
  312     wlen += r; len -= r;
  313 
  314     /* aspath */
  315     if (!peer->conf.ebgp ||
  316         peer->conf.flags & PEERFLAG_TRANS_AS)
  317         pdata = aspath_prepend(asp->aspath, peer->conf.local_as, 0,
  318             &plen);
  319     else
  320         pdata = aspath_prepend(asp->aspath, peer->conf.local_as, 1,
  321             &plen);
  322 
  323     if (!rde_as4byte(peer))
  324         pdata = aspath_deflate(pdata, &plen, &neednewpath);
  325 
  326     if ((r = attr_write(buf + wlen, len, ATTR_WELL_KNOWN,
  327         ATTR_ASPATH, pdata, plen)) == -1)
  328         return (-1);
  329     wlen += r; len -= r;
  330     free(pdata);
  331 
  332     switch (aid) {
  333     case AID_INET:
  334         nexthop = up_get_nexthop(peer, state);
  335         if ((r = attr_write(buf + wlen, len, ATTR_WELL_KNOWN,
  336             ATTR_NEXTHOP, &nexthop, 4)) == -1)
  337             return (-1);
  338         wlen += r; len -= r;
  339         break;
  340     default:
  341         break;
  342     }
  343 
  344     /*
  345      * The old MED from other peers MUST not be announced to others
  346      * unless the MED is originating from us or the peer is an IBGP one.
  347      * Only exception are routers with "transparent-as yes" set.
  348      */
  349     if (asp->flags & F_ATTR_MED && (!peer->conf.ebgp ||
  350         asp->flags & F_ATTR_MED_ANNOUNCE ||
  351         peer->conf.flags & PEERFLAG_TRANS_AS)) {
  352         tmp32 = htonl(asp->med);
  353         if ((r = attr_write(buf + wlen, len, ATTR_OPTIONAL,
  354             ATTR_MED, &tmp32, 4)) == -1)
  355             return (-1);
  356         wlen += r; len -= r;
  357     }
  358 
  359     if (!peer->conf.ebgp) {
  360         /* local preference, only valid for ibgp */
  361         tmp32 = htonl(asp->lpref);
  362         if ((r = attr_write(buf + wlen, len, ATTR_WELL_KNOWN,
  363             ATTR_LOCALPREF, &tmp32, 4)) == -1)
  364             return (-1);
  365         wlen += r; len -= r;
  366     }
  367 
  368     /*
  369      * dump all other path attributes. Following rules apply:
  370      *  1. well-known attrs: ATTR_ATOMIC_AGGREGATE and ATTR_AGGREGATOR
  371      *     pass unmodified (enforce flags to correct values)
  372      *     Actually ATTR_AGGREGATOR may be deflated for OLD 2-byte peers.
  373      *  2. non-transitive attrs: don't re-announce to ebgp peers
  374      *  3. transitive known attrs: announce unmodified
  375      *  4. transitive unknown attrs: set partial bit and re-announce
  376      */
  377     for (l = 0; l < asp->others_len; l++) {
  378         if ((oa = asp->others[l]) == NULL)
  379             break;
  380         switch (oa->type) {
  381         case ATTR_ATOMIC_AGGREGATE:
  382             if ((r = attr_write(buf + wlen, len,
  383                 ATTR_WELL_KNOWN, ATTR_ATOMIC_AGGREGATE,
  384                 NULL, 0)) == -1)
  385                 return (-1);
  386             break;
  387         case ATTR_AGGREGATOR:
  388             if (!rde_as4byte(peer)) {
  389                 /* need to deflate the aggregator */
  390                 u_int8_t    t[6];
  391                 u_int16_t   tas;
  392 
  393                 if ((!(oa->flags & ATTR_TRANSITIVE)) &&
  394                     peer->conf.ebgp) {
  395                     r = 0;
  396                     break;
  397                 }
  398 
  399                 memcpy(&tmp32, oa->data, sizeof(tmp32));
  400                 if (ntohl(tmp32) > USHRT_MAX) {
  401                     tas = htons(AS_TRANS);
  402                     newaggr = oa;
  403                 } else
  404                     tas = htons(ntohl(tmp32));
  405 
  406                 memcpy(t, &tas, sizeof(tas));
  407                 memcpy(t + sizeof(tas),
  408                     oa->data + sizeof(tmp32),
  409                     oa->len - sizeof(tmp32));
  410                 if ((r = attr_write(buf + wlen, len,
  411                     oa->flags, oa->type, &t, sizeof(t))) == -1)
  412                     return (-1);
  413                 break;
  414             }
  415             /* FALLTHROUGH */
  416         case ATTR_COMMUNITIES:
  417         case ATTR_ORIGINATOR_ID:
  418         case ATTR_CLUSTER_LIST:
  419         case ATTR_LARGE_COMMUNITIES:
  420             if ((!(oa->flags & ATTR_TRANSITIVE)) &&
  421                 peer->conf.ebgp) {
  422                 r = 0;
  423                 break;
  424             }
  425             if ((r = attr_write(buf + wlen, len,
  426                 oa->flags, oa->type, oa->data, oa->len)) == -1)
  427                 return (-1);
  428             break;
  429         case ATTR_EXT_COMMUNITIES:
  430             /* handle (non-)transitive extended communities */
  431             if (peer->conf.ebgp) {
  432                 ndata = community_ext_delete_non_trans(oa->data,
  433                     oa->len, &nlen);
  434 
  435                 if (nlen > 0) {
  436                     if ((r = attr_write(buf + wlen, len,
  437                         oa->flags, oa->type, ndata,
  438                         nlen)) == -1) {
  439                         free(ndata);
  440                         return (-1);
  441                     }
  442                     free(ndata);
  443                 } else {
  444                     /* everything got removed */
  445                     r = 0;
  446                 }
  447             } else {
  448                 if ((r = attr_write(buf + wlen, len, oa->flags,
  449                     oa->type, oa->data, oa->len)) == -1)
  450                     return (-1);
  451             }
  452             break;
  453         default:
  454             /* unknown attribute */
  455             if (!(oa->flags & ATTR_TRANSITIVE)) {
  456                 /*
  457                  * RFC 1771:
  458                  * Unrecognized non-transitive optional
  459                  * attributes must be quietly ignored and
  460                  * not passed along to other BGP peers.
  461                  */
  462                 r = 0;
  463                 break;
  464             }
  465             if ((r = attr_write(buf + wlen, len,
  466                 oa->flags | ATTR_PARTIAL, oa->type,
  467                 oa->data, oa->len)) == -1)
  468                 return (-1);
  469             break;
  470         }
  471         wlen += r; len -= r;
  472     }
  473 
  474     /* NEW to OLD conversion when going sending stuff to a 2byte AS peer */
  475     if (neednewpath) {
  476         if (!peer->conf.ebgp ||
  477             peer->conf.flags & PEERFLAG_TRANS_AS)
  478             pdata = aspath_prepend(asp->aspath, peer->conf.local_as,
  479                 0, &plen);
  480         else
  481             pdata = aspath_prepend(asp->aspath, peer->conf.local_as,
  482                 1, &plen);
  483         flags = ATTR_OPTIONAL|ATTR_TRANSITIVE;
  484         if (!(asp->flags & F_PREFIX_ANNOUNCED))
  485             flags |= ATTR_PARTIAL;
  486         if (plen == 0)
  487             r = 0;
  488         else if ((r = attr_write(buf + wlen, len, flags,
  489             ATTR_AS4_PATH, pdata, plen)) == -1)
  490             return (-1);
  491         wlen += r; len -= r;
  492         free(pdata);
  493     }
  494     if (newaggr) {
  495         flags = ATTR_OPTIONAL|ATTR_TRANSITIVE;
  496         if (!(asp->flags & F_PREFIX_ANNOUNCED))
  497             flags |= ATTR_PARTIAL;
  498         if ((r = attr_write(buf + wlen, len, flags,
  499             ATTR_AS4_AGGREGATOR, newaggr->data, newaggr->len)) == -1)
  500             return (-1);
  501         wlen += r; len -= r;
  502     }
  503 
  504     return (wlen);
  505 }
  506 
  507 /*
  508  * Check if the pending element is a EoR marker. If so remove it from the
  509  * tree and return 1.
  510  */
  511 int
  512 up_is_eor(struct rde_peer *peer, u_int8_t aid)
  513 {
  514     struct prefix *p;
  515 
  516     p = RB_MIN(prefix_tree, &peer->updates[aid]);
  517     if (p != NULL && p->eor) {
  518         RB_REMOVE(prefix_tree, &peer->updates[aid], p);
  519         prefix_destroy(p);
  520         return 1;
  521     }
  522     return 0;
  523 }
  524 
  525 /* minimal buffer size > withdraw len + attr len + attr hdr + afi/safi */
  526 #define MIN_UPDATE_LEN  16
  527 
  528 /*
  529  * Write prefixes to buffer until either there is no more space or
  530  * the next prefix has no longer the same ASPATH attributes.
  531  */
  532 static int
  533 up_dump_prefix(u_char *buf, int len, struct prefix_tree *prefix_head,
  534     struct rde_peer *peer, int withdraw)
  535 {
  536     struct prefix   *p, *np;
  537     struct bgpd_addr addr;
  538     int      r, wpos = 0, done = 0;
  539 
  540     RB_FOREACH_SAFE(p, prefix_tree, prefix_head, np) {
  541         pt_getaddr(p->re->prefix, &addr);
  542         if ((r = prefix_write(buf + wpos, len - wpos,
  543             &addr, p->re->prefix->prefixlen, withdraw)) == -1)
  544             break;
  545         wpos += r;
  546 
  547         /* make sure we only dump prefixes which belong together */
  548         if (np == NULL || np->aspath != p->aspath ||
  549             np->nexthop != p->nexthop || np->nhflags != p->nhflags ||
  550             np->eor)
  551             done = 1;
  552 
  553         /* prefix sent, remove from list and clear flag */
  554         RB_REMOVE(prefix_tree, prefix_head, p);
  555         p->flags = 0;
  556 
  557         if (withdraw) {
  558             /* prefix no longer needed, remove it */
  559             prefix_destroy(p);
  560             peer->up_wcnt--;
  561             peer->prefix_sent_withdraw++;
  562         } else {
  563             /* prefix still in Adj-RIB-Out, keep it */
  564             peer->up_nlricnt--;
  565             peer->prefix_sent_update++;
  566         }
  567         if (done)
  568             break;
  569     }
  570     return (wpos);
  571 }
  572 
  573 int
  574 up_dump_withdraws(u_char *buf, int len, struct rde_peer *peer, u_int8_t aid)
  575 {
  576     u_int16_t wpos, wd_len;
  577     int r;
  578 
  579     if (len < MIN_UPDATE_LEN)
  580         return (-1);
  581 
  582     /* reserve space for the length field */
  583     wpos = 2;
  584     r = up_dump_prefix(buf + wpos, len - wpos, &peer->withdraws[aid],
  585         peer, 1);
  586     wd_len = htons(r);
  587     memcpy(buf, &wd_len, 2);
  588 
  589     return (wpos + r);
  590 }
  591 
  592 int
  593 up_dump_mp_unreach(u_char *buf, int len, struct rde_peer *peer, u_int8_t aid)
  594 {
  595     u_char      *attrbuf;
  596     int      wpos, r;
  597     u_int16_t    attr_len, tmp;
  598 
  599     if (len < MIN_UPDATE_LEN || RB_EMPTY(&peer->withdraws[aid]))
  600         return (-1);
  601 
  602     /* reserve space for withdraw len, attr len */
  603     wpos = 2 + 2;
  604     attrbuf = buf + wpos;
  605 
  606     /* attribute header, defaulting to extended length one */
  607     attrbuf[0] = ATTR_OPTIONAL | ATTR_EXTLEN;
  608     attrbuf[1] = ATTR_MP_UNREACH_NLRI;
  609     wpos += 4;
  610 
  611     /* afi & safi */
  612     if (aid2afi(aid, &tmp, buf + wpos + 2))
  613         fatalx("up_dump_mp_unreach: bad AID");
  614     tmp = htons(tmp);
  615     memcpy(buf + wpos, &tmp, sizeof(u_int16_t));
  616     wpos += 3;
  617 
  618     r = up_dump_prefix(buf + wpos, len - wpos, &peer->withdraws[aid],
  619         peer, 1);
  620     if (r == 0)
  621         return (-1);
  622     wpos += r;
  623     attr_len = r + 3;   /* prefixes + afi & safi */
  624 
  625     /* attribute length */
  626     attr_len = htons(attr_len);
  627     memcpy(attrbuf + 2, &attr_len, sizeof(attr_len));
  628 
  629     /* write length fields */
  630     bzero(buf, sizeof(u_int16_t));  /* withdrawn routes len */
  631     attr_len = htons(wpos - 4);
  632     memcpy(buf + 2, &attr_len, sizeof(attr_len));
  633 
  634     return (wpos);
  635 }
  636 
  637 int
  638 up_dump_attrnlri(u_char *buf, int len, struct rde_peer *peer)
  639 {
  640     struct filterstate   state;
  641     struct prefix       *p;
  642     int          r, wpos;
  643     u_int16_t        attr_len;
  644 
  645     if (len < 2)
  646         fatalx("up_dump_attrnlri: buffer way too small");
  647     if (len < MIN_UPDATE_LEN)
  648         goto done;
  649 
  650     p = RB_MIN(prefix_tree, &peer->updates[AID_INET]);
  651     if (p == NULL)
  652         goto done;
  653 
  654     rde_filterstate_prep(&state, prefix_aspath(p), prefix_nexthop(p),
  655         prefix_nhflags(p));
  656 
  657     r = up_generate_attr(buf + 2, len - 2, peer, &state, AID_INET);
  658     rde_filterstate_clean(&state);
  659     if (r == -1) {
  660         /*
  661          * either no packet or not enough space.
  662          * The length field needs to be set to zero else it would be
  663          * an invalid bgp update.
  664          */
  665 done:
  666         bzero(buf, 2);
  667         return (2);
  668     }
  669 
  670     /* first dump the 2-byte path attribute length */
  671     attr_len = htons(r);
  672     memcpy(buf, &attr_len, 2);
  673     wpos = 2;
  674     /* then skip over the already dumped path attributes themselves */
  675     wpos += r;
  676 
  677     /* last but not least dump the nlri */
  678     r = up_dump_prefix(buf + wpos, len - wpos, &peer->updates[AID_INET],
  679         peer, 0);
  680     wpos += r;
  681 
  682     return (wpos);
  683 }
  684 
  685 static int
  686 up_generate_mp_reach(u_char *buf, int len, struct rde_peer *peer,
  687     struct filterstate *state, u_int8_t aid)
  688 {
  689     u_char      *attrbuf;
  690     int      r;
  691     int      wpos, attrlen;
  692     u_int16_t    tmp;
  693 
  694     if (len < 4)
  695         return (-1);
  696     /* attribute header, defaulting to extended length one */
  697     buf[0] = ATTR_OPTIONAL | ATTR_EXTLEN;
  698     buf[1] = ATTR_MP_REACH_NLRI;
  699     wpos = 4;
  700     attrbuf = buf + wpos;
  701 
  702     switch (aid) {
  703     case AID_INET6:
  704         attrlen = 21; /* AFI + SAFI + NH LEN + NH + Reserved */
  705         if (len < wpos + attrlen)
  706             return (-1);
  707         wpos += attrlen;
  708         if (aid2afi(aid, &tmp, &attrbuf[2]))
  709             fatalx("up_generate_mp_reach: bad AID");
  710         tmp = htons(tmp);
  711         memcpy(attrbuf, &tmp, sizeof(tmp));
  712         attrbuf[3] = sizeof(struct in6_addr);
  713         attrbuf[20] = 0; /* Reserved must be 0 */
  714 
  715         /* nexthop dance see also up_get_nexthop() */
  716         attrbuf += 4;
  717         if (state->nhflags & NEXTHOP_NOMODIFY) {
  718             /* no modify flag set */
  719             if (state->nexthop == NULL)
  720                 memcpy(attrbuf, &peer->local_v6_addr.v6,
  721                     sizeof(struct in6_addr));
  722             else
  723                 memcpy(attrbuf,
  724                     &state->nexthop->exit_nexthop.v6,
  725                     sizeof(struct in6_addr));
  726         } else if (state->nhflags & NEXTHOP_SELF)
  727             memcpy(attrbuf, &peer->local_v6_addr.v6,
  728                 sizeof(struct in6_addr));
  729         else if (!peer->conf.ebgp) {
  730             /* ibgp */
  731             if (state->nexthop == NULL ||
  732                 (state->nexthop->exit_nexthop.aid == AID_INET6 &&
  733                 !memcmp(&state->nexthop->exit_nexthop.v6,
  734                 &peer->remote_addr.v6, sizeof(struct in6_addr))))
  735                 memcpy(attrbuf, &peer->local_v6_addr.v6,
  736                     sizeof(struct in6_addr));
  737             else
  738                 memcpy(attrbuf,
  739                     &state->nexthop->exit_nexthop.v6,
  740                     sizeof(struct in6_addr));
  741         } else if (peer->conf.distance == 1) {
  742             /* ebgp directly connected */
  743             if (state->nexthop != NULL &&
  744                 state->nexthop->flags & NEXTHOP_CONNECTED)
  745                 if (prefix_compare(&peer->remote_addr,
  746                     &state->nexthop->nexthop_net,
  747                     state->nexthop->nexthop_netlen) == 0) {
  748                     /*
  749                      * nexthop and peer are in the same
  750                      * subnet
  751                      */
  752                     memcpy(attrbuf,
  753                         &state->nexthop->exit_nexthop.v6,
  754                         sizeof(struct in6_addr));
  755                     break;
  756                 }
  757             memcpy(attrbuf, &peer->local_v6_addr.v6,
  758                 sizeof(struct in6_addr));
  759         } else
  760             /* ebgp multihop */
  761             memcpy(attrbuf, &peer->local_v6_addr.v6,
  762                 sizeof(struct in6_addr));
  763         break;
  764     case AID_VPN_IPv4:
  765         attrlen = 17; /* AFI + SAFI + NH LEN + NH + Reserved */
  766         if (len < wpos + attrlen)
  767             return (-1);
  768         wpos += attrlen;
  769         if (aid2afi(aid, &tmp, &attrbuf[2]))
  770             fatalx("up_generate_mp_reachi: bad AID");
  771         tmp = htons(tmp);
  772         memcpy(attrbuf, &tmp, sizeof(tmp));
  773         attrbuf[3] = sizeof(u_int64_t) + sizeof(struct in_addr);
  774         bzero(attrbuf + 4, sizeof(u_int64_t));
  775         attrbuf[16] = 0; /* Reserved must be 0 */
  776 
  777         /* nexthop dance see also up_get_nexthop() */
  778         attrbuf += 12;
  779         if (state->nhflags & NEXTHOP_NOMODIFY) {
  780             /* no modify flag set */
  781             if (state->nexthop == NULL)
  782                 memcpy(attrbuf, &peer->local_v4_addr.v4,
  783                     sizeof(struct in_addr));
  784             else
  785                 /* nexthops are stored as IPv4 addrs */
  786                 memcpy(attrbuf,
  787                     &state->nexthop->exit_nexthop.v4,
  788                     sizeof(struct in_addr));
  789         } else if (state->nhflags & NEXTHOP_SELF) {
  790             memcpy(attrbuf, &peer->local_v4_addr.v4,
  791                 sizeof(struct in_addr));
  792         } else if (!peer->conf.ebgp) {
  793             /* ibgp */
  794             if (state->nexthop == NULL ||
  795                 (state->nexthop->exit_nexthop.aid == AID_INET &&
  796                 !memcmp(&state->nexthop->exit_nexthop.v4,
  797                 &peer->remote_addr.v4, sizeof(struct in_addr))))
  798                 memcpy(attrbuf, &peer->local_v4_addr.v4,
  799                     sizeof(struct in_addr));
  800             else
  801                 memcpy(attrbuf,
  802                     &state->nexthop->exit_nexthop.v4,
  803                     sizeof(struct in_addr));
  804         } else if (peer->conf.distance == 1) {
  805             /* ebgp directly connected */
  806             if (state->nexthop != NULL &&
  807                 state->nexthop->flags & NEXTHOP_CONNECTED)
  808                 if (prefix_compare(&peer->remote_addr,
  809                     &state->nexthop->nexthop_net,
  810                     state->nexthop->nexthop_netlen) == 0) {
  811                     /*
  812                      * nexthop and peer are in the same
  813                      * subnet
  814                      */
  815                     memcpy(attrbuf,
  816                         &state->nexthop->exit_nexthop.v4,
  817                         sizeof(struct in_addr));
  818                     break;
  819                 }
  820             memcpy(attrbuf, &peer->local_v4_addr.v4,
  821                 sizeof(struct in_addr));
  822         } else
  823             /* ebgp multihop */
  824             memcpy(attrbuf, &peer->local_v4_addr.v4,
  825                 sizeof(struct in_addr));
  826         break;
  827     case AID_VPN_IPv6:
  828         attrlen = 29; /* AFI + SAFI + NH LEN + NH + Reserved */
  829         if (len < wpos + attrlen)
  830             return (-1);
  831         wpos += attrlen;
  832         if (aid2afi(aid, &tmp, &attrbuf[2]))
  833             fatalx("up_generate_mp_reachi: bad AID");
  834         tmp = htons(tmp);
  835         memcpy(attrbuf, &tmp, sizeof(tmp));
  836         attrbuf[3] = sizeof(u_int64_t) + sizeof(struct in6_addr);
  837         bzero(attrbuf + 4, sizeof(u_int64_t));
  838         attrbuf[28] = 0; /* Reserved must be 0 */
  839 
  840         /* nexthop dance see also up_get_nexthop() */
  841         attrbuf += 12;
  842         if (state->nhflags & NEXTHOP_NOMODIFY) {
  843             /* no modify flag set */
  844             if (state->nexthop == NULL)
  845                 memcpy(attrbuf, &peer->local_v6_addr.v6,
  846                     sizeof(struct in6_addr));
  847             else
  848                 memcpy(attrbuf,
  849                     &state->nexthop->exit_nexthop.v6,
  850                     sizeof(struct in6_addr));
  851         } else if (state->nhflags & NEXTHOP_SELF)
  852             memcpy(attrbuf, &peer->local_v6_addr.v6,
  853                 sizeof(struct in6_addr));
  854         else if (!peer->conf.ebgp) {
  855             /* ibgp */
  856             if (state->nexthop == NULL ||
  857                 (state->nexthop->exit_nexthop.aid == AID_INET6 &&
  858                 !memcmp(&state->nexthop->exit_nexthop.v6,
  859                 &peer->remote_addr.v6, sizeof(struct in6_addr))))
  860                 memcpy(attrbuf, &peer->local_v6_addr.v6,
  861                     sizeof(struct in6_addr));
  862             else
  863                 memcpy(attrbuf,
  864                     &state->nexthop->exit_nexthop.v6,
  865                     sizeof(struct in6_addr));
  866         } else if (peer->conf.distance == 1) {
  867             /* ebgp directly connected */
  868             if (state->nexthop != NULL &&
  869                 state->nexthop->flags & NEXTHOP_CONNECTED)
  870                 if (prefix_compare(&peer->remote_addr,
  871                     &state->nexthop->nexthop_net,
  872                     state->nexthop->nexthop_netlen) == 0) {
  873                     /*
  874                     * nexthop and peer are in the same
  875                     * subnet
  876                     */
  877                     memcpy(attrbuf,
  878                         &state->nexthop->exit_nexthop.v6,
  879                         sizeof(struct in6_addr));
  880                     break;
  881                 }
  882             memcpy(attrbuf, &peer->local_v6_addr.v6,
  883                 sizeof(struct in6_addr));
  884         } else
  885             /* ebgp multihop */
  886             memcpy(attrbuf, &peer->local_v6_addr.v6,
  887                 sizeof(struct in6_addr));
  888         break;
  889     default:
  890         fatalx("up_generate_mp_reach: unknown AID");
  891     }
  892 
  893     r = up_dump_prefix(buf + wpos, len - wpos, &peer->updates[aid],
  894         peer, 0);
  895     if (r == 0) {
  896         /* no prefixes written ... */
  897         return (-1);
  898     }
  899     attrlen += r;
  900     wpos += r;
  901     /* update attribute length field */
  902     tmp = htons(attrlen);
  903     memcpy(buf + 2, &tmp, sizeof(tmp));
  904 
  905     return (wpos);
  906 }
  907 
  908 int
  909 up_dump_mp_reach(u_char *buf, int len, struct rde_peer *peer, u_int8_t aid)
  910 {
  911     struct filterstate   state;
  912     struct prefix       *p;
  913     int         r, wpos;
  914     u_int16_t       attr_len;
  915 
  916     if (len < MIN_UPDATE_LEN)
  917         return 0;
  918 
  919     /* get starting point */
  920     p = RB_MIN(prefix_tree, &peer->updates[aid]);
  921     if (p == NULL)
  922         return 0;
  923 
  924     wpos = 4;   /* reserve space for length fields */
  925 
  926     rde_filterstate_prep(&state, prefix_aspath(p), prefix_nexthop(p),
  927         prefix_nhflags(p));
  928 
  929     /* write regular path attributes */
  930     r = up_generate_attr(buf + wpos, len - wpos, peer, &state, aid);
  931     if (r == -1) {
  932         rde_filterstate_clean(&state);
  933         return 0;
  934     }
  935     wpos += r;
  936 
  937     /* write mp attribute */
  938     r = up_generate_mp_reach(buf + wpos, len - wpos, peer, &state, aid);
  939     rde_filterstate_clean(&state);
  940     if (r == -1)
  941         return 0;
  942     wpos += r;
  943 
  944     /* write length fields */
  945     bzero(buf, sizeof(u_int16_t));  /* withdrawn routes len */
  946     attr_len = htons(wpos - 4);
  947     memcpy(buf + 2, &attr_len, sizeof(attr_len));
  948 
  949     return (wpos);
  950 }