diff --git a/conf/confbase.Y b/conf/confbase.Y index e6401f47..43854623 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -34,6 +34,7 @@ CF_DECLS struct f_inst *x; struct filter *f; struct f_tree *e; + struct f_trie *trie; struct f_val v; struct f_path_mask *h; struct password_item *p; diff --git a/doc/bird.sgml b/doc/bird.sgml index a7cda823..21141512 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -508,12 +508,33 @@ incompatible with each other (that is to prevent you from shooting in the foot). Filters recognize four types of sets. Sets are similar to strings: you can pass them around but you can't modify them. Literals of type set int look like [ 1, 2, 5..7 ]. As you can see, both simple values and ranges are permitted in - sets. Sets of prefixes are special: you can specify which prefix lengths should match them by - using [ 1.0.0.0/8+, 2.0.0.0/8-, 3.0.0.0/8{5,6} ]. 3.0.0.0/8{5,6} matches - prefixes address/num+ is a shorthand for address/{0,, - address/ is a shorthand for address/{0,. For example, - 1.2.0.0/16 ˜ [ 1.0.0.0/8{ 15 , 17 } ] is true, but - 1.0.0.0/8 ˜ [ 1.0.0.0/8- ] is false. + sets. + + Sets of prefixes are special: their literals does not allow ranges, but allows + prefix patterns that are written as ipaddress/pxlen{low,high}. + Prefix ip1/len1 matches prefix pattern ip2/len2{l, h} iff + the first min(len1, len2) bits of and len1 ≤ ip1 ≤ len2. + A valid prefix pattern has to satisfy is not constrained by address/ is a shorthand for + address/ (where maxlen is 32 for IPv4 and 128 for IPv6), + that means prefix address/ and all its subprefixes. address/ + is a shorthand for address/, that means prefix address/ + and all its superprefixes (prefixes that contain it). + + For example, [ 1.0.0.0/8, 2.0.0.0/8+, 3.0.0.0/8-, 4.0.0.0/8{16,24} ] matches + prefix 1.0.0.0/8, all subprefixes of 2.0.0.0/8, all superprefixes of 3.0.0.0/8 and prefixes + [ 0.0.0.0/0{20,24} ] matches all prefixes (regardless of + IP address) whose prefix length is 20 to 24, [ 1.2.3.4/32- ] matches any prefix that contains IP address + 1.2.3.4. 1.2.0.0/16 ˜ [ 1.0.0.0/8{ 15 , 17 } ] is true, + but 1.0.0.0/16 ˜ [ 1.0.0.0/8- ] is false. + + Cisco-style patterns like 10.0.0.0/8 ge 16 le 24 can be expressed + in Bird as 10.0.0.0/8{16,24}, 192.168.0.0/16 le 24 as + 192.168.0.0/16{16,24} and 192.168.0.0/16 ge 24 as + 192.168.0.0/16{24,32}. filter filter_body where_filter %type type break_command pair %type set_item set_items switch_body +%type fprefix_set %type set_atom fprefix fprefix_s fipa %type decls declsn one_decl function_params %type bgp_path bgp_path_tail1 bgp_path_tail2 @@ -69,12 +70,20 @@ type: | CLIST { $$ = T_CLIST; } | type SET { switch ($1) { + case T_INT: + case T_IP: + case T_PAIR: + $$ = T_SET; + break; + + case T_PREFIX: + $$ = T_PREFIX_SET; + break; + default: cf_error( "You can't create sets of this type." ); - case T_INT: case T_IP: case T_PREFIX: case T_PAIR: ; - } - $$ = $1 | T_SET; } + } ; one_decl: @@ -201,20 +210,6 @@ pair: /* * Complex types, their bison value is struct f_val */ -fprefix_s: - IPA '/' NUM %prec '/' { - if (!ip_is_prefix($1, $3)) cf_error("Invalid network prefix: %I/%d.", $1, $3); - $$.type = T_PREFIX; $$.val.px.ip = $1; $$.val.px.len = $3; - } - ; - -fprefix: - fprefix_s { $$ = $1; } - | fprefix_s '+' { $$ = $1; $$.val.px.len |= LEN_PLUS; } - | fprefix_s '-' { $$ = $1; $$.val.px.len |= LEN_MINUS; } - | fprefix_s '{' NUM ',' NUM '}' { $$ = $1; $$.val.px.len |= LEN_RANGE | ($3 << 16) | ($5 << 8); } - ; - fipa: IPA %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.px.ip = $1; } ; @@ -223,7 +218,6 @@ set_atom: NUM { $$.type = T_INT; $$.val.i = $1; } | pair { $$.type = T_PAIR; $$.val.i = $1; } | fipa { $$ = $1; } - | fprefix { $$ = $1; } | ENUM { $$.type = $1 >> 16; $$.val.i = $1 & 0xffff; } ; @@ -231,18 +225,12 @@ set_item: set_atom { $$ = f_new_tree(); $$->from = $1; - if ($1.type != T_PREFIX) - $$->to = $1; - else { - $$->to = $1; - $$->to.val.px.ip = ipa_or( $$->to.val.px.ip, ipa_not( ipa_mkmask( $$->to.val.px.len ) )); - } + $$->to = $1; } | set_atom '.' '.' set_atom { $$ = f_new_tree(); $$->from = $1; $$->to = $4; - if (($1.type == T_PREFIX) || ($4.type == T_PREFIX)) cf_error( "You can't use prefixes for range." ); } ; @@ -251,6 +239,28 @@ set_items: | set_items ',' set_item { $$ = $3; $$->left = $1; } ; +fprefix_s: + IPA '/' NUM %prec '/' { + if (($3 < 0) || ($3 > MAX_PREFIX_LENGTH) || !ip_is_prefix($1, $3)) cf_error("Invalid network prefix: %I/%d.", $1, $3); + $$.type = T_PREFIX; $$.val.px.ip = $1; $$.val.px.len = $3; + } + ; + +fprefix: + fprefix_s { $$ = $1; } + | fprefix_s '+' { $$ = $1; $$.val.px.len |= LEN_PLUS; } + | fprefix_s '-' { $$ = $1; $$.val.px.len |= LEN_MINUS; } + | fprefix_s '{' NUM ',' NUM '}' { + if (! ((0 <= $3) && ($3 <= $5) && ($5 <= MAX_PREFIX_LENGTH))) cf_error("Invalid prefix pattern range: {%d, %d}.", $3, $5); + $$ = $1; $$.val.px.len |= LEN_RANGE | ($3 << 16) | ($5 << 8); + } + ; + +fprefix_set: + fprefix { $$ = f_new_trie(); trie_add_prefix($$, &($1.val.px)); } + | fprefix_set ',' fprefix { $$ = $1; trie_add_prefix($$, &($3.val.px)); } + ; + switch_body: /* EMPTY */ { $$ = NULL; } | set_item ':' cmds switch_body { $$ = $1; @@ -294,6 +304,7 @@ constant: | 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" ); } + | '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET; $$->a2.p = $2; } | ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; } | bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; } ; @@ -374,12 +385,16 @@ term: 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_PREFIX: + case SYM_VARIABLE | T_STRING: case SYM_VARIABLE | T_IP: - case SYM_VARIABLE | T_PATH_MASK: + 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; diff --git a/filter/filter.c b/filter/filter.c index 623ab291..2e13c752 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -145,6 +145,25 @@ val_compare(struct f_val v1, struct f_val v2) } } + +void +f_prefix_get_bounds(struct f_prefix *px, int *l, int *h) +{ + *l = *h = px->len & LEN_MASK; + + if (px->len & LEN_MINUS) + *l = 0; + + else if (px->len & LEN_PLUS) + *h = MAX_PREFIX_LENGTH; + + else if (px->len & LEN_RANGE) + { + *l = 0xff & (px->len >> 16); + *h = 0xff & (px->len >> 8); + } +} + /* * val_simple_in_range - check if @v1 ~ @v2 for everything except sets */ @@ -162,21 +181,21 @@ val_simple_in_range(struct f_val v1, struct f_val v2) return !(ipa_compare(ipa_and(v2.val.px.ip, ipa_mkmask(v2.val.px.len)), ipa_and(v1.val.px.ip, ipa_mkmask(v2.val.px.len)))); if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX)) { - ip_addr mask; + if (v1.val.px.len & (LEN_PLUS | LEN_MINUS | LEN_RANGE)) return CMP_ERROR; - mask = ipa_mkmask( v2.val.px.len & LEN_MASK ); + + int p1 = v1.val.px.len & LEN_MASK; + int p2 = v2.val.px.len & LEN_MASK; + ip_addr mask = ipa_mkmask(MIN(p1, p2)); + if (ipa_compare(ipa_and(v2.val.px.ip, mask), ipa_and(v1.val.px.ip, mask))) return 0; - if ((v2.val.px.len & LEN_MINUS) && (v1.val.px.len <= (v2.val.px.len & LEN_MASK))) - return 0; - if ((v2.val.px.len & LEN_PLUS) && (v1.val.px.len < (v2.val.px.len & LEN_MASK))) - return 0; - if ((v2.val.px.len & LEN_RANGE) && ((v1.val.px.len < (0xff & (v2.val.px.len >> 16))) - || (v1.val.px.len > (0xff & (v2.val.px.len >> 8))))) - return 0; - return 1; + int l, h; + f_prefix_get_bounds(&v2.val.px, &l, &h); + + return ((l <= v1.val.px.len) && (v1.val.px.len <= h)); } return CMP_ERROR; } @@ -199,6 +218,9 @@ val_in_range(struct f_val v1, struct f_val v2) if (res != CMP_ERROR) return res; + if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX_SET)) + return trie_match_prefix(v2.val.ti, &v1.val.px); + if (v2.type == T_SET) switch (v1.type) { case T_ENUM: @@ -248,6 +270,7 @@ val_print(struct f_val v) case T_IP: PRINTF( "%I", v.val.px.ip ); break; case T_PREFIX: PRINTF( "%I/%d", v.val.px.ip, v.val.px.len ); break; case T_PAIR: PRINTF( "(%d,%d)", v.val.i >> 16, v.val.i & 0xffff ); break; + case T_PREFIX_SET: trie_print(v.val.ti, buf, 2040); break; case T_SET: tree_print( v.val.t ); PRINTF( "\n" ); break; case T_ENUM: PRINTF( "(enum %x)%d", v.type, v.val.i ); break; case T_PATH: as_path_format(v.val.ad, buf2, 1020); PRINTF( "(path %s)", buf2 ); break; @@ -430,13 +453,17 @@ interpret(struct f_inst *what) switch (res.type = v2.type) { case T_VOID: runtime( "Can't assign void values" ); case T_ENUM: - case T_INT: - case T_IP: - case T_PREFIX: - case T_PAIR: + case T_BOOL: + case T_INT: + case T_PAIR: + case T_STRING: + case T_IP: + case T_PREFIX: + case T_PREFIX_SET: + case T_SET: case T_PATH: - case T_CLIST: case T_PATH_MASK: + case T_CLIST: if (sym->class != (SYM_VARIABLE | v2.type)) runtime( "Assigning to variable of incompatible type" ); * (struct f_val *) sym->def = v2; @@ -447,10 +474,12 @@ interpret(struct f_inst *what) break; /* some constants have value in a2, some in *a1.p, strange. */ - case 'c': /* integer (or simple type) constant, or string, or set */ + case 'c': /* integer (or simple type) constant, string, set, or prefix_set */ res.type = what->aux; - if (res.type == T_SET) + if (res.type == T_PREFIX_SET) + res.val.ti = what->a2.p; + else if (res.type == T_SET) res.val.t = what->a2.p; else if (res.type == T_STRING) res.val.s = what->a2.p; @@ -818,16 +847,21 @@ i_same(struct f_inst *f1, struct f_inst *f2) break; case 'c': - if (f1->aux & T_SET) { + switch (f1->aux) { + + case T_PREFIX_SET: + if (!trie_same(f1->a2.p, f2->a2.p)) + return 0; + + case T_SET: if (!same_tree(f1->a2.p, f2->a2.p)) return 0; - break; - } - switch (f1->aux) { + case T_STRING: if (strcmp(f1->a2.p, f2->a2.p)) return 0; break; + default: A2_SAME; } diff --git a/filter/filter.h b/filter/filter.h index f71e54d3..2277f519 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -50,6 +50,7 @@ struct f_val { struct f_prefix px; char *s; struct f_tree *t; + struct f_trie *ti; struct adata *ad; struct f_path_mask *path_mask; } val; @@ -69,6 +70,12 @@ struct f_tree *build_tree(struct f_tree *); struct f_tree *find_tree(struct f_tree *t, struct f_val val); int same_tree(struct f_tree *t1, struct f_tree *t2); +struct f_trie *f_new_trie(void); +void trie_add_prefix(struct f_trie *t, struct f_prefix *px); +int trie_match_prefix(struct f_trie *t, struct f_prefix *px); +int trie_same(struct f_trie *t1, struct f_trie *t2); +int trie_print(struct f_trie *t, char *buf, int blen); + struct ea_list; struct rte; @@ -128,6 +135,7 @@ void val_print(struct f_val v); #define T_RETURN 0x40 #define T_SET 0x80 +#define T_PREFIX_SET 0x81 struct f_tree { struct f_tree *left, *right; @@ -135,6 +143,19 @@ struct f_tree { void *data; }; +struct f_trie_node +{ + ip_addr addr, mask, accept; + int plen; + struct f_trie_node *c[2]; +}; + +struct f_trie +{ + int zero; + struct f_trie_node root; +}; + #define NEW_F_VAL struct f_val * val; val = cfg_alloc(sizeof(struct f_val)); #define FF_FORCE_TMPATTR 1 /* Force all attributes to be temporary */ diff --git a/filter/test.conf b/filter/test.conf index 96859e53..af889073 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -100,10 +100,25 @@ function __test2() } reject; } +function test_pxset(prefix set pxs) +{ + print " must be true: ", 10.0.0.0/8 ~ pxs, ",", 10.0.0.0/10 ~ pxs, ",", 10.0.0.0/12 ~ pxs, ",", + 20.0.0.0/24 ~ pxs, ",", 20.0.40.0/24 ~ pxs, ",", 20.0.0.0/26 ~ pxs, ",", + 20.0.100.0/26 ~ pxs, ",", 20.0.0.0/28 ~ pxs, ",", 20.0.255.0/28 ~ pxs; + print " must be false: ", 10.0.0.0/7 ~ pxs, ",", 10.0.0.0/13 ~ pxs, ",", 10.0.0.0/16 ~ pxs, ",", + 20.0.0.0/16 ~ pxs, ",", 20.0.0.0/23 ~ pxs, ",", 20.0.0.0/29 ~ pxs, ",", + 11.0.0.0/10 ~ pxs, ",", 20.1.0.0/26 ~ pxs; +} + function __startup() int i; +bool b; prefix px; ip p; +pair pp; +int set is; +prefix set pxs; +string s; { print "Testing filter language:"; i = four; @@ -118,20 +133,37 @@ ip p; if 1234 = i then printn "."; else { print "*** FAIL: if 1 else"; } # if 1 <= 1 then printn "."; else { print "*** FAIL: test 3"; } if 1234 < 1234 then { print "*** FAIL: test 4"; quitbird; } else print "ok"; + is = [ 2, 3, 4, 7..11 ]; print " must be true: ", 1.2.0.0/16 ~ [ 1.0.0.0/8{ 15 , 17 } ]; - print " data types; must be true: ", 1.2.3.4 = 1.2.3.4, ",", 1 ~ [1,2,3], ",", 5 ~ [1..20], ",", 2 ~ [ 1, 2, 3 ], ",", 5 ~ [ 4 .. 7 ], ",", 1.2.3.4 ~ [ 1.2.3.3..1.2.3.5 ], ",", 1.2.3.4 ~ 1.0.0.0/8, ",", 1.0.0.0/8 ~ 1.0.0.0/8, ",", 1.0.0.0/8 ~ [ 1.0.0.0/8+ ]; + print " data types; must be true: ", 1.2.3.4 = 1.2.3.4, ",", 1 ~ [1,2,3], ",", 5 ~ [1..20], ",", 10 ~ is, ",", 2 ~ [ 1, 2, 3 ], ",", 5 ~ [ 4 .. 7 ], ",", 1.2.3.4 ~ [ 1.2.3.3..1.2.3.5 ], ",", 1.2.3.4 ~ 1.0.0.0/8, ",", 1.0.0.0/8 ~ 1.0.0.0/8, ",", 1.0.0.0/8 ~ [ 1.0.0.0/8+ ]; print " must be true: ", true && true, ",", true || false; # print " must be true: ", defined(1), ",", defined(1.2.3.4), ",", 1 != 2, ",", 1 <= 2; - print " data types: must be false: ", 1 ~ [ 2, 3, 4 ], ",", 5 ~ [ 2, 3, 4, 7..11 ], ",", 1.2.3.4 ~ [ 1.2.3.3, 1.2.3.5 ], ",", (1,2) > (2,2), ",", (1,1) > (1,1), ",", 1.0.0.0/8 ~ [ 1.0.0.0/8- ], ",", 1.2.0.0/17 ~ [ 1.0.0.0/8{ 15 , 16 } ], ",", true && false; + print " data types: must be false: ", 1 ~ [ 2, 3, 4 ], ",", 5 ~ is, ",", 1.2.3.4 ~ [ 1.2.3.3, 1.2.3.5 ], ",", (1,2) > (2,2), ",", (1,1) > (1,1), ",", 1.0.0.0/9 ~ [ 1.0.0.0/8- ], ",", 1.2.0.0/17 ~ [ 1.0.0.0/8{ 15 , 16 } ], ",", true && false; px = 1.2.0.0/18; print "Testing prefixes: 1.2.0.0/18 = ", px; p = 127.1.2.3; print "Testing mask : 127.0.0.0 = ", p.mask(8); - print "Testing pairs: (1,2) = ", (1,2); + + pp = (1, 2); + print "Testing pairs: (1,2) = ", (1,2), " = ", pp; print "Testing enums: ", RTS_DUMMY, " ", RTS_STATIC; + s = "Hello"; + print "Testing string: ", s, " true: ", s ~ "Hell*", " false: ", s ~ "ell*"; + + b = true; + print "Testing bool: ", b, ", ", !b; + + pxs = [ 1.2.0.0/16, 1.4.0.0/16+]; + print "Testing prefix sets: "; + print pxs; + print " must be true: ", 1.2.0.0/16 ~ pxs, ",", 1.4.0.0/16 ~ pxs, ",", 1.4.0.0/18 ~ pxs, ",", 1.4.0.0/32 ~ pxs; + print " must be false: ", 1.1.0.0/16 ~ pxs, ",", 1.3.0.0/16 ~ pxs, ",", 1.2.0.0/15 ~ pxs, ",", 1.2.0.0/17 ~ pxs, ",", + 1.2.0.0/32 ~ pxs, ",", 1.4.0.0/15 ~ pxs; + + test_pxset([ 10.0.0.0/16{8,12}, 20.0.0.0/16{24,28} ]); print "What will this do? ", [ 1, 2, 1, 1, 1, 3, 4, 1, 1, 1, 5 ]; print "Testing functions..."; diff --git a/filter/test6.conf b/filter/test6.conf new file mode 100644 index 00000000..f25ffc47 --- /dev/null +++ b/filter/test6.conf @@ -0,0 +1,182 @@ +/* + * This is an example configuration file. + * FIXME: add all examples from docs here. + */ + +# Yet another comment + +router id 62.168.0.1; + +define xyzzy = (120+10); + +function callme(int arg1; int arg2) +int local1; +int local2; +int i; +{ + printn "Function callme called arguments ", arg1, " and ", arg2, ":" ; + i = arg2; + + case arg1 { + 2: print "dva"; print "jeste jednou dva"; + 3 .. 5: print "tri az pet"; + else: print "neco jineho"; + } +} + +function fifteen() +{ + print "fifteen called"; + return 15; +} + +function paths() +bgpmask pm1; +bgpmask pm2; +bgppath p2; +clist l; +{ + pm1 = / 4 3 2 1 /; + pm2 = [= 4 3 2 1 =]; + print "Testing path masks: ", pm1, " ", pm2; + p2 = prepend( + empty +, 1 ); + p2 = prepend( p2, 2 ); + p2 = prepend( p2, 3 ); + p2 = prepend( p2, 4 ); + print "Testing paths: ", p2; + print "Should be true: ", p2 ~ pm1, " ", p2 ~ pm2; + print "4 = ", p2.len; + p2 = prepend( p2, 5 ); + 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 "5 = ", p2.len; + + pm1 = [= 1 2 * 3 4 5 =]; + p2 = prepend( + empty +, 5 ); + p2 = prepend( p2, 4 ); + p2 = prepend( p2, 3 ); + p2 = prepend( p2, 3 ); + p2 = prepend( p2, 2 ); + p2 = prepend( p2, 1 ); + print "Should be true: ", p2 ~ pm1, " ", p2, " ", pm1; + + l = - empty -; + l = add( l, (1,2) ); + l = add( l, (2,3) ); + print "Community list (1,2) (2,3) ", l; + print "Should be true: ", (2,3) ~ l; + l = delete( l, (2,3) ); + print "Community list (1,2) ", l; + print "Should be false: ", (2,3) ~ l; +} + +function bla() +{ + print "fifteen called"; + return 15; +} + +define four=4; + +function test_pxset(prefix set pxs) +{ + print " must be true: ", 1000::/8 ~ pxs, ",", 1000::/10 ~ pxs, ",", 1000::/12 ~ pxs, ",", + 2000::/24 ~ pxs, ",", 2000:4000::/24 ~ pxs, ",", 2000::/26 ~ pxs, ",", + 2000:8000::/26 ~ pxs, ",", 2000::/28 ~ pxs, ",", 2000:FFF0::/28 ~ pxs; + print " must be false: ", 1000::/7 ~ pxs, ",", 1000::/13 ~ pxs, ",", 1000::/16 ~ pxs, ",", + 2000::/16 ~ pxs, ",", 2000::/23 ~ pxs, ",", 2000::/29 ~ pxs, ",", + 1100::/10 ~ pxs, ",", 2010::/26 ~ pxs; +} + +function __startup() +int i; +bool b; +prefix px; +ip p; +pair pp; +int set is; +prefix set pxs; +string s; +{ + print "Testing filter language:"; + i = four; + i = 12*100 + 60/2 + i; + i = ( i + 0 ); + print " arithmetics: 1234 = ", i; + printn " if statements "; + print "what happens here?"; + printn "."; + if (i = 4) then { print "*** FAIL: if 0"; quitbird; } else printn "."; +# if !(i = 3) then { print "*** FAIL: if 0"; quitbird; } else printn "."; + if 1234 = i then printn "."; else { print "*** FAIL: if 1 else"; } +# if 1 <= 1 then printn "."; else { print "*** FAIL: test 3"; } + if 1234 < 1234 then { print "*** FAIL: test 4"; quitbird; } else print "ok"; + is = [ 2, 3, 4, 7..11 ]; + print " must be true: ", 1180::/16 ~ [ 1100::/8{ 15 , 17 } ]; + print " data types; must be true: ", 12::34 = 12::34, ",", 1 ~ [1,2,3], ",", 5 ~ [1..20], ",", 10 ~ is, ",", 2 ~ [ 1, 2, 3 ], ",", 5 ~ [ 4 .. 7 ], ",", 12::34 ~ [ 12::33..12::35 ], ",", 1020::34 ~ 1000::/8, ",", 1000::/8 ~ 1000::/8, ",", 1000::/8 ~ [ 1000::/8+ ]; + print " must be true: ", true && true, ",", true || false; + +# print " must be true: ", defined(1), ",", defined(1.2.3.4), ",", 1 != 2, ",", 1 <= 2; + print " data types: must be false: ", 1 ~ [ 2, 3, 4 ], ",", 5 ~ is, ",", 12::34 ~ [ 12::33, 12::35 ], ",", (1,2) > (2,2), ",", (1,1) > (1,1), ",", 1000::/9 ~ [ 1000::/8- ], ",", 1000::/17 ~ [ 1000::/8{ 15 , 16 } ], ",", true && false; + + px = 1020::/18; + print "Testing prefixes: 1020::/18 = ", px; + p = 1234:5678::; + print "Testing mask : 1200:: = ", p.mask(8); + + pp = (1, 2); + print "Testing pairs: (1,2) = ", (1,2), " = ", pp; + print "Testing enums: ", RTS_DUMMY, " ", RTS_STATIC; + + s = "Hello"; + print "Testing string: ", s, " true: ", s ~ "Hell*", " false: ", s ~ "ell*"; + + b = true; + print "Testing bool: ", b, ", ", !b; + + pxs = [ 1102::/16, 1104::/16+]; + print "Testing prefix sets: "; + print pxs; + print " must be true: ", 1102::/16 ~ pxs, ",", 1104::/16 ~ pxs, ",", 1104::/18 ~ pxs, ",", 1104::/32 ~ pxs; + print " must be false: ", 1101::/16 ~ pxs, ",", 1103::/16 ~ pxs, ",", 1102::/15 ~ pxs, ",", 1102::/17 ~ pxs, ",", + 1102::/32 ~ pxs, ",", 1104::/15 ~ pxs; + + test_pxset([ 1000::/16{8,12}, 2000::/16{24,28} ]); + print "What will this do? ", [ 1, 2, 1, 1, 1, 3, 4, 1, 1, 1, 5 ]; + + print "Testing functions..."; +# callme ( 1, 2 ); + callme ( 2, 2 ); + callme ( 2, 2 ); + callme ( 3, 2 ); + callme ( 4, 2 ); + callme ( 7, 2 ); + + i = fifteen(); + print "Testing function calls: 15 = ", i; + + paths(); + + print "done"; + quitbird; +# print "*** FAIL: this is unreachable"; +} + +filter testf +int j; +{ + print "Heya, filtering route to ", net.ip, " prefixlen ", net.len, " source ", source; + print "This route was from ", from; + j = 7; + j = 17; + if rip_metric > 15 then { + reject "RIP Metric is more than infinity"; + } + rip_metric = 14; + unset(rip_metric); + + accept "ok I take that"; +} + +eval __startup(); \ No newline at end of file diff --git a/filter/trie.c b/filter/trie.c new file mode 100644 index 00000000..7f9f5f88 --- /dev/null +++ b/filter/trie.c @@ -0,0 +1,325 @@ +/* + * Filters: Trie for prefix sets + * + * Copyright 2009 Ondrej Zajicek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +/** + * DOC: Trie for prefix sets + * + * We use a (compressed) trie to represent prefix sets. Every node + * in the trie represents one prefix (&addr/&plen) and &plen also + * indicates the index of the bit in the address that is used to + * branch at the node. If we need to represent just a set of + * prefixes, it would be simple, but we have to represent a + * set of prefix pattern. Each prefix pattern consists of + * &ppaddr/&pplen and two integers: &low and &high, and a prefix + * &paddr/&plen matches that pattern if the first MIN(&plen, &pplen) + * bits of &paddr and &ppaddr are the same and &low <= &plen <= &high. + * + * We use a bitmask (&accept) to represent accepted prefix lengths + * at a node. As there are 33 prefix lengths (0..32 for IPv4), but + * there is just one prefix of zero length in the whole trie so we + * have &zero flag in &f_trie (indicating whether the trie accepts + * prefix 0.0.0.0/0) as a special case, and &accept bitmask + * represents accepted prefix lengths from 1 to 32. + * + * There are two cases in prefix matching - a match when the length + * of the prefix is smaller that the length of the prefix pattern, + * (&plen < &pplen) and otherwise. The second case is simple - we + * just walk through the trie and look at every visited node + * whether that prefix accepts our prefix length (&plen). The + * first case is tricky - we don't want to examine every descendant + * of a final node, so (when we create the trie) we have to propagate + * that information from nodes to their ascendants. + * + * Suppose that we have two masks (M1 and M2) for a node. Mask M1 + * represents accepted prefix lengths by just the node and mask M2 + * represents accepted prefix lengths by the node or any of its + * descendants. Therefore M2 is a bitwise or of M1 and children's + * M2 and this is a maintained invariant during trie building. + * Basically, when we want to match a prefix, we walk through the trie, + * check mask M1 for our prefix length and when we came to + * final node, we check mask M2. + * + * There are two differences in the real implementation. First, + * we use a compressed trie so there is a case that we skip our + * final node (if it is not in the trie) and we came to node that + * is either extension of our prefix, or completely out of path + * In the first case, we also have to check M2. + + * There also might be + * a problem that interval of acceptance (on path from root to the + * final node) might be completely missing (for example if we have + * prefix patterns 192.168.128.0/24{8,10} and 192.168.1.0/24 + * + * Second, we really need not to maintain two separate bitmasks. + * Checks for mask M1 are always larger than &applen and we need + * just the first &pplen bits of mask M2 (if trie compression + * hadn't been used it would suffice to know just $applen-th bit), + * so we have to store them together in &accept mask - the first + * &pplen bits of mask M2 and then mask M1. + * + * There are four cases when we walk through a trie: + * + * - we are in NULL + * - we are out of path (prefixes are inconsistent) + * - we are in the wanted (final) node (node length == &plen) + * - we are beyond the end of path (node length > &plen) + * - we are still on path and keep walking (node length < &plen) + * + * The walking code in add_node_to_trie() and trie_match_prefix() + * is structured according to these cases. + */ + +#include "nest/bird.h" +#include "conf/conf.h" +#include "filter/filter.h" + +/** + * f_new_trie + * + * Allocates and returns a new empty trie. + */ +struct f_trie * +f_new_trie(void) +{ + struct f_trie * ret; + ret = cfg_allocz(sizeof(struct f_trie)); + return ret; +} + +static inline struct f_trie_node * +new_node(int plen, ip_addr paddr, ip_addr pmask, ip_addr amask) +{ + struct f_trie_node *n = cfg_allocz(sizeof(struct f_trie_node)); + n->plen = plen; + n->addr = paddr; + n->mask = pmask; + n->accept = amask; + return n; +} + +static inline void +attach_node(struct f_trie_node *parent, struct f_trie_node *child) +{ + parent->c[ipa_getbit(child->addr, parent->plen) ? 1 : 0] = child; +} + +static void +add_node_to_trie(struct f_trie *t, int plen, ip_addr ip, ip_addr amask) +{ + ip_addr pmask = ipa_mkmask(plen); + ip_addr paddr = ipa_and(ip, pmask); + struct f_trie_node *o = NULL; + struct f_trie_node *n = &t->root; + + while(n) + { + ip_addr cmask = ipa_and(n->mask, pmask); + + if (ipa_compare(ipa_and(paddr, cmask), ipa_and(n->addr, cmask))) + { + /* We are out of path - we have to add branching node 'b' + between node 'o' and node 'n', and attach new node 'a' + as the other child of 'b'. */ + int blen = ipa_pxlen(paddr, n->addr); + ip_addr bmask = ipa_mkmask(blen); + ip_addr baddr = ipa_and(ip, bmask); + + /* Merge accept masks from children to get accept mask for node 'b' */ + ip_addr baccm = ipa_and(ipa_or(amask, n->accept), bmask); + + struct f_trie_node *a = new_node(plen, paddr, pmask, amask); + struct f_trie_node *b = new_node(blen, baddr, bmask, baccm); + attach_node(o, b); + attach_node(b, n); + attach_node(b, a); + return; + } + + if (plen < n->plen) + { + /* We add new node 'a' between node 'o' and node 'n' */ + amask = ipa_or(amask, ipa_and(n->accept, pmask)); + struct f_trie_node *a = new_node(plen, paddr, pmask, amask); + attach_node(o, a); + attach_node(a, n); + return; + } + + if (plen == n->plen) + { + /* We already found added node in trie. Just update accept mask */ + n->accept = ipa_or(n->accept, amask); + return; + } + + /* Update accept mask part M2 and go deeper */ + n->accept = ipa_or(n->accept, ipa_and(amask, n->mask)); + + /* n->plen < plen and plen <= 32 */ + o = n; + n = n->c[ipa_getbit(paddr, n->plen) ? 1 : 0]; + } + + /* We add new tail node 'a' after node 'o' */ + struct f_trie_node *a = new_node(plen, paddr, pmask, amask); + attach_node(o, a); +} + +/** + * trie_add_prefix + * @t: trie to add to + * @px: prefix to add + * + * Adds prefix (prefix pattern) @px to trie @t. + */ +void +trie_add_prefix(struct f_trie *t, struct f_prefix *px) +{ + int l, h; + int plen = px->len & LEN_MASK; + ip_addr pmask = ipa_mkmask(plen); + + /* 'l' and 'h' are lower and upper bounds on accepted + prefix lengths, both inclusive. 0 <= l, h <= 32 */ + f_prefix_get_bounds(px, &l, &h); + + if (l == 0) + t->zero = 1; + else + l--; + + ip_addr amask = ipa_xor(ipa_mkmask(l), ipa_mkmask(h)); + /* MIN(plen, h) instead of just plen is a little trick. */ + add_node_to_trie(t, MIN(plen, h), px->ip, amask); +} + +/** + * trie_match_prefix + * @t: trie + * @px: prefix + * + * Tries to find a matching prefix pattern in the trie such that + * prefix @px matches that prefix pattern. Returns 1 if there + * is such prefix pattern in the trie. + */ +int +trie_match_prefix(struct f_trie *t, struct f_prefix *px) +{ + int plen = px->len & LEN_MASK; + ip_addr pmask = ipa_mkmask(plen); + ip_addr paddr = ipa_and(px->ip, pmask); + + if (plen == 0) + return t->zero; + + int plentest = plen - 1; + struct f_trie_node *n = &t->root; + + while(n) + { + ip_addr cmask = ipa_and(n->mask, pmask); + + /* We are out of path */ + if (ipa_compare(ipa_and(paddr, cmask), ipa_and(n->addr, cmask))) + return 0; + + /* Check accept mask */ + if (ipa_getbit(n->accept, plentest)) + return 1; + + /* We finished trie walk and still no match */ + if (plen <= n->plen) + return 0; + + /* Choose children */ + n = n->c[(ipa_getbit(paddr, n->plen)) ? 1 : 0]; + } + + return 0; +} + +static int +trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2) +{ + if ((t1 == NULL) && (t2 == NULL)) + return 1; + + if ((t1 == NULL) || (t2 == NULL)) + return 0; + + if ((t1->plen != t2->plen) || + (! ipa_equal(t1->addr, t2->addr)) || + (! ipa_equal(t1->accept, t2->accept))) + return 0; + + return trie_node_same(t1->c[0], t2->c[0]) && trie_node_same(t1->c[1], t2->c[1]); +} + +/** + * trie_same + * @t1: first trie to be compared + * @t2: second one + * + * Compares two tries and returns 1 if they are same + */ +int +trie_same(struct f_trie *t1, struct f_trie *t2) +{ + return (t1->zero == t2->zero) && trie_node_same(&t1->root, &t2->root); +} + +static int +trie_node_print(struct f_trie_node *t, char *buf, int blen) +{ + if (t == NULL) + return; + + int old_blen = blen; + int wb = 0; // bsnprintf(buf, blen, "%I/%d accept %I\n", t->addr, t->plen, t->accept); +debug("%I/%d accept %I\n", t->addr, t->plen, t->accept); + + if ((wb < 0) || ((blen - wb) < 10)) + { + bsnprintf(buf, blen, "...\n"); + return -1; + } + + buf += wb; + blen -= wb; + + wb = trie_node_print(t->c[0], buf, blen); + if (wb < 0) + return -1; + + buf += wb; + blen -= wb; + + wb = trie_node_print(t->c[1], buf, blen); + if (wb < 0) + return -1; + + blen -= wb; + + return (old_blen - blen); +} + +/** + * trie_print + * @t: trie to be printed + * @buf: buffer + * @blen: buffer length + * + * Prints the trie to the buffer, using at most blen bytes. + * Returns the number of used bytes, or -1 if there is not + * enough space in the buffer. + */ +int +trie_print(struct f_trie *t, char *buf, int blen) +{ + return trie_node_print(&t->root, buf, blen); +} diff --git a/lib/bitops.c b/lib/bitops.c index 6ca05050..88cef78a 100644 --- a/lib/bitops.c +++ b/lib/bitops.c @@ -45,3 +45,24 @@ u32_masklen(u32 x) if (x & 0xaaaaaaaa) l++; return l; } + +/** + * u32_log2 - compute a binary logarithm. + * @v: number + * + * This function computes a integral part of binary logarithm of given + * integer @v and returns it. The computed value is also an index of the + * first non-zero bit position. + */ + +u32 +u32_log2(u32 v) +{ + u32 r, shift; + r = (v > 0xFFFF) << 4; v >>= r; + shift = (v > 0xFF ) << 3; v >>= shift; r |= shift; + shift = (v > 0xF ) << 2; v >>= shift; r |= shift; + shift = (v > 0x3 ) << 1; v >>= shift; r |= shift; + r |= (v >> 1); + return r; +} diff --git a/lib/bitops.h b/lib/bitops.h index 1b6dc68e..bebd830d 100644 --- a/lib/bitops.h +++ b/lib/bitops.h @@ -17,3 +17,5 @@ u32 u32_mkmask(unsigned n); int u32_masklen(u32 x); + +u32 u32_log2(u32 v); diff --git a/lib/ipv4.h b/lib/ipv4.h index 4c4fab90..b64d9b2a 100644 --- a/lib/ipv4.h +++ b/lib/ipv4.h @@ -35,6 +35,7 @@ typedef u32 ip_addr; #endif +#define MAX_PREFIX_LENGTH 32 #define BITS_PER_IP_ADDRESS 32 #define STD_ADDRESS_P_LENGTH 15 #define SIZE_OF_IP_HEADER 24 @@ -58,6 +59,9 @@ typedef u32 ip_addr; #define ipa_from_u32(x) _MI(x) #define ipa_to_u32(x) _I(x) #define ipa_compare(x,y) ipv4_compare(_I(x),_I(y)) +/* ipa_pxlen() requires that x != y */ +#define ipa_pxlen(x, y) ipv4_pxlen(_I(x), _I(y)) +#define ipa_getbit(x, y) (_I(x) & (0x80000000 >> (y))) #define ip_skip_header(x, y) ipv4_skip_header(x, y) @@ -78,6 +82,12 @@ static inline int ipv4_compare(u32 x, u32 y) return (x > y) - (x < y); } +static inline u32 ipv4_pxlen(u32 a, u32 b) +{ + return 31 - u32_log2(a ^ b); +} + + #define IP_PREC_INTERNET_CONTROL 0xc0 #endif diff --git a/lib/ipv6.h b/lib/ipv6.h index 191c1c74..9193c4f1 100644 --- a/lib/ipv6.h +++ b/lib/ipv6.h @@ -12,6 +12,7 @@ #include #include #include "lib/string.h" +#include "lib/bitops.h" typedef struct ipv6_addr { u32 addr[4]; @@ -23,6 +24,7 @@ typedef struct ipv6_addr { #define _I2(a) ((a).addr[2]) #define _I3(a) ((a).addr[3]) +#define MAX_PREFIX_LENGTH 128 #define BITS_PER_IP_ADDRESS 128 #define STD_ADDRESS_P_LENGTH 39 #define SIZE_OF_IP_HEADER 40 @@ -57,6 +59,9 @@ typedef struct ipv6_addr { /* ipa_from_u32 and ipa_to_u32 replaced by ipa_build */ #define ipa_build(a,b,c,d) _MI(a,b,c,d) #define ipa_compare(x,y) ipv6_compare(x,y) +/* ipa_pxlen() requires that x != y */ +#define ipa_pxlen(x, y) ipv6_pxlen(x, y) +#define ipa_getbit(x, y) ipv6_getbit(x, y) #define ipa_absolutize(x,y) ipv6_absolutize(x,y) ip_addr ipv6_mkmask(unsigned); @@ -81,6 +86,21 @@ static inline unsigned ipv6_hash(ip_addr *a) return (x ^ (x >> 16) ^ (x >> 8)) & 0xffff; } +static inline u32 ipv6_getbit(ip_addr a, u32 y) +{ + return a.addr[y / 32] & (0x80000000 >> (y % 32)); +} + +static inline u32 ipv6_pxlen(ip_addr a, ip_addr b) +{ + int i = 0; + i+= (a.addr[i] == b.addr[i]); + i+= (a.addr[i] == b.addr[i]); + i+= (a.addr[i] == b.addr[i]); + i+= (a.addr[i] == b.addr[i]); + return 32 * i + 31 - u32_log2(a.addr[i] ^ b.addr[i]); +} + /* * RFC 1883 defines packet precendece, but RFC 2460 replaces it * by generic Traffic Class ID with no defined semantics. Better