"Fossies" - the Fresh Open Source Software Archive

Member "openbgpd-6.5p0/src/bgpd/rde_filter.c" (14 Feb 2019, 22887 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_filter.c" see the Fossies "Dox" file reference documentation.

    1 /*  $OpenBSD: rde_filter.c,v 1.117 2019/02/04 18:53:10 claudio Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
    5  * Copyright (c) 2016 Job Snijders <job@instituut.net>
    6  * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org>
    7  * Copyright (c) 2018 Sebastian Benoit <benno@openbsd.org>
    8  *
    9  * Permission to use, copy, modify, and distribute this software for any
   10  * purpose with or without fee is hereby granted, provided that the above
   11  * copyright notice and this permission notice appear in all copies.
   12  *
   13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   20  */
   21 #include <sys/types.h>
   22 #include <sys/queue.h>
   23 
   24 #include <limits.h>
   25 #include <stdlib.h>
   26 #include <string.h>
   27 
   28 #include "bgpd.h"
   29 #include "rde.h"
   30 #include "log.h"
   31 
   32 int rde_filter_match(struct filter_rule *, struct rde_peer *,
   33         struct filterstate *, struct prefix *);
   34 int rde_prefix_match(struct filter_prefix *, struct prefix *);
   35 int filterset_equal(struct filter_set_head *, struct filter_set_head *);
   36 
   37 void
   38 rde_apply_set(struct filter_set_head *sh, struct filterstate *state,
   39     u_int8_t aid, struct rde_peer *from, struct rde_peer *peer)
   40 {
   41     struct filter_set   *set;
   42     u_char          *np;
   43     u_int32_t        prep_as;
   44     u_int16_t        nl;
   45     u_int8_t         prepend;
   46 
   47     if (state == NULL)
   48         return;
   49 
   50     TAILQ_FOREACH(set, sh, entry) {
   51         switch (set->type) {
   52         case ACTION_SET_LOCALPREF:
   53             state->aspath.lpref = set->action.metric;
   54             break;
   55         case ACTION_SET_RELATIVE_LOCALPREF:
   56             if (set->action.relative > 0) {
   57                 if (set->action.relative + state->aspath.lpref <
   58                     state->aspath.lpref)
   59                     state->aspath.lpref = UINT_MAX;
   60                 else
   61                     state->aspath.lpref +=
   62                         set->action.relative;
   63             } else {
   64                 if ((u_int32_t)-set->action.relative >
   65                     state->aspath.lpref)
   66                     state->aspath.lpref = 0;
   67                 else
   68                     state->aspath.lpref +=
   69                         set->action.relative;
   70             }
   71             break;
   72         case ACTION_SET_MED:
   73             state->aspath.flags |= F_ATTR_MED | F_ATTR_MED_ANNOUNCE;
   74             state->aspath.med = set->action.metric;
   75             break;
   76         case ACTION_SET_RELATIVE_MED:
   77             state->aspath.flags |= F_ATTR_MED | F_ATTR_MED_ANNOUNCE;
   78             if (set->action.relative > 0) {
   79                 if (set->action.relative + state->aspath.med <
   80                     state->aspath.med)
   81                     state->aspath.med = UINT_MAX;
   82                 else
   83                     state->aspath.med +=
   84                         set->action.relative;
   85             } else {
   86                 if ((u_int32_t)-set->action.relative >
   87                     state->aspath.med)
   88                     state->aspath.med = 0;
   89                 else
   90                     state->aspath.med +=
   91                         set->action.relative;
   92             }
   93             break;
   94         case ACTION_SET_WEIGHT:
   95             state->aspath.weight = set->action.metric;
   96             break;
   97         case ACTION_SET_RELATIVE_WEIGHT:
   98             if (set->action.relative > 0) {
   99                 if (set->action.relative + state->aspath.weight
  100                     < state->aspath.weight)
  101                     state->aspath.weight = UINT_MAX;
  102                 else
  103                     state->aspath.weight +=
  104                         set->action.relative;
  105             } else {
  106                 if ((u_int32_t)-set->action.relative >
  107                     state->aspath.weight)
  108                     state->aspath.weight = 0;
  109                 else
  110                     state->aspath.weight +=
  111                         set->action.relative;
  112             }
  113             break;
  114         case ACTION_SET_PREPEND_SELF:
  115             prep_as = peer->conf.local_as;
  116             prepend = set->action.prepend;
  117             np = aspath_prepend(state->aspath.aspath, prep_as,
  118                 prepend, &nl);
  119             aspath_put(state->aspath.aspath);
  120             state->aspath.aspath = aspath_get(np, nl);
  121             free(np);
  122             break;
  123         case ACTION_SET_PREPEND_PEER:
  124             if (from == NULL)
  125                 break;
  126             prep_as = from->conf.remote_as;
  127             prepend = set->action.prepend;
  128             np = aspath_prepend(state->aspath.aspath, prep_as,
  129                 prepend, &nl);
  130             aspath_put(state->aspath.aspath);
  131             state->aspath.aspath = aspath_get(np, nl);
  132             free(np);
  133             break;
  134         case ACTION_SET_AS_OVERRIDE:
  135             if (from == NULL)
  136                 break;
  137              np = aspath_override(state->aspath.aspath,
  138                  from->conf.remote_as, from->conf.local_as, &nl);
  139             aspath_put(state->aspath.aspath);
  140             state->aspath.aspath = aspath_get(np, nl);
  141             free(np);
  142             break;
  143         case ACTION_SET_NEXTHOP:
  144         case ACTION_SET_NEXTHOP_REJECT:
  145         case ACTION_SET_NEXTHOP_BLACKHOLE:
  146         case ACTION_SET_NEXTHOP_NOMODIFY:
  147         case ACTION_SET_NEXTHOP_SELF:
  148             nexthop_modify(set->action.nh, set->type, aid,
  149                 &state->nexthop, &state->nhflags);
  150             break;
  151         case ACTION_SET_COMMUNITY:
  152             switch (set->action.community.type) {
  153             case COMMUNITY_TYPE_BASIC:
  154                 community_set(&state->aspath,
  155                     &set->action.community, peer);
  156                 break;
  157             case COMMUNITY_TYPE_LARGE:
  158                 community_large_set(&state->aspath,
  159                     &set->action.community, peer);
  160                 break;
  161             case COMMUNITY_TYPE_EXT:
  162                 community_ext_set(&state->aspath,
  163                     &set->action.community, peer);
  164                 break;
  165             }
  166             break;
  167         case ACTION_DEL_COMMUNITY:
  168             switch (set->action.community.type) {
  169             case COMMUNITY_TYPE_BASIC:
  170                 community_delete(&state->aspath,
  171                     &set->action.community, peer);
  172                 break;
  173             case COMMUNITY_TYPE_LARGE:
  174                 community_large_delete(&state->aspath,
  175                     &set->action.community, peer);
  176                 break;
  177             case COMMUNITY_TYPE_EXT:
  178                 community_ext_delete(&state->aspath,
  179                     &set->action.community, peer);
  180             }
  181             break;
  182         case ACTION_PFTABLE:
  183             /* convert pftable name to an id */
  184             set->action.id = pftable_name2id(set->action.pftable);
  185             set->type = ACTION_PFTABLE_ID;
  186             /* FALLTHROUGH */
  187         case ACTION_PFTABLE_ID:
  188             pftable_unref(state->aspath.pftableid);
  189             state->aspath.pftableid = pftable_ref(set->action.id);
  190             break;
  191         case ACTION_RTLABEL:
  192             /* convert the route label to an id for faster access */
  193             set->action.id = rtlabel_name2id(set->action.rtlabel);
  194             set->type = ACTION_RTLABEL_ID;
  195             /* FALLTHROUGH */
  196         case ACTION_RTLABEL_ID:
  197             rtlabel_unref(state->aspath.rtlabelid);
  198             state->aspath.rtlabelid = rtlabel_ref(set->action.id);
  199             break;
  200         case ACTION_SET_ORIGIN:
  201             state->aspath.origin = set->action.origin;
  202             break;
  203         }
  204     }
  205 }
  206 
  207 int
  208 rde_filter_match(struct filter_rule *f, struct rde_peer *peer,
  209     struct filterstate *state, struct prefix *p)
  210 {
  211     struct rde_aspath *asp = NULL;
  212     int i;
  213 
  214     if (state != NULL)
  215         asp = &state->aspath;
  216 
  217     if (f->peer.ebgp && !peer->conf.ebgp)
  218         return (0);
  219     if (f->peer.ibgp && peer->conf.ebgp)
  220         return (0);
  221 
  222     if (f->match.ovs.is_set) {
  223         if (prefix_vstate(p) != f->match.ovs.validity)
  224             return (0);
  225     }
  226 
  227     if (asp != NULL && f->match.as.type != AS_UNDEF) {
  228         if (aspath_match(asp->aspath, &f->match.as,
  229             peer->conf.remote_as) == 0)
  230             return (0);
  231     }
  232 
  233     if (asp != NULL && f->match.aslen.type != ASLEN_NONE)
  234         if (aspath_lenmatch(asp->aspath, f->match.aslen.type,
  235             f->match.aslen.aslen) == 0)
  236             return (0);
  237 
  238     for (i = 0; asp != NULL && i < MAX_COMM_MATCH; i++) {
  239         switch (f->match.community[i].type) {
  240         case COMMUNITY_TYPE_NONE:
  241             i = MAX_COMM_MATCH;
  242             break;
  243         case COMMUNITY_TYPE_BASIC:
  244             if (community_match(asp, &f->match.community[i],
  245                 peer) == 0)
  246                 return (0);
  247             break;
  248         case COMMUNITY_TYPE_LARGE:
  249             if (community_large_match(asp, &f->match.community[i],
  250                 peer) == 0)
  251                 return (0);
  252             break;
  253         case COMMUNITY_TYPE_EXT:
  254             if (community_ext_match(asp, &f->match.community[i],
  255                 peer) == 0)
  256                 return (0);
  257         }
  258     }
  259 
  260     if (state != NULL && f->match.nexthop.flags != 0) {
  261         struct bgpd_addr *nexthop, *cmpaddr;
  262         if (state->nexthop == NULL)
  263             /* no nexthop, skip */
  264             return (0);
  265         nexthop = &state->nexthop->exit_nexthop;
  266         if (f->match.nexthop.flags == FILTER_NEXTHOP_ADDR)
  267             cmpaddr = &f->match.nexthop.addr;
  268         else
  269             cmpaddr = &prefix_peer(p)->remote_addr;
  270         if (cmpaddr->aid != nexthop->aid)
  271             /* don't use IPv4 rules for IPv6 and vice versa */
  272             return (0);
  273 
  274         switch (cmpaddr->aid) {
  275         case AID_INET:
  276             if (cmpaddr->v4.s_addr != nexthop->v4.s_addr)
  277                 return (0);
  278             break;
  279         case AID_INET6:
  280             if (memcmp(&cmpaddr->v6, &nexthop->v6,
  281                 sizeof(struct in6_addr)))
  282                 return (0);
  283             break;
  284         default:
  285             fatalx("King Bula lost in address space");
  286         }
  287     }
  288 
  289     /* origin-set lookups match only on ROA_VALID */
  290     if (asp != NULL && f->match.originset.ps != NULL) {
  291         struct bgpd_addr addr, *prefix = &addr;
  292         u_int8_t plen;
  293 
  294         pt_getaddr(p->re->prefix, prefix);
  295         plen = p->re->prefix->prefixlen;
  296         if (trie_roa_check(&f->match.originset.ps->th, prefix, plen,
  297             aspath_origin(asp->aspath)) != ROA_VALID)
  298             return (0);
  299     }
  300 
  301     /*
  302      * prefixset and prefix filter rules are mutual exclusive
  303      */
  304     if (f->match.prefixset.flags != 0) {
  305         struct bgpd_addr addr, *prefix = &addr;
  306         u_int8_t plen;
  307 
  308         pt_getaddr(p->re->prefix, prefix);
  309         plen = p->re->prefix->prefixlen;
  310         if (f->match.prefixset.ps == NULL ||
  311             !trie_match(&f->match.prefixset.ps->th, prefix, plen,
  312             (f->match.prefixset.flags & PREFIXSET_FLAG_LONGER)))
  313             return (0);
  314     } else if (f->match.prefix.addr.aid != 0)
  315         return (rde_prefix_match(&f->match.prefix, p));
  316 
  317     /* matched somewhen or is anymatch rule  */
  318     return (1);
  319 }
  320 
  321 /* return 1 when prefix matches filter_prefix, 0 if not */
  322 int
  323 rde_prefix_match(struct filter_prefix *fp, struct prefix *p)
  324 {
  325     struct bgpd_addr addr, *prefix = &addr;
  326     u_int8_t plen;
  327 
  328     pt_getaddr(p->re->prefix, prefix);
  329     plen = p->re->prefix->prefixlen;
  330 
  331     if (fp->addr.aid != prefix->aid)
  332         /* don't use IPv4 rules for IPv6 and vice versa */
  333         return (0);
  334 
  335     if (prefix_compare(prefix, &fp->addr, fp->len))
  336         return (0);
  337 
  338     /* test prefixlen stuff too */
  339     switch (fp->op) {
  340     case OP_NONE: /* perfect match */
  341         return (plen == fp->len);
  342     case OP_EQ:
  343         return (plen == fp->len_min);
  344     case OP_NE:
  345         return (plen != fp->len_min);
  346     case OP_RANGE:
  347         return ((plen >= fp->len_min) &&
  348             (plen <= fp->len_max));
  349     case OP_XRANGE:
  350         return ((plen < fp->len_min) ||
  351             (plen > fp->len_max));
  352     default:
  353         log_warnx("%s: unsupported prefix operation", __func__);
  354         return (0);
  355     }
  356 }
  357 
  358 /* return true when the rule f can never match for this peer */
  359 static int
  360 rde_filter_skip_rule(struct rde_peer *peer, struct filter_rule *f)
  361 {
  362     /* if any of the two is unset then rule can't be skipped */
  363     if (peer == NULL || f == NULL)
  364         return (0);
  365 
  366     if (f->peer.groupid != 0 &&
  367         f->peer.groupid != peer->conf.groupid)
  368         return (1);
  369 
  370     if (f->peer.peerid != 0 &&
  371         f->peer.peerid != peer->conf.id)
  372         return (1);
  373 
  374     if (f->peer.remote_as != 0 &&
  375         f->peer.remote_as != peer->conf.remote_as)
  376         return (1);
  377 
  378     if (f->peer.ebgp != 0 &&
  379         f->peer.ebgp != peer->conf.ebgp)
  380         return (1);
  381 
  382     if (f->peer.ibgp != 0 &&
  383         f->peer.ibgp != !peer->conf.ebgp)
  384         return (1);
  385 
  386     return (0);
  387 }
  388 
  389 int
  390 rde_filter_equal(struct filter_head *a, struct filter_head *b,
  391     struct rde_peer *peer)
  392 {
  393     struct filter_rule  *fa, *fb;
  394     struct rde_prefixset    *psa, *psb, *osa, *osb;
  395     struct as_set       *asa, *asb;
  396     int          r;
  397 
  398     fa = a ? TAILQ_FIRST(a) : NULL;
  399     fb = b ? TAILQ_FIRST(b) : NULL;
  400 
  401     while (fa != NULL || fb != NULL) {
  402         /* skip all rules with wrong peer */
  403         if (rde_filter_skip_rule(peer, fa)) {
  404             fa = TAILQ_NEXT(fa, entry);
  405             continue;
  406         }
  407         if (rde_filter_skip_rule(peer, fb)) {
  408             fb = TAILQ_NEXT(fb, entry);
  409             continue;
  410         }
  411 
  412         /* compare the two rules */
  413         if ((fa == NULL && fb != NULL) || (fa != NULL && fb == NULL))
  414             /* new rule added or removed */
  415             return (0);
  416 
  417         if (fa->action != fb->action || fa->quick != fb->quick)
  418             return (0);
  419         if (memcmp(&fa->peer, &fb->peer, sizeof(fa->peer)))
  420             return (0);
  421 
  422         /* compare filter_rule.match without the prefixset pointer */
  423         psa = fa->match.prefixset.ps;
  424         psb = fb->match.prefixset.ps;
  425         osa = fa->match.originset.ps;
  426         osb = fb->match.originset.ps;
  427         asa = fa->match.as.aset;
  428         asb = fb->match.as.aset;
  429         fa->match.prefixset.ps = fb->match.prefixset.ps = NULL;
  430         fa->match.originset.ps = fb->match.originset.ps = NULL;
  431         fa->match.as.aset = fb->match.as.aset = NULL;
  432         r = memcmp(&fa->match, &fb->match, sizeof(fa->match));
  433         /* fixup the struct again */
  434         fa->match.prefixset.ps = psa;
  435         fb->match.prefixset.ps = psb;
  436         fa->match.originset.ps = osa;
  437         fb->match.originset.ps = osb;
  438         fa->match.as.aset = asa;
  439         fb->match.as.aset = asb;
  440         if (r != 0)
  441             return (0);
  442         if (fa->match.prefixset.ps != NULL &&
  443             fa->match.prefixset.ps->dirty) {
  444             log_debug("%s: prefixset %s has changed",
  445                 __func__, fa->match.prefixset.name);
  446             return (0);
  447         }
  448         if (fa->match.originset.ps != NULL &&
  449             fa->match.originset.ps->dirty) {
  450             log_debug("%s: originset %s has changed",
  451                 __func__, fa->match.originset.name);
  452             return (0);
  453         }
  454         if ((fa->match.as.flags & AS_FLAG_AS_SET) &&
  455             fa->match.as.aset->dirty) {
  456             log_debug("%s: as-set %s has changed",
  457                 __func__, fa->match.as.name);
  458             return (0);
  459         }
  460 
  461         if (!filterset_equal(&fa->set, &fb->set))
  462             return (0);
  463 
  464         fa = TAILQ_NEXT(fa, entry);
  465         fb = TAILQ_NEXT(fb, entry);
  466     }
  467     return (1);
  468 }
  469 
  470 void
  471 rde_filterstate_prep(struct filterstate *state, struct rde_aspath *asp,
  472     struct nexthop *nh, u_int8_t nhflags)
  473 {
  474     memset(state, 0, sizeof(*state));
  475 
  476     path_prep(&state->aspath);
  477     if (asp)
  478         path_copy(&state->aspath, asp);
  479     state->nexthop = nexthop_ref(nh);
  480     state->nhflags = nhflags;
  481 }
  482 
  483 void
  484 rde_filterstate_clean(struct filterstate *state)
  485 {
  486     path_clean(&state->aspath);
  487     nexthop_put(state->nexthop);
  488     state->nexthop = NULL;
  489 }
  490 
  491 void
  492 filterlist_free(struct filter_head *fh)
  493 {
  494     struct filter_rule  *r;
  495 
  496     if (fh == NULL)
  497         return;
  498 
  499     while ((r = TAILQ_FIRST(fh)) != NULL) {
  500         TAILQ_REMOVE(fh, r, entry);
  501         filterset_free(&r->set);
  502         free(r);
  503     }
  504     free(fh);
  505 }
  506 
  507 /* free a filterset and take care of possible name2id references */
  508 void
  509 filterset_free(struct filter_set_head *sh)
  510 {
  511     struct filter_set   *s;
  512 
  513     if (sh == NULL)
  514         return;
  515 
  516     while ((s = TAILQ_FIRST(sh)) != NULL) {
  517         TAILQ_REMOVE(sh, s, entry);
  518         if (s->type == ACTION_RTLABEL_ID)
  519             rtlabel_unref(s->action.id);
  520         else if (s->type == ACTION_PFTABLE_ID)
  521             pftable_unref(s->action.id);
  522         else if (s->type == ACTION_SET_NEXTHOP &&
  523             bgpd_process == PROC_RDE)
  524             nexthop_put(s->action.nh);
  525         free(s);
  526     }
  527 }
  528 
  529 /*
  530  * this function is a bit more complicated than a memcmp() because there are
  531  * types that need to be considered equal e.g. ACTION_SET_MED and
  532  * ACTION_SET_RELATIVE_MED. Also ACTION_SET_COMMUNITY and ACTION_SET_NEXTHOP
  533  * need some special care. It only checks the types and not the values so
  534  * it does not do a real compare.
  535  */
  536 int
  537 filterset_cmp(struct filter_set *a, struct filter_set *b)
  538 {
  539     if (strcmp(filterset_name(a->type), filterset_name(b->type)))
  540         return (a->type - b->type);
  541 
  542     if (a->type == ACTION_SET_COMMUNITY ||
  543         a->type == ACTION_DEL_COMMUNITY) {  /* a->type == b->type */
  544         return (memcmp(&a->action.community, &b->action.community,
  545             sizeof(a->action.community)));
  546     }
  547 
  548     if (a->type == ACTION_SET_NEXTHOP && b->type == ACTION_SET_NEXTHOP) {
  549         /*
  550          * This is the only interesting case, all others are considered
  551          * equal. It does not make sense to e.g. set a nexthop and
  552          * reject it at the same time. Allow one IPv4 and one IPv6
  553          * per filter set or only one of the other nexthop modifiers.
  554          */
  555         return (a->action.nexthop.aid - b->action.nexthop.aid);
  556     }
  557 
  558     /* equal */
  559     return (0);
  560 }
  561 
  562 void
  563 filterset_move(struct filter_set_head *source, struct filter_set_head *dest)
  564 {
  565     TAILQ_INIT(dest);
  566     if (source == NULL)
  567         return;
  568     TAILQ_CONCAT(dest, source, entry);
  569 }
  570 
  571 int
  572 filterset_equal(struct filter_set_head *ah, struct filter_set_head *bh)
  573 {
  574     struct filter_set   *a, *b;
  575     const char      *as, *bs;
  576 
  577     for (a = TAILQ_FIRST(ah), b = TAILQ_FIRST(bh);
  578         a != NULL && b != NULL;
  579         a = TAILQ_NEXT(a, entry), b = TAILQ_NEXT(b, entry)) {
  580         switch (a->type) {
  581         case ACTION_SET_PREPEND_SELF:
  582         case ACTION_SET_PREPEND_PEER:
  583             if (a->type == b->type &&
  584                 a->action.prepend == b->action.prepend)
  585                 continue;
  586             break;
  587         case ACTION_SET_AS_OVERRIDE:
  588             if (a->type == b->type)
  589                 continue;
  590             break;
  591         case ACTION_SET_LOCALPREF:
  592         case ACTION_SET_MED:
  593         case ACTION_SET_WEIGHT:
  594             if (a->type == b->type &&
  595                 a->action.metric == b->action.metric)
  596                 continue;
  597             break;
  598         case ACTION_SET_RELATIVE_LOCALPREF:
  599         case ACTION_SET_RELATIVE_MED:
  600         case ACTION_SET_RELATIVE_WEIGHT:
  601             if (a->type == b->type &&
  602                 a->action.relative == b->action.relative)
  603                 continue;
  604             break;
  605         case ACTION_SET_NEXTHOP:
  606             if (a->type == b->type &&
  607                 memcmp(&a->action.nexthop, &b->action.nexthop,
  608                 sizeof(a->action.nexthop)) == 0)
  609                 continue;
  610             break;
  611         case ACTION_SET_NEXTHOP_BLACKHOLE:
  612         case ACTION_SET_NEXTHOP_REJECT:
  613         case ACTION_SET_NEXTHOP_NOMODIFY:
  614         case ACTION_SET_NEXTHOP_SELF:
  615             if (a->type == b->type)
  616                 continue;
  617             break;
  618         case ACTION_DEL_COMMUNITY:
  619         case ACTION_SET_COMMUNITY:
  620             if (a->type == b->type &&
  621                 memcmp(&a->action.community, &b->action.community,
  622                 sizeof(a->action.community)) == 0)
  623                 continue;
  624             break;
  625         case ACTION_PFTABLE:
  626         case ACTION_PFTABLE_ID:
  627             if (b->type == ACTION_PFTABLE)
  628                 bs = b->action.pftable;
  629             else if (b->type == ACTION_PFTABLE_ID)
  630                 bs = pftable_id2name(b->action.id);
  631             else
  632                 break;
  633 
  634             if (a->type == ACTION_PFTABLE)
  635                 as = a->action.pftable;
  636             else
  637                 as = pftable_id2name(a->action.id);
  638 
  639             if (strcmp(as, bs) == 0)
  640                 continue;
  641             break;
  642         case ACTION_RTLABEL:
  643         case ACTION_RTLABEL_ID:
  644             if (b->type == ACTION_RTLABEL)
  645                 bs = b->action.rtlabel;
  646             else if (b->type == ACTION_RTLABEL_ID)
  647                 bs = rtlabel_id2name(b->action.id);
  648             else
  649                 break;
  650 
  651             if (a->type == ACTION_RTLABEL)
  652                 as = a->action.rtlabel;
  653             else
  654                 as = rtlabel_id2name(a->action.id);
  655 
  656             if (strcmp(as, bs) == 0)
  657                 continue;
  658             break;
  659         case ACTION_SET_ORIGIN:
  660             if (a->type == b->type &&
  661                 a->action.origin == b->action.origin)
  662                 continue;
  663             break;
  664         }
  665         /* compare failed */
  666         return (0);
  667     }
  668     if (a != NULL || b != NULL)
  669         return (0);
  670     return (1);
  671 }
  672 
  673 const char *
  674 filterset_name(enum action_types type)
  675 {
  676     switch (type) {
  677     case ACTION_SET_LOCALPREF:
  678     case ACTION_SET_RELATIVE_LOCALPREF:
  679         return ("localpref");
  680     case ACTION_SET_MED:
  681     case ACTION_SET_RELATIVE_MED:
  682         return ("metric");
  683     case ACTION_SET_WEIGHT:
  684     case ACTION_SET_RELATIVE_WEIGHT:
  685         return ("weight");
  686     case ACTION_SET_PREPEND_SELF:
  687         return ("prepend-self");
  688     case ACTION_SET_PREPEND_PEER:
  689         return ("prepend-peer");
  690     case ACTION_SET_AS_OVERRIDE:
  691         return ("as-override");
  692     case ACTION_SET_NEXTHOP:
  693     case ACTION_SET_NEXTHOP_REJECT:
  694     case ACTION_SET_NEXTHOP_BLACKHOLE:
  695     case ACTION_SET_NEXTHOP_NOMODIFY:
  696     case ACTION_SET_NEXTHOP_SELF:
  697         return ("nexthop");
  698     case ACTION_SET_COMMUNITY:
  699         return ("community");
  700     case ACTION_DEL_COMMUNITY:
  701         return ("community delete");
  702     case ACTION_PFTABLE:
  703     case ACTION_PFTABLE_ID:
  704         return ("pftable");
  705     case ACTION_RTLABEL:
  706     case ACTION_RTLABEL_ID:
  707         return ("rtlabel");
  708     case ACTION_SET_ORIGIN:
  709         return ("origin");
  710     }
  711 
  712     fatalx("filterset_name: got lost");
  713 }
  714 
  715 /*
  716  * Copyright (c) 2001 Daniel Hartmeier
  717  * All rights reserved.
  718  *
  719  * Redistribution and use in source and binary forms, with or without
  720  * modification, are permitted provided that the following conditions
  721  * are met:
  722  *
  723  *    - Redistributions of source code must retain the above copyright
  724  *      notice, this list of conditions and the following disclaimer.
  725  *    - Redistributions in binary form must reproduce the above
  726  *      copyright notice, this list of conditions and the following
  727  *      disclaimer in the documentation and/or other materials provided
  728  *      with the distribution.
  729  *
  730  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  731  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  732  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  733  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  734  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  735  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  736  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  737  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  738  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  739  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  740  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  741  * POSSIBILITY OF SUCH DAMAGE.
  742  *
  743  * Effort sponsored in part by the Defense Advanced Research Projects
  744  * Agency (DARPA) and Air Force Research Laboratory, Air Force
  745  * Materiel Command, USAF, under agreement number F30602-01-2-0537.
  746  *
  747  */
  748 
  749 #define RDE_FILTER_SET_SKIP_STEPS(i)                \
  750     do {                            \
  751         while (head[i] != cur) {            \
  752             head[i]->skip[i] = cur;         \
  753             head[i] = TAILQ_NEXT(head[i], entry);   \
  754         }                       \
  755     } while (0)
  756 
  757 void
  758 rde_filter_calc_skip_steps(struct filter_head *rules)
  759 {
  760     struct filter_rule *cur, *prev, *head[RDE_FILTER_SKIP_COUNT];
  761     int i;
  762 
  763     if (rules == NULL)
  764         return;
  765 
  766     cur = TAILQ_FIRST(rules);
  767 
  768     prev = cur;
  769     for (i = 0; i < RDE_FILTER_SKIP_COUNT; ++i)
  770         head[i] = cur;
  771     while (cur != NULL) {
  772         if (cur->peer.groupid != prev->peer.groupid)
  773             RDE_FILTER_SET_SKIP_STEPS(RDE_FILTER_SKIP_GROUPID);
  774         if (cur->peer.remote_as != prev->peer.remote_as)
  775             RDE_FILTER_SET_SKIP_STEPS(RDE_FILTER_SKIP_REMOTE_AS);
  776         if (cur->peer.peerid != prev->peer.peerid)
  777             RDE_FILTER_SET_SKIP_STEPS(RDE_FILTER_SKIP_PEERID);
  778         prev = cur;
  779         cur = TAILQ_NEXT(cur, entry);
  780     }
  781     for (i = 0; i < RDE_FILTER_SKIP_COUNT; ++i)
  782         RDE_FILTER_SET_SKIP_STEPS(i);
  783 
  784 }
  785 
  786 #define RDE_FILTER_TEST_ATTRIB(t, a)                \
  787     do {                            \
  788         if (t) {                    \
  789             f = a;                  \
  790             goto nextrule;              \
  791         }                       \
  792     } while (0)
  793 
  794 enum filter_actions
  795 rde_filter(struct filter_head *rules, struct rde_peer *peer,
  796     struct prefix *p, struct filterstate *state)
  797 {
  798     struct filter_rule  *f;
  799     enum filter_actions  action = ACTION_DENY; /* default deny */
  800 
  801     if (state == NULL) /* withdraw should be accepted by default */
  802         action = ACTION_ALLOW;
  803 
  804     if (state && state->aspath.flags & F_ATTR_PARSE_ERR)
  805         /*
  806          * don't try to filter bad updates just deny them
  807          * so they act as implicit withdraws
  808          */
  809         return (ACTION_DENY);
  810 
  811     if (rules == NULL)
  812         return (action);
  813 
  814     f = TAILQ_FIRST(rules);
  815     while (f != NULL) {
  816         RDE_FILTER_TEST_ATTRIB(
  817             (f->peer.groupid &&
  818              f->peer.groupid != peer->conf.groupid),
  819              f->skip[RDE_FILTER_SKIP_GROUPID]);
  820         RDE_FILTER_TEST_ATTRIB(
  821             (f->peer.remote_as &&
  822              f->peer.remote_as != peer->conf.remote_as),
  823              f->skip[RDE_FILTER_SKIP_REMOTE_AS]);
  824         RDE_FILTER_TEST_ATTRIB(
  825             (f->peer.peerid &&
  826              f->peer.peerid != peer->conf.id),
  827              f->skip[RDE_FILTER_SKIP_PEERID]);
  828 
  829         if (rde_filter_match(f, peer, state, p)) {
  830             if (state != NULL) {
  831                 rde_apply_set(&f->set, state,
  832                     p->re->prefix->aid, prefix_peer(p), peer);
  833             }
  834             if (f->action != ACTION_NONE)
  835                 action = f->action;
  836             if (f->quick)
  837                 return (action);
  838         }
  839         f = TAILQ_NEXT(f, entry);
  840  nextrule: ;
  841     }
  842     return (action);
  843 }