diff options
Diffstat (limited to 'usr.sbin/bgpd/parse.y')
-rw-r--r-- | usr.sbin/bgpd/parse.y | 237 |
1 files changed, 150 insertions, 87 deletions
diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index b8f7f37ec03..1492a08cb58 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.267 2013/09/27 08:23:11 sthen Exp $ */ +/* $OpenBSD: parse.y,v 1.268 2013/10/19 15:04:25 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -99,6 +99,12 @@ struct filter_prefix_l { struct filter_prefix p; }; +struct filter_prefixlen { + enum comp_ops op; + int len_min; + int len_max; +}; + struct filter_as_l { struct filter_as_l *next; struct filter_as a; @@ -108,7 +114,6 @@ struct filter_match_l { struct filter_match m; struct filter_prefix_l *prefix_l; struct filter_as_l *as_l; - u_int8_t aid; } fmopts; struct peer *alloc_peer(void); @@ -119,6 +124,8 @@ int add_mrtconfig(enum mrt_type, char *, int, struct peer *, int add_rib(char *, u_int, u_int16_t); struct rde_rib *find_rib(char *); int get_id(struct peer *); +int merge_prefixspec(struct filter_prefix_l *, + struct filter_prefixlen *); int expand_rule(struct filter_rule *, struct filter_peers_l *, struct filter_match_l *, struct filter_set_head *); int str2key(char *, char *, size_t); @@ -145,13 +152,13 @@ typedef struct { struct filter_match_l filter_match; struct filter_prefix_l *filter_prefix; struct filter_as_l *filter_as; - struct filter_prefixlen prefixlen; struct filter_set *filter_set; struct filter_set_head *filter_set_head; struct { struct bgpd_addr prefix; u_int8_t len; } prefix; + struct filter_prefixlen prefixlen; struct { u_int8_t enc_alg; char enc_key[IPSEC_ENC_KEY_LEN]; @@ -185,7 +192,7 @@ typedef struct { %token IPSEC ESP AH SPI IKE %token IPV4 IPV6 %token QUALIFY VIA -%token NE LE GE XRANGE +%token NE LE GE XRANGE LONGER %token <v.string> STRING %token <v.number> NUMBER %type <v.number> asnumber as4number optnumber @@ -202,8 +209,7 @@ typedef struct { %type <v.prefixlen> prefixlenop %type <v.filter_set> filter_set_opt %type <v.filter_set_head> filter_set filter_set_l -%type <v.filter_prefix> filter_prefix filter_prefix_l -%type <v.filter_prefix> filter_prefix_h filter_prefix_m +%type <v.filter_prefix> filter_prefix filter_prefix_l filter_prefix_h %type <v.u8> unaryop binaryop filter_as_type %type <v.encspec> encspec %% @@ -1478,23 +1484,32 @@ filter_peer : ANY { } ; -filter_prefix_h : PREFIX filter_prefix { $$ = $2; } - | PREFIX '{' filter_prefix_m '}' { $$ = $3; } - ; - -filter_prefix_m : filter_prefix_l - | '{' filter_prefix_l '}' { $$ = $2; } - | '{' filter_prefix_l '}' filter_prefix_m - { - struct filter_prefix_l *p; - - /* merge, both can be lists */ - for (p = $2; p != NULL && p->next != NULL; p = p->next) - ; /* nothing */ - if (p != NULL) - p->next = $4; - $$ = $2; +filter_prefix_h : IPV4 prefixlenop { + if ($2.op == OP_NONE) + $2.op = OP_GE; + if (($$ = calloc(1, sizeof(struct filter_prefix_l))) == + NULL) + fatal(NULL); + $$->p.addr.aid = AID_INET; + if (merge_prefixspec($$, &$2) == -1) { + free($$); + YYERROR; + } + } + | IPV6 prefixlenop { + if ($2.op == OP_NONE) + $2.op = OP_GE; + if (($$ = calloc(1, sizeof(struct filter_prefix_l))) == + NULL) + fatal(NULL); + $$->p.addr.aid = AID_INET6; + if (merge_prefixspec($$, &$2) == -1) { + free($$); + YYERROR; + } } + | PREFIX filter_prefix { $$ = $2; } + | PREFIX '{' filter_prefix_l '}' { $$ = $3; } ; filter_prefix_l : filter_prefix { $$ = $1; } @@ -1504,20 +1519,18 @@ filter_prefix_l : filter_prefix { $$ = $1; } } ; -filter_prefix : prefix { - if (fmopts.aid && fmopts.aid != $1.prefix.aid) { - yyerror("rules with mixed address families " - "are not allowed"); - YYERROR; - } else - fmopts.aid = $1.prefix.aid; +filter_prefix : prefix prefixlenop { if (($$ = calloc(1, sizeof(struct filter_prefix_l))) == NULL) fatal(NULL); memcpy(&$$->p.addr, &$1.prefix, sizeof($$->p.addr)); $$->p.len = $1.len; - $$->next = NULL; + + if (merge_prefixspec($$, &$2) == -1) { + free($$); + YYERROR; + } } ; @@ -1611,20 +1624,6 @@ filter_elm : filter_prefix_h { } fmopts.prefix_l = $1; } - | PREFIXLEN prefixlenop { - if (fmopts.aid == 0) { - yyerror("address family needs to be specified " - "before \"prefixlen\""); - YYERROR; - } - if (fmopts.m.prefixlen.aid) { - yyerror("\"prefixlen\" already specified"); - YYERROR; - } - memcpy(&fmopts.m.prefixlen, &$2, - sizeof(fmopts.m.prefixlen)); - fmopts.m.prefixlen.aid = fmopts.aid; - } | filter_as_h { if (fmopts.as_l != NULL) { yyerror("AS filters already specified"); @@ -1686,30 +1685,11 @@ filter_elm : filter_prefix_h { free($2); free($3); } - | IPV4 { - if (fmopts.aid) { - yyerror("address family already specified"); - YYERROR; - } - fmopts.aid = AID_INET; - } - | IPV6 { - if (fmopts.aid) { - yyerror("address family already specified"); - YYERROR; - } - 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; } @@ -1722,28 +1702,38 @@ filter_elm : filter_prefix_h { } ; -prefixlenop : unaryop NUMBER { +prefixlenop : /* empty */ { bzero(&$$, sizeof($$)); } + | LONGER { bzero(&$$, sizeof($$)); - if ($2 < 0 || $2 > 128) { - yyerror("prefixlen must be < 128"); + $$.op = OP_GE; + $$.len_min = -1; + } + | PREFIXLEN unaryop NUMBER { + bzero(&$$, sizeof($$)); + if ($3 < 0 || $3 > 128) { + yyerror("prefixlen must be >= 0 and <= 128"); YYERROR; } - $$.op = $1; - $$.len_min = $2; + if ($2 == OP_GT && $3 == 0) { + yyerror("prefixlen must be > 0"); + YYERROR; + } + $$.op = $2; + $$.len_min = $3; } - | NUMBER binaryop NUMBER { + | PREFIXLEN NUMBER binaryop NUMBER { bzero(&$$, sizeof($$)); - if ($1 < 0 || $1 > 128 || $3 < 0 || $3 > 128) { + if ($2 < 0 || $2 > 128 || $4 < 0 || $4 > 128) { yyerror("prefixlen must be < 128"); YYERROR; } - if ($1 >= $3) { + if ($2 >= $4) { yyerror("start prefixlen is bigger than end"); YYERROR; } - $$.op = $2; - $$.len_min = $1; - $$.len_max = $3; + $$.op = $3; + $$.len_min = $2; + $$.len_max = $4; } ; @@ -2181,6 +2171,7 @@ lookup(char *s) { "nexthop", NEXTHOP}, { "no-modify", NOMODIFY}, { "on", ON}, + { "or-longer", LONGER}, { "origin", ORIGIN}, { "out", OUT}, { "passive", PASSIVE}, @@ -3229,6 +3220,78 @@ get_id(struct peer *newpeer) } int +merge_prefixspec(struct filter_prefix_l *p, struct filter_prefixlen *pl) +{ + u_int8_t max_len = 0; + + switch (p->p.addr.aid) { + case AID_INET: + case AID_VPN_IPv4: + max_len = 32; + break; + case AID_INET6: + max_len = 128; + break; + } + + switch (pl->op) { + case OP_NONE: + return (0); + case OP_RANGE: + case OP_XRANGE: + if (pl->len_min > max_len || pl->len_max > max_len) { + yyerror("prefixlen %d too big for AF, limit %d", + pl->len_min > max_len ? pl->len_min : pl->len_max, + max_len); + return (-1); + } + if (pl->len_min < p->p.len) { + yyerror("prefixlen %d smaller than prefix, limit %d", + pl->len_min, p->p.len); + return (-1); + } + p->p.len_max = pl->len_max; + break; + case OP_GE: + /* fix up the "or-longer" case */ + if (pl->len_min == -1) + pl->len_min = p->p.len; + /* FALLTHROUGH */ + case OP_EQ: + case OP_NE: + case OP_LE: + case OP_GT: + if (pl->len_min > max_len) { + yyerror("prefixlen %d to big for AF, limit %d", + pl->len_min, max_len); + return (-1); + } + if (pl->len_min < p->p.len) { + yyerror("prefixlen %d smaller than prefix, limit %d", + pl->len_min, p->p.len); + return (-1); + } + break; + case OP_LT: + if (pl->len_min > max_len - 1) { + yyerror("prefixlen %d to big for AF, limit %d", + pl->len_min, max_len - 1); + return (-1); + } + if (pl->len_min < p->p.len + 1) { + yyerror("prefixlen %d too small for prefix, limit %d", + pl->len_min, p->p.len + 1); + return (-1); + } + break; + } + + p->p.op = pl->op; + p->p.len_min = pl->len_min; + return (0); +} + +int expand_rule(struct filter_rule *rule, struct filter_peers_l *peer, struct filter_match_l *match, struct filter_set_head *set) { @@ -3240,9 +3303,9 @@ expand_rule(struct filter_rule *rule, struct filter_peers_l *peer, p = peer; do { - prefix = match->prefix_l; + a = match->as_l; do { - a = match->as_l; + prefix = match->prefix_l; do { if ((r = calloc(1, sizeof(struct filter_rule))) == NULL) { @@ -3270,13 +3333,13 @@ expand_rule(struct filter_rule *rule, struct filter_peers_l *peer, TAILQ_INSERT_TAIL(filter_l, r, entry); - if (a != NULL) - a = a->next; - } while (a != NULL); + if (prefix != NULL) + prefix = prefix->next; + } while (prefix != NULL); - if (prefix != NULL) - prefix = prefix->next; - } while (prefix != NULL); + if (a != NULL) + a = a->next; + } while (a != NULL); if (p != NULL) p = p->next; @@ -3287,16 +3350,16 @@ expand_rule(struct filter_rule *rule, struct filter_peers_l *peer, free(p); } - for (prefix = match->prefix_l; prefix != NULL; prefix = prefix_next) { - prefix_next = prefix->next; - free(prefix); - } - for (a = match->as_l; a != NULL; a = anext) { anext = a->next; free(a); } + for (prefix = match->prefix_l; prefix != NULL; prefix = prefix_next) { + prefix_next = prefix->next; + free(prefix); + } + if (set != NULL) { while ((s = TAILQ_FIRST(set)) != NULL) { TAILQ_REMOVE(set, s, entry); |