Index: bgpd/parse.y =================================================================== RCS file: /home/cvs/private/hrs/openbgpd/bgpd/parse.y,v retrieving revision 1.1.1.8 retrieving revision 1.12 diff -u -p -r1.1.1.8 -r1.12 --- bgpd/parse.y 14 Feb 2010 20:19:57 -0000 1.1.1.8 +++ bgpd/parse.y 8 Dec 2012 20:17:59 -0000 1.12 @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.231 2009/06/06 01:10:29 claudio Exp $ */ +/* $OpenBSD: parse.y,v 1.264 2012/09/23 09:39:17 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -25,7 +25,10 @@ #include #include #include - +#if !defined(__FreeBSD__) /* FreeBSD has no mpls support. */ +#include +#endif + #include #include #include @@ -33,6 +36,9 @@ #include #include #include +#if defined(__FreeBSD__) +#include +#endif #include #include @@ -74,10 +80,12 @@ char *symget(const char *); static struct bgpd_config *conf; static struct mrt_head *mrtconf; -static struct network_head *netconf; +static struct network_head *netconf, *gnetconf; static struct peer *peer_l, *peer_l_old; static struct peer *curpeer; static struct peer *curgroup; +static struct rdomain *currdom; +static struct rdomain_head *rdom_l; static struct filter_head *filter_l; static struct filter_head *peerfilter_l; static struct filter_head *groupfilter_l; @@ -105,7 +113,7 @@ struct filter_match_l { struct filter_match m; struct filter_prefix_l *prefix_l; struct filter_as_l *as_l; - sa_family_t af; + u_int8_t aid; } fmopts; struct peer *alloc_peer(void); @@ -113,8 +121,8 @@ struct peer *new_peer(void); struct peer *new_group(void); int add_mrtconfig(enum mrt_type, char *, time_t, struct peer *, char *); -int add_rib(char *, u_int16_t); -int find_rib(char *); +int add_rib(char *, u_int, u_int16_t); +struct rde_rib *find_rib(char *); int get_id(struct peer *); int expand_rule(struct filter_rule *, struct filter_peers_l *, struct filter_match_l *, struct filter_set_head *); @@ -123,12 +131,14 @@ int neighbor_consistent(struct peer *) int merge_filterset(struct filter_set_head *, struct filter_set *); void copy_filterset(struct filter_set_head *, struct filter_set_head *); -void move_filterset(struct filter_set_head *, - struct filter_set_head *); struct filter_rule *get_rule(enum action_types); int getcommunity(char *); -int parsecommunity(char *, int *, int *); +int parsecommunity(struct filter_community *, char *); +int parsesubtype(char *); +int parseextvalue(char *, u_int32_t *); +int parseextcommunity(struct filter_extcommunity *, char *, + char *); typedef struct { union { @@ -159,29 +169,33 @@ typedef struct { %} %token AS ROUTERID HOLDTIME YMIN LISTEN ON FIBUPDATE RTABLE +%token RDOMAIN RD EXPORTTRGT IMPORTTRGT %token RDE RIB EVALUATE IGNORE COMPARE %token GROUP NEIGHBOR NETWORK -%token REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART -%token ANNOUNCE DEMOTE CONNECTRETRY -%token ENFORCE NEIGHBORAS CAPABILITIES REFLECTOR DEPEND DOWN SOFTRECONFIG -%token DUMP IN OUT +%token REMOTEAS DESCR LLIFACE LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART +%token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY +%token DEMOTE ENFORCE NEIGHBORAS REFLECTOR DEPEND DOWN SOFTRECONFIG +%token DUMP IN OUT SOCKET RESTRICTED %token LOG ROUTECOLL TRANSPARENT %token TCP MD5SIG PASSWORD KEY TTLSECURITY %token ALLOW DENY MATCH %token QUICK %token FROM TO ANY %token CONNECTED STATIC -%token PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS COMMUNITY DELETE +%token COMMUNITY EXTCOMMUNITY +%token PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS DELETE MAXASLEN MAXASSEQ %token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF -%token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL +%token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN %token ERROR INCLUDE %token IPSEC ESP AH SPI IKE %token IPV4 IPV6 %token QUALIFY VIA +%token NE LE GE XRANGE %token STRING %token NUMBER -%type asnumber as4number optnumber yesno inout -%type espah family restart +%type asnumber as4number optnumber +%type espah family restart origincode nettype +%type yesno inout restricted %type string filter_rib %type address %type prefix addrspec @@ -204,6 +218,7 @@ grammar : /* empty */ | grammar include '\n' | grammar conf_main '\n' | grammar varset '\n' + | grammar rdomain '\n' | grammar neighbor '\n' | grammar group '\n' | grammar filterrule '\n' @@ -211,8 +226,12 @@ grammar : /* empty */ ; asnumber : NUMBER { - if ($1 < 0 || $1 >= ASNUM_MAX) { - yyerror("AS too big: max %u", ASNUM_MAX - 1); + /* + * Accroding to iana 65535 and 4294967295 are reserved + * but enforcing this is not duty of the parser. + */ + if ($1 < 0 || $1 > UINT_MAX) { + yyerror("AS too big: max %u", UINT_MAX); YYERROR; } } @@ -274,6 +293,8 @@ yesno : STRING { else if (!strcmp($1, "no")) $$ = 0; else { + yyerror("syntax error, " + "either yes or no expected"); free($1); YYERROR; } @@ -318,7 +339,7 @@ conf_main : AS as4number { conf->short_as = $3; } | ROUTERID address { - if ($2.af != AF_INET) { + if ($2.aid != AID_INET) { yyerror("router-id must be an IPv4 address"); YYERROR; } @@ -342,42 +363,25 @@ conf_main : AS as4number { } | LISTEN ON address { struct listen_addr *la; - struct sockaddr_in *in; - struct sockaddr_in6 *in6; if ((la = calloc(1, sizeof(struct listen_addr))) == NULL) fatal("parse conf_main listen on calloc"); la->fd = -1; - la->sa.ss_family = $3.af; - switch ($3.af) { - case AF_INET: - la->sa.ss_len = sizeof(struct sockaddr_in); - in = (struct sockaddr_in *)&la->sa; - in->sin_addr.s_addr = $3.v4.s_addr; - in->sin_port = htons(BGP_PORT); - break; - case AF_INET6: - la->sa.ss_len = sizeof(struct sockaddr_in6); - in6 = (struct sockaddr_in6 *)&la->sa; - memcpy(&in6->sin6_addr, &$3.v6, - sizeof(in6->sin6_addr)); - in6->sin6_port = htons(BGP_PORT); - break; - default: - yyerror("king bula does not like family %u", - $3.af); - YYERROR; - } - + memcpy(&la->sa, addr2sa(&$3, BGP_PORT), sizeof(la->sa)); TAILQ_INSERT_TAIL(listen_addrs, la, entry); } | FIBUPDATE yesno { + struct rde_rib *rr; + rr = find_rib("Loc-RIB"); + if (rr == NULL) + fatalx("RTABLE can not find the main RIB!"); + if ($2 == 0) - conf->flags |= BGPD_FLAG_NO_FIB_UPDATE; + rr->flags |= F_RIB_NOFIBSYNC; else - conf->flags &= ~BGPD_FLAG_NO_FIB_UPDATE; + rr->flags &= ~F_RIB_NOFIBSYNC; } | ROUTECOLL yesno { if ($2 == 1) @@ -386,7 +390,7 @@ conf_main : AS as4number { conf->flags &= ~BGPD_FLAG_NO_EVALUATE; } | RDE RIB STRING { - if (add_rib($3, F_RIB_NOFIB)) { + if (add_rib($3, 0, F_RIB_NOFIB)) { free($3); YYERROR; } @@ -395,9 +399,27 @@ conf_main : AS as4number { | RDE RIB STRING yesno EVALUATE { if ($4) { free($3); + yyerror("bad rde rib definition"); YYERROR; } - if (!add_rib($3, F_RIB_NOEVALUATE)) { + if (add_rib($3, 0, F_RIB_NOFIB | F_RIB_NOEVALUATE)) { + free($3); + YYERROR; + } + free($3); + } + | RDE RIB STRING RTABLE NUMBER { + if (add_rib($3, $5, 0)) { + free($3); + YYERROR; + } + free($3); + } + | RDE RIB STRING RTABLE NUMBER FIBUPDATE yesno { + int flags = 0; + if ($7 == 0) + flags = F_RIB_NOFIBSYNC; + if (add_rib($3, $5, flags)) { free($3); YYERROR; } @@ -418,59 +440,7 @@ conf_main : AS as4number { } free($2); } - | NETWORK prefix filter_set { - struct network *n; - - if ((n = calloc(1, sizeof(struct network))) == NULL) - fatal("new_network"); - memcpy(&n->net.prefix, &$2.prefix, - sizeof(n->net.prefix)); - n->net.prefixlen = $2.len; - move_filterset($3, &n->net.attrset); - free($3); - - TAILQ_INSERT_TAIL(netconf, n, entry); - } - | NETWORK family STATIC filter_set { - if ($2 == AFI_IPv4) { - conf->flags |= BGPD_FLAG_REDIST_STATIC; - move_filterset($4, &conf->staticset); - } else if ($2 == AFI_IPv6) { - conf->flags |= BGPD_FLAG_REDIST6_STATIC; - move_filterset($4, &conf->staticset6); - } else { - yyerror("unknown family"); - free($4); - YYERROR; - } - free($4); - } - | NETWORK family CONNECTED filter_set { - if ($2 == AFI_IPv4) { - conf->flags |= BGPD_FLAG_REDIST_CONNECTED; - move_filterset($4, &conf->connectset); - } else if ($2 == AFI_IPv6) { - conf->flags |= BGPD_FLAG_REDIST6_CONNECTED; - move_filterset($4, &conf->connectset6); - } else { - yyerror("unknown family"); - free($4); - YYERROR; - } - free($4); - } - | NETWORK STATIC filter_set { - /* keep for compatibility till after next release */ - conf->flags |= BGPD_FLAG_REDIST_STATIC; - move_filterset($3, &conf->staticset); - free($3); - } - | NETWORK CONNECTED filter_set { - /* keep for compatibility till after next release */ - conf->flags |= BGPD_FLAG_REDIST_CONNECTED; - move_filterset($3, &conf->connectset); - free($3); - } + | network | DUMP STRING STRING optnumber { int action; @@ -484,6 +454,8 @@ conf_main : AS as4number { action = MRT_TABLE_DUMP; else if (!strcmp($2, "table-mp")) action = MRT_TABLE_DUMP_MP; + else if (!strcmp($2, "table-v2")) + action = MRT_TABLE_DUMP_V2; else { yyerror("unknown mrt dump type"); free($2); @@ -511,6 +483,8 @@ conf_main : AS as4number { action = MRT_TABLE_DUMP; else if (!strcmp($4, "table-mp")) action = MRT_TABLE_DUMP_MP; + else if (!strcmp($4, "table-v2")) + action = MRT_TABLE_DUMP_V2; else { yyerror("unknown mrt dump type"); free($3); @@ -575,11 +549,20 @@ conf_main : AS as4number { free($4); } | RTABLE NUMBER { - if ($2 > RT_TABLEID_MAX || $2 < 0) { - yyerror("invalid rtable id"); +#if defined(__FreeBSD__) /* FreeBSD does not support RTABLE */ + yyerror("rtable id not supported in FreeBSD, yet"); + YYERROR; +#else + struct rde_rib *rr; + if (ktable_exists($2, NULL) != 1) { + yyerror("rtable id %lld does not exist", $2); YYERROR; } - conf->rtableid = $2; + rr = find_rib("Loc-RIB"); + if (rr == NULL) + fatalx("RTABLE can not find the main RIB!"); + rr->rtableid = $2; +#endif /* defined(__FreeBSD__) */ } | CONNECTRETRY NUMBER { if ($2 > USHRT_MAX || $2 < 1) { @@ -588,6 +571,15 @@ conf_main : AS as4number { } conf->connectretry = $2; } + | SOCKET STRING restricted { + if ($3) { + free(conf->rcsock); + conf->rcsock = $2; + } else { + free(conf->csock); + conf->csock = $2; + } + } ; mrtdump : DUMP STRING inout STRING optnumber { @@ -620,10 +612,47 @@ mrtdump : DUMP STRING inout STRING optn } ; +network : NETWORK prefix filter_set { + struct network *n; + + if ((n = calloc(1, sizeof(struct network))) == NULL) + fatal("new_network"); + memcpy(&n->net.prefix, &$2.prefix, + sizeof(n->net.prefix)); + n->net.prefixlen = $2.len; + filterset_move($3, &n->net.attrset); + free($3); + + TAILQ_INSERT_TAIL(netconf, n, entry); + } + | NETWORK family nettype filter_set { + struct network *n; + + if ((n = calloc(1, sizeof(struct network))) == NULL) + fatal("new_network"); + if (afi2aid($2, SAFI_UNICAST, &n->net.prefix.aid) == + -1) { + yyerror("unknown family"); + filterset_free($4); + free($4); + YYERROR; + } + n->net.type = $3 ? NETWORK_STATIC : NETWORK_CONNECTED; + filterset_move($4, &n->net.attrset); + free($4); + + TAILQ_INSERT_TAIL(netconf, n, entry); + } + ; + inout : IN { $$ = 1; } | OUT { $$ = 0; } ; +restricted : RESTRICTED { $$ = 1; } + | /* nothing */ { $$ = 0; } + ; + address : STRING { u_int8_t len; @@ -635,11 +664,11 @@ address : STRING { } free($1); - if (($$.af == AF_INET && len != 32) || - ($$.af == AF_INET6 && len != 128)) { + if (($$.aid == AID_INET && len != 32) || + ($$.aid == AID_INET6 && len != 128)) { /* unreachable */ yyerror("got prefixlen %u, expected %u", - len, $$.af == AF_INET ? 32 : 128); + len, $$.aid == AID_INET ? 32 : 128); YYERROR; } } @@ -653,7 +682,7 @@ prefix : STRING '/' NUMBER { free($1); YYERROR; } - if (asprintf(&s, "%s/%lld", $1, $3) == -1) + if (asprintf(&s, "%s/%lld", $1, (long long int)$3) == -1) fatal(NULL); free($1); @@ -672,7 +701,7 @@ prefix : STRING '/' NUMBER { yyerror("bad prefix %lld/%lld", $1, $3); YYERROR; } - if (asprintf(&s, "%lld/%lld", $1, $3) == -1) + if (asprintf(&s, "%lld/%lld", (long long int)$1, (long long int)$3) == -1) fatal(NULL); if (!host(s, &$$.prefix, &$$.len)) { @@ -686,7 +715,7 @@ prefix : STRING '/' NUMBER { addrspec : address { memcpy(&$$.prefix, &$1, sizeof(struct bgpd_addr)); - if ($$.prefix.af == AF_INET) + if ($$.prefix.aid == AID_INET) $$.len = 32; else $$.len = 128; @@ -705,14 +734,150 @@ optnumber : /* empty */ { $$ = 0; } | NUMBER ; +rdomain : RDOMAIN NUMBER optnl '{' optnl { + if (ktable_exists($2, NULL) != 1) { + yyerror("rdomain %lld does not exist", $2); + YYERROR; + } + if (!(currdom = calloc(1, sizeof(struct rdomain)))) + fatal(NULL); + currdom->rtableid = $2; + TAILQ_INIT(&currdom->import); + TAILQ_INIT(&currdom->export); + TAILQ_INIT(&currdom->net_l); + netconf = &currdom->net_l; + } + rdomainopts_l '}' { + /* insert into list */ + SIMPLEQ_INSERT_TAIL(rdom_l, currdom, entry); + currdom = NULL; + netconf = gnetconf; + } + +rdomainopts_l : rdomainopts_l rdomainoptsl + | rdomainoptsl + ; + +rdomainoptsl : rdomainopts nl + ; + +rdomainopts : RD STRING { + struct filter_extcommunity ext; + u_int64_t rd; + + if (parseextcommunity(&ext, "rt", $2) == -1) { + free($2); + YYERROR; + } + free($2); + /* + * RD is almost encode like an ext-community, + * but only almost so convert here. + */ + if (community_ext_conv(&ext, 0, &rd)) { + yyerror("bad encoding of rd"); + YYERROR; + } + rd = betoh64(rd) & 0xffffffffffffULL; + switch (ext.type) { + case EXT_COMMUNITY_TWO_AS: + rd |= (0ULL << 48); + break; + case EXT_COMMUNITY_IPV4: + rd |= (1ULL << 48); + break; + case EXT_COMMUNITY_FOUR_AS: + rd |= (2ULL << 48); + break; + default: + yyerror("bad encoding of rd"); + YYERROR; + } + currdom->rd = htobe64(rd); + } + | EXPORTTRGT STRING STRING { + struct filter_set *set; + + if ((set = calloc(1, sizeof(struct filter_set))) == + NULL) + fatal(NULL); + set->type = ACTION_SET_EXT_COMMUNITY; + if (parseextcommunity(&set->action.ext_community, + $2, $3) == -1) { + free($3); + free($2); + free(set); + YYERROR; + } + free($3); + free($2); + TAILQ_INSERT_TAIL(&currdom->export, set, entry); + } + | IMPORTTRGT STRING STRING { + struct filter_set *set; + + if ((set = calloc(1, sizeof(struct filter_set))) == + NULL) + fatal(NULL); + set->type = ACTION_SET_EXT_COMMUNITY; + if (parseextcommunity(&set->action.ext_community, + $2, $3) == -1) { + free($3); + free($2); + free(set); + YYERROR; + } + free($3); + free($2); + TAILQ_INSERT_TAIL(&currdom->import, set, entry); + } + | DESCR string { + if (strlcpy(currdom->descr, $2, + sizeof(currdom->descr)) >= + sizeof(currdom->descr)) { + yyerror("descr \"%s\" too long: max %u", + $2, sizeof(currdom->descr) - 1); + free($2); + YYERROR; + } + free($2); + } + | FIBUPDATE yesno { + if ($2 == 0) + currdom->flags |= F_RIB_NOFIBSYNC; + else + currdom->flags &= ~F_RIB_NOFIBSYNC; + } + | network + | DEPEND ON STRING { + /* XXX this is a hack */ + if (if_nametoindex($3) == 0) { + yyerror("interface %s does not exist", $3); + free($3); + YYERROR; + } + strlcpy(currdom->ifmpe, $3, IFNAMSIZ); + free($3); + if (get_mpe_label(currdom)) { + yyerror("failed to get mpls label from %s", + currdom->ifmpe); + YYERROR; + } + } + ; + neighbor : { curpeer = new_peer(); } NEIGHBOR addrspec { memcpy(&curpeer->conf.remote_addr, &$3.prefix, sizeof(curpeer->conf.remote_addr)); curpeer->conf.remote_masklen = $3.len; - if (($3.prefix.af == AF_INET && $3.len != 32) || - ($3.prefix.af == AF_INET6 && $3.len != 128)) + if (($3.prefix.aid == AID_INET && $3.len != 32) || + ($3.prefix.aid == AID_INET6 && $3.len != 128)) curpeer->conf.template = 1; + if (curpeer->conf.capabilities.mp[ + curpeer->conf.remote_addr.aid] == -1) + curpeer->conf.capabilities.mp[ + curpeer->conf.remote_addr.aid] = 1; if (get_id(curpeer)) { yyerror("get_id failed"); YYERROR; @@ -802,6 +967,17 @@ peeropts : REMOTEAS as4number { } free($2); } + | LLIFACE string { + if (strlcpy(curpeer->conf.lliface, $2, + sizeof(curpeer->conf.lliface)) >= + sizeof(curpeer->conf.lliface)) { + yyerror("lliface \"%s\" too long: max %u", + $2, sizeof(curpeer->conf.lliface) - 1); + free($2); + YYERROR; + } + free($2); + } | LOCALADDR address { memcpy(&curpeer->conf.local_addr, &$2, sizeof(curpeer->conf.local_addr)); @@ -852,13 +1028,17 @@ peeropts : REMOTEAS as4number { curpeer->conf.min_holdtime = $3; } | ANNOUNCE family STRING { - u_int8_t safi; + u_int8_t aid, safi; + int8_t val = 1; - if (!strcmp($3, "none")) - safi = SAFI_NONE; - else if (!strcmp($3, "unicast")) + if (!strcmp($3, "none")) { safi = SAFI_UNICAST; - else { + val = 0; + } else if (!strcmp($3, "unicast")) { + safi = SAFI_UNICAST; + } else if (!strcmp($3, "vpn")) { + safi = SAFI_MPLSVPN; + } else { yyerror("unknown/unsupported SAFI \"%s\"", $3); free($3); @@ -866,25 +1046,31 @@ peeropts : REMOTEAS as4number { } free($3); - switch ($2) { - case AFI_IPv4: - curpeer->conf.capabilities.mp_v4 = safi; - break; - case AFI_IPv6: - curpeer->conf.capabilities.mp_v6 = safi; - break; - default: - fatal("king bula sees borked AFI"); + if (afi2aid($2, safi, &aid) == -1) { + yyerror("unknown AFI/SAFI pair"); + YYERROR; } + curpeer->conf.capabilities.mp[aid] = val; } | ANNOUNCE CAPABILITIES yesno { curpeer->conf.announce_capa = $3; } + | ANNOUNCE REFRESH yesno { + curpeer->conf.capabilities.refresh = $3; + } + | ANNOUNCE RESTART yesno { + curpeer->conf.capabilities.grestart.restart = $3; + } + | ANNOUNCE AS4BYTE yesno { + curpeer->conf.capabilities.as4byte = $3; + } | ANNOUNCE SELF { curpeer->conf.announce_type = ANNOUNCE_SELF; } | ANNOUNCE STRING { - if (!strcmp($2, "none")) + if (!strcmp($2, "self")) + curpeer->conf.announce_type = ANNOUNCE_SELF; + else if (!strcmp($2, "none")) curpeer->conf.announce_type = ANNOUNCE_NONE; else if (!strcmp($2, "all")) curpeer->conf.announce_type = ANNOUNCE_ALL; @@ -1083,7 +1269,7 @@ peeropts : REMOTEAS as4number { curpeer->conf.reflector_client = 1; } | REFLECTOR address { - if ($2.af != AF_INET) { + if ($2.aid != AID_INET) { yyerror("route reflector cluster-id must be " "an IPv4 address"); YYERROR; @@ -1157,6 +1343,10 @@ family : IPV4 { $$ = AFI_IPv4; } | IPV6 { $$ = AFI_IPv6; } ; +nettype : STATIC { $$ = 1; }, + | CONNECTED { $$ = 0; } + ; + espah : ESP { $$ = 1; } | AH { $$ = 0; } ; @@ -1336,12 +1526,12 @@ filter_prefix_l : filter_prefix { $$ ; filter_prefix : prefix { - if (fmopts.af && fmopts.af != $1.prefix.af) { + if (fmopts.aid && fmopts.aid != $1.prefix.aid) { yyerror("rules with mixed address families " "are not allowed"); YYERROR; } else - fmopts.af = $1.prefix.af; + fmopts.aid = $1.prefix.aid; if (($$ = calloc(1, sizeof(struct filter_prefix_l))) == NULL) fatal(NULL); @@ -1410,6 +1600,12 @@ filter_as : as4number { fatal(NULL); $$->a.as = $1; } + | NEIGHBORAS { + if (($$ = calloc(1, sizeof(struct filter_as_l))) == + NULL) + fatal(NULL); + $$->a.flags = AS_FLAG_NEIGHBORAS; + } ; filter_match_h : /* empty */ { @@ -1437,18 +1633,18 @@ filter_elm : filter_prefix_h { fmopts.prefix_l = $1; } | PREFIXLEN prefixlenop { - if (fmopts.af == 0) { + if (fmopts.aid == 0) { yyerror("address family needs to be specified " "before \"prefixlen\""); YYERROR; } - if (fmopts.m.prefixlen.af) { + if (fmopts.m.prefixlen.aid) { yyerror("\"prefixlen\" already specified"); YYERROR; } memcpy(&fmopts.m.prefixlen, &$2, sizeof(fmopts.m.prefixlen)); - fmopts.m.prefixlen.af = fmopts.af; + fmopts.m.prefixlen.aid = fmopts.aid; } | filter_as_h { if (fmopts.as_l != NULL) { @@ -1457,32 +1653,93 @@ filter_elm : filter_prefix_h { } fmopts.as_l = $1; } + | MAXASLEN NUMBER { + if (fmopts.m.aslen.type != ASLEN_NONE) { + yyerror("AS length filters already specified"); + YYERROR; + } + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("bad max-as-len %lld", $2); + YYERROR; + } + fmopts.m.aslen.type = ASLEN_MAX; + fmopts.m.aslen.aslen = $2; + } + | MAXASSEQ NUMBER { + if (fmopts.m.aslen.type != ASLEN_NONE) { + yyerror("AS length filters already specified"); + YYERROR; + } + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("bad max-as-seq %lld", $2); + YYERROR; + } + fmopts.m.aslen.type = ASLEN_SEQ; + fmopts.m.aslen.aslen = $2; + } | COMMUNITY STRING { if (fmopts.m.community.as != COMMUNITY_UNSET) { yyerror("\"community\" already specified"); free($2); YYERROR; } - if (parsecommunity($2, &fmopts.m.community.as, - &fmopts.m.community.type) == -1) { + if (parsecommunity(&fmopts.m.community, $2) == -1) { free($2); YYERROR; } free($2); } + | EXTCOMMUNITY STRING STRING { + if (fmopts.m.ext_community.flags & + EXT_COMMUNITY_FLAG_VALID) { + yyerror("\"ext-community\" already specified"); + free($2); + free($3); + YYERROR; + } + + if (parseextcommunity(&fmopts.m.ext_community, + $2, $3) == -1) { + free($2); + free($3); + YYERROR; + } + free($2); + free($3); + } | IPV4 { - if (fmopts.af) { + if (fmopts.aid) { yyerror("address family already specified"); YYERROR; } - fmopts.af = AF_INET; + fmopts.aid = AID_INET; } | IPV6 { - if (fmopts.af) { + if (fmopts.aid) { yyerror("address family already specified"); YYERROR; } - fmopts.af = AF_INET6; + fmopts.aid = AID_INET6; + } + | NEXTHOP address { + if (fmopts.m.nexthop.flags) { + yyerror("nexthop already specified"); + YYERROR; + } + if (fmopts.aid && fmopts.aid != $2.aid) { + yyerror("nexthop address family doesn't match " + "rule address family"); + YYERROR; + } + fmopts.m.nexthop.addr = $2; + fmopts.m.nexthop.flags = FILTER_NEXTHOP_ADDR; + } + | NEXTHOP NEIGHBOR { + if (fmopts.m.nexthop.flags) { + yyerror("nexthop already specified"); + YYERROR; + } + fmopts.m.nexthop.flags = FILTER_NEXTHOP_NEIGHBOR; } ; @@ -1588,7 +1845,7 @@ filter_set_opt : LOCALPREF NUMBER { } if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) fatal(NULL); - if ($2 > 0) { + if ($2 >= 0) { $$->type = ACTION_SET_MED; $$->action.metric = $2; } else { @@ -1623,7 +1880,7 @@ filter_set_opt : LOCALPREF NUMBER { } if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) fatal(NULL); - if ($2 > 0) { + if ($2 >= 0) { $$->type = ACTION_SET_MED; $$->action.metric = $2; } else { @@ -1782,8 +2039,7 @@ filter_set_opt : LOCALPREF NUMBER { else $$->type = ACTION_SET_COMMUNITY; - if (parsecommunity($3, &$$->action.community.as, - &$$->action.community.type) == -1) { + if (parsecommunity(&$$->action.community, $3) == -1) { free($3); free($$); YYERROR; @@ -1796,40 +2052,62 @@ filter_set_opt : LOCALPREF NUMBER { free($$); YYERROR; } - /* Don't allow setting of unknown well-known types */ - if ($$->action.community.as == COMMUNITY_WELLKNOWN) { - switch ($$->action.community.type) { - case COMMUNITY_NO_EXPORT: - case COMMUNITY_NO_ADVERTISE: - case COMMUNITY_NO_EXPSUBCONFED: - case COMMUNITY_NO_PEER: - /* valid */ - break; - default: - /* unknown */ - yyerror("Invalid well-known community"); - free($$); - YYERROR; - break; - } + } + | EXTCOMMUNITY delete STRING STRING { + if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) + fatal(NULL); + if ($2) + $$->type = ACTION_DEL_EXT_COMMUNITY; + else + $$->type = ACTION_SET_EXT_COMMUNITY; + + if (parseextcommunity(&$$->action.ext_community, + $3, $4) == -1) { + free($3); + free($4); + free($$); + YYERROR; } + free($3); + free($4); + } + | ORIGIN origincode { + if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) + fatal(NULL); + $$->type = ACTION_SET_ORIGIN; + $$->action.origin = $2; } ; +origincode : string { + if (!strcmp($1, "egp")) + $$ = ORIGIN_EGP; + else if (!strcmp($1, "igp")) + $$ = ORIGIN_IGP; + else if (!strcmp($1, "incomplete")) + $$ = ORIGIN_INCOMPLETE; + else { + yyerror("unknown origin \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + }; + comma : "," | /* empty */ ; unaryop : '=' { $$ = OP_EQ; } - | '!' '=' { $$ = OP_NE; } - | '<' '=' { $$ = OP_LE; } + | NE { $$ = OP_NE; } + | LE { $$ = OP_LE; } | '<' { $$ = OP_LT; } - | '>' '=' { $$ = OP_GE; } + | GE { $$ = OP_GE; } | '>' { $$ = OP_GT; } ; binaryop : '-' { $$ = OP_RANGE; } - | '>' '<' { $$ = OP_XRANGE; } + | XRANGE { $$ = OP_XRANGE; } ; %% @@ -1873,6 +2151,7 @@ lookup(char *s) { "allow", ALLOW}, { "announce", ANNOUNCE}, { "any", ANY}, + { "as-4byte", AS4BYTE }, { "blackhole", BLACKHOLE}, { "capabilities", CAPABILITIES}, { "community", COMMUNITY}, @@ -1889,16 +2168,22 @@ lookup(char *s) { "enforce", ENFORCE}, { "esp", ESP}, { "evaluate", EVALUATE}, + { "export-target", EXPORTTRGT}, + { "ext-community", EXTCOMMUNITY}, { "fib-update", FIBUPDATE}, { "from", FROM}, { "group", GROUP}, { "holdtime", HOLDTIME}, { "ignore", IGNORE}, { "ike", IKE}, + { "import-target", IMPORTTRGT}, { "in", IN}, { "include", INCLUDE}, { "inet", IPV4}, { "inet6", IPV6}, +#if defined(IPV6_LINKLOCAL_PEER) + { "interface", LLIFACE}, +#endif { "ipsec", IPSEC}, { "key", KEY}, { "listen", LISTEN}, @@ -1906,6 +2191,8 @@ lookup(char *s) { "localpref", LOCALPREF}, { "log", LOG}, { "match", MATCH}, + { "max-as-len", MAXASLEN}, + { "max-as-seq", MAXASSEQ}, { "max-prefix", MAXPREFIX}, { "md5sig", MD5SIG}, { "med", MED}, @@ -1918,6 +2205,7 @@ lookup(char *s) { "nexthop", NEXTHOP}, { "no-modify", NOMODIFY}, { "on", ON}, + { "origin", ORIGIN}, { "out", OUT}, { "passive", PASSIVE}, { "password", PASSWORD}, @@ -1929,10 +2217,14 @@ lookup(char *s) { "prepend-self", PREPEND_SELF}, { "qualify", QUALIFY}, { "quick", QUICK}, + { "rd", RD}, { "rde", RDE}, + { "rdomain", RDOMAIN}, + { "refresh", REFRESH }, { "reject", REJECT}, { "remote-as", REMOTEAS}, { "restart", RESTART}, + { "restricted", RESTRICTED}, { "rib", RIB}, { "route-collector", ROUTECOLL}, { "route-reflector", REFLECTOR}, @@ -1941,6 +2233,7 @@ lookup(char *s) { "rtlabel", RTLABEL}, { "self", SELF}, { "set", SET}, + { "socket", SOCKET }, { "softreconfig", SOFTRECONFIG}, { "source-as", SOURCEAS}, { "spi", SPI}, @@ -2117,9 +2410,10 @@ top: return (0); if (next == quotec || c == ' ' || c == '\t') c = next; - else if (next == '\n') + else if (next == '\n') { + file->lineno++; continue; - else + } else lungetc(next); } else if (c == quotec) { *p = '\0'; @@ -2135,6 +2429,26 @@ top: if (yylval.v.string == NULL) fatal("yylex: strdup"); return (STRING); + case '!': + next = lgetc(0); + if (next == '=') + return (NE); + lungetc(next); + break; + case '<': + next = lgetc(0); + if (next == '=') + return (LE); + lungetc(next); + break; + case '>': + next = lgetc(0); + if (next == '<') + return (XRANGE); + else if (next == '=') + return (GE); + lungetc(next); + break; } #define allowed_to_end_number(x) \ @@ -2274,18 +2588,21 @@ popfile(void) int parse_config(char *filename, struct bgpd_config *xconf, struct mrt_head *xmconf, struct peer **xpeers, struct network_head *nc, - struct filter_head *xfilter_l) + struct filter_head *xfilter_l, struct rdomain_head *xrdom_l) { struct sym *sym, *next; struct peer *p, *pnext; struct listen_addr *la; struct network *n; struct filter_rule *r; + struct rde_rib *rr; + struct rdomain *rd; int errors = 0; if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL) fatal(NULL); conf->opts = xconf->opts; + conf->csock = strdup(SOCKET_NAME); if ((file = pushfile(filename, 1)) == NULL) { free(conf); @@ -2316,13 +2633,15 @@ parse_config(char *filename, struct bgpd id = 1; /* network list is always empty in the parent */ - netconf = nc; + gnetconf = netconf = nc; TAILQ_INIT(netconf); /* init the empty filter list for later */ TAILQ_INIT(xfilter_l); + SIMPLEQ_INIT(xrdom_l); + rdom_l = xrdom_l; - add_rib("Adj-RIB-In", F_RIB_NOEVALUATE); - add_rib("Loc-RIB", 0); + add_rib("Adj-RIB-In", 0, F_RIB_NOFIB | F_RIB_NOEVALUATE); + add_rib("Loc-RIB", 0, 0); yyparse(); errors = file->errors; @@ -2344,6 +2663,9 @@ parse_config(char *filename, struct bgpd if (errors) { /* XXX more leaks in this case */ + free(conf->csock); + free(conf->rcsock); + while ((la = TAILQ_FIRST(listen_addrs)) != NULL) { TAILQ_REMOVE(listen_addrs, la, entry); free(la); @@ -2357,23 +2679,44 @@ parse_config(char *filename, struct bgpd while ((n = TAILQ_FIRST(netconf)) != NULL) { TAILQ_REMOVE(netconf, n, entry); + filterset_free(&n->net.attrset); free(n); } while ((r = TAILQ_FIRST(filter_l)) != NULL) { TAILQ_REMOVE(filter_l, r, entry); + filterset_free(&r->set); free(r); } while ((r = TAILQ_FIRST(peerfilter_l)) != NULL) { TAILQ_REMOVE(peerfilter_l, r, entry); + filterset_free(&r->set); free(r); } while ((r = TAILQ_FIRST(groupfilter_l)) != NULL) { TAILQ_REMOVE(groupfilter_l, r, entry); + filterset_free(&r->set); free(r); } + while ((rr = SIMPLEQ_FIRST(&ribnames)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&ribnames, entry); + free(rr); + } + while ((rd = SIMPLEQ_FIRST(rdom_l)) != NULL) { + SIMPLEQ_REMOVE_HEAD(rdom_l, entry); + filterset_free(&rd->export); + filterset_free(&rd->import); + + while ((n = TAILQ_FIRST(&rd->net_l)) != NULL) { + TAILQ_REMOVE(&rd->net_l, n, entry); + filterset_free(&n->net.attrset); + free(n); + } + + free(rd); + } } else { errors += merge_config(xconf, conf, peer_l, listen_addrs); errors += mrt_mergeconfig(xmconf, mrtconf); @@ -2505,27 +2848,27 @@ getcommunity(char *s) } int -parsecommunity(char *s, int *as, int *type) +parsecommunity(struct filter_community *c, char *s) { char *p; - int i; + int i, as; /* Well-known communities */ if (strcasecmp(s, "NO_EXPORT") == 0) { - *as = COMMUNITY_WELLKNOWN; - *type = COMMUNITY_NO_EXPORT; + c->as = COMMUNITY_WELLKNOWN; + c->type = COMMUNITY_NO_EXPORT; return (0); } else if (strcasecmp(s, "NO_ADVERTISE") == 0) { - *as = COMMUNITY_WELLKNOWN; - *type = COMMUNITY_NO_ADVERTISE; + c->as = COMMUNITY_WELLKNOWN; + c->type = COMMUNITY_NO_ADVERTISE; return (0); } else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) { - *as = COMMUNITY_WELLKNOWN; - *type = COMMUNITY_NO_EXPSUBCONFED; + c->as = COMMUNITY_WELLKNOWN; + c->type = COMMUNITY_NO_EXPSUBCONFED; return (0); } else if (strcasecmp(s, "NO_PEER") == 0) { - *as = COMMUNITY_WELLKNOWN; - *type = COMMUNITY_NO_PEER; + c->as = COMMUNITY_WELLKNOWN; + c->type = COMMUNITY_NO_PEER; return (0); } @@ -2537,23 +2880,176 @@ parsecommunity(char *s, int *as, int *ty if ((i = getcommunity(s)) == COMMUNITY_ERROR) return (-1); - if (i == USHRT_MAX) { + if (i == COMMUNITY_WELLKNOWN) { yyerror("Bad community AS number"); return (-1); } - *as = i; + as = i; if ((i = getcommunity(p)) == COMMUNITY_ERROR) return (-1); - *type = i; + c->as = as; + c->type = i; return (0); } +int +parsesubtype(char *type) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + { "bdc", EXT_COMMUNITY_BGP_COLLECT }, + { "odi", EXT_COMMUNITY_OSPF_DOM_ID }, + { "ori", EXT_COMMUNITY_OSPF_RTR_ID }, + { "ort", EXT_COMMUNITY_OSPF_RTR_TYPE }, + { "rt", EXT_COMMUNITY_ROUTE_TGT }, + { "soo", EXT_CUMMUNITY_ROUTE_ORIG } + }; + const struct keywords *p; + + p = bsearch(type, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + return (-1); +} + +int +parseextvalue(char *s, u_int32_t *v) +{ + const char *errstr; + char *p; + struct in_addr ip; + u_int32_t uvalh = 0, uval; + + if ((p = strchr(s, '.')) == NULL) { + /* AS_PLAIN number (4 or 2 byte) */ + uval = strtonum(s, 0, UINT_MAX, &errstr); + if (errstr) { + yyerror("Bad ext-community %s is %s", s, errstr); + return (-1); + } + *v = uval; + if (uval > USHRT_MAX) + return (EXT_COMMUNITY_FOUR_AS); + else + return (EXT_COMMUNITY_TWO_AS); + } else if (strchr(p + 1, '.') == NULL) { + /* AS_DOT number (4-byte) */ + *p++ = '\0'; + uvalh = strtonum(s, 0, USHRT_MAX, &errstr); + if (errstr) { + yyerror("Bad ext-community %s is %s", s, errstr); + return (-1); + } + uval = strtonum(p, 0, USHRT_MAX, &errstr); + if (errstr) { + yyerror("Bad ext-community %s is %s", p, errstr); + return (-1); + } + *v = uval | (uvalh << 16); + return (EXT_COMMUNITY_FOUR_AS); + } else { + /* more then one dot -> IP address */ + if (inet_aton(s, &ip) == 0) { + yyerror("Bad ext-community %s not parseable", s); + return (-1); + } + *v = ip.s_addr; + return (EXT_COMMUNITY_IPV4); + } + return (-1); +} + +int +parseextcommunity(struct filter_extcommunity *c, char *t, char *s) +{ + const struct ext_comm_pairs iana[] = IANA_EXT_COMMUNITIES; + const char *errstr; + u_int64_t ullval = 0; + u_int32_t uval; + char *p, *ep; + unsigned int i; + int type, subtype; + + if ((subtype = parsesubtype(t)) == -1) { + yyerror("Bad ext-community unknown type"); + return (-1); + } + + if ((p = strchr(s, ':')) == NULL) { + type = EXT_COMMUNITY_OPAQUE, + errno = 0; + ullval = strtoull(s, &ep, 0); + if (s[0] == '\0' || *ep != '\0') { + yyerror("Bad ext-community bad value"); + return (-1); + } + if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) { + yyerror("Bad ext-community value to big"); + return (-1); + } + c->data.ext_opaq = ullval; + } else { + *p++ = '\0'; + if ((type = parseextvalue(s, &uval)) == -1) + return (-1); + switch (type) { + case EXT_COMMUNITY_TWO_AS: + ullval = strtonum(p, 0, UINT_MAX, &errstr); + break; + case EXT_COMMUNITY_IPV4: + case EXT_COMMUNITY_FOUR_AS: + ullval = strtonum(p, 0, USHRT_MAX, &errstr); + break; + default: + fatalx("parseextcommunity: unexpected result"); + } + if (errstr) { + yyerror("Bad ext-community %s is %s", p, + errstr); + return (-1); + } + switch (type) { + case EXT_COMMUNITY_TWO_AS: + c->data.ext_as.as = uval; + c->data.ext_as.val = ullval; + break; + case EXT_COMMUNITY_IPV4: + c->data.ext_ip.addr.s_addr = uval; + c->data.ext_ip.val = ullval; + break; + case EXT_COMMUNITY_FOUR_AS: + c->data.ext_as4.as4 = uval; + c->data.ext_as4.val = ullval; + break; + } + } + c->type = type; + c->subtype = subtype; + + /* verify type/subtype combo */ + for (i = 0; i < sizeof(iana)/sizeof(iana[0]); i++) { + if (iana[i].type == type && iana[i].subtype == subtype) { + if (iana[i].transitive) + c->type |= EXT_COMMUNITY_TRANSITIVE; + c->flags |= EXT_COMMUNITY_FLAG_VALID; + return (0); + } + } + + yyerror("Bad ext-community bad format for type"); + return (-1); +} + struct peer * alloc_peer(void) { struct peer *p; + u_int8_t i; if ((p = calloc(1, sizeof(struct peer))) == NULL) fatal("new_peer"); @@ -2564,11 +3060,11 @@ alloc_peer(void) p->conf.distance = 1; p->conf.announce_type = ANNOUNCE_UNDEF; p->conf.announce_capa = 1; - p->conf.capabilities.mp_v4 = SAFI_UNICAST; - p->conf.capabilities.mp_v6 = SAFI_NONE; + for (i = 0; i < AID_MAX; i++) + p->conf.capabilities.mp[i] = -1; p->conf.capabilities.refresh = 1; - p->conf.capabilities.restart = 0; - p->conf.capabilities.as4byte = 0; + p->conf.capabilities.grestart.restart = 1; + p->conf.capabilities.as4byte = 1; p->conf.local_as = conf->as; p->conf.local_short_as = conf->short_as; p->conf.softreconfig_in = 1; @@ -2592,6 +3088,9 @@ new_peer(void) if (strlcpy(p->conf.descr, curgroup->conf.descr, sizeof(p->conf.descr)) >= sizeof(p->conf.descr)) fatalx("new_peer descr strlcpy"); + if (strlcpy(p->conf.lliface, curgroup->conf.lliface, + sizeof(p->conf.lliface)) >= sizeof(p->conf.lliface)) + fatalx("new_peer lliface strlcpy"); p->conf.groupid = curgroup->conf.id; p->conf.local_as = curgroup->conf.local_as; p->conf.local_short_as = curgroup->conf.local_short_as; @@ -2674,39 +3173,52 @@ add_mrtconfig(enum mrt_type type, char * } int -add_rib(char *name, u_int16_t flags) +add_rib(char *name, u_int rtableid, u_int16_t flags) { struct rde_rib *rr; + u_int rdom; - if (find_rib(name)) { - yyerror("rib \"%s\" allready exists.", name); - return (-1); - } - - if ((rr = calloc(1, sizeof(*rr))) == NULL) { - log_warn("add_rib"); - return (-1); + if ((rr = find_rib(name)) == NULL) { + if ((rr = calloc(1, sizeof(*rr))) == NULL) { + log_warn("add_rib"); + return (-1); + } } if (strlcpy(rr->name, name, sizeof(rr->name)) >= sizeof(rr->name)) { yyerror("rib name \"%s\" too long: max %u", name, sizeof(rr->name) - 1); + free(rr); return (-1); } rr->flags |= flags; + if ((rr->flags & F_RIB_HASNOFIB) == 0) { + if (ktable_exists(rtableid, &rdom) != 1) { + yyerror("rtable id %lld does not exist", rtableid); + free(rr); + return (-1); + } + if (rdom != 0) { + yyerror("rtable %lld does not belong to rdomain 0", + rtableid); + free(rr); + return (-1); + } + rr->rtableid = rtableid; + } SIMPLEQ_INSERT_TAIL(&ribnames, rr, entry); return (0); } -int +struct rde_rib * find_rib(char *name) { struct rde_rib *rr; SIMPLEQ_FOREACH(rr, &ribnames, entry) { if (!strcmp(rr->name, name)) - return (1); + return (rr); } - return (0); + return (NULL); } int @@ -2715,7 +3227,7 @@ get_id(struct peer *newpeer) struct peer *p; for (p = peer_l_old; p != NULL; p = p->next) - if (newpeer->conf.remote_addr.af) { + if (newpeer->conf.remote_addr.aid) { if (!memcmp(&p->conf.remote_addr, &newpeer->conf.remote_addr, sizeof(p->conf.remote_addr))) { @@ -2856,9 +3368,11 @@ str2key(char *s, char *dest, size_t max_ int neighbor_consistent(struct peer *p) { + u_int8_t i; + /* local-address and peer's address: same address family */ - if (p->conf.local_addr.af && - p->conf.local_addr.af != p->conf.remote_addr.af) { + if (p->conf.local_addr.aid && + p->conf.local_addr.aid != p->conf.remote_addr.aid) { yyerror("local-address and neighbor address " "must be of the same address family"); return (-1); @@ -2869,7 +3383,7 @@ neighbor_consistent(struct peer *p) p->conf.auth.method == AUTH_IPSEC_IKE_AH || p->conf.auth.method == AUTH_IPSEC_MANUAL_ESP || p->conf.auth.method == AUTH_IPSEC_MANUAL_AH) && - !p->conf.local_addr.af) { + !p->conf.local_addr.aid) { yyerror("neighbors with any form of IPsec configured " "need local-address to be specified"); return (-1); @@ -2889,18 +3403,14 @@ neighbor_consistent(struct peer *p) return (-1); } - /* for testing: enable 4-byte AS number capability if necessary */ - if (conf->as > USHRT_MAX || p->conf.remote_as > USHRT_MAX) - p->conf.capabilities.as4byte = 1; - /* set default values if they where undefined */ p->conf.ebgp = (p->conf.remote_as != conf->as); if (p->conf.announce_type == ANNOUNCE_UNDEF) - p->conf.announce_type = p->conf.ebgp == 0 ? - ANNOUNCE_ALL : ANNOUNCE_SELF; + p->conf.announce_type = p->conf.ebgp ? + ANNOUNCE_SELF : ANNOUNCE_ALL; if (p->conf.enforce_as == ENFORCE_AS_UNDEF) - p->conf.enforce_as = p->conf.ebgp == 0 ? - ENFORCE_AS_OFF : ENFORCE_AS_ON; + p->conf.enforce_as = p->conf.ebgp ? + ENFORCE_AS_ON : ENFORCE_AS_OFF; /* EBGP neighbors are not allowed in route reflector clusters */ if (p->conf.reflector_client && p->conf.ebgp) { @@ -2909,6 +3419,11 @@ neighbor_consistent(struct peer *p) return (-1); } + /* the default MP capability is NONE */ + for (i = 0; i < AID_MAX; i++) + if (p->conf.capabilities.mp[i] == -1) + p->conf.capabilities.mp[i] = 0; + return (0); } @@ -2927,6 +3442,11 @@ merge_filterset(struct filter_set_head * yyerror("community is already set"); else if (s->type == ACTION_DEL_COMMUNITY) yyerror("community will already be deleted"); + else if (s->type == ACTION_SET_EXT_COMMUNITY) + yyerror("ext-community is already set"); + else if (s->type == ACTION_DEL_EXT_COMMUNITY) + yyerror( + "ext-community will already be deleted"); else yyerror("redefining set parameter %s", filterset_name(s->type)); @@ -2953,9 +3473,18 @@ merge_filterset(struct filter_set_head * return (0); } break; + case ACTION_SET_EXT_COMMUNITY: + case ACTION_DEL_EXT_COMMUNITY: + if (memcmp(&s->action.ext_community, + &t->action.ext_community, + sizeof(s->action.ext_community)) < 0) { + TAILQ_INSERT_BEFORE(t, s, entry); + return (0); + } + break; case ACTION_SET_NEXTHOP: - if (s->action.nexthop.af < - t->action.nexthop.af) { + if (s->action.nexthop.aid < + t->action.nexthop.aid) { TAILQ_INSERT_BEFORE(t, s, entry); return (0); } @@ -2985,22 +3514,6 @@ copy_filterset(struct filter_set_head *s } } -void -move_filterset(struct filter_set_head *source, struct filter_set_head *dest) -{ - struct filter_set *s; - - TAILQ_INIT(dest); - - if (source == NULL) - return; - - while ((s = TAILQ_FIRST(source)) != NULL) { - TAILQ_REMOVE(source, s, entry); - TAILQ_INSERT_TAIL(dest, s, entry); - } -} - struct filter_rule * get_rule(enum action_types type) {