Merge branch 'mq-custom' into int-new
This commit is contained in:
commit
9a5ef043c1
9 changed files with 215 additions and 21 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" );
|
||||
|
|
144
filter/f-util.c
144
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue