Filters: VPN Route Distinguishers, Prefix Type, Docs Update

This commit is contained in:
Jan Moskyto Matejka 2017-03-13 13:50:32 +01:00
parent 54334b5667
commit 8c9986d310
9 changed files with 128 additions and 21 deletions

View file

@ -36,8 +36,8 @@ $(o)%.ps: $(o)%.dvi
dvips -D600 -ta4 -o $@ $<
$(o)%.pdf: $(o)%.tex
pdflatex -output-directory=$(dir $@) $<
pdflatex -output-directory=$(dir $@) $<
TEXINPUTS=$(TEXINPUTS):$(doc-srcdir)/tex pdflatex -output-directory=$(dir $@) $<
TEXINPUTS=$(TEXINPUTS):$(doc-srcdir)/tex pdflatex -output-directory=$(dir $@) $<
$(o)%.txt: $(o)%.sgml
cd $(dir $@) && $(sgml2)txt $(notdir $<)

View file

@ -1193,13 +1193,31 @@ foot).
<cf/1.2.3.4.mask(8) = 1.0.0.0/ is true.
<tag><label id="type-prefix">prefix</tag>
This type can hold a network prefix consisting of IP address and prefix
length. Prefix literals are written as <cf><m/ipaddress//<m/pxlen/</cf>,
This type can hold a network prefix consisting of IP address, prefix
length and several other values. This is the key in route tables.
Prefices may be of several types, which can be determined by the special
operator <cf/.type/ of type <m/enum/. The type may be:
<cf/NET_IP4/ and <cf/NET_IP6/ prefices hold an IP prefix. The literals are
written as <cf><m/ipaddress//<m/pxlen/</cf>,
or <cf><m>ipaddress</m>/<m>netmask</m></cf>. There are two special
operators on prefixes: <cf/.ip/ which extracts the IP address from the
operators on IP prefices: <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.len = 16</cf> is true.
<cf/NET_VPN4/ and <cf/NET_VPN6/ prefices hold an IP prefix with
VPN Route Distinguisher (<rfc id="4364">). They support the same special
operators as IP prefices, and also <cf/.rd/ which extracts the Route Distinguisher.
Their literals are written as <cf><m/vpnrd/ <m/ipprefix/</cf>
<cf/NET_ROA4/ and <cf/NET_ROA6/ prefices hold an IP prefix range together
with an ASN. They support the same special operators as IP prefices, and also
<cf/.maxlen/ which extracts maximal prefix length, and <cf/.asn/ which extracts the ASN.
<cf/NET_FLOW4/ and <cf/NET_FLOW6/ hold an IP prefix together with
a flowspec rule. Filters currently don't support flowspec parsing.
<tag><label id="type-ec">ec</tag>
This is a specialized type used to represent BGP extended community
values. It is essentially a 64bit value, literals of this type are

View file

@ -393,7 +393,7 @@ CF_DECLS
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR, QUITBIRD,
INT, BOOL, IP, PREFIX, PAIR, QUAD, EC, LC,
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
@ -460,6 +460,7 @@ type:
INT { $$ = T_INT; }
| BOOL { $$ = T_BOOL; }
| IP { $$ = T_IP; }
| RD { $$ = T_RD; }
| PREFIX { $$ = T_NET; }
| PAIR { $$ = T_PAIR; }
| QUAD { $$ = T_QUAD; }
@ -786,6 +787,7 @@ constant:
| FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; }
| 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; }
| VPN_RD { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_RD; val->val.ec = $1; $$->a1.p = val; }
| net_ { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_NET; val->val.net = $1; $$->a1.p = val; }
| '[' 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; }
@ -888,7 +890,9 @@ term:
| rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); }
| term '.' TYPE { $$ = f_new_inst(); $$->code = 'T'; $$->a1.p = $1; }
| term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; }
| term '.' RD { $$ = f_new_inst(); $$->code = P('R','D'); $$->a1.p = $1; $$->aux = T_RD; }
| term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
| term '.' MAXLEN { $$ = f_new_inst(); $$->code = P('R','m'); $$->a1.p = $1; }
| term '.' ASN { $$ = f_new_inst(); $$->code = P('R','a'); $$->a1.p = $1; }

View file

@ -151,6 +151,7 @@ val_compare(struct f_val v1, struct f_val v2)
case T_QUAD:
return uint_cmp(v1.val.i, v2.val.i);
case T_EC:
case T_RD:
return u64_cmp(v1.val.ec, v2.val.ec);
case T_LC:
return lcomm_cmp(v1.val.lc, v2.val.lc);
@ -515,6 +516,7 @@ val_format(struct f_val v, buffer *buf)
case T_QUAD: buffer_print(buf, "%R", v.val.i); return;
case T_EC: ec_format(buf2, v.val.ec); buffer_print(buf, "%s", buf2); return;
case T_LC: lc_format(buf2, v.val.lc); buffer_print(buf, "%s", buf2); return;
case T_RD: rd_format(v.val.ec, buf2, 1024); buffer_print(buf, "%s", buf2); return;
case T_PREFIX_SET: trie_format(v.val.ti, buf); return;
case T_SET: tree_format(v.val.t, buf); return;
case T_ENUM: buffer_print(buf, "(enum %x)%u", v.type, v.val.i); return;
@ -815,6 +817,18 @@ interpret(struct f_inst *what)
res.type = T_BOOL;
res.val.i = (v1.type != T_VOID);
break;
case 'T':
ONEARG;
switch (v1.type)
{
case T_NET:
res.type = T_ENUM_NETTYPE;
res.val.i = v1.val.net->type;
break;
default:
runtime( "Can't determine type of this item" );
}
break;
/* Set to indirect value, a1 = variable, a2 = value */
case 's':
@ -1209,6 +1223,16 @@ interpret(struct f_inst *what)
res.type = T_IP;
res.val.ip = net_prefix(v1.val.net);
break;
case P('R','D'):
ONEARG;
if (v1.type != T_NET)
runtime( "Prefix expected" );
res.type = T_RD;
if ((1 << v1.val.net->type) & (NB_VPN4 | NB_VPN6))
res.val.ec = net_rd(v1.val.net);
else
runtime( "VPN address expected" );
break;
case P('a','f'): /* Get first ASN from AS PATH */
ONEARG;
if (v1.type != T_PATH)
@ -1581,6 +1605,8 @@ i_same(struct f_inst *f1, struct f_inst *f2)
case P('!', '~'):
case '~': TWOARGS; break;
case P('d','e'): ONEARG; break;
case 'T': ONEARG; break;
case P('n','T'): break;
case P('m','l'):
TWOARGS;
@ -1646,6 +1672,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
case 'r': ONEARG; break;
case P('c','p'): ONEARG; break;
case P('R','D'): ONEARG; break;
case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */
ONEARG;
if (!i_same(f1->a2.p, f2->a2.p))

View file

@ -146,6 +146,8 @@ void val_format(struct f_val v, buffer *buf);
#define T_ENUM_RTC 0x33
#define T_ENUM_RTD 0x34
#define T_ENUM_ROA 0x35
#define T_ENUM_NETTYPE 0x36
/* new enums go here */
#define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */
@ -162,6 +164,7 @@ void val_format(struct f_val v, buffer *buf);
#define T_ECLIST 0x27 /* Extended community list */
#define T_LC 0x28 /* Large community value, lcomm */
#define T_LCLIST 0x29 /* Large community list */
#define T_RD 0x2a /* Route distinguisher for VPN addresses */
#define T_RETURN 0x40
#define T_SET 0x80

View file

@ -1167,9 +1167,7 @@ int j;
filter roa_filter
{
print(net);
if net ~ [ 10.0.0.0/8{16,24}, 2000::/3{16,96} ] then {
print("accepted");
accept;
}
reject;
@ -1263,7 +1261,6 @@ prefix set pxs;
prefix set pxt;
{
pxs = [ 98.45.0.0/16, 128.128.0.0/12+, 2200::/42-, ::ffff:d000:0/100{98,102}];
print format(pxs);
bt_assert(format(pxs) = "[::/0, ::/2{c000::}, 98.45.0.0/112{::0.1.0.0}, 128.128.0.0/108{::0.31.255.255}, 208.0.0.0/100{::124.0.0.0}, 2200::/42{ffff:ffff:ffc0::}]");
bt_assert(::fe00:0:0/88 !~ pxs);
bt_assert(::fffe:0:0/95 !~ pxs);
@ -1275,6 +1272,39 @@ prefix set pxt;
bt_assert(::/0 ~ pxs);
bt_assert(0.0.0.0/0 !~ pxs);
bt_assert(128.135.64.17/32 ~ pxs);
# pxt = [ 0:1:2 10.1.10.0/24, 0:5:10000 10.1.10.0/24 ];
# print pxt;
bt_assert(format(NET_IP4) = "(enum 36)1"); ## if (net.type = NET_IP4) ...
bt_assert(format(NET_VPN6) = "(enum 36)4");
bt_assert(format(0:1:2) = "0:1:2");
}
bt_test_suite(t_mixed_prefix, "Testing mixed net types");
filter vpn_filter
{
bt_assert(format(net) = "0:1:2 10.1.10.0/24");
bt_assert(net.type = NET_VPN4);
bt_assert(net.type != NET_IP4);
bt_assert(net.type != NET_IP6);
bt_assert(net.rd = 0:1:2);
case (net.type) {
NET_IP4: print "IPV4";
NET_IP6: print "IPV6";
}
accept;
}
vpn4 table v4;
vpn4 table v6;
protocol static
{
vpn4 { table v4; import filter vpn_filter; };
route 0:1:2 10.1.10.0/24 unreachable;
}

View file

@ -54,6 +54,18 @@ const u16 net_max_text_length[] = {
};
int
rd_format(const u64 rd, char *buf, int buflen)
{
switch (rd >> 48)
{
case 0: return bsnprintf(buf, buflen, "0:%u:%u", (u32) (rd >> 32), (u32) rd);
case 1: return bsnprintf(buf, buflen, "1:%I4:%u", ip4_from_u32(rd >> 16), (u32) (rd & 0xffff));
case 2: return bsnprintf(buf, buflen, "2:%u:%u", (u32) (rd >> 16), (u32) (rd & 0xffff));
default: return bsnprintf(buf, buflen, "X:%08x:%08x", (u32) (rd >> 32), (u32) rd);
}
}
int
net_format(const net_addr *N, char *buf, int buflen)
{
@ -67,21 +79,17 @@ net_format(const net_addr *N, char *buf, int buflen)
case NET_IP6:
return bsnprintf(buf, buflen, "%I6/%d", n->ip6.prefix, n->ip6.pxlen);
case NET_VPN4:
switch (n->vpn4.rd >> 48)
{
case 0: return bsnprintf(buf, buflen, "0:%u:%u %I4/%d", (u32) (n->vpn4.rd >> 32), (u32) n->vpn4.rd, n->vpn4.prefix, n->vpn4.pxlen);
case 1: return bsnprintf(buf, buflen, "1:%I4:%u %I4/%d", ip4_from_u32(n->vpn4.rd >> 16), (u32) (n->vpn4.rd & 0xffff), n->vpn4.prefix, n->vpn4.pxlen);
case 2: return bsnprintf(buf, buflen, "2:%u:%u %I4/%d", (u32) (n->vpn4.rd >> 16), (u32) (n->vpn4.rd & 0xffff), n->vpn4.prefix, n->vpn4.pxlen);
default: return bsnprintf(buf, buflen, "X:%08x:%08x %I4/%d", (u32) (n->vpn4.rd >> 32), (u32) n->vpn4.rd, n->vpn4.prefix, n->vpn4.pxlen);
int c = rd_format(n->vpn4.rd, buf, buflen);
buf += c; buflen -= c;
return bsnprintf(buf, buflen, " %I4/%d", n->vpn4.prefix, n->vpn4.pxlen);
}
case NET_VPN6:
/* XXX: RD format is specified for VPN4; not found any for VPN6, reusing the same as for VPN4 */
switch (n->vpn6.rd >> 48)
{
case 0: return bsnprintf(buf, buflen, "0:%u:%u %I6/%d", (u32) (n->vpn6.rd >> 32), (u32) n->vpn6.rd, n->vpn6.prefix, n->vpn6.pxlen);
case 1: return bsnprintf(buf, buflen, "1:%I4:%u %I6/%d", ip4_from_u32(n->vpn6.rd >> 16), (u32) (n->vpn6.rd & 0xffff), n->vpn6.prefix, n->vpn6.pxlen);
case 2: return bsnprintf(buf, buflen, "2:%u:%u %I6/%d", (u32) (n->vpn6.rd >> 16), (u32) (n->vpn6.rd & 0xffff), n->vpn6.prefix, n->vpn6.pxlen);
default: return bsnprintf(buf, buflen, "X:%08x:%08x %I6/%d", (u32) (n->vpn6.rd >> 32), (u32) n->vpn6.rd, n->vpn6.prefix, n->vpn6.pxlen);
/* XXX: RD format is specified for VPN4; not found any for VPN6, reusing the same as for VPN4 */
int c = rd_format(n->vpn6.rd, buf, buflen);
buf += c; buflen -= c;
return bsnprintf(buf, buflen, " %I6/%d", n->vpn6.prefix, n->vpn6.pxlen);
}
case NET_ROA4:
return bsnprintf(buf, buflen, "%I4/%u-%u AS%u", n->roa4.prefix, n->roa4.pxlen, n->roa4.max_pxlen, n->roa4.asn);

View file

@ -230,6 +230,9 @@ static inline int net_is_ip(const net_addr *a)
static inline int net_is_roa(const net_addr *a)
{ return (a->type == NET_ROA4) || (a->type == NET_ROA6); }
static inline int net_is_vpn(const net_addr *a)
{ return (a->type == NET_VPN4) || (a->type == NET_VPN6); }
static inline ip4_addr net4_prefix(const net_addr *a)
{ return ((net_addr_ip4 *) a)->prefix; }
@ -278,6 +281,18 @@ static inline uint net_pxlen(const net_addr *a)
ip_addr net_pxmask(const net_addr *a);
static inline u64 net_rd(const net_addr *a)
{
switch (a->type)
{
case NET_VPN4:
return ((net_addr_vpn4 *)a)->rd;
case NET_VPN6:
return ((net_addr_vpn6 *)a)->rd;
}
return 0;
}
static inline int net_equal(const net_addr *a, const net_addr *b)
{ return (a->length == b->length) && !memcmp(a, b, a->length); }
@ -471,7 +486,7 @@ void net_normalize(net_addr *N);
int net_classify(const net_addr *N);
int net_format(const net_addr *N, char *buf, int buflen);
int rd_format(const u64 rd, char *buf, int buflen);
int ipa_in_netX(const ip_addr A, const net_addr *N);
int net_in_netX(const net_addr *A, const net_addr *N);

View file

@ -153,6 +153,8 @@ net_type:
| FLOW6{ $$ = NET_FLOW6; }
;
CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6)
/* Creation of routing tables */