summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/bird.sgml19
-rw-r--r--filter/config.Y1
-rw-r--r--filter/filter.c52
-rw-r--r--filter/test.conf4
-rw-r--r--nest/a-path.c63
-rw-r--r--nest/attrs.h2
-rw-r--r--proto/bgp/bgp.c3
-rw-r--r--tools/Makefile.in6
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@ ; \