diff options
Diffstat (limited to 'net/openbgpd/files/patch-bgpd_parse.y')
-rw-r--r-- | net/openbgpd/files/patch-bgpd_parse.y | 1626 |
1 files changed, 1626 insertions, 0 deletions
diff --git a/net/openbgpd/files/patch-bgpd_parse.y b/net/openbgpd/files/patch-bgpd_parse.y new file mode 100644 index 000000000000..0f9160187aac --- /dev/null +++ b/net/openbgpd/files/patch-bgpd_parse.y @@ -0,0 +1,1626 @@ +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 <henning@openbsd.org> +@@ -25,7 +25,10 @@ + #include <sys/stat.h> + #include <netinet/in.h> + #include <arpa/inet.h> +- ++#if !defined(__FreeBSD__) /* FreeBSD has no mpls support. */ ++#include <netmpls/mpls.h> ++#endif ++ + #include <ctype.h> + #include <err.h> + #include <unistd.h> +@@ -33,6 +36,9 @@ + #include <limits.h> + #include <stdarg.h> + #include <stdio.h> ++#if defined(__FreeBSD__) ++#include <stdlib.h> ++#endif + #include <string.h> + #include <syslog.h> + +@@ -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 <v.string> STRING + %token <v.number> NUMBER +-%type <v.number> asnumber as4number optnumber yesno inout +-%type <v.number> espah family restart ++%type <v.number> asnumber as4number optnumber ++%type <v.number> espah family restart origincode nettype ++%type <v.number> yesno inout restricted + %type <v.string> string filter_rib + %type <v.addr> address + %type <v.prefix> 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) + { |