diff options
-rw-r--r-- | doc/bird.sgml | 19 | ||||
-rw-r--r-- | filter/config.Y | 1 | ||||
-rw-r--r-- | filter/filter.c | 52 | ||||
-rw-r--r-- | filter/test.conf | 4 | ||||
-rw-r--r-- | nest/a-path.c | 63 | ||||
-rw-r--r-- | nest/attrs.h | 2 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 3 | ||||
-rw-r--r-- | tools/Makefile.in | 6 |
8 files changed, 141 insertions, 9 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml index 5ceb8ab9..b3f6a85c 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 @@ -1169,7 +1182,7 @@ undefined value is regarded as empty clist for most purposes. Preference of the route. Valid values are 0-65535. (See the chapter about routing tables.) <tag><m/ip/ from</tag> - The router which the route has originated from. Read-only. + The router which the route has originated from. <tag><m/ip/ gw</tag> Next hop packets routed using this route should be forwarded to. diff --git a/filter/config.Y b/filter/config.Y index 7f73b895..66234050 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -681,7 +681,6 @@ symbol: static_attr: FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = OFFSETOF(struct rta, from); $$->a1.i = 1; } - | GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = OFFSETOF(struct rta, gw); $$->a1.i = 1; } | NET { $$ = f_new_inst(); $$->aux = T_PREFIX; $$->a2.i = 0x12345678; /* This is actually ok - T_PREFIX is special-cased. */ } | PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = 0x12345678; /* T_STRING is also special-cased. */ } diff --git a/filter/filter.c b/filter/filter.c index d784c253..25587e0f 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -853,10 +853,29 @@ interpret(struct f_inst *what) f_rta_cow(); { struct rta *rta = (*f_rte)->attrs; + ip_addr ip; + switch (what->aux) { case T_IP: - * (ip_addr *) ((char *) rta + what->a2.i) = v1.val.px.ip; + ip = v1.val.px.ip; + + /* "gw" attribute? */ + if (what->a2.i == OFFSETOF(struct rta, gw)) + { + neighbor *n = neigh_find(rta->proto, &ip, 0); + if (!n || (n->scope == SCOPE_HOST)) + runtime( "Invalid gw address" ); + + rta->dest = RTD_ROUTER; + rta->gw = ip; + rta->iface = n->iface; + rta->nexthops = NULL; + rta->hostentry = NULL; + } + else /* or "from" attribute? */ + rta->from = ip; + break; case T_ENUM_SCOPE: @@ -867,10 +886,12 @@ interpret(struct f_inst *what) i = v1.val.i; if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT)) runtime( "Destination can be changed only to blackhole, unreachable or prohibit" ); + rta->dest = i; rta->gw = IPA_NONE; rta->iface = NULL; rta->nexthops = NULL; + rta->hostentry = NULL; break; default: @@ -1144,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 diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 32153452..7cad75df 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -743,6 +743,9 @@ bgp_neigh_notify(neighbor *n) { struct bgp_proto *p = (struct bgp_proto *) n->proto; + if (! (n->flags & NEF_STICKY)) + return; + if (n->scope > 0) { if ((p->p.proto_state == PS_START) && (p->start_state == BSS_PREPARE)) diff --git a/tools/Makefile.in b/tools/Makefile.in index feb83b9f..062ba916 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -69,10 +69,10 @@ tags: install: all $(INSTALL) -d $(DESTDIR)/$(sbindir) $(DESTDIR)/$(sysconfdir) $(DESTDIR)/@runtimedir@ - $(INSTALL_PROGRAM) -s $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@ - $(INSTALL_PROGRAM) -s $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl@SUFFIX@ + $(INSTALL_PROGRAM) $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@ + $(INSTALL_PROGRAM) $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl@SUFFIX@ if test -n "@CLIENT@" ; then \ - $(INSTALL_PROGRAM) -s $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ; \ + $(INSTALL_PROGRAM) $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ; \ fi if ! test -f $(DESTDIR)/@CONFIG_FILE@ ; then \ $(INSTALL_DATA) $(srcdir)/doc/bird.conf.example $(DESTDIR)/@CONFIG_FILE@ ; \ |