Custom route attributes
For local route marking purposes, local custom route attributes may be defined. These attributes are seamlessly stripped after export filter to every real protocol like Kernel, BGP or OSPF, they however pass through pipes. We currently allow at most 256 custom attributes. This should be much faster than currently used bgp communities for marking routes.
This commit is contained in:
parent
0642fb4d45
commit
265419a369
9 changed files with 215 additions and 21 deletions
|
@ -15,7 +15,6 @@
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
#include "lib/timer.h"
|
#include "lib/timer.h"
|
||||||
|
|
||||||
|
|
||||||
/* Configuration structure */
|
/* Configuration structure */
|
||||||
|
|
||||||
struct config {
|
struct config {
|
||||||
|
@ -128,9 +127,12 @@ struct sym_scope {
|
||||||
#define SYM_FUNCTION 3
|
#define SYM_FUNCTION 3
|
||||||
#define SYM_FILTER 4
|
#define SYM_FILTER 4
|
||||||
#define SYM_TABLE 5
|
#define SYM_TABLE 5
|
||||||
|
#define SYM_ATTRIBUTE 6
|
||||||
|
|
||||||
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
|
#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 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_TYPE(s) (((struct f_val *) (s)->def)->type)
|
||||||
#define SYM_VAL(s) (((struct f_val *) (s)->def)->val)
|
#define SYM_VAL(s) (((struct f_val *) (s)->def)->val)
|
||||||
|
|
|
@ -25,7 +25,7 @@ configuration - something in config which is not keyword.
|
||||||
Ondrej Filip <it/<feela@network.cz>/,
|
Ondrej Filip <it/<feela@network.cz>/,
|
||||||
Pavel Machek <it/<pavel@ucw.cz>/,
|
Pavel Machek <it/<pavel@ucw.cz>/,
|
||||||
Martin Mares <it/<mj@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>/
|
Ondrej Zajicek <it/<santiago@crfreenet.org>/
|
||||||
</author>
|
</author>
|
||||||
|
|
||||||
|
@ -550,6 +550,12 @@ include "tablename.conf";;
|
||||||
constants based on /etc/iproute2/rt_* files. A list of defined constants
|
constants based on /etc/iproute2/rt_* files. A list of defined constants
|
||||||
can be seen (together with other symbols) using 'show symbols' command.
|
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>
|
<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
|
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
|
router, usually one of router's IPv4 addresses. Default: the lowest
|
||||||
|
|
|
@ -417,7 +417,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||||
ADD, DELETE, CONTAINS, RESET,
|
ADD, DELETE, CONTAINS, RESET,
|
||||||
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
|
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
|
||||||
EMPTY,
|
EMPTY,
|
||||||
FILTER, WHERE, EVAL,
|
FILTER, WHERE, EVAL, ATTRIBUTE,
|
||||||
BT_ASSERT, BT_TEST_SUITE, FORMAT)
|
BT_ASSERT, BT_TEST_SUITE, FORMAT)
|
||||||
|
|
||||||
%nonassoc THEN
|
%nonassoc THEN
|
||||||
|
@ -455,6 +455,11 @@ filter_eval:
|
||||||
EVAL term { f_eval_int($2); }
|
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 ;
|
conf: bt_test_suite ;
|
||||||
bt_test_suite:
|
bt_test_suite:
|
||||||
BT_TEST_SUITE '(' SYM ',' text ')' {
|
BT_TEST_SUITE '(' SYM ',' text ')' {
|
||||||
|
@ -834,14 +839,22 @@ function_call:
|
||||||
|
|
||||||
symbol:
|
symbol:
|
||||||
SYM {
|
SYM {
|
||||||
switch ($1->class & 0xff00) {
|
switch ($1->class & 0xffff) {
|
||||||
case SYM_CONSTANT: $$ = f_new_inst(FI_CONSTANT_INDIRECT); break;
|
case SYM_CONSTANT_RANGE:
|
||||||
case SYM_VARIABLE: $$ = f_new_inst(FI_VARIABLE); break;
|
$$ = f_new_inst(FI_CONSTANT_INDIRECT);
|
||||||
default: cf_error("%s: variable expected.", $1->name);
|
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:
|
static_attr:
|
||||||
|
@ -1001,11 +1014,15 @@ cmd:
|
||||||
}
|
}
|
||||||
| SYM '=' term ';' {
|
| SYM '=' term ';' {
|
||||||
DBG( "Ook, we'll set value\n" );
|
DBG( "Ook, we'll set value\n" );
|
||||||
if (($1->class & ~T_MASK) != SYM_VARIABLE)
|
if ($1->class == SYM_ATTRIBUTE) {
|
||||||
cf_error( "You may set only variables." );
|
$$ = f_new_inst_da(FI_EA_SET, *((struct f_dynamic_attr *) $1->def));
|
||||||
$$ = f_new_inst(FI_SET);
|
$$->a1.p = $3;
|
||||||
$$->a1.p = $1;
|
} else if (($1->class & ~T_MASK) == SYM_VARIABLE) {
|
||||||
$$->a2.p = $3;
|
$$ = f_new_inst(FI_SET);
|
||||||
|
$$->a1.p = $1;
|
||||||
|
$$->a2.p = $3;
|
||||||
|
} else
|
||||||
|
cf_error( "Symbol `%s' is read-only.", $1->name );
|
||||||
}
|
}
|
||||||
| RETURN term ';' {
|
| RETURN term ';' {
|
||||||
DBG( "Ook, we'll return the value\n" );
|
DBG( "Ook, we'll return the value\n" );
|
||||||
|
|
144
filter/f-util.c
144
filter/f-util.c
|
@ -10,6 +10,9 @@
|
||||||
#include "nest/bird.h"
|
#include "nest/bird.h"
|
||||||
#include "conf/conf.h"
|
#include "conf/conf.h"
|
||||||
#include "filter/filter.h"
|
#include "filter/filter.h"
|
||||||
|
#include "lib/idm.h"
|
||||||
|
#include "nest/protocol.h"
|
||||||
|
#include "nest/route.h"
|
||||||
|
|
||||||
#define P(a,b) ((a<<8) | b)
|
#define P(a,b) ((a<<8) | b)
|
||||||
|
|
||||||
|
@ -105,3 +108,144 @@ filter_name(struct filter *filter)
|
||||||
else
|
else
|
||||||
return filter->name;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1063,7 +1063,7 @@ interpret(struct f_inst *what)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (what->aux & EAF_TYPE_MASK) {
|
switch (e->type & EAF_TYPE_MASK) {
|
||||||
case EAF_TYPE_INT:
|
case EAF_TYPE_INT:
|
||||||
res.type = f_type;
|
res.type = f_type;
|
||||||
res.val.i = e->u.data;
|
res.val.i = e->u.data;
|
||||||
|
|
|
@ -287,6 +287,15 @@ struct f_trie
|
||||||
|
|
||||||
#define FF_SILENT 2 /* Silent filter execution */
|
#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 */
|
/* Bird Tests */
|
||||||
struct f_bt_test_suite {
|
struct f_bt_test_suite {
|
||||||
node n; /* Node in config->tests */
|
node n; /* Node in config->tests */
|
||||||
|
|
|
@ -532,6 +532,7 @@ r_args:
|
||||||
$$ = cfg_allocz(sizeof(struct rt_show_data));
|
$$ = cfg_allocz(sizeof(struct rt_show_data));
|
||||||
init_list(&($$->tables));
|
init_list(&($$->tables));
|
||||||
$$->filter = FILTER_ACCEPT;
|
$$->filter = FILTER_ACCEPT;
|
||||||
|
$$->running_on_config = new_config->fallback;
|
||||||
}
|
}
|
||||||
| r_args net_any {
|
| r_args net_any {
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
|
@ -586,7 +587,6 @@ r_args:
|
||||||
if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
|
if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
|
||||||
$$->export_mode = $2;
|
$$->export_mode = $2;
|
||||||
$$->export_protocol = c->proto;
|
$$->export_protocol = c->proto;
|
||||||
$$->running_on_config = c->proto->cf->global;
|
|
||||||
$$->tables_defined_by = RSD_TDB_INDIRECT;
|
$$->tables_defined_by = RSD_TDB_INDIRECT;
|
||||||
}
|
}
|
||||||
| r_args export_mode SYM '.' r_args_channel {
|
| r_args export_mode SYM '.' r_args_channel {
|
||||||
|
@ -597,7 +597,6 @@ r_args:
|
||||||
$$->export_mode = $2;
|
$$->export_mode = $2;
|
||||||
$$->export_channel = proto_find_channel_by_name(c->proto, $5);
|
$$->export_channel = proto_find_channel_by_name(c->proto, $5);
|
||||||
if (!$$->export_channel) cf_error("Export channel not found");
|
if (!$$->export_channel) cf_error("Export channel not found");
|
||||||
$$->running_on_config = c->proto->cf->global;
|
|
||||||
$$->tables_defined_by = RSD_TDB_INDIRECT;
|
$$->tables_defined_by = RSD_TDB_INDIRECT;
|
||||||
}
|
}
|
||||||
| r_args PROTOCOL SYM {
|
| r_args PROTOCOL SYM {
|
||||||
|
@ -606,7 +605,6 @@ r_args:
|
||||||
if ($$->show_protocol) cf_error("Protocol specified twice");
|
if ($$->show_protocol) cf_error("Protocol specified twice");
|
||||||
if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
|
if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
|
||||||
$$->show_protocol = c->proto;
|
$$->show_protocol = c->proto;
|
||||||
$$->running_on_config = c->proto->cf->global;
|
|
||||||
$$->tables_defined_by = RSD_TDB_INDIRECT;
|
$$->tables_defined_by = RSD_TDB_INDIRECT;
|
||||||
}
|
}
|
||||||
| r_args STATS {
|
| r_args STATS {
|
||||||
|
|
|
@ -471,13 +471,20 @@ typedef struct eattr {
|
||||||
} u;
|
} u;
|
||||||
} eattr;
|
} eattr;
|
||||||
|
|
||||||
|
|
||||||
#define EA_CODE(proto,id) (((proto) << 8) | (id))
|
#define EA_CODE(proto,id) (((proto) << 8) | (id))
|
||||||
#define EA_PROTO(ea) ((ea) >> 8)
|
|
||||||
#define EA_ID(ea) ((ea) & 0xff)
|
#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_GEN_IGP_METRIC EA_CODE(PROTOCOL_NONE, 0)
|
||||||
|
|
||||||
#define EA_CODE_MASK 0xffff
|
#define EA_CODE_MASK 0xffff
|
||||||
|
#define EA_CUSTOM_BIT 0x8000
|
||||||
#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */
|
#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */
|
||||||
#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */
|
#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */
|
||||||
|
|
||||||
|
|
|
@ -884,7 +884,18 @@ ea_show(struct cli *c, eattr *e)
|
||||||
byte buf[CLI_MSG_SIZE];
|
byte buf[CLI_MSG_SIZE];
|
||||||
byte *pos = buf, *end = buf + sizeof(buf);
|
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);
|
pos += bsprintf(pos, "%s.", p->name);
|
||||||
if (p->get_attr)
|
if (p->get_attr)
|
||||||
|
|
Loading…
Reference in a new issue