diff options
-rw-r--r-- | doc/bird.sgml | 17 | ||||
-rw-r--r-- | filter/filter.c | 29 | ||||
-rw-r--r-- | filter/test.conf | 4 | ||||
-rw-r--r-- | nest/a-path.c | 63 | ||||
-rw-r--r-- | nest/attrs.h | 2 |
5 files changed, 112 insertions, 3 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml index 7db9fad2..3cd80c32 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1033,10 +1033,23 @@ incompatible with each other (that is to prevent you from shooting in the foot). <cf><m/P/.len</cf> returns the length of path <m/P/. - <cf>prepend(<m/P/,<m/A/)</cf> prepends ASN <m/A/ to path <m/P/ and returns the result. + <cf>prepend(<m/P/,<m/A/)</cf> prepends ASN <m/A/ to path + <m/P/ and returns the result. + + <cf>delete(<m/P/,<m/A/)</cf> deletes all instances of ASN + <m/A/ from from path <m/P/ and returns the result. + <m/A/ may also be an integer set, in that case the + operator deletes all ASNs from path <m/P/ that are also + members of set <m/A/. + + <cf>filter(<m/P/,<m/A/)</cf> deletes all ASNs from path + <m/P/ that are not members of integer set <m/A/. + I.e., <cf/filter/ do the same as <cf/delete/ with inverted + set <m/A/. + Statement <cf><m/P/ = prepend(<m/P/, <m/A/);</cf> can be shortened to <cf><m/P/.prepend(<m/A/);</cf> if <m/P/ is appropriate route attribute - (for example <cf/bgp_path/). + (for example <cf/bgp_path/). Similarly for <cf/delete/ and <cf/filter/. <tag/bgpmask/ BGP masks are patterns used for BGP path matching diff --git a/filter/filter.c b/filter/filter.c index 98bae331..25587e0f 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1165,7 +1165,34 @@ interpret(struct f_inst *what) case P('C','a'): /* (Extended) Community list add or delete */ TWOARGS; - if (v1.type == T_CLIST) + if (v1.type == T_PATH) + { + struct f_tree *set = NULL; + u32 key = 0; + int pos; + + if (v2.type == T_INT) + key = v2.val.i; + else if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT)) + set = v2.val.t; + else + runtime("Can't delete non-integer (set)"); + + switch (what->aux) + { + case 'a': runtime("Can't add to path"); + case 'd': pos = 0; break; + case 'f': pos = 1; break; + default: bug("unknown Ca operation"); + } + + if (pos && !set) + runtime("Can't filter integer"); + + res.type = T_PATH; + res.val.ad = as_path_filter(f_pool, v1.val.ad, set, key, pos); + } + else if (v1.type == T_CLIST) { /* Community (or cluster) list */ struct f_val dummy; diff --git a/filter/test.conf b/filter/test.conf index 4f40abff..048983b5 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -104,6 +104,8 @@ eclist el2; print "Should be true: ", p2 ~ [= (3+2) (2*2) 3 2 1 =], " ", p2 ~ mkpath(5, 4); print "Should be true: ", p2.len = 5, " ", p2.first = 5, " ", p2.last = 1; print "5 = ", p2.len; + print "Delete 3: ", delete(p2, 3); + print "Filter 1-3: ", filter(p2, [1..3]); pm1 = [= 1 2 * 3 4 5 =]; p2 = prepend( + empty +, 5 ); @@ -113,6 +115,8 @@ eclist el2; p2 = prepend( p2, 2 ); p2 = prepend( p2, 1 ); print "Should be true: ", p2 ~ pm1, " ", p2, " ", pm1; + print "Delete 3: ", delete(p2, 3); + print "Delete 4-5: ", delete(p2, [4..5]); l = - empty -; print "Should be false in this special case: ", l ~ [(*,*)]; diff --git a/nest/a-path.c b/nest/a-path.c index 712e77a3..b1812981 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -287,6 +287,69 @@ as_path_match_set(struct adata *path, struct f_tree *set) return 0; } +struct adata * +as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos) +{ + if (!path) + return NULL; + + int len = path->length; + u8 *p = path->data; + u8 *q = path->data + len; + u8 *d, *d2; + int i, bt, sn, dn; + u8 buf[len]; + + d = buf; + while (p<q) + { + /* Read block header (type and length) */ + bt = p[0]; + sn = p[1]; + dn = 0; + p += 2; + d2 = d + 2; + + for (i = 0; i < sn; i++) + { + u32 as = get_as(p); + int match; + + if (set) + match = !!find_tree(set, (struct f_val){T_INT, .val.i = as}); + else + match = (as == key); + + if (match == pos) + { + put_as(d2, as); + d2 += BS; + dn++; + } + + p += BS; + } + + if (dn > 0) + { + /* Nonempty block, set block header and advance */ + d[0] = bt; + d[1] = dn; + d = d2; + } + } + + int nl = d - buf; + if (nl == path->length) + return path; + + struct adata *res = lp_alloc(pool, sizeof(struct adata) + nl); + res->length = nl; + memcpy(res->data, buf, nl); + + return res; +} + struct pm_pos { diff --git a/nest/attrs.h b/nest/attrs.h index 12f2fcf4..44a23e18 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -37,6 +37,8 @@ int as_path_get_first(struct adata *path, u32 *orig_as); int as_path_get_last(struct adata *path, u32 *last_as); int as_path_is_member(struct adata *path, u32 as); int as_path_match_set(struct adata *path, struct f_tree *set); +struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos); + #define PM_ASN 0 #define PM_QUESTION 1 |