- Adds bgp_originator_id and bgp_cluster_list route attributes.
- Adds dotted quad filter datatype (for router IDs, used by
bgp_originator_id and ospf_router_id route attributes).
- Fixes pair ~ pair set matching.
- Documentation updates.
65535. Literals of this type are written as <cf/(1234,5678)/. The same syntax can also be
used to construct a pair from two arbitrary integer expressions (for example <cf/(1+2,a)/).
+ <tag/quad/ This is a dotted quad of numbers used to represent
+ router IDs (and others). Each component can have a value
+ from 0 to 255. Literals of this type are written like IPv4
+ addresses.
+
<tag/string/ This is a string of characters. There are no ways to modify strings in
filters. You can pass them between functions, assign them to variables of type <cf/string/, print
such variables, but you can't concatenate two strings. String literals
<cf/.ip/ which extracts the IP address from the pair, and <cf/.len/, which separates prefix
length from the pair. So <cf>1.2.0.0/16.pxlen = 16</cf> is true.
- <tag/int|ip|prefix|pair|enum set/
+ <tag/int|pair|quad|ip|prefix|enum set/
Filters recognize four types of sets. Sets are similar to strings: you can pass them around
- but you can't modify them. Literals of type <cf>set int</cf> look like <cf>
+ but you can't modify them. Literals of type <cf>int set</cf> look like <cf>
[ 1, 2, 5..7 ]</cf>. As you can see, both simple values and ranges are permitted in
sets.
and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>.
There is also old syntax that uses / .. / instead of [= .. =] and ? instead of *.
- <tag/clist/
- Community list is similar to set of pairs,
- except that unlike other sets, it can be modified.
- There exist no literals of this type.
- There are two special operators on clists:
+ <tag/clist/
+ Clist is similar to a set, except that unlike other sets, it
+ can be modified. The type is used for community list (a set
+ of pairs) and for cluster list (a set of quads). There exist
+ no literals of this type. There are two special operators on
+ clists:
- <cf>add(<m/C/,<m/P/)</cf> adds pair <m/P/ to clist <m/C/ and returns the result.
+ <cf>add(<m/C/,<m/P/)</cf> adds pair (or quad) <m/P/ to clist <m/C/ and returns the result.
- <cf>delete(<m/C/,<m/P/)</cf> deletes pair <m/P/ from clist <m/C/ and returns the result.
+ <cf>delete(<m/C/,<m/P/)</cf> deletes pair (or quad) <m/P/ from clist <m/C/ and returns the result.
Statement <cf><m/C/ = add(<m/C/, <m/P/);</cf> can be shortened to
<cf><m/C/.add(<m/P/);</cf> if <m/C/ is appropriate route attribute
Special operators include <cf/˜/ for "is element of a set" operation - it can be
used on element and set of elements of the same type (returning true if element is contained in the given set), or
on two strings (returning true if first string matches a shell-like pattern stored in second string) or on IP and prefix (returning true if IP is within the range defined by that prefix), or on
-prefix and prefix (returning true if first prefix is more specific than second one) or on bgppath and bgpmask (returning true if the path matches the mask) or on pair and clist (returning true if the community is element of the community list).
+prefix and prefix (returning true if first prefix is more specific than second one) or on bgppath and bgpmask (returning true if the path matches the mask) or on pair and clist (returning true if the pair (or quad) is element of the clist).
<sect>Control structures
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 <cf>defined( <m>attribute</m> )</cf> operator.
+One notable exception to this rule are attributes of clist type, where
+undefined value is regarded as empty clist for most purposes.
<descrip>
<tag><m/prefix/ net</tag>
policy information like "don't export to USA peers". As each AS can define
its own routing policy, it also has a complete freedom about which community
attributes it defines and what will their semantics be.
+
+ <tag>quad <cf/bgp_originator_id/ [O]</tag> 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.
+
+ <tag>clist <cf/bgp_cluster_list/ [O]</tag> This attribute contains a list
+ of cluster IDs of route reflectors. Each route reflector prepends its
+ cluster ID when reflecting the route.
</descrip>
<sect1>Example
A <cf/metric of type 1/ is comparable with internal <cf/metric/, a
<cf/metric of type 2/ is always longer
than any <cf/metric of type 1/ or any <cf/internal metric/.
+<cf/Internal metric/ or <cf/metric of type 1/ is stored in attribute
+<cf/ospf_metric1/, <cf/metric type 2/ is stored in attribute <cf/ospf_metric2/.
If you specify both metrics only metric1 is used.
-Each external route can also carry a <cf/tag/ which is a 32-bit
-integer which is used when exporting routes to other protocols;
+
+Each external route can also carry attribute <cf/ospf_tag/ which is a
+32-bit integer which is used when exporting routes to other protocols;
otherwise, it doesn't affect routing inside the OSPF domain at all.
-The fourth attribute is a <cf/router_id/ of the router advertising
-that route/network. This attribute is read-only.
-Default is <cf/metric of type 2 = 10000/ and <cf/tag = 0/.
+The fourth attribute <cf/ospf_router_id/ is a router ID of the router
+advertising that route/network. This attribute is read-only. Default
+is <cf/ospf_metric2 = 10000/ and <cf/ospf_tag = 0/.
<sect1>Example
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,
| IP { $$ = T_IP; }
| PREFIX { $$ = T_PREFIX; }
| PAIR { $$ = T_PAIR; }
+ | QUAD { $$ = T_QUAD; }
| STRING { $$ = T_STRING; }
| BGPMASK { $$ = T_PATH_MASK; }
| BGPPATH { $$ = T_PATH; }
| type SET {
switch ($1) {
case T_INT:
- case T_IP:
case T_PAIR:
+ case T_QUAD:
+ case T_IP:
$$ = T_SET;
break;
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; }
| 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; }
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:
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;
*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
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;
}
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:
{
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);
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);
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;
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;
/* 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;
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. */
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;
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;
#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
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;
{
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*";
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
| 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)
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, [<name>], [[Show information about OSPF protocol]])
{ ospf_sh(proto_get_named($3, &proto_ospf)); };
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;
}
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;
}