BGP Extended communities.
This commit is contained in:
parent
bde872bba7
commit
42a0c05408
14 changed files with 793 additions and 110 deletions
5
aclocal.m4
vendored
5
aclocal.m4
vendored
|
@ -6,7 +6,8 @@ AC_DEFUN(BIRD_CHECK_INTEGERS,
|
||||||
AC_CHECK_SIZEOF(short int, 0)
|
AC_CHECK_SIZEOF(short int, 0)
|
||||||
AC_CHECK_SIZEOF(int, 0)
|
AC_CHECK_SIZEOF(int, 0)
|
||||||
AC_CHECK_SIZEOF(long 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`
|
bits=`expr $size "*" 8`
|
||||||
AC_MSG_CHECKING([for $bits-bit type])
|
AC_MSG_CHECKING([for $bits-bit type])
|
||||||
if test $ac_cv_sizeof_int = $size ; then
|
if test $ac_cv_sizeof_int = $size ; then
|
||||||
|
@ -17,6 +18,8 @@ for size in 1 2 4 ; do
|
||||||
res="short int"
|
res="short int"
|
||||||
elif test $ac_cv_sizeof_long_int = $size ; then
|
elif test $ac_cv_sizeof_long_int = $size ; then
|
||||||
res="long int"
|
res="long int"
|
||||||
|
elif test $ac_cv_sizeof_long_long_int = $size ; then
|
||||||
|
res="long long int"
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT([not found])
|
AC_MSG_RESULT([not found])
|
||||||
AC_MSG_ERROR([Cannot find $bits-bit integer type.])
|
AC_MSG_ERROR([Cannot find $bits-bit integer type.])
|
||||||
|
|
238
filter/config.Y
238
filter/config.Y
|
@ -75,13 +75,185 @@ f_new_pair_set(int fa, int ta, int fb, int tb)
|
||||||
return lst;
|
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_DECLS
|
||||||
|
|
||||||
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||||
ACCEPT, REJECT, ERROR, QUITBIRD,
|
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,
|
IF, THEN, ELSE, CASE,
|
||||||
TRUE, FALSE,
|
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
||||||
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, PREFERENCE,
|
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, PREFERENCE,
|
||||||
LEN,
|
LEN,
|
||||||
DEFINED,
|
DEFINED,
|
||||||
|
@ -93,11 +265,11 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||||
%nonassoc THEN
|
%nonassoc THEN
|
||||||
%nonassoc ELSE
|
%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 <f> filter filter_body where_filter
|
||||||
%type <i> type break_command pair_expr
|
%type <i> type break_command pair_expr ec_kind
|
||||||
%type <i32> pair_atom
|
%type <i32> pair_atom ec_expr
|
||||||
%type <e> pair_item set_item switch_item set_items switch_items switch_body
|
%type <e> pair_item ec_item set_item switch_item set_items switch_items switch_body
|
||||||
%type <trie> fprefix_set
|
%type <trie> fprefix_set
|
||||||
%type <v> set_atom switch_atom fprefix fprefix_s fipa
|
%type <v> set_atom switch_atom fprefix fprefix_s fipa
|
||||||
%type <s> decls declsn one_decl function_params
|
%type <s> decls declsn one_decl function_params
|
||||||
|
@ -128,15 +300,18 @@ type:
|
||||||
| PREFIX { $$ = T_PREFIX; }
|
| PREFIX { $$ = T_PREFIX; }
|
||||||
| PAIR { $$ = T_PAIR; }
|
| PAIR { $$ = T_PAIR; }
|
||||||
| QUAD { $$ = T_QUAD; }
|
| QUAD { $$ = T_QUAD; }
|
||||||
|
| EC { $$ = T_EC; }
|
||||||
| STRING { $$ = T_STRING; }
|
| STRING { $$ = T_STRING; }
|
||||||
| BGPMASK { $$ = T_PATH_MASK; }
|
| BGPMASK { $$ = T_PATH_MASK; }
|
||||||
| BGPPATH { $$ = T_PATH; }
|
| BGPPATH { $$ = T_PATH; }
|
||||||
| CLIST { $$ = T_CLIST; }
|
| CLIST { $$ = T_CLIST; }
|
||||||
|
| ECLIST { $$ = T_ECLIST; }
|
||||||
| type SET {
|
| type SET {
|
||||||
switch ($1) {
|
switch ($1) {
|
||||||
case T_INT:
|
case T_INT:
|
||||||
case T_PAIR:
|
case T_PAIR:
|
||||||
case T_QUAD:
|
case T_QUAD:
|
||||||
|
case T_EC:
|
||||||
case T_IP:
|
case T_IP:
|
||||||
$$ = T_SET;
|
$$ = T_SET;
|
||||||
break;
|
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:
|
set_item:
|
||||||
pair_item
|
pair_item
|
||||||
|
| ec_item
|
||||||
| set_atom { $$ = f_new_item($1, $1); }
|
| set_atom { $$ = f_new_item($1, $1); }
|
||||||
| set_atom DDOT set_atom { $$ = f_new_item($1, $3); }
|
| set_atom DDOT set_atom { $$ = f_new_item($1, $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
switch_item:
|
switch_item:
|
||||||
pair_item
|
pair_item
|
||||||
|
| ec_item
|
||||||
| switch_atom { $$ = f_new_item($1, $1); }
|
| switch_atom { $$ = f_new_item($1, $1); }
|
||||||
| switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); }
|
| switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); }
|
||||||
;
|
;
|
||||||
|
@ -411,20 +604,6 @@ bgp_path_tail2:
|
||||||
| { $$ = NULL; }
|
| { $$ = 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:
|
constant:
|
||||||
NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; }
|
NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; }
|
||||||
| TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->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; }
|
| 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.
|
* Maybe there are no dynamic attributes defined by protocols.
|
||||||
|
@ -490,6 +674,7 @@ symbol:
|
||||||
case SYM_VARIABLE | T_INT:
|
case SYM_VARIABLE | T_INT:
|
||||||
case SYM_VARIABLE | T_PAIR:
|
case SYM_VARIABLE | T_PAIR:
|
||||||
case SYM_VARIABLE | T_QUAD:
|
case SYM_VARIABLE | T_QUAD:
|
||||||
|
case SYM_VARIABLE | T_EC:
|
||||||
case SYM_VARIABLE | T_STRING:
|
case SYM_VARIABLE | T_STRING:
|
||||||
case SYM_VARIABLE | T_IP:
|
case SYM_VARIABLE | T_IP:
|
||||||
case SYM_VARIABLE | T_PREFIX:
|
case SYM_VARIABLE | T_PREFIX:
|
||||||
|
@ -498,6 +683,7 @@ symbol:
|
||||||
case SYM_VARIABLE | T_PATH:
|
case SYM_VARIABLE | T_PATH:
|
||||||
case SYM_VARIABLE | T_PATH_MASK:
|
case SYM_VARIABLE | T_PATH_MASK:
|
||||||
case SYM_VARIABLE | T_CLIST:
|
case SYM_VARIABLE | T_CLIST:
|
||||||
|
case SYM_VARIABLE | T_ECLIST:
|
||||||
$$->code = 'V';
|
$$->code = 'V';
|
||||||
$$->a1.p = $1->def;
|
$$->a1.p = $1->def;
|
||||||
$$->a2.p = $1->name;
|
$$->a2.p = $1->name;
|
||||||
|
@ -539,7 +725,7 @@ term:
|
||||||
|
|
||||||
| symbol { $$ = $1; }
|
| symbol { $$ = $1; }
|
||||||
| constant { $$ = $1; }
|
| constant { $$ = $1; }
|
||||||
| dpair { $$ = $1; }
|
| constructor { $$ = $1; }
|
||||||
|
|
||||||
| PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; }
|
| 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_PATH; }
|
||||||
| '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
|
| '-' 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; }
|
| 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'; }
|
| 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'; }
|
| DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
|
||||||
|
@ -702,12 +889,11 @@ cmd:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
| rtadot dynamic_attr '.' EMPTY ';'
|
| rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
|
||||||
{ 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 '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); }
|
| 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 '.' 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 ); }
|
| rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); }
|
||||||
| rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); }
|
| rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); }
|
||||||
;
|
;
|
||||||
|
|
||||||
CF_END
|
CF_END
|
||||||
|
|
280
filter/filter.c
280
filter/filter.c
|
@ -51,10 +51,10 @@
|
||||||
#define CMP_ERROR 999
|
#define CMP_ERROR 999
|
||||||
|
|
||||||
static struct adata *
|
static struct adata *
|
||||||
adata_empty(struct linpool *pool)
|
adata_empty(struct linpool *pool, int l)
|
||||||
{
|
{
|
||||||
struct adata *res = lp_alloc(pool, sizeof(struct adata));
|
struct adata *res = lp_alloc(pool, sizeof(struct adata) + l);
|
||||||
res->length = 0;
|
res->length = l;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +126,13 @@ static inline int uint_cmp(unsigned int i1, unsigned int i2)
|
||||||
else return 1;
|
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
|
* val_compare - compare two values
|
||||||
* @v1: first value
|
* @v1: first value
|
||||||
|
@ -167,6 +174,8 @@ val_compare(struct f_val v1, struct f_val v2)
|
||||||
case T_PAIR:
|
case T_PAIR:
|
||||||
case T_QUAD:
|
case T_QUAD:
|
||||||
return uint_cmp(v1.val.i, v2.val.i);
|
return uint_cmp(v1.val.i, v2.val.i);
|
||||||
|
case T_EC:
|
||||||
|
return u64_cmp(v1.val.ec, v2.val.ec);
|
||||||
case T_IP:
|
case T_IP:
|
||||||
return ipa_compare(v1.val.px.ip, v2.val.px.ip);
|
return ipa_compare(v1.val.px.ip, v2.val.px.ip);
|
||||||
case T_PREFIX:
|
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))
|
if ((v1.type == T_IP) && (v2.type == T_CLIST))
|
||||||
return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.px.ip));
|
return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.px.ip));
|
||||||
#endif
|
#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))
|
if ((v1.type == T_STRING) && (v2.type == T_STRING))
|
||||||
return patmatch(v2.val.s, v1.val.s);
|
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
|
static int
|
||||||
clist_match_set(struct adata *clist, struct f_tree *set)
|
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 *l = (u32 *) clist->data;
|
||||||
u32 *end = l + clist->length/4;
|
u32 *end = l + clist->length/4;
|
||||||
|
|
||||||
while (l < end) {
|
while (l < end) {
|
||||||
v.val.i = *l++;
|
v.val.i = *l++;
|
||||||
if (find_tree(set, v))
|
if (find_tree(set, v))
|
||||||
|
@ -278,6 +295,30 @@ clist_match_set(struct adata *clist, struct f_tree *set)
|
||||||
return 0;
|
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 *
|
static struct adata *
|
||||||
clist_filter(struct linpool *pool, struct adata *clist, struct f_tree *set, int pos)
|
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)
|
if (nl == clist->length)
|
||||||
return clist;
|
return clist;
|
||||||
|
|
||||||
struct adata *res = lp_alloc(pool, sizeof(struct adata) + nl);
|
struct adata *res = adata_empty(pool, nl);
|
||||||
res->length = 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);
|
memcpy(res->data, tmp, nl);
|
||||||
return res;
|
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))
|
if ((v1.type == T_CLIST) && (v2.type == T_SET))
|
||||||
return clist_match_set(v1.val.ad, v2.val.t);
|
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)
|
if (v2.type == T_SET)
|
||||||
switch (v1.type) {
|
switch (v1.type) {
|
||||||
case T_ENUM:
|
case T_ENUM:
|
||||||
|
@ -339,6 +414,7 @@ val_in_range(struct f_val v1, struct f_val v2)
|
||||||
case T_PAIR:
|
case T_PAIR:
|
||||||
case T_QUAD:
|
case T_QUAD:
|
||||||
case T_IP:
|
case T_IP:
|
||||||
|
case T_EC:
|
||||||
{
|
{
|
||||||
struct f_tree *n;
|
struct f_tree *n;
|
||||||
n = find_tree(v2.val.t, v1);
|
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_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_PAIR: logn("(%d,%d)", v.val.i >> 16, v.val.i & 0xffff); return;
|
||||||
case T_QUAD: logn("%R", v.val.i); 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_PREFIX_SET: trie_print(v.val.ti); return;
|
||||||
case T_SET: tree_print(v.val.t); 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_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_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_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;
|
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;
|
default: logn( "[unknown type %x]", v.type ); return;
|
||||||
}
|
}
|
||||||
|
@ -541,7 +619,7 @@ interpret(struct f_inst *what)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('m','p'):
|
case P('m','p'):
|
||||||
TWOARGS_C;
|
TWOARGS;
|
||||||
if ((v1.type != T_INT) || (v2.type != T_INT))
|
if ((v1.type != T_INT) || (v2.type != T_INT))
|
||||||
runtime( "Can't operate with value of non-integer type in pair constructor" );
|
runtime( "Can't operate with value of non-integer type in pair constructor" );
|
||||||
u1 = v1.val.i;
|
u1 = v1.val.i;
|
||||||
|
@ -552,6 +630,53 @@ interpret(struct f_inst *what)
|
||||||
res.type = T_PAIR;
|
res.type = T_PAIR;
|
||||||
break;
|
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 */
|
/* Relational operators */
|
||||||
|
|
||||||
#define COMPARE(x) \
|
#define COMPARE(x) \
|
||||||
|
@ -723,9 +848,16 @@ interpret(struct f_inst *what)
|
||||||
/* A special case: undefined int_set looks like empty int_set */
|
/* A special case: undefined int_set looks like empty int_set */
|
||||||
if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_INT_SET) {
|
if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_INT_SET) {
|
||||||
res.type = T_CLIST;
|
res.type = T_CLIST;
|
||||||
res.val.ad = adata_empty(f_pool);
|
res.val.ad = adata_empty(f_pool, 0);
|
||||||
break;
|
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 */
|
/* Undefined value */
|
||||||
res.type = T_VOID;
|
res.type = T_VOID;
|
||||||
break;
|
break;
|
||||||
|
@ -757,6 +889,10 @@ interpret(struct f_inst *what)
|
||||||
res.type = T_CLIST;
|
res.type = T_CLIST;
|
||||||
res.val.ad = e->u.ptr;
|
res.val.ad = e->u.ptr;
|
||||||
break;
|
break;
|
||||||
|
case EAF_TYPE_EC_SET:
|
||||||
|
res.type = T_ECLIST;
|
||||||
|
res.val.ad = e->u.ptr;
|
||||||
|
break;
|
||||||
case EAF_TYPE_UNDEF:
|
case EAF_TYPE_UNDEF:
|
||||||
res.type = T_VOID;
|
res.type = T_VOID;
|
||||||
break;
|
break;
|
||||||
|
@ -802,7 +938,12 @@ interpret(struct f_inst *what)
|
||||||
break;
|
break;
|
||||||
case EAF_TYPE_INT_SET:
|
case EAF_TYPE_INT_SET:
|
||||||
if (v1.type != T_CLIST)
|
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;
|
l->attrs[0].u.ptr = v1.val.ad;
|
||||||
break;
|
break;
|
||||||
case EAF_TYPE_UNDEF:
|
case EAF_TYPE_UNDEF:
|
||||||
|
@ -926,7 +1067,7 @@ interpret(struct f_inst *what)
|
||||||
|
|
||||||
case 'E': /* Create empty attribute */
|
case 'E': /* Create empty attribute */
|
||||||
res.type = what->aux;
|
res.type = what->aux;
|
||||||
res.val.ad = adata_empty(f_pool);
|
res.val.ad = adata_empty(f_pool, 0);
|
||||||
break;
|
break;
|
||||||
case P('A','p'): /* Path prepend */
|
case P('A','p'): /* Path prepend */
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
|
@ -939,52 +1080,93 @@ interpret(struct f_inst *what)
|
||||||
res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i);
|
res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('C','a'): /* Community list add or delete */
|
case P('C','a'): /* (Extended) Community list add or delete */
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
if (v1.type != T_CLIST)
|
if (v1.type == T_CLIST)
|
||||||
runtime("Can't add/delete to non-clist");
|
|
||||||
|
|
||||||
struct f_val dummy;
|
|
||||||
int arg_set = 0;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
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 if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
|
|
||||||
arg_set = 1;
|
|
||||||
else
|
|
||||||
runtime("Can't add/delete non-pair");
|
|
||||||
|
|
||||||
res.type = T_CLIST;
|
|
||||||
switch (what->aux)
|
|
||||||
{
|
{
|
||||||
case 'a':
|
/* Community (or cluster) list */
|
||||||
if (arg_set)
|
struct f_val dummy;
|
||||||
runtime("Can't add set");
|
int arg_set = 0;
|
||||||
res.val.ad = int_set_add(f_pool, v1.val.ad, i);
|
i = 0;
|
||||||
break;
|
|
||||||
|
if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
|
||||||
case 'd':
|
i = v2.val.i;
|
||||||
if (!arg_set)
|
#ifndef IPV6
|
||||||
res.val.ad = int_set_del(f_pool, v1.val.ad, i);
|
/* IP->Quad implicit conversion */
|
||||||
|
else if (v2.type == T_IP)
|
||||||
|
i = ipa_to_u32(v2.val.px.ip);
|
||||||
|
#endif
|
||||||
|
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
|
||||||
|
arg_set = 1;
|
||||||
else
|
else
|
||||||
res.val.ad = clist_filter(f_pool, v1.val.ad, v2.val.t, 0);
|
runtime("Can't add/delete non-pair");
|
||||||
break;
|
|
||||||
|
|
||||||
case 'f':
|
res.type = T_CLIST;
|
||||||
if (!arg_set)
|
switch (what->aux)
|
||||||
runtime("Can't filter pair");
|
{
|
||||||
res.val.ad = clist_filter(f_pool, v1.val.ad, v2.val.t, 1);
|
case 'a':
|
||||||
break;
|
if (arg_set)
|
||||||
|
runtime("Can't add set");
|
||||||
|
res.val.ad = int_set_add(f_pool, v1.val.ad, i);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
if (!arg_set)
|
||||||
|
res.val.ad = int_set_del(f_pool, v1.val.ad, i);
|
||||||
|
else
|
||||||
|
res.val.ad = clist_filter(f_pool, v1.val.ad, v2.val.t, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
case 'f':
|
||||||
bug("unknown Ca operation");
|
if (!arg_set)
|
||||||
|
runtime("Can't filter pair");
|
||||||
|
res.val.ad = clist_filter(f_pool, v1.val.ad, v2.val.t, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -46,6 +46,7 @@ struct f_val {
|
||||||
int type;
|
int type;
|
||||||
union {
|
union {
|
||||||
int i;
|
int i;
|
||||||
|
u64 ec;
|
||||||
/* ip_addr ip; Folded into prefix */
|
/* ip_addr ip; Folded into prefix */
|
||||||
struct f_prefix px;
|
struct f_prefix px;
|
||||||
char *s;
|
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_MASK 0x23 /* mask for BGP path */
|
||||||
#define T_PATH 0x24 /* BGP path */
|
#define T_PATH 0x24 /* BGP path */
|
||||||
#define T_CLIST 0x25 /* Community list */
|
#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_RETURN 0x40
|
||||||
#define T_SET 0x80
|
#define T_SET 0x80
|
||||||
|
|
|
@ -11,6 +11,7 @@ router id 62.168.0.1;
|
||||||
define xyzzy = (120+10);
|
define xyzzy = (120+10);
|
||||||
define '1a-a1' = (20+10);
|
define '1a-a1' = (20+10);
|
||||||
define one = 1;
|
define one = 1;
|
||||||
|
define ten = 10;
|
||||||
|
|
||||||
function onef(int a)
|
function onef(int a)
|
||||||
{
|
{
|
||||||
|
@ -56,6 +57,7 @@ bgpmask pm1;
|
||||||
bgpmask pm2;
|
bgpmask pm2;
|
||||||
bgppath p2;
|
bgppath p2;
|
||||||
clist l;
|
clist l;
|
||||||
|
eclist el;
|
||||||
{
|
{
|
||||||
pm1 = / 4 3 2 1 /;
|
pm1 = / 4 3 2 1 /;
|
||||||
pm2 = [= 4 3 2 1 =];
|
pm2 = [= 4 3 2 1 =];
|
||||||
|
@ -118,6 +120,29 @@ clist l;
|
||||||
print "Community list (3,1) ", l;
|
print "Community list (3,1) ", l;
|
||||||
l = delete( l, [(*,(onef(5)))] );
|
l = delete( l, [(*,(onef(5)))] );
|
||||||
print "Community list empty ", l;
|
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()
|
function bla()
|
||||||
|
@ -175,11 +200,13 @@ prefix px;
|
||||||
ip p;
|
ip p;
|
||||||
pair pp;
|
pair pp;
|
||||||
quad qq;
|
quad qq;
|
||||||
|
ec cc;
|
||||||
int set is;
|
int set is;
|
||||||
int set is1;
|
int set is1;
|
||||||
int set is2;
|
int set is2;
|
||||||
int set is3;
|
int set is3;
|
||||||
pair set ps;
|
pair set ps;
|
||||||
|
ec set ecs;
|
||||||
prefix set pxs;
|
prefix set pxs;
|
||||||
string s;
|
string s;
|
||||||
{
|
{
|
||||||
|
@ -250,6 +277,19 @@ string s;
|
||||||
", true: ", qq = 1.2.3.4, " ", qq ~ [1.2.3.4, 5.6.7.8],
|
", 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];
|
", 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";
|
s = "Hello";
|
||||||
print "Testing string: ", s, " true: ", s ~ "Hell*", " false: ", s ~ "ell*";
|
print "Testing string: ", s, " true: ", s ~ "Hell*", " false: ", s ~ "ell*";
|
||||||
|
|
177
nest/a-set.c
177
nest/a-set.c
|
@ -36,10 +36,11 @@ int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int siz
|
||||||
{
|
{
|
||||||
u32 *z = (u32 *) set->data;
|
u32 *z = (u32 *) set->data;
|
||||||
byte *end = buf + size - 24;
|
byte *end = buf + size - 24;
|
||||||
|
int from2 = MAX(from, 0);
|
||||||
int to = set->length / 4;
|
int to = set->length / 4;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = MAX(from, 0); i < to; i++)
|
for (i = from2; i < to; i++)
|
||||||
{
|
{
|
||||||
if (buf > end)
|
if (buf > end)
|
||||||
{
|
{
|
||||||
|
@ -50,7 +51,7 @@ int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int siz
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i > from)
|
if (i > from2)
|
||||||
*buf++ = ' ';
|
*buf++ = ' ';
|
||||||
|
|
||||||
if (way)
|
if (way)
|
||||||
|
@ -62,6 +63,84 @@ int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int siz
|
||||||
return 0;
|
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
|
||||||
int_set_contains(struct adata *list, u32 val)
|
int_set_contains(struct adata *list, u32 val)
|
||||||
{
|
{
|
||||||
|
@ -69,10 +148,32 @@ int_set_contains(struct adata *list, u32 val)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
u32 *l = (u32 *) list->data;
|
u32 *l = (u32 *) list->data;
|
||||||
unsigned int i;
|
int len = int_set_get_size(list);
|
||||||
for (i=0; i<list->length/4; i++)
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
if (*l++ == val)
|
if (*l++ == val)
|
||||||
return 1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +187,7 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val)
|
||||||
return list;
|
return list;
|
||||||
|
|
||||||
len = list ? list->length : 0;
|
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;
|
res->length = len + 4;
|
||||||
* (u32 *) res->data = val;
|
* (u32 *) res->data = val;
|
||||||
if (list)
|
if (list)
|
||||||
|
@ -94,24 +195,72 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val)
|
||||||
return res;
|
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 *
|
struct adata *
|
||||||
int_set_del(struct linpool *pool, struct adata *list, u32 val)
|
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))
|
if (!int_set_contains(list, val))
|
||||||
return list;
|
return list;
|
||||||
|
|
||||||
res = lp_alloc(pool, list->length + sizeof(struct adata) - 4);
|
struct adata *res;
|
||||||
res->length = list->length-4;
|
res = lp_alloc(pool, sizeof(struct adata) + list->length - 4);
|
||||||
|
res->length = list->length - 4;
|
||||||
|
|
||||||
l = (u32 *) list->data;
|
u32 *l = int_set_get_data(list);
|
||||||
k = (u32 *) res->data;
|
u32 *k = int_set_get_data(res);
|
||||||
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[i] != val)
|
if (l[i] != val)
|
||||||
*k++ = l[i];
|
*k++ = l[i];
|
||||||
|
|
||||||
return res;
|
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;
|
||||||
|
}
|
||||||
|
|
48
nest/attrs.h
48
nest/attrs.h
|
@ -50,12 +50,52 @@ int as_path_match(struct adata *path, struct f_path_mask *mask);
|
||||||
|
|
||||||
/* a-set.c */
|
/* 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);
|
/* Extended Community subtypes (kinds) */
|
||||||
int int_set_contains(struct adata *list, u32 val);
|
#define EC_RT 0x0002
|
||||||
struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val);
|
#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)
|
static inline int int_set_get_size(struct adata *list)
|
||||||
{ return list->length / 4; }
|
{ 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
|
#endif
|
||||||
|
|
|
@ -363,6 +363,7 @@ typedef struct eattr {
|
||||||
#define EAF_TYPE_ROUTER_ID 0x05 /* Router ID (IPv4 address) */
|
#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_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_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_TYPE_UNDEF 0x0f /* `force undefined' entry */
|
||||||
#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */
|
#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) */
|
#define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */
|
||||||
|
|
|
@ -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
|
* ea_show - print an &eattr to CLI
|
||||||
* @c: destination CLI
|
* @c: destination CLI
|
||||||
|
@ -523,6 +535,9 @@ ea_show(struct cli *c, eattr *e)
|
||||||
case EAF_TYPE_INT_SET:
|
case EAF_TYPE_INT_SET:
|
||||||
ea_show_int_set(c, ad, 1, pos, buf, end);
|
ea_show_int_set(c, ad, 1, pos, buf, end);
|
||||||
return;
|
return;
|
||||||
|
case EAF_TYPE_EC_SET:
|
||||||
|
ea_show_ec_set(c, ad, pos, buf, end);
|
||||||
|
return;
|
||||||
case EAF_TYPE_UNDEF:
|
case EAF_TYPE_UNDEF:
|
||||||
default:
|
default:
|
||||||
bsprintf(pos, "<type %02x>", e->type);
|
bsprintf(pos, "<type %02x>", e->type);
|
||||||
|
|
|
@ -247,7 +247,6 @@ bgp_check_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
|
||||||
return ((len % 4) == 0) ? 0 : WITHDRAW;
|
return ((len % 4) == 0) ? 0 : WITHDRAW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
bgp_check_cluster_list(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
|
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;
|
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[] = {
|
static struct attr_desc bgp_attr_table[] = {
|
||||||
{ NULL, -1, 0, 0, 0, /* Undefined */
|
{ NULL, -1, 0, 0, 0, /* Undefined */
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
|
@ -311,7 +317,8 @@ static struct attr_desc bgp_attr_table[] = {
|
||||||
bgp_check_reach_nlri, NULL },
|
bgp_check_reach_nlri, NULL },
|
||||||
{ "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */
|
{ "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */
|
||||||
bgp_check_unreach_nlri, NULL },
|
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 */
|
{ "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
{ "as4_aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
|
{ "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
|
unsigned int
|
||||||
bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
|
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;
|
byte *start = w;
|
||||||
int len, rv;
|
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];
|
eattr *a = &attrs->attrs[i];
|
||||||
ASSERT(EA_PROTO(a->id) == EAP_BGP);
|
ASSERT(EA_PROTO(a->id) == EAP_BGP);
|
||||||
code = EA_ID(a->id);
|
code = EA_ID(a->id);
|
||||||
|
|
||||||
#ifdef IPV6
|
#ifdef IPV6
|
||||||
/* When talking multiprotocol BGP, the NEXT_HOP attributes are used only temporarily. */
|
/* When talking multiprotocol BGP, the NEXT_HOP attributes are used only temporarily. */
|
||||||
if (code == BA_NEXT_HOP)
|
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 ... */
|
/* Standard path continues here ... */
|
||||||
|
|
||||||
|
type = a->type & EAF_TYPE_MASK;
|
||||||
flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL);
|
flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL);
|
||||||
len = bgp_get_attr_len(a);
|
len = bgp_get_attr_len(a);
|
||||||
|
|
||||||
/* Skip empty int sets */
|
/* Skip empty sets */
|
||||||
if (((a->type & EAF_TYPE_MASK) == EAF_TYPE_INT_SET) && (len == 0))
|
if (((type == EAF_TYPE_INT_SET) || (type == EAF_TYPE_EC_SET)) && (len == 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (remains < len + 4)
|
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);
|
rv = bgp_encode_attr_hdr(w, flags, code, len);
|
||||||
ADVANCE(w, remains, rv);
|
ADVANCE(w, remains, rv);
|
||||||
|
|
||||||
switch (a->type & EAF_TYPE_MASK)
|
switch (type)
|
||||||
{
|
{
|
||||||
case EAF_TYPE_INT:
|
case EAF_TYPE_INT:
|
||||||
case EAF_TYPE_ROUTER_ID:
|
case EAF_TYPE_ROUTER_ID:
|
||||||
|
@ -589,8 +598,9 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EAF_TYPE_INT_SET:
|
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;
|
int i;
|
||||||
for(i=0; i<len; i+=4)
|
for(i=0; i<len; i+=4)
|
||||||
put_u32(w+i, *z++);
|
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;
|
return (*x < *y) ? -1 : (*x > *y) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static inline void
|
||||||
bgp_normalize_set(u32 *dest, u32 *src, unsigned cnt)
|
bgp_normalize_int_set(u32 *dest, u32 *src, unsigned cnt)
|
||||||
{
|
{
|
||||||
memcpy(dest, src, sizeof(u32) * cnt);
|
memcpy(dest, src, sizeof(u32) * cnt);
|
||||||
qsort(dest, cnt, sizeof(u32), (int(*)(const void *, const void *)) bgp_compare_u32);
|
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
|
static void
|
||||||
bgp_rehash_buckets(struct bgp_proto *p)
|
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);
|
struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length);
|
||||||
z->length = 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;
|
d->u.ptr = z;
|
||||||
break;
|
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);
|
ipa_ntoh(*(ip_addr *)ad->data);
|
||||||
break;
|
break;
|
||||||
case EAF_TYPE_INT_SET:
|
case EAF_TYPE_INT_SET:
|
||||||
|
case EAF_TYPE_EC_SET:
|
||||||
{
|
{
|
||||||
u32 *z = (u32 *) ad->data;
|
u32 *z = (u32 *) ad->data;
|
||||||
for(i=0; i<ad->length/4; i++)
|
for(i=0; i<ad->length/4; i++)
|
||||||
|
|
|
@ -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_RCID_PATH 0x0d
|
||||||
#define BA_MP_REACH_NLRI 0x0e /* [RFC2283] */
|
#define BA_MP_REACH_NLRI 0x0e /* [RFC2283] */
|
||||||
#define BA_MP_UNREACH_NLRI 0x0f
|
#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_PATH 0x11 /* [RFC4893] */
|
||||||
#define BA_AS4_AGGREGATOR 0x12
|
#define BA_AS4_AGGREGATOR 0x12
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,11 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
|
||||||
PATH, METRIC, ERROR, START, DELAY, FORGET, WAIT, ENABLE,
|
PATH, METRIC, ERROR, START, DELAY, FORGET, WAIT, ENABLE,
|
||||||
DISABLE, AFTER, BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN,
|
DISABLE, AFTER, BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN,
|
||||||
BGP_NEXT_HOP, BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY,
|
BGP_NEXT_HOP, BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY,
|
||||||
SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4,
|
BGP_EXT_COMMUNITY, SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT,
|
||||||
ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER,
|
CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE,
|
||||||
MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH, INTERPRET,
|
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
|
||||||
COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP, TABLE,
|
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
|
||||||
GATEWAY, DIRECT, RECURSIVE, MED)
|
TABLE, GATEWAY, DIRECT, RECURSIVE, MED)
|
||||||
|
|
||||||
CF_GRAMMAR
|
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)); })
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID)); })
|
||||||
CF_ADDTO(dynamic_attr, BGP_CLUSTER_LIST
|
CF_ADDTO(dynamic_attr, BGP_CLUSTER_LIST
|
||||||
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP, BA_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)
|
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
/* 32-bit integer type */
|
/* 32-bit integer type */
|
||||||
#define INTEGER_32 ?
|
#define INTEGER_32 ?
|
||||||
|
|
||||||
|
/* 64-bit integer type */
|
||||||
|
#define INTEGER_64 ?
|
||||||
|
|
||||||
/* CPU endianity */
|
/* CPU endianity */
|
||||||
#undef CPU_LITTLE_ENDIAN
|
#undef CPU_LITTLE_ENDIAN
|
||||||
#undef CPU_BIG_ENDIAN
|
#undef CPU_BIG_ENDIAN
|
||||||
|
|
|
@ -30,6 +30,8 @@ typedef INTEGER_16 s16;
|
||||||
typedef unsigned INTEGER_16 u16;
|
typedef unsigned INTEGER_16 u16;
|
||||||
typedef INTEGER_32 s32;
|
typedef INTEGER_32 s32;
|
||||||
typedef unsigned INTEGER_32 u32;
|
typedef unsigned INTEGER_32 u32;
|
||||||
|
typedef INTEGER_64 s64;
|
||||||
|
typedef unsigned INTEGER_64 u64;
|
||||||
typedef u8 byte;
|
typedef u8 byte;
|
||||||
typedef u16 word;
|
typedef u16 word;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue