Commit 126683fe authored by Ondřej Zajíček's avatar Ondřej Zajíček

Filter language updates; new route attributes and datatype.

 - 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.
parent eb0f129f
......@@ -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 <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
......@@ -688,9 +693,9 @@ incompatible with each other (that is to prevent you from shooting in the foot).
<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.
......@@ -756,15 +761,16 @@ incompatible with each other (that is to prevent you from shooting in the foot).
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
......@@ -779,7 +785,7 @@ incompatible with each other (that is to prevent you from shooting in the foot).
Special operators include <cf/&tilde;/ 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
......@@ -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 <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>
......@@ -1176,6 +1184,14 @@ with `<tt/O/') are optional.
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
......@@ -1595,13 +1611,16 @@ External routes use <cf/metric type 1/ or <cf/metric type 2/.
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
......
......@@ -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:
......
......@@ -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;
......
......@@ -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
......
......@@ -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*";
......
......@@ -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)
......
......@@ -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, [<name>], [[Show information about OSPF protocol]])
{ ospf_sh(proto_get_named($3, &proto_ospf)); };
......
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment