summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2016-02-16 17:33:58 +0100
committerOndrej Zajicek (work) <santiago@crfreenet.org>2016-02-16 17:33:58 +0100
commit9c9cc35c0273f8bcae10fb8b546d199514b2bbc5 (patch)
tree060b7ebe4012294ee4468a47cb4e73e3f6b1f0c1
parentc2106b674ca632f7c0bffd7cab4b1940f74d353c (diff)
Filter: Implement last_nonaggregated operator on bgp_path
-rw-r--r--doc/bird.sgml5
-rw-r--r--filter/config.Y3
-rw-r--r--filter/filter.c8
-rw-r--r--nest/a-path.c31
-rw-r--r--nest/attrs.h1
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);