BIRD has variety of types, used to represent most common attributes in protocols. But sometimes wery complex attributes might be found. An good example of such attribute is BGP_AGGREGATOR which consists from an INT, and QUAD types (ASN,ROUTER_ID).
Rather than making *special* type for aggregator, introduce general infrastructure for structured types and implement aggregator as STRUCT ASID with its literal format written as { ASN, ROUTER_ID }. Variables of STRUCT ASID also supported. Introduce additional operator for dereference structure members: ->, So for example bgp_aggregator->'ai.as' gies an ASN number from structure. Assignment to structure members also supported on both extended attributes and variables. --- conf/cf-lex.l | 30 +++++++++++ conf/conf.h | 2 + conf/confbase.Y | 4 +- doc/bird.sgml | 61 ++++++++++++++++++++-- filter/config.Y | 96 ++++++++++++++++++++++++++++++++++ filter/f-util.c | 91 ++++++++++++++++++++++++++++++-- filter/filter.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++ filter/filter.h | 31 +++++++++-- nest/route.h | 1 + proto/bgp/attrs.c | 14 +++-- proto/bgp/config.Y | 2 +- 11 files changed, 457 insertions(+), 21 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index b1bbeae..5996f30 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -252,6 +252,7 @@ else: { \[\= return PO; \=\] return PC; +\-\> return REF; %% static int @@ -512,6 +513,33 @@ cf_define_symbol(struct symbol *sym, int type, void *def) return sym; } +/** + * cf_define_symbol_if_undef - redefine symbol with new meaning + * @sym: symbol to be defined + * @type: symbol class to assign + * @def: class dependent data + * + * Takes symbol and re-defines it to the new type if it's type was + * undefined previously (%SYM_VOID) or returns symbol unmodified + * if it's type same as specified with @type parameter, overwise + * an error is reported via cf_error(). + */ +struct symbol * +cf_define_symbol_if_undef(struct symbol *sym, int type, void *def) +{ + if (sym->class) + { + if (sym->class != type) + cf_error("Symbol already defined"); + } + else + { + sym->class = type; + sym->def = def; + } + return sym; +} + static void cf_lex_init_kh(void) { @@ -642,6 +670,8 @@ cf_symbol_class_name(struct symbol *sym) return "routing table"; case SYM_ROA: return "ROA table"; + case SYM_REF: + return "reference to struct member"; default: return "unknown type"; } diff --git a/conf/conf.h b/conf/conf.h index 2862429..5aaa1f3 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -115,6 +115,7 @@ struct symbol { #define SYM_FILTER 4 #define SYM_TABLE 5 #define SYM_ROA 6 +#define SYM_REF 255 #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ #define SYM_CONSTANT 0x200 /* 0x200-0x2ff are variable types */ @@ -141,6 +142,7 @@ void cf_lex_init(int is_cli, struct config *c); struct symbol *cf_find_symbol(byte *c); struct symbol *cf_default_name(char *template, int *counter); struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def); +struct symbol *cf_define_symbol_if_undef(struct symbol *sym, int type, void *def); void cf_push_scope(struct symbol *); void cf_pop_scope(void); struct symbol *cf_walk_symbols(struct config *cf, struct symbol *sym, int *pos); diff --git a/conf/confbase.Y b/conf/confbase.Y index c6678e7..f5f701f 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -62,7 +62,7 @@ CF_DECLS struct timeformat *tf; } -%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT +%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT REF %token GEQ LEQ NEQ AND OR %token PO PC %token <i> NUM ENUM @@ -84,7 +84,7 @@ CF_DECLS %left '+' '-' %left '*' '/' '%' %left '!' -%nonassoc '.' +%nonassoc '.' REF CF_KEYWORDS(DEFINE, ON, OFF, YES, NO) diff --git a/doc/bird.sgml b/doc/bird.sgml index e4839a6..5791c0b 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1049,6 +1049,60 @@ function is_bird_ipv6() variables of such type, but some route attributes are of enumeration type. Enumeration types are incompatible with each other. + <tag/asid struct/ + Structured types are supported in filters. Structure aggregates fixed set of + objects with possibly different types in given order. Each object within structure + has it's own label by which object can be referenced. Literals of structured type look + like <cf/{ 1, 192.168.1.1, ... }/ and order of objects in literals is important. + You can not define your own structures, but any literal of predefined structure + could be used in configuration. Standard operations like assignment to variable + of structured type, printing, comparison also supported. + + There is special operator on structured type <cf/->/ which could be used for + referencing labeled object in structured type. This allows retrieving/assignment + of particular object in structured type. + + BIRD defines following structured types: + + <cf/asid/ represents AS number of integer type, followed by a router ID of quad + type. There are two corresponding labels to reference each object within structure: + <cf/ai.as/ refering to AS number object and <cf/ai.as/ refering to router ID object. + This type is used to represent <cf/bgp_aggregator/ attribute. + + There examples on how to work with structured types in filters: +<code> +define C_IP = 10.20.20.20; +define C_BGP_AGGREGATOR = { 10, C_IP }; + +function make_aggregator(int asn; quad router_id) +asid struct ai; +{ + ai = { asn, router_id }; + return ai; +} + +function print_aggregator(asid struct ai) +{ + print "aggregator: ", ai; + print "ai.as: ", ai->'ai.as', ", ai.id: ", ai->'ai.id'; + print "--"; +} + +function test_aggregator() +{ + print { 10, 10.10.10.10 }->'ai.as', ", ", { 10, 10.10.10.10 }->'ai.id'; + print_aggregator({ 10, 10.10.10.10 }); + print_aggregator(C_BGP_AGGREGATOR); + + bgp_aggregator = { 10, 10.30.30.30 }; + print_aggregator(bgp_aggregator); + + bgp_aggregator->'ai.as' = 1; + bgp_aggregator->'ai.id' = 1.1.1.1; + print_aggregator(bgp_aggregator); +} +</code> + <tag/bgppath/ BGP path is a list of autonomous system numbers. You can't write literals of this type. There are several special operators on bgppaths: @@ -1705,9 +1759,10 @@ with `<tt/O/') are optional. has been aggregated from multiple routes by some router on the path from the originator. -<!-- we don't handle aggregators right since they are of a very obscure type - <tag>bgp_aggregator</tag> ---> + <tag>asid struct bgp_aggregator</tag> This is an optional attribute which carries + ASN and router ID that formed aggregated route. See <cf/asid struct/ type + description for more information on type of this attribute. + <tag>clist <cf/bgp_community/ [O]</tag> List of community values associated with the route. Each such value is a pair (represented as a <cf/pair/ data type inside the filters) of 16-bit integers, the first of them containing the number of the AS which defines diff --git a/filter/config.Y b/filter/config.Y index a7a69a4..77b2446 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -250,6 +250,48 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) return rv; } +static inline struct f_inst * +f_generate_ref(struct f_inst *t, struct symbol *s) +{ + struct f_inst *rv; + + s = cf_define_symbol_if_undef(s, SYM_REF, NULL); + + if (t->code == 'C') { + struct f_val res, *v = t->a1.p; + + res.type = T_VOID; + switch (v->type) { + case T_STRUCT_ASID: + if (!strcmp(s->name, "ai.as")) { + res.type = T_INT; + res.val.i = v->val.ai.as; + } else if (!strcmp(s->name, "ai.id")) { + res.type = T_QUAD; + res.val.i = v->val.ai.id; + } + break; + default: + cf_error("Expected structured type: %x", v->type); + } + + if (res.type == T_VOID) + cf_error("Unknown member in structure: %s", s->name); + + NEW_F_VAL; + rv = f_new_inst(); + rv->code = 'C'; + rv->a1.p = val; + memcpy(val, &res, sizeof(*val)); + } else { + rv = f_new_inst(); + rv->code = P('s','s'); + rv->a1.p = s; + rv->a2.p = t; + } + + return rv; +} CF_DECLS @@ -259,6 +301,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, INT, BOOL, IP, PREFIX, PAIR, QUAD, EC, SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, IF, THEN, ELSE, CASE, TRY, CATCH, + STRUCT, ASID, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX, PREFERENCE, @@ -313,6 +356,7 @@ type: | BGPPATH { $$ = T_PATH; } | CLIST { $$ = T_CLIST; } | ECLIST { $$ = T_ECLIST; } + | ASID STRUCT { $$ = T_STRUCT_ASID; } | type SET { switch ($1) { case T_INT: @@ -628,6 +672,7 @@ constant: constructor: '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); } | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); } + | '{' term ',' term '}' { $$ = f_generate_struct(T_STRUCT_ASID, $2, $4); } ; @@ -719,6 +764,8 @@ term: | rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); } + | term REF SYM { $$ = f_generate_ref($1, $3); } + | term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; } | term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; } | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; } @@ -877,6 +924,55 @@ cmd: $$->a1.p = $2; $$->a2.p = build_tree( $4 ); } + | rtadot dynamic_attr REF SYM '=' term ';' { + struct f_inst_data *get_dyn; + struct f_inst *set_ref; + char **c; + + $4 = cf_define_symbol_if_undef($4, SYM_REF, NULL); + + get_dyn = f_new_inst2(sizeof(char *)); + get_dyn->i = *($2); + get_dyn->i.code = P('e','a'); + c = (char **) get_dyn->data; + *c = $4->name; + + set_ref = f_new_inst(); + set_ref->code = P('s','S'); + set_ref->a1.p = get_dyn; + set_ref->a2.p = $6; + + $$ = $2; + $$->code = P('e','S'); + $$->a1.p = set_ref; + } + | SYM REF SYM '=' term ';' { + struct f_inst_data *get_sym; + struct f_inst *set_ref; + char **c; + + if (($1->class & ~T_MASK) != SYM_VARIABLE) + cf_error( "You may set only variables." ); + + $3 = cf_define_symbol_if_undef($3, SYM_REF, NULL); + + get_sym = f_new_inst2(sizeof(char *)); + get_sym->i.code = 'V'; + get_sym->i.a1.p = $1->def; + get_sym->i.a2.p = $1->name; + c = (char **) get_sym->data; + *c = $3->name; + + set_ref = f_new_inst(); + set_ref->code = P('s','S'); + set_ref->a1.p = get_sym; + set_ref->a2.p = $5; + + $$ = f_new_inst(); + $$->code = 's'; + $$->a1.p = $1; + $$->a2.p = set_ref; + } | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); } diff --git a/filter/f-util.c b/filter/f-util.c index ad91002..a02d058 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -15,14 +15,21 @@ struct f_inst * f_new_inst(void) { - struct f_inst * ret; - ret = cfg_alloc(sizeof(struct f_inst)); - ret->code = ret->aux = 0; - ret->arg1 = ret->arg2 = ret->next = NULL; + struct f_inst *ret; + ret = cfg_allocz(sizeof(struct f_inst)); ret->lineno = ifs->lino; return ret; } +struct f_inst_data * +f_new_inst2(unsigned int size) +{ + struct f_inst_data *ret; + ret = cfg_allocz(sizeof(struct f_inst_data) + size); + ret->i.lineno = ifs->lino; + return ret; +} + struct f_inst * f_new_dynamic_attr(int type, int f_type, int code) { @@ -71,6 +78,82 @@ f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *a return &ret->i; } +struct f_inst * +f_generate_struct(int type, ...) +{ + struct f_inst *rv; + va_list args; + + va_start(args, type); + + switch (type) { + case T_STRUCT_ASID: + { + struct f_inst *t1 = va_arg(args, struct f_inst *); + struct f_inst *t2 = va_arg(args, struct f_inst *); + int c1 = 0, c2 = 0; + struct f_asid ai = { 0, 0 }; + + if (t1->code == 'c') { + if (t1->aux != T_INT) + cf_error("Can't operate with non-integer value of AS part in AS,ID constructor"); + + ai.as = t1->a2.i; + c1 = 1; + } + + if (t2->code == 'c') { + if (t2->aux != T_QUAD) + cf_error("Can't operate with non-quad value of ID part in AS,ID constructor"); + + ai.id = t2->a2.i; + c2 = 1; + } + +#ifndef IPV6 + /* IP->Quad implicit conversion */ + else if (t2->code == 'C') { + struct f_val *val = t2->a1.p; + + if (val->type == T_QUAD) + ai.id = val->val.i; + else if (val->type == T_IP) + ai.id = ipa_to_u32(val->val.px.ip); + else + cf_error("Can't operate with non-quad value of ID part in AS,ID constructor"); + + c2 = 1; + } +#endif + + if (c1 && c2) { + NEW_F_VAL; + rv = f_new_inst(); + rv->code = 'C'; + rv->a1.p = val; + val->type = T_STRUCT_ASID; + val->val.ai = ai; + } else { + rv = f_new_inst(); + rv->code = P('m','a'); + rv->a1.p = t1; + rv->a2.p = t2; + } + } + break; + + default: + rv = NULL; + } + + va_end(args); + + if (!rv) + cf_error("Unknown structure type: %x", type); + + return rv; +} + char * filter_name(struct filter *filter) { diff --git a/filter/filter.c b/filter/filter.c index 9b99f28..4b41d4f 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -50,6 +50,19 @@ #define CMP_ERROR 999 +/* [S]tructured types [E]rror codes and their text representation */ +#define SE_OK 0 +#define SE_NOT_STRUCT 1 +#define SE_IN_TYPE 2 +#define SE_NO_MEMBER 3 + +static char *se_desc[] = { + [SE_OK] = "", + [SE_NOT_STRUCT] = "Requested type is not structure", + [SE_IN_TYPE] = "Attempt to set member to incompatible type", + [SE_NO_MEMBER] = "Unknown member in structure", +}; + static struct adata * adata_empty(struct linpool *pool, int l) { @@ -188,6 +201,9 @@ val_compare(struct f_val v1, struct f_val v2) return uint_cmp(v1.val.i, v2.val.i); case T_EC: return u64_cmp(v1.val.ec, v2.val.ec); + case T_STRUCT_ASID: + return u64_cmp((((u64) v1.val.ai.as) << 32) | v1.val.ai.id, + (((u64) v2.val.ai.as) << 32) | v2.val.ai.id); case T_IP: return ipa_compare(v1.val.px.ip, v2.val.px.ip); case T_PREFIX: @@ -490,6 +506,7 @@ val_print(struct f_val v) 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_STRUCT_ASID: logn("{ %u, %R }", v.val.ai.as, v.val.ai.id); 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; @@ -710,6 +727,102 @@ interpret(struct f_inst *what) break; } + case P('m','a'): + { + TWOARGS; + + struct f_asid ai; + + if (v1.type != T_INT) + runtime("Can't operate with non-integer value of AS part in AS,ID constructor"); + ai.as = v1.val.i; + + if (v2.type == T_QUAD) + ai.id = v2.val.i; +#ifndef IPV6 + /* IP->Quad implicit conversion */ + else if (v2.type == T_IP) + ai.id = ipa_to_u32(v2.val.px.ip); +#endif + else + runtime("Can't operate with non-quad value of ID part in AS,ID constructor"); + + res.val.ai = ai; + res.type = T_STRUCT_ASID; + break; + } + + case P('s','s'): + { + char *name; + + ARG(v2, a2.p); + + sym = what->a1.p; + name = sym->name; + + i = SE_OK; + switch (v2.type) { + case T_STRUCT_ASID: + if (!strcmp(name, "ai.as")) { + res.type = T_INT; + res.val.i = v2.val.ai.as; + } else if (!strcmp(name, "ai.id")) { + res.type = T_QUAD; + res.val.i = v2.val.ai.id; + } else + i = SE_NO_MEMBER; + break; + default: + i = SE_NOT_STRUCT; + } + + if (i != SE_OK) + runtime(se_desc[i]); + } + break; + + case P('s','S'): + { + char **pname; + char *name; + + TWOARGS; + + pname = (char **) ((struct f_inst_data *) what->a1.p)->data; + name = *pname; + + res = v1; + i = SE_OK; + switch (res.type) { + case T_STRUCT_ASID: + if (!strcmp(name, "ai.as")) { + if (v2.type == T_INT) + res.val.ai.as = v2.val.i; + else + i = SE_IN_TYPE; + } else if (!strcmp(name, "ai.id")) { + if (v2.type == T_QUAD) + res.val.ai.id = v2.val.i; +#ifndef IPV6 + /* IP->Quad implicit conversion */ + else if (v2.type == T_IP) + res.val.ai.id = ipa_to_u32(v2.val.px.ip); +#endif + else + i = SE_IN_TYPE; + } else + i = SE_NO_MEMBER; + break; + default: + i = SE_NOT_STRUCT; + } + + if (i != SE_OK) + runtime(se_desc[i]); + } + break; + /* Relational operators */ #define COMPARE(f,x) \ @@ -937,6 +1050,12 @@ interpret(struct f_inst *what) res.val.px.ip = * (ip_addr *) ad->data; } break; + case T_STRUCT_ASID: + { + struct adata * ad = e->u.ptr; + res.val.ai = * (struct f_asid *) ad->data; + } + break; case T_PATH: case T_CLIST: case T_ECLIST: @@ -998,6 +1117,14 @@ interpret(struct f_inst *what) l->attrs[0].u.ptr = ad; } break; + case T_STRUCT_ASID: + { + struct adata *ad = lp_alloc(f_pool, sizeof(struct adata) + sizeof(struct f_asid)); + ad->length = sizeof(struct f_asid); + (* (struct f_asid *) ad->data) = v1.val.ai; + l->attrs[0].u.ptr = ad; + } + break; case T_PATH: case T_CLIST: case T_ECLIST: @@ -1352,6 +1479,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) case '&': case P('m','p'): case P('m','c'): + case P('m','a'): case P('!','='): case P('=','='): case '<': @@ -1361,6 +1489,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) case '~': TWOARGS; break; case P('d','e'): ONEARG; break; + case P('s','s'): case 's': ARG(v2, a2.p); { @@ -1374,6 +1503,23 @@ i_same(struct f_inst *f1, struct f_inst *f2) } break; + case P('s','S'): + TWOARGS; + { + char **pname1, **pname2; + char *name1, *name2; + + pname1 = (char **) ((struct f_inst_data *) f1->a1.p)->data; + name1 = *pname1; + + pname2 = (char **) ((struct f_inst_data *) f2->a1.p)->data; + name2 = *pname2; + + if (strcmp(name1, name2)) + return 0; + } + break; + case 'c': switch (f1->aux) { diff --git a/filter/filter.h b/filter/filter.h index aed4418..970d093 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -29,6 +29,11 @@ struct f_inst { /* Instruction */ int lineno; }; +struct f_inst_data { + struct f_inst i; + byte data[0]; +}; + #define arg1 a1.p #define arg2 a2.p @@ -48,11 +53,17 @@ struct f_prefix { /* If range then prefix must be in range (len >> 16 & 0xff, len >> 8 & 0xff) */ }; +struct f_asid { + u32 as; + u32 id; +}; + struct f_val { int type; union { int i; u64 ec; + struct f_asid ai; /* ip_addr ip; Folded into prefix */ struct f_prefix px; char *s; @@ -69,10 +80,12 @@ struct filter { }; struct f_inst *f_new_inst(void); +struct f_inst_data *f_new_inst2(unsigned int size); struct f_inst *f_new_dynamic_attr(int type, int f_type, int code); /* Type as core knows it, type as filters know it, and code of dynamic attribute */ struct f_tree *f_new_tree(void); struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument); struct f_inst *f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn); +struct f_inst *f_generate_struct(int type, ...); struct f_tree *build_tree(struct f_tree *); @@ -177,9 +190,21 @@ void val_print(struct f_val v); #define T_ECLIST 0x26 /* Extended community list */ #define T_EC 0x27 /* Extended community value, u64 */ -#define T_RETURN 0x40 -#define T_SET 0x80 -#define T_PREFIX_SET 0x81 +/* Put set types in 0x40..0x4f range */ +#define T_SET 0x40 /* General set type */ +#define T_PREFIX_SET 0x41 /* Special set for prefixes */ +/* new sets go here */ + +/* Put structured types in 0x50..0x5f range */ +#define T_STRUCT_LO 0x50 +#define T_STRUCT_HI 0x5f + +#define T_STRUCT_ASID 0x50 /* AS,ID structure (BGP_AGGREGATOR) */ +/* new structs go here */ + +#define T_STRUCT T_STRUCT_LO ... T_STRUCT_HI + +#define T_RETURN 0x80 #define SA_FROM 1 diff --git a/nest/route.h b/nest/route.h index 35b5fa1..7aadfc9 100644 --- a/nest/route.h +++ b/nest/route.h @@ -392,6 +392,7 @@ typedef struct eattr { #define EAF_TYPE_IP_ADDRESS 0x04 /* IP 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_BGP_AGGREGATOR 0x08 /* BGP AGGREGATOR attribute */ #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 */ diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index c27a498..706ed7d 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -231,14 +231,10 @@ bgp_check_aggregator(struct bgp_proto *p, byte *a UNUSED, int len) static void bgp_format_aggregator(eattr *a, byte *buf, int buflen UNUSED) { - struct adata *ad = a->u.ptr; - byte *data = ad->data; - u32 as; - - as = get_u32(data); - data += 4; + struct adata *ad = a->u.ptr; + struct f_asid *ai = (struct f_asid *) ad->data; - bsprintf(buf, "%d.%d.%d.%d AS%u", data[0], data[1], data[2], data[3], as); + bsprintf(buf, "%R AS%u", ai->id, ai->as); } static int @@ -302,7 +298,7 @@ static struct attr_desc bgp_attr_table[] = { NULL, NULL }, { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */ NULL, NULL }, - { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */ + { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_BGP_AGGREGATOR, 1,/* BA_AGGREGATOR */ bgp_check_aggregator, bgp_format_aggregator }, { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */ bgp_check_community, NULL }, @@ -597,6 +593,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains) memcpy(w, &ip, len); break; } + case EAF_TYPE_BGP_AGGREGATOR: case EAF_TYPE_INT_SET: case EAF_TYPE_EC_SET: { @@ -1660,6 +1657,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin case EAF_TYPE_IP_ADDRESS: ipa_ntoh(*(ip_addr *)ad->data); break; + case EAF_TYPE_BGP_AGGREGATOR: case EAF_TYPE_INT_SET: case EAF_TYPE_EC_SET: { diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 19f7a21..0c949ff 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -125,7 +125,7 @@ CF_ADDTO(dynamic_attr, BGP_LOCAL_PREF CF_ADDTO(dynamic_attr, BGP_ATOMIC_AGGR { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_EMPTY, EA_CODE(EAP_BGP, BA_ATOMIC_AGGR)); }) CF_ADDTO(dynamic_attr, BGP_AGGREGATOR - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_EMPTY, EA_CODE(EAP_BGP, BA_AGGREGATOR)); }) + { $$ = f_new_dynamic_attr(EAF_TYPE_BGP_AGGREGATOR, T_STRUCT_ASID, EA_CODE(EAP_BGP, BA_AGGREGATOR)); }) CF_ADDTO(dynamic_attr, BGP_COMMUNITY { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP, BA_COMMUNITY)); }) CF_ADDTO(dynamic_attr, BGP_ORIGINATOR_ID -- 1.7.10.4