From 42a0c05408c4151442e6a0ec1c6889acbcfe9c17 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 12 Aug 2011 21:03:43 +0200 Subject: BGP Extended communities. --- aclocal.m4 | 5 +- filter/config.Y | 238 +++++++++++++++++++++++++++++++++++++++----- filter/filter.c | 272 ++++++++++++++++++++++++++++++++++++++++++--------- filter/filter.h | 3 + filter/test.conf | 40 ++++++++ nest/a-set.c | 177 ++++++++++++++++++++++++++++++--- nest/attrs.h | 46 ++++++++- nest/route.h | 1 + nest/rt-attr.c | 15 +++ proto/bgp/attrs.c | 76 ++++++++++++-- proto/bgp/bgp.h | 2 +- proto/bgp/config.Y | 13 ++- sysdep/autoconf.h.in | 3 + sysdep/config.h | 2 + 14 files changed, 788 insertions(+), 105 deletions(-) diff --git a/aclocal.m4 b/aclocal.m4 index 34a0511..ee54525 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -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.]) diff --git a/filter/config.Y b/filter/config.Y index 80e7428..3c5f18b 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -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 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 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 filter filter_body where_filter -%type type break_command pair_expr -%type pair_atom -%type pair_item set_item switch_item set_items switch_items switch_body +%type type break_command pair_expr ec_kind +%type pair_atom ec_expr +%type pair_item ec_item set_item switch_item set_items switch_items switch_body %type fprefix_set %type set_atom switch_atom fprefix fprefix_s fipa %type 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,12 +889,11 @@ 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 ); } - | rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'f', $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 '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); } ; CF_END diff --git a/filter/filter.c b/filter/filter.c index 0f44a6e..98c657b 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -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,52 +1080,93 @@ 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"); - - struct f_val dummy; - int arg_set = 0; - i = 0; + if (v1.type == T_CLIST) + { + /* Community (or cluster) list */ + struct f_val dummy; + int arg_set = 0; + i = 0; - if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) - i = v2.val.i; + 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); + /* 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"); + 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': + 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; - res.type = T_CLIST; - switch (what->aux) + case 'f': + 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) { - case 'a': - if (arg_set) - runtime("Can't add set"); - res.val.ad = int_set_add(f_pool, v1.val.ad, i); - break; + /* Extended community list */ + int arg_set = 0; - 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; + /* 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 pair"); - res.val.ad = clist_filter(f_pool, v1.val.ad, v2.val.t, 1); - 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"); + default: + bug("unknown Ca operation"); + } } + else + runtime("Can't add/delete to non-(e)clist"); + break; default: diff --git a/filter/filter.h b/filter/filter.h index c3eb8b4..2cf4652 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -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 diff --git a/filter/test.conf b/filter/test.conf index ca8e26f..19372f2 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -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*"; diff --git a/nest/a-set.c b/nest/a-set.c index 3cbd635..020d097 100644 --- a/nest/a-set.c +++ b/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; 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; ilength/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) @@ -95,23 +196,71 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val) } struct adata * -int_set_del(struct linpool *pool, struct adata *list, u32 val) +ec_set_add(struct linpool *pool, struct adata *list, u64 val) { - struct adata *res; - u32 *l, *k; - unsigned int i; + 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) +{ 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; + + u32 *l = int_set_get_data(list); + u32 *k = int_set_get_data(res); + int len = int_set_get_size(list); + int i; - l = (u32 *) list->data; - k = (u32 *) res->data; - for (i=0; ilength/4; 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; +} diff --git a/nest/attrs.h b/nest/attrs.h index 6ce5755..85e4e59 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -50,12 +50,52 @@ int as_path_match(struct adata *path, struct f_path_mask *mask); /* a-set.c */ + +/* 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); -struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val); +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); -static inline int int_set_get_size(struct adata *list) -{ return list->length / 4; } #endif diff --git a/nest/route.h b/nest/route.h index c77fe41..641b924 100644 --- a/nest/route.h +++ b/nest/route.h @@ -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) */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 486a543..5a78f16 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -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, "", e->type); diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 83ca249..59d8a8c 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -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 *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; ilength/4; i++) diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 097faa6..1247870 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -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 diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 93f832a..93cc85f 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -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) diff --git a/sysdep/autoconf.h.in b/sysdep/autoconf.h.in index 4739fba..d029e2a 100644 --- a/sysdep/autoconf.h.in +++ b/sysdep/autoconf.h.in @@ -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 diff --git a/sysdep/config.h b/sysdep/config.h index 0b06a70..8c7f8be 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -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; -- cgit v1.2.3