diff options
-rw-r--r-- | conf/conf.h | 4 | ||||
-rw-r--r-- | doc/bird.sgml | 8 | ||||
-rw-r--r-- | filter/config.Y | 43 | ||||
-rw-r--r-- | filter/f-util.c | 144 | ||||
-rw-r--r-- | filter/filter.c | 2 | ||||
-rw-r--r-- | filter/filter.h | 9 | ||||
-rw-r--r-- | nest/config.Y | 4 | ||||
-rw-r--r-- | nest/route.h | 9 | ||||
-rw-r--r-- | nest/rt-attr.c | 13 |
9 files changed, 215 insertions, 21 deletions
diff --git a/conf/conf.h b/conf/conf.h index 5689fb67..427569fd 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -15,7 +15,6 @@ #include "lib/resource.h" #include "lib/timer.h" - /* Configuration structure */ struct config { @@ -128,9 +127,12 @@ struct sym_scope { #define SYM_FUNCTION 3 #define SYM_FILTER 4 #define SYM_TABLE 5 +#define SYM_ATTRIBUTE 6 #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ +#define SYM_VARIABLE_RANGE SYM_VARIABLE ... (SYM_VARIABLE | 0xff) #define SYM_CONSTANT 0x200 /* 0x200-0x2ff are variable types */ +#define SYM_CONSTANT_RANGE SYM_CONSTANT ... (SYM_CONSTANT | 0xff) #define SYM_TYPE(s) (((struct f_val *) (s)->def)->type) #define SYM_VAL(s) (((struct f_val *) (s)->def)->val) diff --git a/doc/bird.sgml b/doc/bird.sgml index 386d3e8d..60c38fa4 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -25,7 +25,7 @@ configuration - something in config which is not keyword. Ondrej Filip <it/<feela@network.cz>/, Pavel Machek <it/<pavel@ucw.cz>/, Martin Mares <it/<mj@ucw.cz>/, -Jan Matejka <it/<mq@jmq.cz>/, +Maria Jan Matejka <it/<mq@jmq.cz>/, Ondrej Zajicek <it/<santiago@crfreenet.org>/ </author> @@ -552,6 +552,12 @@ include "tablename.conf";; constants based on /etc/iproute2/rt_* files. A list of defined constants can be seen (together with other symbols) using 'show symbols' command. + <tag><label id="opt-attribute">attribute <m/type/ <m/name/</tag> + Define a custom route attribute. You can set and get it in filters like + any other route atribute. This feature is intended for marking routes + in import filters for export filtering purposes instead of locally + assigned BGP communities which have to be deleted in export filters. + <tag><label id="opt-router-id">router id <m/IPv4 address/</tag> Set BIRD's router ID. It's a world-wide unique identification of your router, usually one of router's IPv4 addresses. Default: the lowest diff --git a/filter/config.Y b/filter/config.Y index d865d11f..c1e74531 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -417,7 +417,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, ADD, DELETE, CONTAINS, RESET, PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH, EMPTY, - FILTER, WHERE, EVAL, + FILTER, WHERE, EVAL, ATTRIBUTE, BT_ASSERT, BT_TEST_SUITE, FORMAT) %nonassoc THEN @@ -455,6 +455,11 @@ filter_eval: EVAL term { f_eval_int($2); } ; +conf: custom_attr ; +custom_attr: ATTRIBUTE type SYM ';' { + cf_define_symbol($3, SYM_ATTRIBUTE, ca_lookup(new_config->pool, $3->name, $2)->fda); +}; + conf: bt_test_suite ; bt_test_suite: BT_TEST_SUITE '(' SYM ',' text ')' { @@ -834,14 +839,22 @@ function_call: symbol: SYM { - switch ($1->class & 0xff00) { - case SYM_CONSTANT: $$ = f_new_inst(FI_CONSTANT_INDIRECT); break; - case SYM_VARIABLE: $$ = f_new_inst(FI_VARIABLE); break; - default: cf_error("%s: variable expected.", $1->name); + switch ($1->class & 0xffff) { + case SYM_CONSTANT_RANGE: + $$ = f_new_inst(FI_CONSTANT_INDIRECT); + goto cv_common; + case SYM_VARIABLE_RANGE: + $$ = f_new_inst(FI_VARIABLE); + cv_common: + $$->a1.p = $1->def; + $$->a2.p = $1->name; + break; + case SYM_ATTRIBUTE: + $$ = f_new_inst_da(FI_EA_GET, *((struct f_dynamic_attr *) $1->def)); + break; + default: + cf_error("%s: variable expected.", $1->name); } - - $$->a1.p = $1->def; - $$->a2.p = $1->name; } static_attr: @@ -1001,11 +1014,15 @@ cmd: } | SYM '=' term ';' { DBG( "Ook, we'll set value\n" ); - if (($1->class & ~T_MASK) != SYM_VARIABLE) - cf_error( "You may set only variables." ); - $$ = f_new_inst(FI_SET); - $$->a1.p = $1; - $$->a2.p = $3; + if ($1->class == SYM_ATTRIBUTE) { + $$ = f_new_inst_da(FI_EA_SET, *((struct f_dynamic_attr *) $1->def)); + $$->a1.p = $3; + } else if (($1->class & ~T_MASK) == SYM_VARIABLE) { + $$ = f_new_inst(FI_SET); + $$->a1.p = $1; + $$->a2.p = $3; + } else + cf_error( "Symbol `%s' is read-only.", $1->name ); } | RETURN term ';' { DBG( "Ook, we'll return the value\n" ); diff --git a/filter/f-util.c b/filter/f-util.c index 6170760b..ee9490b4 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -10,6 +10,9 @@ #include "nest/bird.h" #include "conf/conf.h" #include "filter/filter.h" +#include "lib/idm.h" +#include "nest/protocol.h" +#include "nest/route.h" #define P(a,b) ((a<<8) | b) @@ -105,3 +108,144 @@ filter_name(struct filter *filter) else return filter->name; } + +#define CA_KEY(n) n->name, n->fda.type +#define CA_NEXT(n) n->next +#define CA_EQ(na,ta,nb,tb) (!strcmp(na,nb) && (ta == tb)) +#define CA_FN(n,t) (mem_hash(n, strlen(n)) ^ (t*0xaae99453U)) +#define CA_ORDER 8 /* Fixed */ + +struct ca_storage { + struct ca_storage *next; + struct f_dynamic_attr fda; + u32 uc; + char name[0]; +}; + +HASH(struct ca_storage) ca_hash; + +static struct idm ca_idm; +static struct ca_storage **ca_storage; +static uint ca_storage_max; + +static void +ca_free(resource *r) +{ + struct custom_attribute *ca = (void *) r; + struct ca_storage *cas = HASH_FIND(ca_hash, CA, ca->name, ca->fda->type); + ASSERT(cas); + + ca->name = NULL; + ca->fda = NULL; + if (!--cas->uc) { + uint id = EA_CUSTOM_ID(cas->fda.ea_code); + idm_free(&ca_idm, id); + HASH_REMOVE(ca_hash, CA, cas); + ca_storage[id] = NULL; + mb_free(cas); + } +} + +static void +ca_dump(resource *r) +{ + struct custom_attribute *ca = (void *) r; + debug("name \"%s\" id 0x%04x ea_type 0x%02x f_type 0x%02x\n", + ca->name, ca->fda->ea_code, ca->fda->type, ca->fda->f_type); +} + +static struct resclass ca_class = { + .name = "Custom attribute", + .size = sizeof(struct custom_attribute), + .free = ca_free, + .dump = ca_dump, + .lookup = NULL, + .memsize = NULL, +}; + +struct custom_attribute * +ca_lookup(pool *p, const char *name, int f_type) +{ + int ea_type; + + switch (f_type) { + case T_INT: + ea_type = EAF_TYPE_INT; + break; + case T_IP: + ea_type = EAF_TYPE_IP_ADDRESS; + break; + case T_QUAD: + ea_type = EAF_TYPE_ROUTER_ID; + break; + case T_PATH: + ea_type = EAF_TYPE_AS_PATH; + break; + case T_CLIST: + ea_type = EAF_TYPE_INT_SET; + break; + case T_ECLIST: + ea_type = EAF_TYPE_EC_SET; + break; + case T_LCLIST: + ea_type = EAF_TYPE_LC_SET; + break; + default: + cf_error("Custom route attribute of unsupported type"); + } + + static int inited = 0; + if (!inited) { + idm_init(&ca_idm, &root_pool, 8); + HASH_INIT(ca_hash, &root_pool, CA_ORDER); + + ca_storage_max = 256; + ca_storage = mb_allocz(&root_pool, sizeof(struct ca_storage *) * ca_storage_max); + + inited++; + } + + struct ca_storage *cas = HASH_FIND(ca_hash, CA, name, ea_type); + if (cas) { + cas->uc++; + } else { + + uint id = idm_alloc(&ca_idm); + + if (id >= EA_CUSTOM_BIT) + cf_error("Too many custom attributes."); + + if (id >= ca_storage_max) { + ca_storage_max *= 2; + ca_storage = mb_realloc(ca_storage, sizeof(struct ca_storage *) * ca_storage_max * 2); + } + + cas = mb_allocz(&root_pool, sizeof(struct ca_storage) + strlen(name) + 1); + cas->fda = f_new_dynamic_attr(ea_type, f_type, EA_CUSTOM(id)); + cas->uc = 1; + + strcpy(cas->name, name); + ca_storage[id] = cas; + + HASH_INSERT(ca_hash, CA, cas); + } + + struct custom_attribute *ca = ralloc(p, &ca_class); + ca->fda = &(cas->fda); + ca->name = cas->name; + return ca; +} + +const char * +ea_custom_name(uint ea) +{ + uint id = EA_CUSTOM_ID(ea); + if (id >= ca_storage_max) + return NULL; + + if (!ca_storage[id]) + return NULL; + + return ca_storage[id]->name; +} + diff --git a/filter/filter.c b/filter/filter.c index f308e7fd..37cf16a3 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1063,7 +1063,7 @@ interpret(struct f_inst *what) break; } - switch (what->aux & EAF_TYPE_MASK) { + switch (e->type & EAF_TYPE_MASK) { case EAF_TYPE_INT: res.type = f_type; res.val.i = e->u.data; diff --git a/filter/filter.h b/filter/filter.h index febfdc65..a8c33287 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -287,6 +287,15 @@ struct f_trie #define FF_SILENT 2 /* Silent filter execution */ +/* Custom route attributes */ +struct custom_attribute { + resource r; + struct f_dynamic_attr *fda; + const char *name; +}; + +struct custom_attribute *ca_lookup(pool *p, const char *name, int ea_type); + /* Bird Tests */ struct f_bt_test_suite { node n; /* Node in config->tests */ diff --git a/nest/config.Y b/nest/config.Y index 88f74b96..34bde3fa 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -532,6 +532,7 @@ r_args: $$ = cfg_allocz(sizeof(struct rt_show_data)); init_list(&($$->tables)); $$->filter = FILTER_ACCEPT; + $$->running_on_config = new_config->fallback; } | r_args net_any { $$ = $1; @@ -586,7 +587,6 @@ r_args: if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name); $$->export_mode = $2; $$->export_protocol = c->proto; - $$->running_on_config = c->proto->cf->global; $$->tables_defined_by = RSD_TDB_INDIRECT; } | r_args export_mode SYM '.' r_args_channel { @@ -597,7 +597,6 @@ r_args: $$->export_mode = $2; $$->export_channel = proto_find_channel_by_name(c->proto, $5); if (!$$->export_channel) cf_error("Export channel not found"); - $$->running_on_config = c->proto->cf->global; $$->tables_defined_by = RSD_TDB_INDIRECT; } | r_args PROTOCOL SYM { @@ -606,7 +605,6 @@ r_args: if ($$->show_protocol) cf_error("Protocol specified twice"); if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name); $$->show_protocol = c->proto; - $$->running_on_config = c->proto->cf->global; $$->tables_defined_by = RSD_TDB_INDIRECT; } | r_args STATS { diff --git a/nest/route.h b/nest/route.h index 080bbf58..2600f087 100644 --- a/nest/route.h +++ b/nest/route.h @@ -471,13 +471,20 @@ typedef struct eattr { } u; } eattr; + #define EA_CODE(proto,id) (((proto) << 8) | (id)) -#define EA_PROTO(ea) ((ea) >> 8) #define EA_ID(ea) ((ea) & 0xff) +#define EA_PROTO(ea) ((ea) >> 8) +#define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT) +#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT) +#define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT) + +const char *ea_custom_name(uint ea); #define EA_GEN_IGP_METRIC EA_CODE(PROTOCOL_NONE, 0) #define EA_CODE_MASK 0xffff +#define EA_CUSTOM_BIT 0x8000 #define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */ #define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 7a91a4f6..eeeaaa4c 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -884,7 +884,18 @@ ea_show(struct cli *c, eattr *e) byte buf[CLI_MSG_SIZE]; byte *pos = buf, *end = buf + sizeof(buf); - if (p = class_to_protocol[EA_PROTO(e->id)]) + if (EA_IS_CUSTOM(e->id)) + { + const char *name = ea_custom_name(e->id); + if (name) + { + pos += bsprintf(pos, "%s", name); + status = GA_NAME; + } + else + pos += bsprintf(pos, "%02x.", EA_PROTO(e->id)); + } + else if (p = class_to_protocol[EA_PROTO(e->id)]) { pos += bsprintf(pos, "%s.", p->name); if (p->get_attr) |