diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2009-06-01 19:32:41 +0200 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2009-06-01 19:32:41 +0200 |
commit | 92a72a4cbdd010f69e8d054019770e55a47637e0 (patch) | |
tree | 75c45f29473f4ad20a3ff7031ad7720273c1e149 | |
parent | f429d4348275030a9f488046c4021aa377ad1a79 (diff) |
Adds support for dynamic pair and bgp mask expressions.
-rw-r--r-- | doc/bird.sgml | 5 | ||||
-rw-r--r-- | filter/config.Y | 112 | ||||
-rw-r--r-- | filter/filter.c | 48 | ||||
-rw-r--r-- | filter/test.conf | 18 | ||||
-rw-r--r-- | nest/a-path.c | 11 | ||||
-rw-r--r-- | nest/attrs.h | 3 |
6 files changed, 149 insertions, 48 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml index 8d8ec35e..5027c3e4 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -572,7 +572,8 @@ incompatible with each other (that is to prevent you from shooting in the foot). to +2000000000. Overflows are not checked. You can use <cf/0x1234/ syntax to write hexadecimal values. <tag/pair/ This is a pair of two short integers. Each component can have values from 0 to - 65535. Literals of this type is written as <cf/(1234,5678)/. + 65535. Literals of this type are written as <cf/(1234,5678)/. The same syntax can also be + used to construct a pair from two arbitrary integer expressions (for example <cf/(1+2,a)/). <tag/string/ This is a string of characters. There are no ways to modify strings in filters. You can pass them between functions, assign them to variables of type <cf/string/, print @@ -640,6 +641,8 @@ incompatible with each other (that is to prevent you from shooting in the foot). For example, if <cf>bgp_path</cf> is 4 3 2 1, then: <tt>bgp_path ˜ [= * 4 3 * =]</tt> is true, but <tt>bgp_path ˜ [= * 4 5 * =]</tt> is false. + BGP mask expressions can also contain integer expressions enclosed in parenthesis + and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>. There is also old syntax that uses / .. / instead of [= .. =] and ? instead of *. <tag/clist/ Community list is similar to set of pairs, diff --git a/filter/config.Y b/filter/config.Y index fc255519..5cff47eb 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -14,6 +14,17 @@ CF_DEFINES #define P(a,b) ((a<<8) | b) +static int make_pair(int i1, int i2) +{ + unsigned u1 = i1; + unsigned u2 = i2; + + if ((u1 > 0xFFFF) || (u2 > 0xFFFF)) + cf_error( "Can't operate with value out of bounds in pair constructor"); + + return (u1 << 16) | u2; +} + CF_DECLS CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, @@ -32,9 +43,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %nonassoc THEN %nonassoc ELSE -%type <x> term block cmds cmd function_body constant print_one print_list var_list var_listn dynamic_attr static_attr function_call +%type <x> term block cmds cmd function_body constant print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol dpair bgp_path_expr %type <f> filter filter_body where_filter -%type <i> type break_command pair +%type <i> type break_command cpair %type <e> set_item set_items switch_body %type <trie> fprefix_set %type <v> set_atom fprefix fprefix_s fipa @@ -203,8 +214,8 @@ block: /* * Simple types, their bison value is int */ -pair: - '(' NUM ',' NUM ')' { $$ = $2 << 16 | $4; } +cpair: + '(' NUM ',' NUM ')' { $$ = make_pair($2, $4); } ; /* @@ -215,10 +226,10 @@ fipa: ; set_atom: - NUM { $$.type = T_INT; $$.val.i = $1; } - | pair { $$.type = T_PAIR; $$.val.i = $1; } - | fipa { $$ = $1; } - | ENUM { $$.type = $1 >> 16; $$.val.i = $1 & 0xffff; } + NUM { $$.type = T_INT; $$.val.i = $1; } + | cpair { $$.type = T_PAIR; $$.val.i = $1; } + | fipa { $$ = $1; } + | ENUM { $$.type = $1 >> 16; $$.val.i = $1 & 0xffff; } ; set_item: @@ -277,6 +288,11 @@ switch_body: /* EMPTY */ { $$ = NULL; } /* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */ +bgp_path_expr: + symbol { $$ = $1; } + | '(' term ')' { $$ = $2; } + ; + bgp_path: PO bgp_path_tail1 PC { $$ = $2; } | '/' bgp_path_tail2 '/' { $$ = $2; } @@ -286,6 +302,7 @@ bgp_path_tail1: NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } | '*' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } | '?' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; $$->val = 0; } + | bgp_path_expr bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; } | { $$ = NULL; } ; @@ -295,12 +312,24 @@ bgp_path_tail2: | { $$ = NULL; } ; +dpair: + '(' term ',' term ')' { + if (($2->code == 'c') && ($4->code == 'c')) + { + if (($2->aux != T_INT) || ($4->aux != T_INT)) + cf_error( "Can't operate with value of non-integer type in pair constructor" ); + $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PAIR; $$->a2.i = make_pair($2->a2.i, $4->a2.i); + } + else + { $$ = f_new_inst(); $$->code = P('m', 'p'); $$->a1.p = $2; $$->a2.p = $4; } + } + ; + constant: NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; } | TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; } | FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; } | TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; } - | pair { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PAIR; $$->a2.i = $1; } | fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; } | fprefix_s {NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; } | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); } @@ -309,6 +338,7 @@ constant: | bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; } ; + /* * Maybe there are no dynamic attributes defined by protocols. * For such cases, we force the dynamic_attr list to contain @@ -342,6 +372,38 @@ function_call: } ; +symbol: + SYM { + $$ = f_new_inst(); + switch ($1->class) { + case SYM_NUMBER: + $$ = f_new_inst(); + $$->code = 'c'; + $$->aux = T_INT; + $$->a2.i = $1->aux; + break; + case SYM_IPA: + { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; val->type = T_IP; val->val.px.ip = * (ip_addr *) ($1->def); } + break; + case SYM_VARIABLE | T_BOOL: + case SYM_VARIABLE | T_INT: + case SYM_VARIABLE | T_PAIR: + case SYM_VARIABLE | T_STRING: + case SYM_VARIABLE | T_IP: + case SYM_VARIABLE | T_PREFIX: + case SYM_VARIABLE | T_PREFIX_SET: + case SYM_VARIABLE | T_SET: + case SYM_VARIABLE | T_PATH: + case SYM_VARIABLE | T_PATH_MASK: + case SYM_VARIABLE | T_CLIST: + $$->code = 'C'; + $$->a1.p = $1->def; + break; + default: + cf_error("%s: variable expected.", $1->name ); + } + } + static_attr: FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = OFFSETOF(struct rta, from); $$->a1.i = 1; } @@ -372,37 +434,9 @@ term: | '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; } | DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; } + | symbol { $$ = $1; } | constant { $$ = $1; } - | SYM { - $$ = f_new_inst(); - switch ($1->class) { - case SYM_NUMBER: - $$ = f_new_inst(); - $$->code = 'c'; - $$->aux = T_INT; - $$->a2.i = $1->aux; - break; - case SYM_IPA: - { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; val->type = T_IP; val->val.px.ip = * (ip_addr *) ($1->def); } - break; - case SYM_VARIABLE | T_BOOL: - case SYM_VARIABLE | T_INT: - case SYM_VARIABLE | T_PAIR: - case SYM_VARIABLE | T_STRING: - case SYM_VARIABLE | T_IP: - case SYM_VARIABLE | T_PREFIX: - case SYM_VARIABLE | T_PREFIX_SET: - case SYM_VARIABLE | T_SET: - case SYM_VARIABLE | T_PATH: - case SYM_VARIABLE | T_PATH_MASK: - case SYM_VARIABLE | T_CLIST: - $$->code = 'C'; - $$->a1.p = $1->def; - break; - default: - cf_error("%s: variable expected.", $1->name ); - } - } + | dpair { $$ = $1; } | PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; } diff --git a/filter/filter.c b/filter/filter.c index 7499843a..9c1dfa34 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -69,6 +69,8 @@ pm_path_compare(struct f_path_mask *m1, struct f_path_mask *m2) } } +u32 f_eval_asn(struct f_inst *expr); + static void pm_format(struct f_path_mask *p, byte *buf, unsigned int size) { @@ -82,10 +84,24 @@ pm_format(struct f_path_mask *p, byte *buf, unsigned int size) return; } - if (p->kind == PM_ASN) - buf += bsprintf(buf, " %u", p->val); - else - buf += bsprintf(buf, (p->kind == PM_ASTERISK) ? " *" : " ?"); + switch(p->kind) + { + case PM_ASN: + buf += bsprintf(buf, " %u", p->val); + break; + + case PM_QUESTION: + buf += bsprintf(buf, " ?"); + break; + + case PM_ASTERISK: + buf += bsprintf(buf, " *"); + break; + + case PM_ASN_EXPR: + buf += bsprintf(buf, " %u", f_eval_asn((struct f_inst *) p->val)); + break; + } p = p->next; } @@ -333,6 +349,7 @@ interpret(struct f_inst *what) { struct symbol *sym; struct f_val v1, v2, res; + unsigned u1, u2; int i; res.type = T_VOID; @@ -395,6 +412,18 @@ interpret(struct f_inst *what) res.val.i = v1.val.i || v2.val.i; break; + case P('m','p'): + TWOARGS_C; + if ((v1.type != T_INT) || (v2.type != T_INT)) + runtime( "Can't operate with value of non-integer type in pair constructor" ); + u1 = v1.val.i; + u2 = v2.val.i; + if ((u1 > 0xFFFF) || (u2 > 0xFFFF)) + runtime( "Can't operate with value out of bounds in pair constructor" ); + res.val.i = (u1 << 16) | u2; + res.type = T_PAIR; + break; + /* Relational operators */ #define COMPARE(x) \ @@ -810,6 +839,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) case '/': case '|': case '&': + case P('m','p'): case P('!','='): case P('=','='): case '<': @@ -933,6 +963,16 @@ f_eval_int(struct f_inst *expr) return res.val.i; } +u32 +f_eval_asn(struct f_inst *expr) +{ + struct f_val res = interpret(expr); + if (res.type != T_INT) + cf_error("Can't operate with value of non-integer type in AS path mask constructor"); + + return res.val.i; +} + /** * filter_same - compare two filters * @new: first filter to be compared diff --git a/filter/test.conf b/filter/test.conf index af889073..7c05af05 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -9,6 +9,17 @@ router id 62.168.0.1; define xyzzy = (120+10); + +function mkpair(int a) +{ + return (1, a); +} + +function mkpath(int a; int b) +{ + return [= a b 3 2 1 =]; +} + function callme(int arg1; int arg2) int local1; int local2; @@ -50,6 +61,7 @@ clist l; print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2; print "Should be true: ", p2 ~ / ? 4 3 2 1 /, " ", p2, " ", / ? 4 3 2 1 /; print "Should be true: ", p2 ~ [= * 4 3 * 1 =], " ", p2, " ", [= * 4 3 * 1 =]; + print "Should be true: ", p2 ~ [= (3+2) (2*2) 3 2 1 =], " ", p2 ~ mkpath(5, 4); print "5 = ", p2.len; pm1 = [= 1 2 * 3 4 5 =]; @@ -143,11 +155,15 @@ string s; px = 1.2.0.0/18; print "Testing prefixes: 1.2.0.0/18 = ", px; + print " must be true: ", 192.168.0.0/16 ~ 192.168.0.0/16, " ", 192.168.0.0/17 ~ 192.168.0.0/16, " ", 192.168.254.0/24 ~ 192.168.0.0/16; + print " must be false: ", 192.168.0.0/15 ~ 192.168.0.0/16, " ", 192.160.0.0/17 ~ 192.168.0.0/16; + p = 127.1.2.3; print "Testing mask : 127.0.0.0 = ", p.mask(8); pp = (1, 2); - print "Testing pairs: (1,2) = ", (1,2), " = ", pp; + print "Testing pairs: (1,2) = ", (1,2), " = ", pp, " = ", (1,1+1), " = ", mkpair(2); + print " must be true: ", (1,2) = (1,1+1); print "Testing enums: ", RTS_DUMMY, " ", RTS_STATIC; s = "Hello"; diff --git a/nest/a-path.c b/nest/a-path.c index f5499877..dba214d2 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -401,6 +401,7 @@ as_path_match(struct adata *path, struct f_path_mask *mask) struct pm_pos pos[2048 + 1]; int plen = parse_path(path, pos); int l, h, i, nh, nl; + u32 val; /* l and h are bound of interval of positions where are marked states */ @@ -424,14 +425,20 @@ as_path_match(struct adata *path, struct f_path_mask *mask) h = plen; break; - case PM_QUESTION: case PM_ASN: + val = mask->val; + goto step; + case PM_ASN_EXPR: + val = f_eval_asn((struct f_inst *) mask->val); + goto step; + case PM_QUESTION: + step: nh = -1; for (i = h; i >= l; i--) if (pos[i].mark) { pos[i].mark = 0; - if ((mask->kind == PM_QUESTION) || pm_match(pos + i, mask->val)) + if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val)) pm_mark(pos, i, plen, &nl, &nh); } diff --git a/nest/attrs.h b/nest/attrs.h index 5542be6f..b838ce96 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -35,11 +35,12 @@ int as_path_is_member(struct adata *path, u32 as); #define PM_ASN 0 #define PM_QUESTION 1 #define PM_ASTERISK 2 +#define PM_ASN_EXPR 3 struct f_path_mask { struct f_path_mask *next; int kind; - u32 val; + uintptr_t val; }; int as_path_match(struct adata *path, struct f_path_mask *mask); |