BGP Extended communities.

This commit is contained in:
Ondrej Zajicek 2011-08-12 21:03:43 +02:00
parent bde872bba7
commit 42a0c05408
14 changed files with 793 additions and 110 deletions

5
aclocal.m4 vendored
View file

@ -6,7 +6,8 @@ AC_DEFUN(BIRD_CHECK_INTEGERS,
AC_CHECK_SIZEOF(short int, 0)
AC_CHECK_SIZEOF(int, 0)
AC_CHECK_SIZEOF(long int, 0)
for size in 1 2 4 ; do
AC_CHECK_SIZEOF(long long int, 0)
for size in 1 2 4 8; do
bits=`expr $size "*" 8`
AC_MSG_CHECKING([for $bits-bit type])
if test $ac_cv_sizeof_int = $size ; then
@ -17,6 +18,8 @@ for size in 1 2 4 ; do
res="short int"
elif test $ac_cv_sizeof_long_int = $size ; then
res="long int"
elif test $ac_cv_sizeof_long_long_int = $size ; then
res="long long int"
else
AC_MSG_RESULT([not found])
AC_MSG_ERROR([Cannot find $bits-bit integer type.])

View file

@ -75,13 +75,185 @@ f_new_pair_set(int fa, int ta, int fb, int tb)
return lst;
}
#define EC_ALL 0xFFFFFFFF
static struct f_tree *
f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt)
{
u64 fm, to;
if (ipv4_used || (key >= 0x10000)) {
check_u16(vf);
if (vt == EC_ALL)
vt = 0xFFFF;
else
check_u16(vt);
}
if (kind == EC_GENERIC) {
fm = ec_generic(key, vf);
to = ec_generic(key, vt);
}
else if (ipv4_used) {
fm = ec_ip4(kind, key, vf);
to = ec_ip4(kind, key, vt);
}
else if (key < 0x10000) {
fm = ec_as2(kind, key, vf);
to = ec_as2(kind, key, vt);
}
else {
fm = ec_as4(kind, key, vf);
to = ec_as4(kind, key, vt);
}
struct f_tree *t = f_new_tree();
t->right = t;
t->from.type = t->to.type = T_EC;
t->from.val.ec = fm;
t->to.val.ec = to;
return t;
}
static inline struct f_inst *
f_generate_empty(struct f_inst *dyn)
{
struct f_inst *e = f_new_inst();
e->code = 'E';
switch (dyn->aux & EAF_TYPE_MASK) {
case EAF_TYPE_AS_PATH:
e->aux = T_PATH;
break;
case EAF_TYPE_INT_SET:
e->aux = T_CLIST;
break;
case EAF_TYPE_EC_SET:
e->aux = T_ECLIST;
break;
default:
cf_error("Can't empty that attribute");
}
dyn->code = P('e','S');
dyn->a1.p = e;
return dyn;
}
static inline struct f_inst *
f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
{
struct f_inst *rv;
if ((t1->code == 'c') && (t2->code == 'c')) {
if ((t1->aux != T_INT) || (t2->aux != T_INT))
cf_error( "Can't operate with value of non-integer type in pair constructor");
check_u16(t1->a2.i);
check_u16(t2->a2.i);
rv = f_new_inst();
rv->code = 'c';
rv->aux = T_PAIR;
rv->a2.i = pair(t1->a2.i, t2->a2.i);
}
else {
rv = f_new_inst();
rv->code = P('m', 'p');
rv->a1.p = t1;
rv->a2.p = t2;
}
return rv;
}
static inline struct f_inst *
f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
{
struct f_inst *rv;
int c1 = 0, c2 = 0, ipv4_used = 0;
u32 key = 0, val2 = 0;
if (tk->code == 'c') {
c1 = 1;
if (tk->aux == T_INT) {
ipv4_used = 0; key = tk->a2.i;
}
else if (tk->aux == T_QUAD) {
ipv4_used = 1; key = tk->a2.i;
}
else
cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
}
#ifndef IPV6
/* IP->Quad implicit conversion */
else if (tk->code == 'C') {
c1 = 1;
struct f_val *val = tk->a1.p;
if (val->type == T_IP) {
ipv4_used = 1; key = ipa_to_u32(val->val.px.ip);
}
else
cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
}
#endif
if (tv->code == 'c') {
if (tv->aux != T_INT)
cf_error("Can't operate with value of non-integer type in EC constructor");
c2 = 1;
val2 = tv->a2.i;
}
if (c1 && c2) {
u64 ec;
if (kind == EC_GENERIC) {
ec = ec_generic(key, val2);
}
else if (ipv4_used) {
check_u16(val2);
ec = ec_ip4(kind, key, val2);
}
else if (key < 0x10000) {
ec = ec_as2(kind, key, val2);
}
else {
check_u16(val2);
ec = ec_as4(kind, key, val2);
}
NEW_F_VAL;
rv = f_new_inst();
rv->code = 'C';
rv->a1.p = val;
val->type = T_EC;
val->val.ec = ec;
}
else {
rv = f_new_inst();
rv->code = P('m','c');
rv->aux = kind;
rv->a1.p = tk;
rv->a2.p = tv;
}
return rv;
};
CF_DECLS
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR, QUITBIRD,
INT, BOOL, IP, PREFIX, PAIR, QUAD, SET, STRING, BGPMASK, BGPPATH, CLIST,
INT, BOOL, IP, PREFIX, PAIR, QUAD, EC,
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST,
IF, THEN, ELSE, CASE,
TRUE, FALSE,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, PREFERENCE,
LEN,
DEFINED,
@ -93,11 +265,11 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc THEN
%nonassoc ELSE
%type <x> term block cmds cmds_int cmd function_body constant print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol dpair bgp_path_expr
%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol bgp_path_expr
%type <f> filter filter_body where_filter
%type <i> type break_command pair_expr
%type <i32> pair_atom
%type <e> pair_item set_item switch_item set_items switch_items switch_body
%type <i> type break_command pair_expr ec_kind
%type <i32> pair_atom ec_expr
%type <e> pair_item ec_item set_item switch_item set_items switch_items switch_body
%type <trie> fprefix_set
%type <v> set_atom switch_atom fprefix fprefix_s fipa
%type <s> decls declsn one_decl function_params
@ -128,15 +300,18 @@ type:
| PREFIX { $$ = T_PREFIX; }
| PAIR { $$ = T_PAIR; }
| QUAD { $$ = T_QUAD; }
| EC { $$ = T_EC; }
| STRING { $$ = T_STRING; }
| BGPMASK { $$ = T_PATH_MASK; }
| BGPPATH { $$ = T_PATH; }
| CLIST { $$ = T_CLIST; }
| ECLIST { $$ = T_ECLIST; }
| type SET {
switch ($1) {
case T_INT:
case T_PAIR:
case T_QUAD:
case T_EC:
case T_IP:
$$ = T_SET;
break;
@ -324,14 +499,32 @@ pair_item:
}
;
ec_expr:
term { $$ = f_eval_int($1); }
ec_kind:
RT { $$ = EC_RT; }
| RO { $$ = EC_RO; }
| UNKNOWN NUM { $$ = $2; }
| GENERIC { $$ = EC_GENERIC; }
;
ec_item:
'(' ec_kind ',' ec_expr ',' ec_expr ')' { $$ = f_new_ec_item($2, 0, $4, $6, $6); }
| '(' ec_kind ',' ec_expr ',' ec_expr DDOT ec_expr ')' { $$ = f_new_ec_item($2, 0, $4, $6, $8); }
| '(' ec_kind ',' ec_expr ',' '*' ')' { $$ = f_new_ec_item($2, 0, $4, 0, EC_ALL); }
;
set_item:
pair_item
| ec_item
| set_atom { $$ = f_new_item($1, $1); }
| set_atom DDOT set_atom { $$ = f_new_item($1, $3); }
;
switch_item:
pair_item
| ec_item
| switch_atom { $$ = f_new_item($1, $1); }
| switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); }
;
@ -411,20 +604,6 @@ bgp_path_tail2:
| { $$ = NULL; }
;
dpair:
'(' term ',' term ')' {
if (($2->code == 'c') && ($4->code == 'c'))
{
if (($2->aux != T_INT) || ($4->aux != T_INT))
cf_error( "Can't operate with value of non-integer type in pair constructor" );
check_u16($2->a2.i); check_u16($4->a2.i);
$$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PAIR; $$->a2.i = pair($2->a2.i, $4->a2.i);
}
else
{ $$ = f_new_inst(); $$->code = P('m', 'p'); $$->a1.p = $2; $$->a2.p = $4; }
}
;
constant:
NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; }
| TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; }
@ -439,6 +618,11 @@ constant:
| bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; }
;
constructor:
'(' term ',' term ')' { $$ = f_generate_dpair($2, $4); };
| '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); };
;
/*
* Maybe there are no dynamic attributes defined by protocols.
@ -490,6 +674,7 @@ symbol:
case SYM_VARIABLE | T_INT:
case SYM_VARIABLE | T_PAIR:
case SYM_VARIABLE | T_QUAD:
case SYM_VARIABLE | T_EC:
case SYM_VARIABLE | T_STRING:
case SYM_VARIABLE | T_IP:
case SYM_VARIABLE | T_PREFIX:
@ -498,6 +683,7 @@ symbol:
case SYM_VARIABLE | T_PATH:
case SYM_VARIABLE | T_PATH_MASK:
case SYM_VARIABLE | T_CLIST:
case SYM_VARIABLE | T_ECLIST:
$$->code = 'V';
$$->a1.p = $1->def;
$$->a2.p = $1->name;
@ -539,7 +725,7 @@ term:
| symbol { $$ = $1; }
| constant { $$ = $1; }
| dpair { $$ = $1; }
| constructor { $$ = $1; }
| PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; }
@ -563,6 +749,7 @@ term:
| '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; }
| '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; }
| ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
| DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
@ -702,8 +889,7 @@ cmd:
}
| rtadot dynamic_attr '.' EMPTY ';'
{ struct f_inst *i = f_new_inst(); i->code = 'E'; i->aux = T_CLIST; $$ = $2; $$->code = P('e','S'); $$->a1.p = i; }
| rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
| rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); }
| rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); }
| rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); }

View file

@ -51,10 +51,10 @@
#define CMP_ERROR 999
static struct adata *
adata_empty(struct linpool *pool)
adata_empty(struct linpool *pool, int l)
{
struct adata *res = lp_alloc(pool, sizeof(struct adata));
res->length = 0;
struct adata *res = lp_alloc(pool, sizeof(struct adata) + l);
res->length = l;
return res;
}
@ -126,6 +126,13 @@ static inline int uint_cmp(unsigned int i1, unsigned int i2)
else return 1;
}
static inline int u64_cmp(u64 i1, u64 i2)
{
if (i1 == i2) return 0;
if (i1 < i2) return -1;
else return 1;
}
/**
* val_compare - compare two values
* @v1: first value
@ -167,6 +174,8 @@ val_compare(struct f_val v1, struct f_val v2)
case T_PAIR:
case T_QUAD:
return uint_cmp(v1.val.i, v2.val.i);
case T_EC:
return u64_cmp(v1.val.ec, v2.val.ec);
case T_IP:
return ipa_compare(v1.val.px.ip, v2.val.px.ip);
case T_PREFIX:
@ -226,6 +235,9 @@ val_simple_in_range(struct f_val v1, struct f_val v2)
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_EC) && (v2.type == T_ECLIST))
return ec_set_contains(v2.val.ad, v1.val.ec);
if ((v1.type == T_STRING) && (v2.type == T_STRING))
return patmatch(v2.val.s, v1.val.s);
@ -258,6 +270,10 @@ clist_set_type(struct f_tree *set, struct f_val *v)
}
}
static inline int
eclist_set_type(struct f_tree *set)
{ return set->from.type == T_EC; }
static int
clist_match_set(struct adata *clist, struct f_tree *set)
{
@ -270,6 +286,7 @@ clist_match_set(struct adata *clist, struct f_tree *set)
u32 *l = (u32 *) clist->data;
u32 *end = l + clist->length/4;
while (l < end) {
v.val.i = *l++;
if (find_tree(set, v))
@ -278,6 +295,30 @@ clist_match_set(struct adata *clist, struct f_tree *set)
return 0;
}
static int
eclist_match_set(struct adata *list, struct f_tree *set)
{
if (!list)
return 0;
if (!eclist_set_type(set))
return CMP_ERROR;
struct f_val v;
u32 *l = int_set_get_data(list);
int len = int_set_get_size(list);
int i;
v.type = T_EC;
for (i = 0; i < len; i += 2) {
v.val.ec = ec_get(l, i);
if (find_tree(set, v))
return 1;
}
return 0;
}
static struct adata *
clist_filter(struct linpool *pool, struct adata *clist, struct f_tree *set, int pos)
{
@ -302,8 +343,39 @@ clist_filter(struct linpool *pool, struct adata *clist, struct f_tree *set, int
if (nl == clist->length)
return clist;
struct adata *res = lp_alloc(pool, sizeof(struct adata) + nl);
res->length = nl;
struct adata *res = adata_empty(pool, nl);
memcpy(res->data, tmp, nl);
return res;
}
static struct adata *
eclist_filter(struct linpool *pool, struct adata *list, struct f_tree *set, int pos)
{
if (!list)
return NULL;
struct f_val v;
int len = int_set_get_size(list);
u32 *l = int_set_get_data(list);
u32 tmp[len];
u32 *k = tmp;
int i;
v.type = T_EC;
for (i = 0; i < len; i += 2) {
v.val.ec = ec_get(l, i);
if (pos == !!find_tree(set, v)) { /* pos && find_tree || !pos && !find_tree */
*k++ = l[i];
*k++ = l[i+1];
}
}
int nl = (k - tmp) * 4;
if (nl == list->length)
return list;
struct adata *res = adata_empty(pool, nl);
memcpy(res->data, tmp, nl);
return res;
}
@ -332,6 +404,9 @@ val_in_range(struct f_val v1, struct f_val v2)
if ((v1.type == T_CLIST) && (v2.type == T_SET))
return clist_match_set(v1.val.ad, v2.val.t);
if ((v1.type == T_ECLIST) && (v2.type == T_SET))
return eclist_match_set(v1.val.ad, v2.val.t);
if (v2.type == T_SET)
switch (v1.type) {
case T_ENUM:
@ -339,6 +414,7 @@ val_in_range(struct f_val v1, struct f_val v2)
case T_PAIR:
case T_QUAD:
case T_IP:
case T_EC:
{
struct f_tree *n;
n = find_tree(v2.val.t, v1);
@ -397,11 +473,13 @@ val_print(struct f_val v)
case T_PREFIX: logn("%I/%d", v.val.px.ip, v.val.px.len); return;
case T_PAIR: logn("(%d,%d)", v.val.i >> 16, v.val.i & 0xffff); return;
case T_QUAD: logn("%R", v.val.i); return;
case T_EC: ec_format(buf2, v.val.ec); logn("%s", buf2); return;
case T_PREFIX_SET: trie_print(v.val.ti); return;
case T_SET: tree_print(v.val.t); return;
case T_ENUM: logn("(enum %x)%d", v.type, v.val.i); return;
case T_PATH: as_path_format(v.val.ad, buf2, 1000); logn("(path %s)", buf2); return;
case T_CLIST: int_set_format(v.val.ad, 1, -1, buf2, 1000); logn("(clist %s)", buf2); return;
case T_ECLIST: ec_set_format(v.val.ad, -1, buf2, 1000); logn("(eclist %s)", buf2); return;
case T_PATH_MASK: pm_format(v.val.path_mask, buf2, 1000); logn("(pathmask%s)", buf2); return;
default: logn( "[unknown type %x]", v.type ); return;
}
@ -541,7 +619,7 @@ interpret(struct f_inst *what)
break;
case P('m','p'):
TWOARGS_C;
TWOARGS;
if ((v1.type != T_INT) || (v2.type != T_INT))
runtime( "Can't operate with value of non-integer type in pair constructor" );
u1 = v1.val.i;
@ -552,6 +630,53 @@ interpret(struct f_inst *what)
res.type = T_PAIR;
break;
case P('m','c'):
{
TWOARGS;
int check, ipv4_used;
u32 key, val;
if (v1.type == T_INT) {
ipv4_used = 0; key = v1.val.i;
}
else if (v1.type == T_QUAD) {
ipv4_used = 1; key = v1.val.i;
}
#ifndef IPV6
/* IP->Quad implicit conversion */
else if (v1.type == T_IP) {
ipv4_used = 1; key = ipa_to_u32(v1.val.px.ip);
}
#endif
else
runtime("Can't operate with key of non-integer/IPv4 type in EC constructor");
if (v2.type != T_INT)
runtime("Can't operate with value of non-integer type in EC constructor");
val = v2.val.i;
res.type = T_EC;
if (what->aux == EC_GENERIC) {
check = 0; res.val.ec = ec_generic(key, val);
}
else if (ipv4_used) {
check = 1; res.val.ec = ec_ip4(what->aux, key, val);
}
else if (key < 0x10000) {
check = 0; res.val.ec = ec_as2(what->aux, key, val);
}
else {
check = 1; res.val.ec = ec_as4(what->aux, key, val);
}
if (check && (val > 0xFFFF))
runtime("Can't operate with value out of bounds in EC constructor");
break;
}
/* Relational operators */
#define COMPARE(x) \
@ -723,9 +848,16 @@ interpret(struct f_inst *what)
/* A special case: undefined int_set looks like empty int_set */
if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_INT_SET) {
res.type = T_CLIST;
res.val.ad = adata_empty(f_pool);
res.val.ad = adata_empty(f_pool, 0);
break;
}
/* The same special case for ec_set */
else if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) {
res.type = T_ECLIST;
res.val.ad = adata_empty(f_pool, 0);
break;
}
/* Undefined value */
res.type = T_VOID;
break;
@ -757,6 +889,10 @@ interpret(struct f_inst *what)
res.type = T_CLIST;
res.val.ad = e->u.ptr;
break;
case EAF_TYPE_EC_SET:
res.type = T_ECLIST;
res.val.ad = e->u.ptr;
break;
case EAF_TYPE_UNDEF:
res.type = T_VOID;
break;
@ -802,7 +938,12 @@ interpret(struct f_inst *what)
break;
case EAF_TYPE_INT_SET:
if (v1.type != T_CLIST)
runtime( "Setting int set attribute to non-clist value" );
runtime( "Setting clist attribute to non-clist value" );
l->attrs[0].u.ptr = v1.val.ad;
break;
case EAF_TYPE_EC_SET:
if (v1.type != T_ECLIST)
runtime( "Setting eclist attribute to non-eclist value" );
l->attrs[0].u.ptr = v1.val.ad;
break;
case EAF_TYPE_UNDEF:
@ -926,7 +1067,7 @@ interpret(struct f_inst *what)
case 'E': /* Create empty attribute */
res.type = what->aux;
res.val.ad = adata_empty(f_pool);
res.val.ad = adata_empty(f_pool, 0);
break;
case P('A','p'): /* Path prepend */
TWOARGS;
@ -939,11 +1080,11 @@ interpret(struct f_inst *what)
res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i);
break;
case P('C','a'): /* Community list add or delete */
case P('C','a'): /* (Extended) Community list add or delete */
TWOARGS;
if (v1.type != T_CLIST)
runtime("Can't add/delete to non-clist");
if (v1.type == T_CLIST)
{
/* Community (or cluster) list */
struct f_val dummy;
int arg_set = 0;
i = 0;
@ -985,6 +1126,47 @@ interpret(struct f_inst *what)
default:
bug("unknown Ca operation");
}
}
else if (v1.type == T_ECLIST)
{
/* Extended community list */
int arg_set = 0;
/* v2.val is either EC or EC-set */
if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
arg_set = 1;
else if (v2.type != T_EC)
runtime("Can't add/delete non-pair");
res.type = T_ECLIST;
switch (what->aux)
{
case 'a':
if (arg_set)
runtime("Can't add set");
res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec);
break;
case 'd':
if (!arg_set)
res.val.ad = ec_set_del(f_pool, v1.val.ad, v2.val.ec);
else
res.val.ad = eclist_filter(f_pool, v1.val.ad, v2.val.t, 0);
break;
case 'f':
if (!arg_set)
runtime("Can't filter ec");
res.val.ad = eclist_filter(f_pool, v1.val.ad, v2.val.t, 1);
break;
default:
bug("unknown Ca operation");
}
}
else
runtime("Can't add/delete to non-(e)clist");
break;
default:

View file

@ -46,6 +46,7 @@ struct f_val {
int type;
union {
int i;
u64 ec;
/* ip_addr ip; Folded into prefix */
struct f_prefix px;
char *s;
@ -152,6 +153,8 @@ int tree_compare(const void *p1, const void *p2);
#define T_PATH_MASK 0x23 /* mask for BGP path */
#define T_PATH 0x24 /* BGP path */
#define T_CLIST 0x25 /* Community list */
#define T_ECLIST 0x26 /* Extended community list */
#define T_EC 0x27 /* Extended community value, u64 */
#define T_RETURN 0x40
#define T_SET 0x80

View file

@ -11,6 +11,7 @@ router id 62.168.0.1;
define xyzzy = (120+10);
define '1a-a1' = (20+10);
define one = 1;
define ten = 10;
function onef(int a)
{
@ -56,6 +57,7 @@ bgpmask pm1;
bgpmask pm2;
bgppath p2;
clist l;
eclist el;
{
pm1 = / 4 3 2 1 /;
pm2 = [= 4 3 2 1 =];
@ -118,6 +120,29 @@ clist l;
print "Community list (3,1) ", l;
l = delete( l, [(*,(onef(5)))] );
print "Community list empty ", l;
el = -- empty --;
el = add(el, (rt, 10, 20));
el = add(el, (ro, 10.20.30.40, 100));
el = add(el, (ro, 11.21.31.41.mask(16), 200));
print "EC list (rt, 10, 20) (ro, 10.20.30.40, 100) (ro, 11.21.0.0, 200):";
print el;
el = delete(el, (rt, 10, 20));
el = delete(el, (rt, 10, 30));
el = add(el, (unknown 2, ten, 1));
el = add(el, (unknown 5, ten, 1));
el = add(el, (rt, ten, one+one));
el = add(el, (rt, 10, 3));
el = add(el, (rt, 10, 4));
el = add(el, (rt, 10, 5));
el = add(el, (generic, 0x2000a, 3*ten));
el = delete(el, [(rt, 10, 2..ten)]);
print "EC list (ro, 10.20.30.40, 100) (ro, 11.21.0.0, 200) (rt, 10, 1) (unknown 0x5, 10, 1) (rt, 10, 30):";
print el;
el = filter(el, [(rt, 10, *)]);
print "EC list (rt, 10, 1) (rt, 10, 30): ", el;
print "Testing EC list, true: ", (rt, 10, 1) ~ el, " ", el ~ [(rt, 10, ten..40)];
print "Testing EC list, false: ", (rt, 10, 20) ~ el, " ", (ro, 10.20.30.40, 100) ~ el, " ", el ~ [(rt, 10, 35..40)], " ", el ~ [(ro, 10, *)];
}
function bla()
@ -175,11 +200,13 @@ prefix px;
ip p;
pair pp;
quad qq;
ec cc;
int set is;
int set is1;
int set is2;
int set is3;
pair set ps;
ec set ecs;
prefix set pxs;
string s;
{
@ -250,6 +277,19 @@ string s;
", 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];
cc = (rt, 12345, 200000);
print "Testing EC: (rt, 12345, 200000) = ", cc;
print "Testing EC: (ro, 100000, 20000) = ", (ro, 100000, 20000);
print "Testing EC: (rt, 10.20.30.40, 20000) = ", (rt, 10.20.30.40, 20000);
print " true: ", cc = (rt, 12345, 200000), " ", cc < (rt, 12345, 200010),
", false: ", cc = (rt, 12346, 200000), " ", cc = (ro, 12345, 200000), " ", cc > (rt, 12345, 200010);
ecs = [(rt, ten, (one+onef(0))*10), (ro, 100000, 100..200), (rt, 12345, *)];
print "EC set: ", ecs;
print "Testing EC set, true: ", (rt, 10, 20) ~ ecs, " ", (ro, 100000, 100) ~ ecs, " ", (ro, 100000, 200) ~ ecs,
" ", (rt, 12345, 0) ~ ecs, " ", cc ~ ecs, " ", (rt, 12345, 4000000) ~ ecs;
print "Testing EC set, false: ", (ro, 10, 20) ~ ecs, " ", (rt, 10, 21) ~ ecs, " ", (ro, 100000, 99) ~ ecs,
" ", (ro, 12345, 10) ~ ecs, " ", (rt, 12346, 0) ~ ecs, " ", (ro, 0.1.134.160, 150) ~ ecs;
s = "Hello";
print "Testing string: ", s, " true: ", s ~ "Hell*", " false: ", s ~ "ell*";

View file

@ -36,10 +36,11 @@ int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int siz
{
u32 *z = (u32 *) set->data;
byte *end = buf + size - 24;
int from2 = MAX(from, 0);
int to = set->length / 4;
int i;
for (i = MAX(from, 0); i < to; i++)
for (i = from2; i < to; i++)
{
if (buf > end)
{
@ -50,7 +51,7 @@ int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int siz
return i;
}
if (i > from)
if (i > from2)
*buf++ = ' ';
if (way)
@ -62,6 +63,84 @@ int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int siz
return 0;
}
int
ec_format(byte *buf, u64 ec)
{
u32 type, key, val;
char tbuf[16], *kind;
type = ec >> 48;
switch (type & 0xf0ff)
{
case EC_RT: kind = "rt"; break;
case EC_RO: kind = "ro"; break;
default:
kind = tbuf;
bsprintf(kind, "unknown 0x%x", type);
}
switch (ec >> 56)
{
/* RFC 4360 3.1. Two-Octet AS Specific Extended Community */
case 0x00:
case 0x40:
key = (ec >> 32) & 0xFFFF;
val = ec;
return bsprintf(buf, "(%s, %u, %u)", kind, key, val);
/* RFC 4360 3.2. IPv4 Address Specific Extended Community */
case 0x01:
case 0x41:
key = ec >> 16;
val = ec & 0xFFFF;
return bsprintf(buf, "(%s, %R, %u)", kind, key, val);
/* RFC 5668 4-Octet AS Specific BGP Extended Community */
case 0x02:
case 0x42:
key = ec >> 16;
val = ec & 0xFFFF;
return bsprintf(buf, "(%s, %u, %u)", kind, key, val);
/* Generic format for unknown kinds of extended communities */
default:
key = ec >> 32;
val = ec;
return bsprintf(buf, "(generic, 0x%x, 0x%x)", key, val);
}
}
int
ec_set_format(struct adata *set, int from, byte *buf, unsigned int size)
{
u32 *z = int_set_get_data(set);
byte *end = buf + size - 24;
int from2 = MAX(from, 0);
int to = int_set_get_size(set);
int i;
for (i = from2; i < to; i += 2)
{
if (buf > end)
{
if (from < 0)
strcpy(buf, " ...");
else
*buf = 0;
return i;
}
if (i > from2)
*buf++ = ' ';
buf += ec_format(buf, ec_get(z, i));
}
*buf = 0;
return 0;
}
int
int_set_contains(struct adata *list, u32 val)
{
@ -69,10 +148,32 @@ int_set_contains(struct adata *list, u32 val)
return 0;
u32 *l = (u32 *) list->data;
unsigned int i;
for (i=0; i<list->length/4; i++)
int len = int_set_get_size(list);
int i;
for (i = 0; i < len; i++)
if (*l++ == val)
return 1;
return 0;
}
int
ec_set_contains(struct adata *list, u64 val)
{
if (!list)
return 0;
u32 *l = int_set_get_data(list);
int len = int_set_get_size(list);
u32 eh = ec_hi(val);
u32 el = ec_lo(val);
int i;
for (i=0; i < len; i += 2)
if (l[i] == eh && l[i+1] == el)
return 1;
return 0;
}
@ -86,7 +187,7 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val)
return list;
len = list ? list->length : 0;
res = lp_alloc(pool, len + sizeof(struct adata) + 4);
res = lp_alloc(pool, sizeof(struct adata) + len + 4);
res->length = len + 4;
* (u32 *) res->data = val;
if (list)
@ -94,24 +195,72 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val)
return res;
}
struct adata *
ec_set_add(struct linpool *pool, struct adata *list, u64 val)
{
if (ec_set_contains(list, val))
return list;
int olen = list ? list->length : 0;
struct adata *res = lp_alloc(pool, sizeof(struct adata) + olen + 8);
res->length = olen + 8;
if (list)
memcpy(res->data, list->data, list->length);
u32 *l = (u32 *) (res->data + res->length - 8);
l[0] = ec_hi(val);
l[1] = ec_lo(val);
return res;
}
struct adata *
int_set_del(struct linpool *pool, struct adata *list, u32 val)
{
struct adata *res;
u32 *l, *k;
unsigned int i;
if (!int_set_contains(list, val))
return list;
res = lp_alloc(pool, list->length + sizeof(struct adata) - 4);
res->length = list->length-4;
struct adata *res;
res = lp_alloc(pool, sizeof(struct adata) + list->length - 4);
res->length = list->length - 4;
l = (u32 *) list->data;
k = (u32 *) res->data;
for (i=0; i<list->length/4; i++)
u32 *l = int_set_get_data(list);
u32 *k = int_set_get_data(res);
int len = int_set_get_size(list);
int i;
for (i = 0; i < len; i++)
if (l[i] != val)
*k++ = l[i];
return res;
}
struct adata *
ec_set_del(struct linpool *pool, struct adata *list, u64 val)
{
if (!ec_set_contains(list, val))
return list;
struct adata *res;
res = lp_alloc(pool, sizeof(struct adata) + list->length - 8);
res->length = list->length - 8;
u32 *l = int_set_get_data(list);
u32 *k = int_set_get_data(res);
int len = int_set_get_size(list);
u32 eh = ec_hi(val);
u32 el = ec_lo(val);
int i;
for (i=0; i < len; i += 2)
if (! (l[i] == eh && l[i+1] == el))
{
*k++ = l[i];
*k++ = l[i+1];
}
return res;
}

View file

@ -50,12 +50,52 @@ int as_path_match(struct adata *path, struct f_path_mask *mask);
/* a-set.c */
int int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int size);
struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val);
int int_set_contains(struct adata *list, u32 val);
struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val);
/* Extended Community subtypes (kinds) */
#define EC_RT 0x0002
#define EC_RO 0x0003
#define EC_GENERIC 0xFFFF
/* Transitive bit (for first u32 half of EC) */
#define EC_TBIT 0x40000000
static inline int int_set_get_size(struct adata *list)
{ return list->length / 4; }
static inline u32 *int_set_get_data(struct adata *list)
{ return (u32 *) list->data; }
static inline u32 ec_hi(u64 ec) { return ec >> 32; }
static inline u32 ec_lo(u64 ec) { return ec; }
static inline u64 ec_get(const u32 *l, int i)
{ return (((u64) l[i]) << 32) | l[i+1]; }
/* RFC 4360 3.1. Two-Octet AS Specific Extended Community */
static inline u64 ec_as2(u64 kind, u64 key, u64 val)
{ return ((kind | 0x0000) << 48) | (key << 32) | val; }
/* RFC 5668 4-Octet AS Specific BGP Extended Community */
static inline u64 ec_as4(u64 kind, u64 key, u64 val)
{ return ((kind | 0x0200) << 48) | (key << 16) | val; }
/* RFC 4360 3.2. IPv4 Address Specific Extended Community */
static inline u64 ec_ip4(u64 kind, u64 key, u64 val)
{ return ((kind | 0x0100) << 48) | (key << 16) | val; }
static inline u64 ec_generic(u64 key, u64 val)
{ return (key << 32) | val; }
int int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int size);
int ec_format(byte *buf, u64 ec);
int ec_set_format(struct adata *set, int from, byte *buf, unsigned int size);
int int_set_contains(struct adata *list, u32 val);
int ec_set_contains(struct adata *list, u64 val);
struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val);
struct adata *ec_set_add(struct linpool *pool, struct adata *list, u64 val);
struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val);
struct adata *ec_set_del(struct linpool *pool, struct adata *list, u64 val);
#endif

View file

@ -363,6 +363,7 @@ typedef struct eattr {
#define EAF_TYPE_ROUTER_ID 0x05 /* Router ID (IPv4 address) */
#define EAF_TYPE_AS_PATH 0x06 /* BGP AS path (encoding per RFC 1771:4.3) */
#define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */
#define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */
#define EAF_TYPE_UNDEF 0x0f /* `force undefined' entry */
#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */
#define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */

View file

@ -465,6 +465,18 @@ ea_show_int_set(struct cli *c, struct adata *ad, int way, byte *pos, byte *buf,
}
}
static inline void
ea_show_ec_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end)
{
int i = ec_set_format(ad, 0, pos, end - pos);
cli_printf(c, -1012, "%s", buf);
while (i)
{
i = ec_set_format(ad, i, buf, end - buf - 1);
cli_printf(c, -1012, "\t%s", buf);
}
}
/**
* ea_show - print an &eattr to CLI
* @c: destination CLI
@ -523,6 +535,9 @@ ea_show(struct cli *c, eattr *e)
case EAF_TYPE_INT_SET:
ea_show_int_set(c, ad, 1, pos, buf, end);
return;
case EAF_TYPE_EC_SET:
ea_show_ec_set(c, ad, pos, buf, end);
return;
case EAF_TYPE_UNDEF:
default:
bsprintf(pos, "<type %02x>", e->type);

View file

@ -247,7 +247,6 @@ bgp_check_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
return ((len % 4) == 0) ? 0 : WITHDRAW;
}
static int
bgp_check_cluster_list(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
{
@ -281,6 +280,13 @@ bgp_check_unreach_nlri(struct bgp_proto *p UNUSED, byte *a UNUSED, int len UNUSE
return IGNORE;
}
static int
bgp_check_ext_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
{
return ((len % 8) == 0) ? 0 : WITHDRAW;
}
static struct attr_desc bgp_attr_table[] = {
{ NULL, -1, 0, 0, 0, /* Undefined */
NULL, NULL },
@ -311,7 +317,8 @@ static struct attr_desc bgp_attr_table[] = {
bgp_check_reach_nlri, NULL },
{ "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */
bgp_check_unreach_nlri, NULL },
{ .name = NULL }, /* BA_EXTENDED_COMM */
{ "ext_community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_EC_SET, 1, /* BA_EXT_COMMUNITY */
bgp_check_ext_community, NULL },
{ "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
NULL, NULL },
{ "as4_aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
@ -468,7 +475,7 @@ bgp_get_attr_len(eattr *a)
unsigned int
bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
{
unsigned int i, code, flags;
unsigned int i, code, type, flags;
byte *start = w;
int len, rv;
@ -477,6 +484,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
eattr *a = &attrs->attrs[i];
ASSERT(EA_PROTO(a->id) == EAP_BGP);
code = EA_ID(a->id);
#ifdef IPV6
/* When talking multiprotocol BGP, the NEXT_HOP attributes are used only temporarily. */
if (code == BA_NEXT_HOP)
@ -559,11 +567,12 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
/* Standard path continues here ... */
type = a->type & EAF_TYPE_MASK;
flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL);
len = bgp_get_attr_len(a);
/* Skip empty int sets */
if (((a->type & EAF_TYPE_MASK) == EAF_TYPE_INT_SET) && (len == 0))
/* Skip empty sets */
if (((type == EAF_TYPE_INT_SET) || (type == EAF_TYPE_EC_SET)) && (len == 0))
continue;
if (remains < len + 4)
@ -572,7 +581,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
rv = bgp_encode_attr_hdr(w, flags, code, len);
ADVANCE(w, remains, rv);
switch (a->type & EAF_TYPE_MASK)
switch (type)
{
case EAF_TYPE_INT:
case EAF_TYPE_ROUTER_ID:
@ -589,8 +598,9 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
break;
}
case EAF_TYPE_INT_SET:
case EAF_TYPE_EC_SET:
{
u32 *z = (u32 *)a->u.ptr->data;
u32 *z = int_set_get_data(a->u.ptr);
int i;
for(i=0; i<len; i+=4)
put_u32(w+i, *z++);
@ -624,13 +634,50 @@ bgp_compare_u32(const u32 *x, const u32 *y)
return (*x < *y) ? -1 : (*x > *y) ? 1 : 0;
}
static void
bgp_normalize_set(u32 *dest, u32 *src, unsigned cnt)
static inline void
bgp_normalize_int_set(u32 *dest, u32 *src, unsigned cnt)
{
memcpy(dest, src, sizeof(u32) * cnt);
qsort(dest, cnt, sizeof(u32), (int(*)(const void *, const void *)) bgp_compare_u32);
}
static int
bgp_compare_ec(const u32 *xp, const u32 *yp)
{
u64 x = ec_get(xp, 0);
u64 y = ec_get(yp, 0);
return (x < y) ? -1 : (x > y) ? 1 : 0;
}
static inline void
bgp_normalize_ec_set(struct adata *ad, u32 *src, int internal)
{
u32 *dst = int_set_get_data(ad);
/* Remove non-transitive communities (EC_TBIT active) on external sessions */
if (! internal)
{
int len = int_set_get_size(ad);
u32 *t = dst;
int i;
for (i=0; i < len; i += 2)
{
if (src[i] & EC_TBIT)
continue;
*t++ = src[i];
*t++ = src[i+1];
}
ad->length = (t - dst) * 4;
}
else
memcpy(dst, src, ad->length);
qsort(dst, ad->length / 8, 8, (int(*)(const void *, const void *)) bgp_compare_ec);
}
static void
bgp_rehash_buckets(struct bgp_proto *p)
{
@ -763,7 +810,15 @@ bgp_get_bucket(struct bgp_proto *p, net *n, ea_list *attrs, int originate)
{
struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length);
z->length = d->u.ptr->length;
bgp_normalize_set((u32 *) z->data, (u32 *) d->u.ptr->data, z->length / 4);
bgp_normalize_int_set((u32 *) z->data, (u32 *) d->u.ptr->data, z->length / 4);
d->u.ptr = z;
break;
}
case EAF_TYPE_EC_SET:
{
struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length);
z->length = d->u.ptr->length;
bgp_normalize_ec_set(z, (u32 *) d->u.ptr->data, p->is_internal);
d->u.ptr = z;
break;
}
@ -1447,6 +1502,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
ipa_ntoh(*(ip_addr *)ad->data);
break;
case EAF_TYPE_INT_SET:
case EAF_TYPE_EC_SET:
{
u32 *z = (u32 *) ad->data;
for(i=0; i<ad->length/4; i++)

View file

@ -236,7 +236,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
#define BA_RCID_PATH 0x0d
#define BA_MP_REACH_NLRI 0x0e /* [RFC2283] */
#define BA_MP_UNREACH_NLRI 0x0f
#define BA_EXTENDED_COMM 0x10 /* draft-ramachandra-bgp-ext-communities */
#define BA_EXT_COMMUNITY 0x10 /* [RFC4360] */
#define BA_AS4_PATH 0x11 /* [RFC4893] */
#define BA_AS4_AGGREGATOR 0x12

View file

@ -21,11 +21,11 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
PATH, METRIC, ERROR, START, DELAY, FORGET, WAIT, ENABLE,
DISABLE, AFTER, BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN,
BGP_NEXT_HOP, 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, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP, TABLE,
GATEWAY, DIRECT, RECURSIVE, MED)
BGP_EXT_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, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
TABLE, GATEWAY, DIRECT, RECURSIVE, MED)
CF_GRAMMAR
@ -120,6 +120,9 @@ 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_ADDTO(dynamic_attr, BGP_EXT_COMMUNITY
{ $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(EAP_BGP, BA_EXT_COMMUNITY)); })
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)

View file

@ -18,6 +18,9 @@
/* 32-bit integer type */
#define INTEGER_32 ?
/* 64-bit integer type */
#define INTEGER_64 ?
/* CPU endianity */
#undef CPU_LITTLE_ENDIAN
#undef CPU_BIG_ENDIAN

View file

@ -30,6 +30,8 @@ typedef INTEGER_16 s16;
typedef unsigned INTEGER_16 u16;
typedef INTEGER_32 s32;
typedef unsigned INTEGER_32 u32;
typedef INTEGER_64 s64;
typedef unsigned INTEGER_64 u64;
typedef u8 byte;
typedef u16 word;