diff --git a/doc/bird.sgml b/doc/bird.sgml
index 6ad06909..43f0c9c6 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -671,6 +671,11 @@ incompatible with each other (that is to prevent you from shooting in the foot).
65535. Literals of this type are written as 1.2.0.0/16.pxlen = 16 is true.
- set int look like
+ but you can't modify them. Literals of type int set look like
[ 1, 2, 5..7 ]. As you can see, both simple values and ranges are permitted in
sets.
@@ -756,15 +761,16 @@ incompatible with each other (that is to prevent you from shooting in the foot).
and integer variables, for example [= * 4 (1+2) a =].
There is also old syntax that uses / .. / instead of [= .. =] and ? instead of *.
- add( adds pair add( adds pair (or quad) delete( deletes pair delete( deletes pair (or quad) can be shortened to
if Control structures
@@ -818,6 +824,8 @@ if 1234 = i then printn "."; else {
attributes just like it accesses variables. Attempts to access undefined
attribute result in a runtime error; you can check if an attribute is
defined by using the defined( attribute ) operator.
+One notable exception to this rule are attributes of clist type, where
+undefined value is regarded as empty clist for most purposes.
@@ -1176,6 +1184,14 @@ with `quad This attribute is created by the
+ route reflector when reflecting the route and contains the router ID of the
+ originator of the route in the local AS.
+
+ clist This attribute contains a list
+ of cluster IDs of route reflectors. Each route reflector prepends its
+ cluster ID when reflecting the route.
Example
@@ -1595,13 +1611,16 @@ External routes use Example
diff --git a/filter/config.Y b/filter/config.Y
index 1af5649c..77236586 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -29,7 +29,7 @@ CF_DECLS
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR, QUITBIRD,
- INT, BOOL, IP, PREFIX, PAIR, SET, STRING, BGPMASK, BGPPATH, CLIST,
+ INT, BOOL, IP, PREFIX, PAIR, QUAD, SET, STRING, BGPMASK, BGPPATH, CLIST,
IF, THEN, ELSE, CASE,
TRUE, FALSE,
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, PREFERENCE,
@@ -75,6 +75,7 @@ type:
| IP { $$ = T_IP; }
| PREFIX { $$ = T_PREFIX; }
| PAIR { $$ = T_PAIR; }
+ | QUAD { $$ = T_QUAD; }
| STRING { $$ = T_STRING; }
| BGPMASK { $$ = T_PATH_MASK; }
| BGPPATH { $$ = T_PATH; }
@@ -82,8 +83,9 @@ type:
| type SET {
switch ($1) {
case T_INT:
- case T_IP:
case T_PAIR:
+ case T_QUAD:
+ case T_IP:
$$ = T_SET;
break;
@@ -234,6 +236,7 @@ fipa:
set_atom:
NUM { $$.type = T_INT; $$.val.i = $1; }
+ | RTRID { $$.type = T_QUAD; $$.val.i = $1; }
| cpair { $$.type = T_PAIR; $$.val.i = $1; }
| fipa { $$ = $1; }
| ENUM { $$.type = $1 >> 16; $$.val.i = $1 & 0xffff; }
@@ -340,6 +343,7 @@ constant:
| TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; }
| fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
| fprefix_s {NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
+ | RTRID { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_QUAD; $$->a2.i = $1; }
| '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
| '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
| ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
@@ -396,6 +400,7 @@ symbol:
case SYM_VARIABLE | T_BOOL:
case SYM_VARIABLE | T_INT:
case SYM_VARIABLE | T_PAIR:
+ case SYM_VARIABLE | T_QUAD:
case SYM_VARIABLE | T_STRING:
case SYM_VARIABLE | T_IP:
case SYM_VARIABLE | T_PREFIX:
diff --git a/filter/filter.c b/filter/filter.c
index de7a97bc..ae3b03ab 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -65,6 +65,7 @@ pm_path_compare(struct f_path_mask *m1, struct f_path_mask *m2)
if ((!m1) || (!m2))
return !((!m1) && (!m2));
+ /* FIXME: buggy, should return -1, 0, 1; but it doesn't matter */
if ((m1->kind != m2->kind) || (m1->val != m2->val)) return 1;
m1 = m1->next;
m2 = m2->next;
@@ -111,6 +112,13 @@ pm_format(struct f_path_mask *p, byte *buf, unsigned int size)
*buf = 0;
}
+static inline int int_cmp(int i1, int i2)
+{
+ if (i1 == i2) return 0;
+ if (i1 < i2) return -1;
+ else return 1;
+}
+
/**
* val_compare - compare two values
* @v1: first value
@@ -133,6 +141,14 @@ val_compare(struct f_val v1, struct f_val v2)
return 1;
if (v1.type != v2.type) {
+#ifndef IPV6
+ /* IP->Quad implicit conversion */
+ if ((v1.type == T_QUAD) && (v2.type == T_IP))
+ return int_cmp(v1.val.i, ipa_to_u32(v2.val.px.ip));
+ if ((v1.type == T_IP) && (v2.type == T_QUAD))
+ return int_cmp(ipa_to_u32(v1.val.px.ip), v2.val.i);
+#endif
+
debug( "Types do not match in val_compare\n" );
return CMP_ERROR;
}
@@ -141,9 +157,8 @@ val_compare(struct f_val v1, struct f_val v2)
case T_INT:
case T_BOOL:
case T_PAIR:
- if (v1.val.i == v2.val.i) return 0;
- if (v1.val.i < v2.val.i) return -1;
- return 1;
+ case T_QUAD:
+ return int_cmp(v1.val.i, v2.val.i);
case T_IP:
return ipa_compare(v1.val.px.ip, v2.val.px.ip);
case T_PREFIX:
@@ -196,8 +211,13 @@ val_simple_in_range(struct f_val v1, struct f_val v2)
{
if ((v1.type == T_PATH) && (v2.type == T_PATH_MASK))
return as_path_match(v1.val.ad, v2.val.path_mask);
- if ((v1.type == T_PAIR) && (v2.type == T_CLIST))
+ if (((v1.type == T_PAIR) || (v1.type == T_QUAD)) && (v2.type == T_CLIST))
return int_set_contains(v2.val.ad, v1.val.i);
+#ifndef IPV6
+ /* IP->Quad implicit conversion */
+ if ((v1.type == T_IP) && (v2.type == T_CLIST))
+ return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.px.ip));
+#endif
if ((v1.type == T_STRING) && (v2.type == T_STRING))
return patmatch(v2.val.s, v1.val.s);
@@ -235,8 +255,9 @@ val_in_range(struct f_val v1, struct f_val v2)
switch (v1.type) {
case T_ENUM:
case T_INT:
+ case T_PAIR:
+ case T_QUAD:
case T_IP:
- case T_PREFIX:
{
struct f_tree *n;
n = find_tree(v2.val.t, v1);
@@ -280,6 +301,7 @@ val_print(struct f_val v)
case T_IP: PRINTF( "%I", v.val.px.ip ); break;
case T_PREFIX: PRINTF( "%I/%d", v.val.px.ip, v.val.px.len ); break;
case T_PAIR: PRINTF( "(%d,%d)", v.val.i >> 16, v.val.i & 0xffff ); break;
+ case T_QUAD: PRINTF( "%R", v.val.i ); break;
case T_PREFIX_SET: trie_print(v.val.ti, buf, 2040); break;
case T_SET: tree_print( v.val.t ); PRINTF( "\n" ); break;
case T_ENUM: PRINTF( "(enum %x)%d", v.type, v.val.i ); break;
@@ -355,7 +377,7 @@ static struct f_val
interpret(struct f_inst *what)
{
struct symbol *sym;
- struct f_val v1, v2, res;
+ struct f_val v1, v2, res, *vp;
unsigned u1, u2;
int i;
u32 as;
@@ -435,11 +457,11 @@ interpret(struct f_inst *what)
/* Relational operators */
#define COMPARE(x) \
- TWOARGS_C; \
- res.type = T_BOOL; \
+ TWOARGS; \
i = val_compare(v1, v2); \
if (i==CMP_ERROR) \
- runtime( "Error in comparison" ); \
+ runtime( "Can't compare values of incompatible types" ); \
+ res.type = T_BOOL; \
res.val.i = (x); \
break;
@@ -473,10 +495,19 @@ interpret(struct f_inst *what)
case 's':
ARG(v2, a2.p);
sym = what->a1.p;
- if ((sym->class != (SYM_VARIABLE | v2.type)) &&
- (v2.type != T_VOID))
+ vp = sym->def;
+ if ((sym->class != (SYM_VARIABLE | v2.type)) && (v2.type != T_VOID)) {
+#ifndef IPV6
+ /* IP->Quad implicit conversion */
+ if ((sym->class == (SYM_VARIABLE | T_QUAD)) && (v2.type == T_IP)) {
+ vp->type = T_QUAD;
+ vp->val.i = ipa_to_u32(v2.val.px.ip);
+ break;
+ }
+#endif
runtime( "Assigning to variable of incompatible type" );
- * (struct f_val *) sym->def = v2;
+ }
+ *vp = v2;
break;
/* some constants have value in a2, some in *a1.p, strange. */
@@ -597,10 +628,13 @@ interpret(struct f_inst *what)
switch (what->aux & EAF_TYPE_MASK) {
case EAF_TYPE_INT:
- case EAF_TYPE_ROUTER_ID:
res.type = T_INT;
res.val.i = e->u.data;
break;
+ case EAF_TYPE_ROUTER_ID:
+ res.type = T_QUAD;
+ res.val.i = e->u.data;
+ break;
case EAF_TYPE_OPAQUE:
res.type = T_ENUM_EMPTY;
res.val.i = 0;
@@ -808,13 +842,21 @@ interpret(struct f_inst *what)
v1.val.ad = adata_empty(f_pool);
else if (v1.type != T_CLIST)
runtime("Can't add/delete to non-clist");
- if (v2.type != T_PAIR)
+
+ if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
+ i = v2.val.i;
+#ifndef IPV6
+ /* IP->Quad implicit conversion */
+ else if (v2.type == T_IP)
+ i = ipa_to_u32(v2.val.px.ip);
+#endif
+ else
runtime("Can't add/delete non-pair");
res.type = T_CLIST;
switch (what->aux) {
- case 'a': res.val.ad = int_set_add(f_pool, v1.val.ad, v2.val.i); break;
- case 'd': res.val.ad = int_set_del(f_pool, v1.val.ad, v2.val.i); break;
+ case 'a': res.val.ad = int_set_add(f_pool, v1.val.ad, i); break;
+ case 'd': res.val.ad = int_set_del(f_pool, v1.val.ad, i); break;
default: bug("unknown Ca operation");
}
break;
diff --git a/filter/filter.h b/filter/filter.h
index 11e0623a..46dc1a23 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -115,6 +115,7 @@ void val_print(struct f_val v);
#define T_INT 0x10
#define T_BOOL 0x11
#define T_PAIR 0x12 /* Notice that pair is stored as integer: first << 16 | second */
+#define T_QUAD 0x13
/* Put enumerational types in 0x30..0x3f range */
#define T_ENUM_LO 0x30
diff --git a/filter/test.conf b/filter/test.conf
index 8eeb5c35..0483c3d9 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -138,10 +138,12 @@ bool b;
prefix px;
ip p;
pair pp;
+quad qq;
int set is;
int set is1;
int set is2;
int set is3;
+pair set ps;
prefix set pxs;
string s;
{
@@ -190,7 +192,18 @@ string s;
pp = (1, 2);
print "Testing pairs: (1,2) = ", (1,2), " = ", pp, " = ", (1,1+1), " = ", 'mkpair-a'(2);
print " must be true: ", (1,2) = (1,1+1);
- print "Testing enums: ", RTS_DUMMY, " ", RTS_STATIC;
+ print "Testing enums: ", RTS_DUMMY, " ", RTS_STATIC, " ",
+ ", true: ", RTS_STATIC ~ [RTS_STATIC, RTS_DEVICE],
+ ", false: ", RTS_BGP ~ [RTS_STATIC, RTS_DEVICE];
+
+ ps = [(1,2), (3,4)..(3,8)];
+ print "Testing pair set (TTF):", pp ~ ps, " ", (3,5) ~ ps, " ", (3,9) ~ ps;
+
+ qq = 1.2.3.4;
+ print "Testinq quad: 1.2.3.4 = ", qq,
+ ", true: ", qq = 1.2.3.4, " ", qq ~ [1.2.3.4, 5.6.7.8],
+ ", false: ", qq = 4.3.2.1, " ", qq ~ [1.2.1.1, 1.2.3.5];
+
s = "Hello";
print "Testing string: ", s, " true: ", s ~ "Hell*", " false: ", s ~ "ell*";
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index f882aaa5..c4ed1032 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -23,7 +23,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY, SOURCE, ADDRESS,
PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4,
CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER, MISSING, LLADDR,
- DROP, IGNORE, ROUTE, REFRESH, INTERPRET, COMMUNITIES)
+ DROP, IGNORE, ROUTE, REFRESH, INTERPRET, COMMUNITIES,
+ BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST)
CF_GRAMMAR
@@ -90,22 +91,27 @@ bgp_proto:
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
;
-CF_ADDTO(dynamic_attr, BGP_PATH
- { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(EAP_BGP, BA_AS_PATH)); })
-CF_ADDTO(dynamic_attr, BGP_LOCAL_PREF
- { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); })
-CF_ADDTO(dynamic_attr, BGP_MED
- { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); })
CF_ADDTO(dynamic_attr, BGP_ORIGIN
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_BGP_ORIGIN, EA_CODE(EAP_BGP, BA_ORIGIN)); })
+CF_ADDTO(dynamic_attr, BGP_PATH
+ { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(EAP_BGP, BA_AS_PATH)); })
CF_ADDTO(dynamic_attr, BGP_NEXT_HOP
{ $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_CODE(EAP_BGP, BA_NEXT_HOP)); })
+CF_ADDTO(dynamic_attr, BGP_MED
+ { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); })
+CF_ADDTO(dynamic_attr, BGP_LOCAL_PREF
+ { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); })
CF_ADDTO(dynamic_attr, BGP_ATOMIC_AGGR
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)); })
CF_ADDTO(dynamic_attr, BGP_AGGREGATOR
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(EAP_BGP, BA_AGGREGATOR)); })
CF_ADDTO(dynamic_attr, BGP_COMMUNITY
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP, BA_COMMUNITY)); })
+CF_ADDTO(dynamic_attr, BGP_ORIGINATOR_ID
+ { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID)); })
+CF_ADDTO(dynamic_attr, BGP_CLUSTER_LIST
+ { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP, BA_CLUSTER_LIST)); })
+
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index aeb8a0df..da7c97e2 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -304,7 +304,7 @@ opttext:
CF_ADDTO(dynamic_attr, OSPF_METRIC1 { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_METRIC1); })
CF_ADDTO(dynamic_attr, OSPF_METRIC2 { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_METRIC2); })
CF_ADDTO(dynamic_attr, OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_TAG); })
-CF_ADDTO(dynamic_attr, OSPF_ROUTER_ID { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_ROUTER_ID); })
+CF_ADDTO(dynamic_attr, OSPF_ROUTER_ID { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID | EAF_TEMP, T_QUAD, EA_OSPF_ROUTER_ID); })
CF_CLI(SHOW OSPF, optsym, [], [[Show information about OSPF protocol]])
{ ospf_sh(proto_get_named($3, &proto_ospf)); };
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index d345e49b..5ed8abb1 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -333,7 +333,7 @@ ospf_build_attrs(ea_list * next, struct linpool *pool, u32 m1, u32 m2,
l->attrs[2].u.data = tag;
l->attrs[3].id = EA_OSPF_ROUTER_ID;
l->attrs[3].flags = 0;
- l->attrs[3].type = EAF_TYPE_INT | EAF_TEMP;
+ l->attrs[3].type = EAF_TYPE_ROUTER_ID | EAF_TEMP;
l->attrs[3].u.data = rid;
return l;
}
@@ -598,11 +598,11 @@ ospf_get_attr(eattr * a, byte * buf, int buflen UNUSED)
bsprintf(buf, "metric2");
return GA_NAME;
case EA_OSPF_TAG:
- bsprintf(buf, "tag: %08x (%u)", a->u.data, a->u.data);
- return GA_FULL;
- case EA_OSPF_ROUTER_ID:
- bsprintf(buf, "router_id: %R (%u)", a->u.data, a->u.data);
+ bsprintf(buf, "tag: 0x%08x", a->u.data);
return GA_FULL;
+ case EA_OSPF_ROUTER_ID:
+ bsprintf(buf, "router_id");
+ return GA_NAME;
default:
return GA_UNKNOWN;
}