From: Maria Matejka Date: Sun, 29 Oct 2023 14:42:46 +0000 (+0100) Subject: Merge commit '0a729b50' into thread-next X-Git-Tag: v3.0.0~369 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c5f6dc8142e7139a8b16f95e572be7f9d87ba5bd;p=thirdparty%2Fbird.git Merge commit '0a729b50' into thread-next This merge was particularly difficult. I finally resorted to delete the symbol scope active flag altogether and replace its usage by other means. Also I had to update custom route attribute registration to fit both the scope updates in v2 and the data model in v3. --- c5f6dc8142e7139a8b16f95e572be7f9d87ba5bd diff --cc conf/cf-lex.l index c99bd7144,28479ff38..2a25e6298 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@@ -73,20 -73,9 +73,18 @@@ static uint cf_hash(const byte *c) HASH_DEFINE_REHASH_FN(SYM, struct symbol) -struct sym_scope *global_root_scope; +/* Global symbol scopes */ pool *global_root_scope_pool; linpool *global_root_scope_linpool; +static struct sym_scope + global_root_scope = { - .active = 1, + }, + global_filter_scope = { - .active = 0, + .next = &global_root_scope, + }; + +/* Local symbol scope: TODO this isn't thread-safe */ +struct sym_scope *conf_this_scope; linpool *cfg_mem; @@@ -634,7 -598,7 +632,8 @@@ cf_find_symbol_scope(const struct sym_s /* Find the symbol here or anywhere below */ while (scope) - if (scope->active && scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c))) - if (scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c))) ++ if (((scope != &global_filter_scope) || !new_config || new_config->allow_attributes) && ++ scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c))) return s; else scope = scope->next; @@@ -727,37 -691,6 +726,33 @@@ cf_lex_symbol(const char *data } } +void +ea_lex_register(struct ea_class *def) +{ + def->sym = cf_root_symbol(def->name, &global_filter_scope); + def->sym->class = SYM_ATTRIBUTE; + def->sym->attribute = def; +} + ++#if 0 ++/* When we start to support complete protocol removal, we may need this function */ +void +ea_lex_unregister(struct ea_class *def) +{ + struct symbol *sym = def->sym; + HASH_REMOVE2(global_filter_scope.hash, SYM, &root_pool, sym); + mb_free(sym); + def->sym = NULL; +} ++#endif + +struct ea_class * +ea_class_find_by_name(const char *name) +{ - if (!global_filter_scope.hash.data) - return NULL; - - struct symbol *sym = HASH_FIND(global_filter_scope.hash, SYM, name); - - if (!sym || (sym->class != SYM_ATTRIBUTE)) - return NULL; - else - return sym->attribute; ++ struct symbol *sym = cf_find_symbol_scope(config ? config->root_scope : &global_filter_scope, name); ++ return sym && (sym->class == SYM_ATTRIBUTE) ? sym->attribute : NULL; +} + void f_type_methods_register(void); /** @@@ -828,7 -763,7 +822,6 @@@ cf_push_scope(struct config *conf, stru s->next = conf->current_scope; conf->current_scope = s; -- s->active = 1; s->name = sym; s->slots = 0; } @@@ -844,10 -779,10 +837,7 @@@ voi cf_pop_scope(struct config *conf) { ASSERT(!conf->current_scope->soft_scopes); -- -- conf->current_scope->active = 0; conf->current_scope = conf->current_scope->next; -- ASSERT(conf->current_scope); } @@@ -897,27 -832,6 +887,27 @@@ cf_swap_soft_scope(struct config *conf } } +/** + * cf_enter_filters - enable filter / route attributes namespace + */ +void +cf_enter_filters(void) +{ - ASSERT_DIE(!global_filter_scope.active); - global_filter_scope.active = 1; ++ ASSERT_DIE(!new_config->allow_attributes); ++ new_config->allow_attributes = 1; +} + +/** + * cf_exit_filters - disable filter / route attributes namespace + */ +void +cf_exit_filters(void) +{ - ASSERT_DIE(global_filter_scope.active); - global_filter_scope.active = 0; ++ ASSERT_DIE(new_config->allow_attributes); ++ new_config->allow_attributes = 0; +} + + /** * cf_symbol_class_name - get name of a symbol class * @sym: symbol diff --cc conf/conf.c index d828b6df9,b9239d9be..c76046ee6 --- a/conf/conf.c +++ b/conf/conf.c @@@ -60,7 -60,7 +60,8 @@@ static jmp_buf conf_jmpbuf; --struct config *config, *new_config; ++struct config *config; ++_Thread_local struct config *new_config; pool *config_pool; static struct config *old_config; /* Old configuration */ diff --cc conf/conf.h index b5168873b,486499ad1..841d5c1fc --- a/conf/conf.h +++ b/conf/conf.h @@@ -58,8 -55,7 +58,9 @@@ struct config struct sym_scope *root_scope; /* Scope for root symbols */ struct sym_scope *current_scope; /* Current scope where we are actually in while parsing */ - int obstacle_count; /* Number of items blocking freeing of this config */ ++ int allow_attributes; /* Allow attributes in the current state of configuration parsing */ + _Atomic int obstacle_count; /* Number of items blocking freeing of this config */ + event done_event; /* Called when obstacle_count reaches zero */ int shutdown; /* This is a pseudo-config for daemon shutdown */ int gr_down; /* This is a pseudo-config for graceful restart */ btime load_time; /* When we've got this configuration */ @@@ -67,7 -63,7 +68,7 @@@ /* Please don't use these variables in protocols. Use proto_config->global instead. */ extern struct config *config; /* Currently active configuration */ --extern struct config *new_config; /* Configuration being parsed */ ++extern _Thread_local struct config *new_config; /* Configuration being parsed */ struct config *config_alloc(const char *name); int config_parse(struct config *); @@@ -149,7 -145,7 +150,6 @@@ struct sym_scope uint slots; /* Variable slots */ byte soft_scopes; /* Number of soft scopes above */ -- byte active:1; /* Currently entered */ byte block:1; /* No independent stack frame */ byte readonly:1; /* Do not add new symbols */ }; diff --cc filter/config.Y index 76e804361,dfabddf7b..f27d9f58b --- a/filter/config.Y +++ b/filter/config.Y @@@ -28,6 -28,6 +28,8 @@@ static struct f_method_scope } f_method_scope_stack[32]; static int f_method_scope_pos = -1; ++static struct sym_scope *f_for_stored_scope; ++ #define FM (f_method_scope_stack[f_method_scope_pos]) static inline void f_method_call_start(struct f_inst *object) @@@ -41,13 -41,13 +43,14 @@@ if (!scope) cf_error("No methods defined for type %s", f_type_name(object->type)); ++ /* Replacing the current symbol scope with the appropriate method scope ++ for the given type. */ FM = (struct f_method_scope) { .object = object, .main = new_config->current_scope, .scope = { .next = NULL, .hash = scope->hash, -- .active = 1, .block = 1, .readonly = 1, }, @@@ -57,22 -57,22 +60,16 @@@ static inline void f_method_call_args(void) { -- ASSERT_DIE(FM.scope.active); -- FM.scope.active = 0; -- ++ /* For argument parsing, we need to revert back to the standard symbol scope. */ new_config->current_scope = FM.main; } static inline void f_method_call_end(void) { ASSERT_DIE(f_method_scope_pos >= 0); -- if (FM.scope.active) { -- ASSERT_DIE(&FM.scope == new_config->current_scope); ++ if (&FM.scope == new_config->current_scope) new_config->current_scope = FM.main; -- FM.scope.active = 0; -- } -- f_method_scope_pos--; } @@@ -423,14 -399,7 +420,18 @@@ filter_eval conf: custom_attr ; custom_attr: ATTRIBUTE type symbol ';' { - if (($3->class == SYM_ATTRIBUTE) && ($3->scope == new_config->root_scope)) - cf_error("Duplicate attribute %s definition", $3->name); - - cf_define_symbol(new_config, $3, SYM_ATTRIBUTE, attribute, - ea_register_alloc(new_config->pool, (struct ea_class) { - cf_define_symbol(new_config, $3, SYM_ATTRIBUTE, attribute, ca_lookup(new_config->pool, $3->name, $2)->fda); ++ cf_enter_filters(); ++ struct ea_class *ac = ea_class_find_by_name($3->name); ++ cf_exit_filters(); ++ if (ac && (ac->type == $2)) ++ ea_ref_class(new_config->pool, ac); ++ else ++ ac = ea_register_alloc(new_config->pool, (struct ea_class) { + .name = $3->name, + .type = $2, - })->class); ++ })->class; ++ ++ cf_define_symbol(new_config, $3, SYM_ATTRIBUTE, attribute, ac); }; conf: bt_test_suite ; @@@ -1003,7 -958,7 +1004,15 @@@ cmd new_config->current_scope->slots += 2; } for_var IN /* Parse term in the parent scope */ -- { new_config->current_scope->active = 0; } term { new_config->current_scope->active = 1; } ++ { ++ ASSERT_DIE(f_for_stored_scope == NULL); ++ f_for_stored_scope = new_config->current_scope; ++ new_config->current_scope = new_config->current_scope->next; ++ } term { ++ ASSERT_DIE(f_for_stored_scope); ++ new_config->current_scope = f_for_stored_scope; ++ f_for_stored_scope = NULL; ++ } DO cmd { cf_pop_block_scope(new_config); $$ = f_for_cycle($3, $6, $9); diff --cc filter/decl.m4 index 2cb09f28c,57bf94546..0d3b83fe4 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@@ -424,9 -423,9 +424,9 @@@ m4_undivert(112 } FID_METHOD_SCOPE_INIT()m4_dnl - [INST_METHOD_OBJECT_TYPE] = { .active = 1, }, + [INST_METHOD_OBJECT_TYPE] = {}, FID_METHOD_REGISTER()m4_dnl - method = lp_allocz(global_root_scope_linpool, sizeof(struct f_method) + INST_METHOD_NUM_ARGS * sizeof(enum f_type)); + method = lp_allocz(global_root_scope_linpool, sizeof(struct f_method) + INST_METHOD_NUM_ARGS * sizeof(enum btype)); method->new_inst = f_new_method_]]INST_NAME()[[; method->arg_num = INST_METHOD_NUM_ARGS; m4_undivert(113) diff --cc filter/f-inst.c index 6baa1fcb3,a7bec81ed..8bbb04918 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@@ -868,24 -846,56 +868,24 @@@ INST(FI_EA_SET, 1, 0) { ACCESS_RTE; - ACCESS_EATTRS; ARG_ANY(1); DYNAMIC_ATTR; - ARG_TYPE(1, da.f_type); + ARG_TYPE(1, da->type); { - struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); - - l->next = NULL; - l->flags = EALF_SORTED; - l->count = 1; - l->attrs[0].id = da.ea_code; - l->attrs[0].flags = da.flags; - l->attrs[0].type = da.type; - l->attrs[0].originated = 1; - l->attrs[0].fresh = 1; - l->attrs[0].undef = 0; - - switch (da.type) { - case EAF_TYPE_INT: - case EAF_TYPE_ROUTER_ID: - l->attrs[0].u.data = v1.val.i; - break; + struct eattr *a; - case EAF_TYPE_IP_ADDRESS:; - int len = sizeof(ip_addr); - struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len); - ad->length = len; - (* (ip_addr *) ad->data) = v1.val.ip; - l->attrs[0].u.ptr = ad; - break; + if (da->type >= EAF_TYPE__MAX) + bug("Unsupported attribute type"); - case EAF_TYPE_OPAQUE: - case EAF_TYPE_AS_PATH: - case EAF_TYPE_INT_SET: - case EAF_TYPE_EC_SET: - case EAF_TYPE_LC_SET: - l->attrs[0].u.ptr = v1.val.ad; + switch (da->type) { - case T_OPAQUE: + case T_IFACE: ++ case T_OPAQUE: + runtime( "Setting opaque attribute is not allowed" ); break; - case EAF_TYPE_BITFIELD: - { - /* First, we have to find the old value */ - eattr *e = ea_find(*fs->eattrs, da.ea_code); - u32 data = e ? e->u.data : 0; - - if (v1.val.i) - l->attrs[0].u.data = data | (1u << da.bit); - else - l->attrs[0].u.data = data & ~(1u << da.bit); - } + case T_IP: + a = ea_set_attr(&fs->rte->attrs, + EA_LITERAL_STORE_ADATA(da, 0, &v1.val.ip, sizeof(ip_addr))); break; default: diff --cc filter/f-inst.h index a476c998e,955cfbdc7..82767a929 --- a/filter/f-inst.h +++ b/filter/f-inst.h @@@ -107,15 -107,32 +107,16 @@@ void f_add_lines(const struct f_line_it struct filter *f_new_where(struct f_inst *); struct f_inst *f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args, int skip); -struct f_inst *f_dispatch_method_x(const char *name, enum f_type t, struct f_inst *obj, struct f_inst *args); +struct f_inst *f_dispatch_method_x(const char *name, enum btype t, struct f_inst *obj, struct f_inst *args); struct f_inst *f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block); +struct f_inst *f_implicit_roa_check(struct rtable_config *tab); struct f_inst *f_print(struct f_inst *vars, int flush, enum filter_return fret); -static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ -{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ -static inline struct f_dynamic_attr f_new_dynamic_attr_bit(u8 bit, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ -{ return (struct f_dynamic_attr) { .type = EAF_TYPE_BITFIELD, .bit = bit, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ -static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly) -{ return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; } - -static inline int f_type_attr(int f_type) { - switch (f_type) { - case T_INT: return EAF_TYPE_INT; - case T_IP: return EAF_TYPE_IP_ADDRESS; - case T_QUAD: return EAF_TYPE_ROUTER_ID; - case T_PATH: return EAF_TYPE_AS_PATH; - case T_CLIST: return EAF_TYPE_INT_SET; - case T_ECLIST: return EAF_TYPE_EC_SET; - case T_LCLIST: return EAF_TYPE_LC_SET; - case T_BYTESTRING: return EAF_TYPE_OPAQUE; - default: - cf_error("Custom route attribute of unsupported type"); - } -} +static inline struct f_static_attr f_new_static_attr(btype type, int code, int readonly) +{ return (struct f_static_attr) { .type = type, .sa_code = code, .readonly = readonly }; } +struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn); + + /* Hook for call bt_assert() function in configuration */ extern void (*bt_assert_hook)(int result, const struct f_line_item *assert); diff --cc filter/filter_test.c index aa325aef2,782b3c029..65b2e4f0f --- a/filter/filter_test.c +++ b/filter/filter_test.c @@@ -79,7 -78,9 +79,9 @@@ main(int argc, char *argv[] if (!bt_config_file_parse(BT_CONFIG_FILE)) abort(); - bt_test_suite_extra(t_reconfig, 0, BT_TIMEOUT, "Testing reconfiguration"); - bt_test_suite_arg(t_reconfig, BT_CONFIG_FILE ".overlay", "Testing reconfiguration to overlay"); - bt_test_suite_arg(t_reconfig, BT_CONFIG_FILE, "Testing reconfiguration back"); - bt_test_suite_arg(t_reconfig, BT_CONFIG_FILE, "Testing reconfiguration to the same file"); ++ bt_test_suite_arg_extra(t_reconfig, BT_CONFIG_FILE ".overlay", 0, BT_TIMEOUT, "Testing reconfiguration to overlay"); ++ bt_test_suite_arg_extra(t_reconfig, BT_CONFIG_FILE, 0, BT_TIMEOUT, "Testing reconfiguration back"); ++ bt_test_suite_arg_extra(t_reconfig, BT_CONFIG_FILE, 0, BT_TIMEOUT, "Testing reconfiguration to the same file"); struct f_bt_test_suite *t; WALK_LIST(t, config->tests) diff --cc lib/route.h index f0934f061,000000000..cb1e3f4e3 mode 100644,000000..100644 --- a/lib/route.h +++ b/lib/route.h @@@ -1,564 -1,0 +1,565 @@@ +/* + * BIRD Internet Routing Daemon -- Routing data structures + * + * (c) 1998--2000 Martin Mares + * (c) 2022 Maria Matejka + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_LIB_ROUTE_H_ +#define _BIRD_LIB_ROUTE_H_ + +#undef RT_SOURCE_DEBUG + +#include "lib/type.h" +#include "lib/rcu.h" +#include "lib/hash.h" +#include "lib/event.h" + +struct network; +struct proto; +struct cli; +struct rtable_private; +struct rte_storage; + +#define RTE_IN_TABLE_WRITABLE \ + byte pflags; /* Protocol-specific flags; may change in-table (!) */ \ + u8 stale_cycle; /* Auxiliary value for route refresh; may change in-table (!) */ \ + +typedef struct rte { + RTE_IN_TABLE_WRITABLE; + byte flags; /* Table-specific flags */ + u8 generation; /* If this route import is based on other previously exported route, + this value should be 1 + MAX(generation of the parent routes). + Otherwise the route is independent and this value is zero. */ + u32 id; /* Table specific route id */ + struct ea_list *attrs; /* Attributes of this route */ + const net_addr *net; /* Network this RTE belongs to */ + struct rte_src *src; /* Route source that created the route */ + struct rt_import_hook *sender; /* Import hook used to send the route to the routing table */ + btime lastmod; /* Last modified (set by table) */ +} rte; + +#define REF_FILTERED 2 /* Route is rejected by import filter */ +#define REF_PENDING 32 /* Route has not propagated completely yet */ + +/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */ +static inline int rte_is_valid(const rte *r) { return r && !(r->flags & REF_FILTERED); } + +/* Route just has REF_FILTERED flag */ +static inline int rte_is_filtered(const rte *r) { return !!(r->flags & REF_FILTERED); } + +/* Strip the route of the table-specific values */ +static inline rte rte_init_from(const rte *r) +{ + return (rte) { + .attrs = r->attrs, + .net = r->net, + .src = r->src, + }; +} + +struct rte_src { + struct rte_src *next; /* Hash chain */ + struct rte_owner *owner; /* Route source owner */ + u32 private_id; /* Private ID, assigned by the protocol */ + u32 global_id; /* Globally unique ID of the source */ + _Atomic u64 uc; /* Use count */ +}; + +struct rte_owner_class { + void (*get_route_info)(const rte *, byte *buf); /* Get route information (for `show route' command) */ + int (*rte_better)(const rte *, const rte *); + int (*rte_mergable)(const rte *, const rte *); + u32 (*rte_igp_metric)(const rte *); +}; + +struct rte_owner { + struct rte_owner_class *class; + int (*rte_recalculate)(struct rtable_private *, struct network *, struct rte_storage *new, struct rte_storage *, struct rte_storage *); + HASH(struct rte_src) hash; + const char *name; + u32 hash_key; + u32 uc; + event_list *list; + event *prune; + event *stop; +}; + +DEFINE_DOMAIN(attrs); +extern DOMAIN(attrs) attrs_domain; + +#define RTA_LOCK LOCK_DOMAIN(attrs, attrs_domain) +#define RTA_UNLOCK UNLOCK_DOMAIN(attrs, attrs_domain) + +#define RTE_SRC_PU_SHIFT 44 +#define RTE_SRC_IN_PROGRESS (1ULL << RTE_SRC_PU_SHIFT) + +/* Get a route source. This also locks the source, therefore the caller has to + * unlock the source after the route has been propagated. */ +struct rte_src *rt_get_source_o(struct rte_owner *o, u32 id); +#define rt_get_source(p, id) rt_get_source_o(&(p)->sources, (id)) + +struct rte_src *rt_find_source_global(u32 id); + +#ifdef RT_SOURCE_DEBUG +#define rt_lock_source _rt_lock_source_internal +#define rt_unlock_source _rt_unlock_source_internal +#endif + +static inline void rt_lock_source(struct rte_src *src) +{ + /* Locking a source is trivial; somebody already holds it so we just increase + * the use count. Nothing can be freed underneath our hands. */ + u64 uc = atomic_fetch_add_explicit(&src->uc, 1, memory_order_acq_rel); + ASSERT_DIE(uc > 0); +} + +static inline void rt_unlock_source(struct rte_src *src) +{ + /* Unlocking is tricky. We do it lockless so at the same time, the prune + * event may be running, therefore if the unlock gets us to zero, it must be + * the last thing in this routine, otherwise the prune routine may find the + * source's usecount zeroed, freeing it prematurely. + * + * The usecount is split into two parts: + * the top 20 bits are an in-progress indicator + * the bottom 44 bits keep the actual usecount. + * + * Therefore at most 1 million of writers can simultaneously unlock the same + * source, while at most ~17T different routes can reference it. Both limits + * are insanely high from the 2022 point of view. Let's suppose that when 17T + * routes or 1M writers get real, we get also 128bit atomic variables in the + * C norm. */ + + /* First, we push the in-progress indicator */ + u64 uc = atomic_fetch_add_explicit(&src->uc, RTE_SRC_IN_PROGRESS, memory_order_acq_rel); + + /* Then we split the indicator to its parts. Remember, we got the value before the operation happened. */ + u64 pending = (uc >> RTE_SRC_PU_SHIFT) + 1; + uc &= RTE_SRC_IN_PROGRESS - 1; + + /* We per-use the RCU critical section indicator to make the prune event wait + * until we finish here in the rare case we get preempted. */ + rcu_read_lock(); + + /* Obviously, there can't be more pending unlocks than the usecount itself */ + if (uc == pending) + /* If we're the last unlocker, schedule the owner's prune event */ + ev_send(src->owner->list, src->owner->prune); + else + ASSERT_DIE(uc > pending); + + /* And now, finally, simultaneously pop the in-progress indicator and the + * usecount, possibly allowing the source pruning routine to free this structure */ + atomic_fetch_sub_explicit(&src->uc, RTE_SRC_IN_PROGRESS + 1, memory_order_acq_rel); + + /* ... and to reduce the load a bit, the source pruning routine will better wait for + * RCU synchronization instead of a busy loop. */ + rcu_read_unlock(); +} + +#ifdef RT_SOURCE_DEBUG +#undef rt_lock_source +#undef rt_unlock_source + +#define rt_lock_source(x) ( log(L_INFO "Lock source %uG at %s:%d", (x)->global_id, __FILE__, __LINE__), _rt_lock_source_internal(x) ) +#define rt_unlock_source(x) ( log(L_INFO "Unlock source %uG at %s:%d", (x)->global_id, __FILE__, __LINE__), _rt_unlock_source_internal(x) ) +#endif + +void rt_init_sources(struct rte_owner *, const char *name, event_list *list); +void rt_destroy_sources(struct rte_owner *, event *); + +/* + * Route Attributes + * + * Beware: All standard BGP attributes must be represented here instead + * of making them local to the route. This is needed to ensure proper + * construction of BGP route attribute lists. + */ + +/* Nexthop structure */ +struct nexthop { + ip_addr gw; /* Next hop */ + struct iface *iface; /* Outgoing interface */ + byte flags; + byte weight; + byte labels; /* Number of all labels */ + u32 label[0]; +}; + +/* For packing one into eattrs */ +struct nexthop_adata { + struct adata ad; + /* There is either a set of nexthops or a special destination (RTD_*) */ + union { + struct nexthop nh; + uint dest; + }; +}; + +#define NEXTHOP_DEST_SIZE (OFFSETOF(struct nexthop_adata, dest) + sizeof(uint) - OFFSETOF(struct adata, data)) +#define NEXTHOP_DEST_LITERAL(x) ((struct nexthop_adata) { \ + .ad.length = NEXTHOP_DEST_SIZE, .dest = (x), }) + +#define RNF_ONLINK 0x1 /* Gateway is onlink regardless of IP ranges */ + + +#define RTS_STATIC 1 /* Normal static route */ +#define RTS_INHERIT 2 /* Route inherited from kernel */ +#define RTS_DEVICE 3 /* Device route */ +#define RTS_STATIC_DEVICE 4 /* Static device route */ +#define RTS_REDIRECT 5 /* Learned via redirect */ +#define RTS_RIP 6 /* RIP route */ +#define RTS_OSPF 7 /* OSPF route */ +#define RTS_OSPF_IA 8 /* OSPF inter-area route */ +#define RTS_OSPF_EXT1 9 /* OSPF external route type 1 */ +#define RTS_OSPF_EXT2 10 /* OSPF external route type 2 */ +#define RTS_BGP 11 /* BGP route */ +#define RTS_PIPE 12 /* Inter-table wormhole */ +#define RTS_BABEL 13 /* Babel route */ +#define RTS_RPKI 14 /* Route Origin Authorization */ +#define RTS_PERF 15 /* Perf checker */ +#define RTS_MAX 16 + +#define RTD_NONE 0 /* Undefined next hop */ +#define RTD_UNICAST 1 /* A standard next hop */ +#define RTD_BLACKHOLE 2 /* Silently drop packets */ +#define RTD_UNREACHABLE 3 /* Reject as unreachable */ +#define RTD_PROHIBIT 4 /* Administratively prohibited */ +#define RTD_MAX 5 + +extern const char * rta_dest_names[RTD_MAX]; + +static inline const char *rta_dest_name(uint n) +{ return (n < RTD_MAX) ? rta_dest_names[n] : "???"; } + + +/* + * Extended Route Attributes + */ + +typedef struct eattr { + word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */ + byte flags; /* Protocol-dependent flags */ + byte type; /* Attribute type */ + byte rfu:5; + byte originated:1; /* The attribute has originated locally */ + byte fresh:1; /* An uncached attribute (e.g. modified in export filter) */ + byte undef:1; /* Explicitly undefined */ + + PADDING(unused, 3, 3); + + union bval u; +} eattr; + + +#define EA_CODE_MASK 0xffff +#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */ +#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */ +#define EA_BIT_GET(ea) ((ea) >> 24) + +typedef struct ea_list { + struct ea_list *next; /* In case we have an override list */ + byte flags; /* Flags: EALF_... */ + byte rfu; + word count; /* Number of attributes */ + eattr attrs[0]; /* Attribute definitions themselves */ +} ea_list; + +struct ea_storage { + struct ea_storage *next_hash; /* Next in hash chain */ + struct ea_storage **pprev_hash; /* Previous in hash chain */ + _Atomic u32 uc; /* Use count */ + u32 hash_key; /* List hash */ + ea_list l[0]; /* The list itself */ +}; + +#define EALF_SORTED 1 /* Attributes are sorted by code */ +#define EALF_BISECT 2 /* Use interval bisection for searching */ +#define EALF_CACHED 4 /* List is cached */ +#define EALF_HUGE 8 /* List is too big to fit into slab */ + +struct ea_class { +#define EA_CLASS_INSIDE \ + const char *name; /* Name (both print and filter) */ \ + struct symbol *sym; /* Symbol to export to configs */ \ + uint id; /* Autoassigned attribute ID */ \ + uint uc; /* Reference count */ \ + btype type; /* Data type ID */ \ + uint readonly:1; /* This attribute can't be changed by filters */ \ + uint conf:1; /* Requested by config */ \ + uint hidden:1; /* Technical attribute, do not show, do not expose to filters */ \ + void (*format)(const eattr *ea, byte *buf, uint size); \ + void (*stored)(const eattr *ea); /* When stored into global hash */ \ + void (*freed)(const eattr *ea); /* When released from global hash */ \ + + EA_CLASS_INSIDE; +}; + +struct ea_class_ref { + resource r; + struct ea_class *class; +}; + +void ea_register_init(struct ea_class *); +struct ea_class_ref *ea_register_alloc(pool *, struct ea_class); ++struct ea_class_ref *ea_ref_class(pool *, struct ea_class *); /* Reference for an attribute alias */ + +#define EA_REGISTER_ALL_HELPER(x) ea_register_init(x); +#define EA_REGISTER_ALL(...) MACRO_FOREACH(EA_REGISTER_ALL_HELPER, __VA_ARGS__) + +struct ea_class *ea_class_find_by_id(uint id); +struct ea_class *ea_class_find_by_name(const char *name); +static inline struct ea_class *ea_class_self(struct ea_class *self) { return self; } +#define ea_class_find(_arg) _Generic((_arg), \ + uint: ea_class_find_by_id, \ + word: ea_class_find_by_id, \ + char *: ea_class_find_by_name, \ + const char *: ea_class_find_by_name, \ + struct ea_class *: ea_class_self)(_arg) + +struct ea_walk_state { + ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */ + eattr *ea; /* Current eattr, initially NULL */ + u32 visited[4]; /* Bitfield, limiting max to 128 */ +}; + +#define ea_find(_l, _arg) _Generic((_arg), uint: ea_find_by_id, struct ea_class *: ea_find_by_class, char *: ea_find_by_name)(_l, _arg) +eattr *ea_find_by_id(ea_list *, unsigned ea); +static inline eattr *ea_find_by_class(ea_list *l, const struct ea_class *def) +{ return ea_find_by_id(l, def->id); } +static inline eattr *ea_find_by_name(ea_list *l, const char *name) +{ + const struct ea_class *def = ea_class_find_by_name(name); + return def ? ea_find_by_class(l, def) : NULL; +} + +#define ea_get_int(_l, _ident, _def) ({ \ + struct ea_class *cls = ea_class_find((_ident)); \ + ASSERT_DIE(cls->type & EAF_EMBEDDED); \ + const eattr *ea = ea_find((_l), cls->id); \ + (ea ? ea->u.data : (_def)); \ + }) + +#define ea_get_ip(_l, _ident, _def) ({ \ + struct ea_class *cls = ea_class_find((_ident)); \ + ASSERT_DIE(cls->type == T_IP); \ + const eattr *ea = ea_find((_l), cls->id); \ + (ea ? *((const ip_addr *) ea->u.ptr->data) : (_def)); \ + }) + +eattr *ea_walk(struct ea_walk_state *s, uint id, uint max); +void ea_dump(ea_list *); +int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */ +uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */ +ea_list *ea_append(ea_list *to, ea_list *what); +void ea_format_bitfield(const struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max); + +/* Normalize ea_list; allocates the result from tmp_linpool */ +ea_list *ea_normalize(ea_list *e, int overlay); + +uint ea_list_size(ea_list *); +void ea_list_copy(ea_list *dest, ea_list *src, uint size); + +#define EA_LOCAL_LIST(N) struct { ea_list l; eattr a[N]; } + +#define EA_LITERAL_EMBEDDED(_class, _flags, _val) ({ \ + btype _type = (_class)->type; \ + ASSERT_DIE(_type & EAF_EMBEDDED); \ + EA_LITERAL_GENERIC((_class)->id, _type, _flags, .u.i = _val); \ + }) + +#define EA_LITERAL_STORE_ADATA(_class, _flags, _buf, _len) ({ \ + btype _type = (_class)->type; \ + ASSERT_DIE(!(_type & EAF_EMBEDDED)); \ + EA_LITERAL_GENERIC((_class)->id, _type, _flags, .u.ad = tmp_store_adata((_buf), (_len))); \ + }) + +#define EA_LITERAL_DIRECT_ADATA(_class, _flags, _adata) ({ \ + btype _type = (_class)->type; \ + ASSERT_DIE(!(_type & EAF_EMBEDDED)); \ + EA_LITERAL_GENERIC((_class)->id, _type, _flags, .u.ad = _adata); \ + }) + +#define EA_LITERAL_GENERIC(_id, _type, _flags, ...) \ + ((eattr) { .id = _id, .type = _type, .flags = _flags, __VA_ARGS__ }) + +static inline eattr * +ea_set_attr(ea_list **to, eattr a) +{ + EA_LOCAL_LIST(1) *ea = tmp_alloc(sizeof(*ea)); + *ea = (typeof(*ea)) { + .l.flags = EALF_SORTED, + .l.count = 1, + .l.next = *to, + .a[0] = a, + }; + + *to = &ea->l; + return &ea->a[0]; +} + +static inline void +ea_unset_attr(ea_list **to, _Bool local, const struct ea_class *def) +{ + ea_set_attr(to, EA_LITERAL_GENERIC(def->id, 0, 0, + .fresh = local, .originated = local, .undef = 1)); +} + +static inline void +ea_set_attr_u32(ea_list **to, const struct ea_class *def, uint flags, u64 data) +{ ea_set_attr(to, EA_LITERAL_EMBEDDED(def, flags, data)); } + +static inline void +ea_set_attr_data(ea_list **to, const struct ea_class *def, uint flags, const void *data, uint len) +{ ea_set_attr(to, EA_LITERAL_STORE_ADATA(def, flags, data, len)); } + +static inline void +ea_copy_attr(ea_list **to, ea_list *from, const struct ea_class *def) +{ + eattr *e = ea_find_by_class(from, def); + if (e) + if (e->type & EAF_EMBEDDED) + ea_set_attr_u32(to, def, e->flags, e->u.data); + else + ea_set_attr_data(to, def, e->flags, e->u.ptr->data, e->u.ptr->length); + else + ea_unset_attr(to, 0, def); +} + +/* + * Common route attributes + */ + +/* Preference: first-order comparison */ +extern struct ea_class ea_gen_preference; +static inline u32 rt_get_preference(const rte *rt) +{ return ea_get_int(rt->attrs, &ea_gen_preference, 0); } + +/* IGP metric: second-order comparison */ +extern struct ea_class ea_gen_igp_metric; +u32 rt_get_igp_metric(const rte *rt); +#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other + protocol-specific metric is availabe */ + +/* From: Advertising router */ +extern struct ea_class ea_gen_from; + +/* Source: An old method to devise the route source protocol and kind. + * To be superseded in a near future by something more informative. */ +extern struct ea_class ea_gen_source; +static inline u32 rt_get_source_attr(const rte *rt) +{ return ea_get_int(rt->attrs, &ea_gen_source, 0); } + +/* Flowspec validation result */ +enum flowspec_valid { + FLOWSPEC_UNKNOWN = 0, + FLOWSPEC_VALID = 1, + FLOWSPEC_INVALID = 2, + FLOWSPEC__MAX, +}; + +extern const char * flowspec_valid_names[FLOWSPEC__MAX]; +static inline const char *flowspec_valid_name(enum flowspec_valid v) +{ return (v < FLOWSPEC__MAX) ? flowspec_valid_names[v] : "???"; } + +extern struct ea_class ea_gen_flowspec_valid; +static inline enum flowspec_valid rt_get_flowspec_valid(const rte *rt) +{ return ea_get_int(rt->attrs, &ea_gen_flowspec_valid, FLOWSPEC_UNKNOWN); } + +/* Next hop: For now, stored as adata */ +extern struct ea_class ea_gen_nexthop; + +static inline void ea_set_dest(struct ea_list **to, uint flags, uint dest) +{ + struct nexthop_adata nhad = NEXTHOP_DEST_LITERAL(dest); + ea_set_attr_data(to, &ea_gen_nexthop, flags, &nhad.ad.data, nhad.ad.length); +} + +/* Next hop structures */ + +#define NEXTHOP_ALIGNMENT (_Alignof(struct nexthop)) +#define NEXTHOP_MAX_SIZE (sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK) +#define NEXTHOP_SIZE(_nh) NEXTHOP_SIZE_CNT(((_nh)->labels)) +#define NEXTHOP_SIZE_CNT(cnt) BIRD_ALIGN((sizeof(struct nexthop) + sizeof(u32) * (cnt)), NEXTHOP_ALIGNMENT) +#define nexthop_size(nh) NEXTHOP_SIZE((nh)) + +#define NEXTHOP_NEXT(_nh) ((void *) (_nh) + NEXTHOP_SIZE(_nh)) +#define NEXTHOP_END(_nhad) ((_nhad)->ad.data + (_nhad)->ad.length) +#define NEXTHOP_VALID(_nh, _nhad) ((void *) (_nh) < (void *) NEXTHOP_END(_nhad)) +#define NEXTHOP_ONE(_nhad) (NEXTHOP_NEXT(&(_nhad)->nh) == NEXTHOP_END(_nhad)) + +#define NEXTHOP_WALK(_iter, _nhad) for ( \ + struct nexthop *_iter = &(_nhad)->nh; \ + (void *) _iter < (void *) NEXTHOP_END(_nhad); \ + _iter = NEXTHOP_NEXT(_iter)) + + +static inline int nexthop_same(struct nexthop_adata *x, struct nexthop_adata *y) +{ return adata_same(&x->ad, &y->ad); } +struct nexthop_adata *nexthop_merge(struct nexthop_adata *x, struct nexthop_adata *y, int max, linpool *lp); +struct nexthop_adata *nexthop_sort(struct nexthop_adata *x, linpool *lp); +int nexthop_is_sorted(struct nexthop_adata *x); + +#define NEXTHOP_IS_REACHABLE(nhad) ((nhad)->ad.length > NEXTHOP_DEST_SIZE) + +/* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */ +static inline int rte_is_reachable(rte *r) +{ + eattr *nhea = ea_find(r->attrs, &ea_gen_nexthop); + if (!nhea) + return 0; + + struct nexthop_adata *nhad = (void *) nhea->u.ptr; + return NEXTHOP_IS_REACHABLE(nhad); +} + +static inline int nhea_dest(eattr *nhea) +{ + if (!nhea) + return RTD_NONE; + + struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL; + if (NEXTHOP_IS_REACHABLE(nhad)) + return RTD_UNICAST; + else + return nhad->dest; +} + +static inline int rte_dest(const rte *r) +{ + return nhea_dest(ea_find(r->attrs, &ea_gen_nexthop)); +} + +void rta_init(void); +ea_list *ea_lookup(ea_list *, int overlay); /* Get a cached (and normalized) variant of this attribute list */ +static inline int ea_is_cached(const ea_list *r) { return r->flags & EALF_CACHED; } +static inline struct ea_storage *ea_get_storage(ea_list *r) +{ + ASSERT_DIE(ea_is_cached(r)); + return SKIP_BACK(struct ea_storage, l[0], r); +} + +static inline ea_list *ea_clone(ea_list *r) { + ASSERT_DIE(0 < atomic_fetch_add_explicit(&ea_get_storage(r)->uc, 1, memory_order_acq_rel)); + return r; +} +void ea__free(struct ea_storage *r); +static inline void ea_free(ea_list *l) { + if (!l) return; + struct ea_storage *r = ea_get_storage(l); + if (1 == atomic_fetch_sub_explicit(&r->uc, 1, memory_order_acq_rel)) ea__free(r); +} + +void ea_dump(ea_list *); +void ea_dump_all(void); +void ea_show_list(struct cli *, ea_list *); + +#define rta_lookup ea_lookup +#define rta_is_cached ea_is_cached +#define rta_clone ea_clone +#define rta_free ea_free + +#endif diff --cc nest/cmds.c index 4805b5a24,d49bbc53c..7984f0880 --- a/nest/cmds.c +++ b/nest/cmds.c @@@ -54,9 -54,9 +54,6 @@@ cmd_show_symbols(struct sym_show_data * for (const struct sym_scope *scope = config->root_scope; scope; scope = scope->next) HASH_WALK(scope->hash, next, sym) { -- if (!sym->scope->active) -- continue; -- if (sd->type && (sym->class != sd->type)) continue; diff --cc nest/rt-attr.c index 0457e68ad,d793c72e1..201dff30f --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@@ -562,135 -351,55 +562,141 @@@ nexthop_is_sorted(struct nexthop_adata return 1; } -static inline slab * -nexthop_slab(struct nexthop *nh) +/* + * Extended Attributes + */ + +#define EA_CLASS_INITIAL_MAX 128 +static struct ea_class **ea_class_global = NULL; +static uint ea_class_max; +static struct idm ea_class_idm; + +/* Config parser lex register function */ +void ea_lex_register(struct ea_class *def); - void ea_lex_unregister(struct ea_class *def); + +static void +ea_class_free(struct ea_class *cl) { - return nexthop_slab_[MIN(nh->labels, 3)]; ++ RTA_LOCK; ++ + /* No more ea class references. Unregister the attribute. */ + idm_free(&ea_class_idm, cl->id); + ea_class_global[cl->id] = NULL; - if (!cl->hidden) - ea_lex_unregister(cl); ++ ++ /* When we start supporting full protocol removal, we may need to call ++ * ea_lex_unregister(cl), see where ea_lex_register() is called. */ ++ ++ RTA_UNLOCK; } -static struct nexthop * -nexthop_copy(struct nexthop *o) +static void +ea_class_ref_free(resource *r) { - struct nexthop *first = NULL; - struct nexthop **last = &first; - - for (; o; o = o->next) - { - struct nexthop *n = sl_allocz(nexthop_slab(o)); - n->gw = o->gw; - n->iface = o->iface; - n->next = NULL; - n->flags = o->flags; - n->weight = o->weight; - n->labels_orig = o->labels_orig; - n->labels = o->labels; - for (int i=0; ilabels; i++) - n->label[i] = o->label[i]; - - *last = n; - last = &(n->next); - } + struct ea_class_ref *ref = SKIP_BACK(struct ea_class_ref, r, r); + if (!--ref->class->uc) + ea_class_free(ref->class); +} - return first; +static void +ea_class_ref_dump(resource *r, unsigned indent UNUSED) +{ + struct ea_class_ref *ref = SKIP_BACK(struct ea_class_ref, r, r); + debug("name \"%s\", type=%d\n", ref->class->name, ref->class->type); } +static struct resclass ea_class_ref_class = { + .name = "Attribute class reference", + .size = sizeof(struct ea_class_ref), + .free = ea_class_ref_free, + .dump = ea_class_ref_dump, + .lookup = NULL, + .memsize = NULL, +}; + static void -nexthop_free(struct nexthop *o) +ea_class_init(void) { - struct nexthop *n; + ASSERT_DIE(ea_class_global == NULL); - while (o) - { - n = o->next; - sl_free(o); - o = n; - } + idm_init(&ea_class_idm, rta_pool, EA_CLASS_INITIAL_MAX); + ea_class_global = mb_allocz(rta_pool, + sizeof(*ea_class_global) * (ea_class_max = EA_CLASS_INITIAL_MAX)); } - static struct ea_class_ref * ++struct ea_class_ref * +ea_ref_class(pool *p, struct ea_class *def) +{ + def->uc++; + struct ea_class_ref *ref = ralloc(p, &ea_class_ref_class); + ref->class = def; + return ref; +} -/* - * Extended Attributes - */ +static struct ea_class_ref * +ea_register(pool *p, struct ea_class *def) +{ + def->id = idm_alloc(&ea_class_idm); + + ASSERT_DIE(ea_class_global); + while (def->id >= ea_class_max) + ea_class_global = mb_realloc(ea_class_global, sizeof(*ea_class_global) * (ea_class_max *= 2)); + + ASSERT_DIE(def->id < ea_class_max); + ea_class_global[def->id] = def; + - if (!def->hidden) - ea_lex_register(def); - + return ea_ref_class(p, def); +} + +struct ea_class_ref * +ea_register_alloc(pool *p, struct ea_class cl) +{ + struct ea_class_ref *ref; + + RTA_LOCK; + struct ea_class *clp = ea_class_find_by_name(cl.name); + if (clp && clp->type == cl.type) + { + ref = ea_ref_class(p, clp); + RTA_UNLOCK; + return ref; + } + + uint namelen = strlen(cl.name) + 1; + + struct { + struct ea_class cl; + char name[0]; + } *cla = mb_alloc(rta_pool, sizeof(struct ea_class) + namelen); + cla->cl = cl; + memcpy(cla->name, cl.name, namelen); + cla->cl.name = cla->name; + + ref = ea_register(p, &cla->cl); + RTA_UNLOCK; + return ref; +} + +void +ea_register_init(struct ea_class *clp) +{ + RTA_LOCK; + ASSERT_DIE(!ea_class_find_by_name(clp->name)); - ea_register(&root_pool, clp); ++ ++ struct ea_class *def = ea_register(&root_pool, clp)->class; ++ ++ if (!clp->hidden) ++ ea_lex_register(def); ++ + RTA_UNLOCK; +} + +struct ea_class * +ea_class_find_by_id(uint id) +{ + ASSERT_DIE(id < ea_class_max); + ASSERT_DIE(ea_class_global[id]); + return ea_class_global[id]; +} static inline eattr * ea__find(ea_list *e, unsigned id) diff --cc proto/bgp/attrs.c index dfd6f1d3e,de45cae0e..510456e8c --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@@ -1209,34 -1145,20 +1209,40 @@@ static union bgp_attr_desc bgp_attr_tab }, }; -static inline int -bgp_attr_known(uint code) +eattr * +bgp_find_attr(ea_list *attrs, uint code) { - return (code < ARRAY_SIZE(bgp_attr_table)) && bgp_attr_table[code].name; + return ea_find(attrs, BGP_EA_ID(code)); } -void bgp_fix_attr_flags(ea_list *attrs) +void +bgp_register_attrs(void) { - for (u8 i = 0; i < attrs->count; i++) + for (uint i=0; iattrs[i].flags = bgp_attr_table[EA_ID(attrs->attrs[i].id)].flags; + if (!bgp_attr_table[i].name) + bgp_attr_table[i] = (union bgp_attr_desc) { + .name = mb_sprintf(&root_pool, "bgp_unknown_0x%02x", i), - .type = T_OPAQUE, - .flags = BAF_OPTIONAL, ++ .type = T_BYTESTRING, ++ .flags = BAF_OPTIONAL | BAF_TRANSITIVE, + .readonly = 1, + .export = bgp_export_unknown, + .encode = bgp_encode_raw, + .decode = bgp_decode_unknown, + .format = bgp_format_unknown, + }; + + ea_register_init(&bgp_attr_table[i].class); } } ++struct ea_class * ++bgp_find_ea_class_by_id(uint id) ++{ ++ return (id < ARRAY_SIZE(bgp_attr_table)) ? &bgp_attr_table[id].class : NULL; ++} ++ + /* * Attribute export */ diff --cc proto/bgp/bgp.h index 997c84560,c11433ecc..079a98ea2 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@@ -661,8 -650,6 +661,9 @@@ bgp_total_aigp_metric(const rte *e return metric; } +void bgp_register_attrs(void); ++struct ea_class *bgp_find_ea_class_by_id(uint id); + /* packets.c */ diff --cc proto/bgp/config.Y index 963bd724a,d9ff24d82..b0a2df337 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@@ -335,6 -332,47 +335,19 @@@ bgp_channel_end bgp_proto_channel: bgp_channel_start bgp_channel_opt_list bgp_channel_end; - -dynamic_attr: BGP_ORIGIN - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_BGP_ORIGIN, EA_CODE(PROTOCOL_BGP, BA_ORIGIN)); } ; -dynamic_attr: BGP_PATH - { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); } ; -dynamic_attr: BGP_NEXT_HOP - { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP)); } ; -dynamic_attr: BGP_MED - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC)); } ; -dynamic_attr: BGP_LOCAL_PREF - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF)); } ; -dynamic_attr: BGP_ATOMIC_AGGR - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); } ; -dynamic_attr: BGP_AGGREGATOR - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); } ; -dynamic_attr: BGP_COMMUNITY - { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); } ; -dynamic_attr: BGP_ORIGINATOR_ID - { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID)); } ; -dynamic_attr: BGP_CLUSTER_LIST - { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); } ; -dynamic_attr: BGP_EXT_COMMUNITY - { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); } ; -dynamic_attr: BGP_AIGP - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ; -dynamic_attr: BGP_LARGE_COMMUNITY - { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ; -dynamic_attr: BGP_OTC - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_ONLY_TO_CUSTOMER)); } ; - + custom_attr: ATTRIBUTE BGP NUM type symbol ';' { - if($3 > 255 || $3 < 1) ++ if ($3 > 255 || $3 < 1) + cf_error("Invalid attribute number. (Given %i, must be 1-255.)", $3); - if($4 != T_BYTESTRING) - cf_error("Attribute type must be bytestring, not %s.", f_type_name($4)); - struct f_dynamic_attr* a = (struct f_dynamic_attr*) malloc(sizeof(struct f_dynamic_attr)); - *a = f_new_dynamic_attr(f_type_attr($4), T_BYTESTRING, EA_CODE(PROTOCOL_BGP, $3)); - a->flags = BAF_TRANSITIVE | BAF_OPTIONAL; - cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, a); ++ ++ struct ea_class *ac = bgp_find_ea_class_by_id($3); ++ ASSERT_DIE(ac); ++ if ($4 != ac->type) ++ cf_error("Attribute %d type must be %s, not %s.", $3, f_type_name(ac->type), f_type_name($4)); ++ ++ ea_ref_class(new_config->pool, ac); ++ cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, ac); + }; + CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE) CF_CODE