diff options
-rw-r--r-- | doc/bird.sgml | 5 | ||||
-rw-r--r-- | filter/config.Y | 3 | ||||
-rw-r--r-- | filter/filter.c | 8 | ||||
-rw-r--r-- | nest/a-path.c | 31 | ||||
-rw-r--r-- | nest/attrs.h | 1 |
5 files changed, 45 insertions, 3 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml index 86df0456..c5316d87 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1119,9 +1119,12 @@ foot). <cf><m/P/.last</cf> returns the last ASN (the source ASN) in path <m/P/. + <cf><m/P/.last_nonaggregated</cf> returns the last ASN in the non-aggregated part of the path <m/P/. + Both <cf/first/ and <cf/last/ return zero if there is no appropriate ASN, for example if the path contains an AS set element as the first (or - the last) part. + the last) part. If the path ends with an AS set, <cf/last_nonaggregated/ + may be used to get last ASN before any AS set. <cf><m/P/.len</cf> returns the length of path <m/P/. diff --git a/filter/config.Y b/filter/config.Y index 7eb2c0a3..b94f5dff 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -283,7 +283,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, LEN, DEFINED, ADD, DELETE, CONTAINS, RESET, - PREPEND, FIRST, LAST, MATCH, + PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH, ROA_CHECK, EMPTY, FILTER, WHERE, EVAL) @@ -752,6 +752,7 @@ term: | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; } | term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; } | term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; } + | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; } /* Communities */ /* This causes one shift/reduce conflict diff --git a/filter/filter.c b/filter/filter.c index 55062aca..eddf4228 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1091,6 +1091,14 @@ interpret(struct f_inst *what) res.type = T_INT; res.val.i = as; break; + case P('a','L'): /* Get last ASN from non-aggregated part of AS PATH */ + ONEARG; + if (v1.type != T_PATH) + runtime( "AS path expected" ); + + res.type = T_INT; + res.val.i = as_path_get_last_nonaggregated(v1.val.ad); + break; case 'r': ONEARG; res = v1; diff --git a/nest/a-path.c b/nest/a-path.c index c9c5aefb..32e2d27e 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -220,7 +220,7 @@ as_path_get_last(struct adata *path, u32 *orig_as) p += BS * len; } break; - default: bug("as_path_get_first: Invalid path segment"); + default: bug("Invalid path segment"); } } @@ -229,6 +229,35 @@ as_path_get_last(struct adata *path, u32 *orig_as) return found; } +u32 +as_path_get_last_nonaggregated(struct adata *path) +{ + u8 *p = path->data; + u8 *q = p+path->length; + u32 res = 0; + int len; + + while (p<q) + { + switch (*p++) + { + case AS_PATH_SET: + return res; + + case AS_PATH_SEQUENCE: + if (len = *p++) + res = get_as(p + BS * (len - 1)); + p += BS * len; + break; + + default: bug("Invalid path segment"); + } + } + + return res; +} + + int as_path_get_first(struct adata *path, u32 *last_as) { diff --git a/nest/attrs.h b/nest/attrs.h index 1d005a6a..0171c6a8 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -35,6 +35,7 @@ int as_path_getlen(struct adata *path); int as_path_getlen_int(struct adata *path, int bs); int as_path_get_first(struct adata *path, u32 *orig_as); int as_path_get_last(struct adata *path, u32 *last_as); +u32 as_path_get_last_nonaggregated(struct adata *path); int as_path_contains(struct adata *path, u32 as, int min); 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); |