mirror of
https://git.freebsd.org/ports.git
synced 2025-06-08 14:20:33 -04:00
- openbgpd version 6.5p0 was the "portable" version, which specifically does *not* support kernel routing updates. - Therefore this is only suitable for route servers/collectors, not for production use in routers. This significantly violates POLA :) PR: 213445 Submitted by: Oliver H <oliver@watershed.co.uk>
1626 lines
42 KiB
Text
1626 lines
42 KiB
Text
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)
|
|
{
|