From: Maria Matejka Date: Wed, 8 Nov 2023 20:51:46 +0000 (+0100) Subject: Merge branch 'mq-aggregator-for-v3' into thread-next X-Git-Tag: v3.0.0~350 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=30712a2bdf67f1bd4a4a3ec18a11c14c31ee9ad9;p=thirdparty%2Fbird.git Merge branch 'mq-aggregator-for-v3' into thread-next --- 30712a2bdf67f1bd4a4a3ec18a11c14c31ee9ad9 diff --cc conf/cf-lex.l index 263311c26,5fb88e03d..6d22f64e1 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@@ -934,8 -858,10 +934,12 @@@ cf_symbol_class_name(struct symbol *sym return "routing table"; case SYM_ATTRIBUTE: return "custom attribute"; + case SYM_MPLS_DOMAIN: + return "MPLS domain"; + case SYM_MPLS_RANGE: + return "MPLS label range"; + case SYM_KEYWORD: + return "symbol"; case SYM_CONSTANT_RANGE: return "constant"; case SYM_VARIABLE_RANGE: diff --cc conf/conf.h index 841d5c1fc,5ef7216c0..691c86686 --- a/conf/conf.h +++ b/conf/conf.h @@@ -132,7 -128,9 +133,9 @@@ struct symbol const struct f_line *function; /* For SYM_FUNCTION */ const struct filter *filter; /* For SYM_FILTER */ struct rtable_config *table; /* For SYM_TABLE */ - struct f_dynamic_attr *attribute; /* For SYM_ATTRIBUTE */ + struct ea_class *attribute; /* For SYM_ATTRIBUTE */ + struct mpls_domain_config *mpls_domain; /* For SYM_MPLS_DOMAIN */ + struct mpls_range_config *mpls_range; /* For SYM_MPLS_RANGE */ struct f_val *val; /* For SYM_CONSTANT */ uint offset; /* For SYM_VARIABLE */ const struct keyword *keyword; /* For SYM_KEYWORD */ diff --cc lib/route.h index 067725771,000000000..9fcf20614 mode 100644,000000..100644 --- a/lib/route.h +++ b/lib/route.h @@@ -1,570 -1,0 +1,576 @@@ +/* + * 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, + }; +} + +int rte_same(const rte *, const rte *); + +struct rte_src { + struct rte_src *next; /* Hash chain */ + struct rte_owner *owner; /* Route source owner */ + u64 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 *); + +void rt_dump_sources(struct rte_owner *); + +/* + * 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_AGGREGATED 16 /* Aggregated route */ +#define RTS_MAX 17 + +#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; + ++ ++/* MPLS Label, Policy and Class */ ++extern struct ea_class ea_gen_mpls_label, ++ ea_gen_mpls_policy, ea_gen_mpls_class; ++ ++ +/* 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 lib/type.h index 32f16382b,000000000..e07c98ec0 mode 100644,000000..100644 --- a/lib/type.h +++ b/lib/type.h @@@ -1,122 -1,0 +1,123 @@@ +/* + * BIRD Internet Routing Daemon -- Internal Data Types + * + * (c) 2022 Maria Matejka + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_TYPE_H_ +#define _BIRD_TYPE_H_ + +#include "lib/birdlib.h" +#include "lib/attrs.h" + +union bval { +#define BVAL_ITEMS \ + struct { \ + u32 data; /* Integer type inherited from eattrs */ \ + PADDING(data, 0, 4); /* Must be padded on 64-bits */ \ + }; \ + struct { \ + u32 i; /* Integer type inherited from filters */ \ + PADDING(i, 0, 4); /* Must be padded on 64-bits */ \ + }; \ + const struct adata *ptr; /* Generic attribute data inherited from eattrs */ \ + const struct adata *ad; /* Generic attribute data inherited from filters */ \ + + BVAL_ITEMS; +}; + +union bval_long { + union bval bval; /* For direct assignments */ + BVAL_ITEMS; /* For item-wise access */ + + u64 ec; + lcomm lc; + ip_addr ip; + const net_addr *net; + const char *s; + const struct adata *bs; + const struct f_tree *t; + const struct f_trie *ti; + const struct f_path_mask *path_mask; + struct f_path_mask_item pmi; + struct rte *rte; + struct rte_block { + struct rte **rte; + uint len; + } rte_block; +}; + + +/* Internal types */ +enum btype { +/* Nothing. Simply nothing. */ + T_VOID = 0, + T_NONE = 0xff, + +/* Something but inaccessible. */ + T_OPAQUE = 0x02, /* Opaque byte string (not filterable) */ + T_IFACE = 0x0c, /* Pointer to an interface (inside adata) */ + T_ROUTES_BLOCK = 0x68, /* Block of route pointers */ + T_ROUTE = 0x6a, /* One route pointer */ + T_NEXTHOP_LIST = 0x6c, /* The whole nexthop block */ + T_HOSTENTRY = 0x6e, /* Hostentry with possible MPLS labels */ + +/* Types shared with eattrs */ + T_INT = 0x01, /* 32-bit unsigned integer number */ + T_IP = 0x04, /* IP address */ + T_QUAD = 0x05, /* Router ID (IPv4 address) */ + T_PATH = 0x06, /* BGP AS path (encoding per RFC 1771:4.3) */ + T_CLIST = 0x0a, /* Set of u32's (e.g., a community list) */ + T_ECLIST = 0x0e, /* Set of pairs of u32's - ext. community list */ + T_LCLIST = 0x08, /* Set of triplets of u32's - large community list */ + + T_ENUM_BGP_ORIGIN = 0x11, /* BGP Origin enum */ + T_ENUM_RA_PREFERENCE = 0x13, /* RA Preference enum */ + T_ENUM_FLOWSPEC_VALID = 0x15, /* Flowspec validation result */ + +#define EAF_TYPE__MAX 0x1f +#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */ + /* Otherwise, attribute data is adata */ + +/* Other user visible types which fit in int */ + T_BOOL = 0xa0, + T_PAIR = 0xa4, /* Notice that pair is stored as integer: first << 16 | second */ + +/* Put enumerational types in 0x20..0x3f range */ + T_ENUM_LO = 0x10, + T_ENUM_HI = 0x3f, + + T_ENUM_RTS = 0x31, + T_ENUM_SCOPE = 0x33, ++ T_ENUM_MPLS_POLICY = 0x35, + T_ENUM_RTD = 0x37, + T_ENUM_ROA = 0x39, + T_ENUM_NETTYPE = 0x3b, + T_ENUM_AF = 0x3d, + +/* new enums go here */ + +#define T_ENUM T_ENUM_LO ... T_ENUM_HI + +/* Bigger ones */ + T_NET = 0xb0, + T_STRING = 0xb4, + T_PATH_MASK = 0xb8, /* mask for BGP path */ + T_EC = 0xbc, /* Extended community value, u64 */ + T_LC = 0xc0, /* Large community value, lcomm */ + T_RD = 0xc4, /* Route distinguisher for VPN addresses */ + T_PATH_MASK_ITEM = 0xc8, /* Path mask item for path mask constructors */ + T_BYTESTRING = 0xcc, + + T_SET = 0x80, + T_PREFIX_SET = 0x84, +} PACKED; + +typedef enum btype btype; + +STATIC_ASSERT(sizeof(btype) == sizeof(byte)); + + +#endif diff --cc nest/Makefile index bb141f5c3,ef0fdf676..b75992452 --- a/nest/Makefile +++ b/nest/Makefile @@@ -1,4 -1,4 +1,4 @@@ - src := cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c proto-build.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c -src := a-path.c a-set.c cli.c cmds.c iface.c locks.c mpls.c neighbor.c password.c proto.c proto-build.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c ++src := cli.c cmds.c iface.c locks.c mpls.c neighbor.c password.c proto.c proto-build.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c obj := $(src-o-files) $(all-daemon) $(cf-local) diff --cc nest/config.Y index 8c06126e5,31b9bd44f..f212e7113 --- a/nest/config.Y +++ b/nest/config.Y @@@ -163,7 -126,8 +164,8 @@@ CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LON CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS) CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE) CF_KEYWORDS(CHECK, LINK) -CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME, GC, THRESHOLD, PERIOD) +CF_KEYWORDS(CORK, SORTED, TRIE, MIN, MAX, ROA, ROUTE, REFRESH, SETTLE, TIME, GC, THRESHOLD, PERIOD) + CF_KEYWORDS(MPLS_LABEL, MPLS_POLICY, MPLS_CLASS) /* For r_args_channel */ CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC) @@@ -171,9 -135,10 +173,10 @@@ CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL) CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) -CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT) +CF_ENUM(T_ENUM_RTD, RTD_, BLACKHOLE, UNREACHABLE, PROHIBIT) CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6) + CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE) %type idval %type imexport @@@ -181,7 -146,7 +184,7 @@@ %type optproto %type r_args %type sym_args - %type proto_start debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type tos password_algorithm -%type proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type net_type_base tos password_algorithm ++%type proto_start debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type net_type_base tos password_algorithm %type proto_patt proto_patt2 %type channel_start proto_channel %type limit_spec diff --cc nest/mpls.Y index 000000000,b4ae990b4..c3a03671f mode 000000,100644..100644 --- a/nest/mpls.Y +++ b/nest/mpls.Y @@@ -1,0 -1,141 +1,141 @@@ + /* + * BIRD Internet Routing Daemon -- MPLS Structures + * + * (c) 2022 Ondrej Zajicek + * (c) 2022 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + + CF_HDR + + #include "nest/mpls.h" + + CF_DEFINES + + static struct mpls_domain_config *this_mpls_domain; + static struct mpls_range_config *this_mpls_range; + + #define MPLS_CC ((struct mpls_channel_config *) this_channel) + + CF_DECLS + + CF_KEYWORDS(MPLS, DOMAIN, LABEL, RANGE, STATIC, DYNAMIC, START, LENGTH, POLICY, PREFIX, AGGREGATE) + + %type mpls_label_policy + %type mpls_channel_start mpls_channel + + CF_GRAMMAR + + conf: mpls_domain; + + mpls_domain: mpls_domain_start mpls_domain_opt_list mpls_domain_end; + + mpls_domain_start: MPLS DOMAIN symbol { this_mpls_domain = mpls_domain_config_new($3); }; + + mpls_domain_opt: + mpls_range + ; + + mpls_domain_opts: + /* empty */ + | mpls_domain_opts mpls_domain_opt ';' + ; + + mpls_domain_opt_list: + /* empty */ + | '{' mpls_domain_opts '}' + ; + + mpls_domain_end: { mpls_domain_postconfig(this_mpls_domain); this_mpls_domain = NULL; }; + + + mpls_range: mpls_range_start mpls_range_opt_list mpls_range_end; + + mpls_range_start: LABEL RANGE symbol + { + if (($3->class == SYM_KEYWORD) && ($3->keyword->value == STATIC)) + this_mpls_range = this_mpls_domain->static_range; + else if (($3->class == SYM_KEYWORD) && ($3->keyword->value == DYNAMIC)) + this_mpls_range = this_mpls_domain->dynamic_range; + else + this_mpls_range = mpls_range_config_new(this_mpls_domain, $3); + }; + + mpls_range_opt: + START expr { this_mpls_range->start = $2; if ($2 >= MPLS_MAX_LABEL) cf_error("MPLS label range start must be less than 2^20"); } + | LENGTH expr { this_mpls_range->length = $2; if ($2 >= MPLS_MAX_LABEL) cf_error("MPLS label range length must be less than 2^20"); if (!$2) cf_error("MPLS label range length must be nonzero"); } + ; + + mpls_range_opts: + /* empty */ + | mpls_range_opts mpls_range_opt ';' + ; + + mpls_range_opt_list: + /* empty */ + | '{' mpls_range_opts '}' + ; + + mpls_range_end: + { + struct mpls_range_config *r = this_mpls_range; + + if ((r->start == (uint) -1) || (r->length == (uint) -1)) + cf_error("MPLS label range start and length must be specified"); + + if (r->start + r->length > MPLS_MAX_LABEL) + cf_error("MPLS label range end must be less than 2^20"); + + this_mpls_range = NULL; + }; + + + mpls_channel: mpls_channel_start mpls_channel_opt_list mpls_channel_end; + + mpls_channel_start: MPLS + { + $$ = this_channel = channel_config_get(&channel_mpls, net_label[NET_MPLS], NET_MPLS, this_proto); + + if (EMPTY_LIST(new_config->mpls_domains)) + cf_error("No MPLS domain defined"); + + /* Default values for new channel */ + if (!MPLS_CC->domain) + { + MPLS_CC->domain = cf_default_mpls_domain(new_config); + MPLS_CC->label_policy = MPLS_POLICY_PREFIX; + } + }; + + mpls_label_policy: + STATIC { $$ = MPLS_POLICY_STATIC; } + | PREFIX { $$ = MPLS_POLICY_PREFIX; } + | AGGREGATE { $$ = MPLS_POLICY_AGGREGATE; } + ; + + mpls_channel_opt: + channel_item - | DOMAIN symbol_known { cf_assert_symbol($2, SYM_MPLS_DOMAIN); MPLS_CC->domain = $2->mpls_domain; } - | LABEL RANGE symbol_known { cf_assert_symbol($3, SYM_MPLS_RANGE); MPLS_CC->range = $3->mpls_range; } ++ | DOMAIN CF_SYM_KNOWN { cf_assert_symbol($2, SYM_MPLS_DOMAIN); MPLS_CC->domain = $2->mpls_domain; } ++ | LABEL RANGE CF_SYM_KNOWN { cf_assert_symbol($3, SYM_MPLS_RANGE); MPLS_CC->range = $3->mpls_range; } + | LABEL RANGE STATIC { MPLS_CC->range = MPLS_CC->domain->static_range; } + | LABEL RANGE DYNAMIC { MPLS_CC->range = MPLS_CC->domain->dynamic_range; } + | LABEL POLICY mpls_label_policy { MPLS_CC->label_policy = $3; } + ; + + mpls_channel_opts: + /* empty */ + | mpls_channel_opts mpls_channel_opt ';' + ; + + mpls_channel_opt_list: + /* empty */ + | '{' mpls_channel_opts '}' + ; + + mpls_channel_end: { mpls_channel_postconfig(this_channel); } channel_end; + + + CF_CODE + + CF_END diff --cc nest/mpls.c index 000000000,1992234ca..96bafcb3f mode 000000,100644..100644 --- a/nest/mpls.c +++ b/nest/mpls.c @@@ -1,0 -1,1001 +1,995 @@@ + /* + * BIRD Internet Routing Daemon -- MPLS Structures + * + * (c) 2022 Ondrej Zajicek + * (c) 2022 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + + /** + * DOC: MPLS + * + * The MPLS subsystem manages MPLS labels and handles their allocation to + * MPLS-aware routing protocols. These labels are then attached to IP or VPN + * routes representing label switched paths -- LSPs. MPLS labels are also used + * in special MPLS routes (which use labels as network address) that are + * exported to MPLS routing table in kernel. The MPLS subsystem consists of MPLS + * domains (struct &mpls_domain), MPLS channels (struct &mpls_channel) and FEC + * maps (struct &mpls_fec_map). + * + * The MPLS domain represents one MPLS label address space, implements the label + * allocator, and handles associated configuration and management. The domain is + * declared in the configuration (struct &mpls_domain_config). There might be + * multiple MPLS domains representing separate label spaces, but in most cases + * one domain is enough. MPLS-aware protocols and routing tables are associated + * with a specific MPLS domain. + * + * The MPLS domain has configurable label ranges (struct &mpls_range), by + * default it has two ranges: static (16-1000) and dynamic (1000-10000). When + * a protocol wants to allocate labels, it first acquires a handle (struct + * &mpls_handle) for a specific range using mpls_new_handle(), and then it + * allocates labels from that with mpls_new_label(). When not needed, labels are + * freed by mpls_free_label() and the handle is released by mpls_free_handle(). + * Note that all labels and handles must be freed manually. + * + * Both MPLS domain and MPLS range are reference counted, so when deconfigured + * they could be freed just after all labels and ranges are freed. Users are + * expected to hold a reference to a MPLS domain for whole time they use + * something from that domain (e.g. &mpls_handle), but releasing reference to + * a range while holding associated handle is OK. + * + * The MPLS channel is subclass of a generic protocol channel. It has two + * distinct purposes - to handle per-protocol MPLS configuration (e.g. which + * MPLS domain is associated with the protocol, which label range is used by the + * protocol), and to announce MPLS routes to a routing table (as a regular + * protocol channel). + * + * The FEC map is a helper structure that maps forwarding equivalent classes + * (FECs) to MPLS labels. It is an internal matter of a routing protocol how to + * assign meaning to allocated labels, announce LSP routes and associated MPLS + * routes (i.e. ILM entries). But the common behavior is implemented in the FEC + * map, which can be used by the protocols that work with IP-prefix-based FECs. + * + * The FEC map keeps hash tables of FECs (struct &mpls_fec) based on network + * prefix, next hop eattr and assigned label. It has three labeling policies: + * static assignment (%MPLS_POLICY_STATIC), per-prefix policy (%MPLS_POLICY_PREFIX), + * and aggregating policy (%MPLS_POLICY_AGGREGATE). In per-prefix policy, each + * distinct LSP is a separate FEC and uses a separate label, which is kept even + * if the next hop of the LSP changes. In aggregating policy, LSPs with a same + * next hop form one FEC and use one label, but when a next hop (or remote + * label) of such LSP changes then the LSP must be moved to a different FEC and + * assigned a different label. + * + * The overall process works this way: A protocol wants to announce a LSP route, + * it does that by announcing e.g. IP route with %EA_MPLS_POLICY attribute. + * After the route is accepted by filters (which may also change the policy + * attribute or set a static label), the mpls_handle_rte() is called from + * rte_update2(), which applies selected labeling policy, finds existing FEC or + * creates a new FEC (which includes allocating new label and announcing related + * MPLS route by mpls_announce_fec()), and attach FEC label to the LSP route. + * After that, the LSP route is stored in routing table by rte_recalculate(). + * Changes in routing tables trigger mpls_rte_insert() and mpls_rte_remove() + * hooks, which refcount FEC structures and possibly trigger removal of FECs + * and withdrawal of MPLS routes. + * + * TODO: + * - show mpls labels CLI command + * - label range non-intersection check + * - better range reconfigurations (allow reduce ranges over unused labels) + * - protocols should do route refresh instead of resetart when reconfiguration + * requires changing labels (e.g. different label range) + * - registering static allocations + * - checking range in static allocations + * - special handling of reserved labels + */ + + #include "nest/bird.h" + #include "nest/route.h" + #include "nest/mpls.h" + + static struct mpls_range *mpls_new_range(struct mpls_domain *m, struct mpls_range_config *cf); + static struct mpls_range *mpls_find_range_(list *l, const char *name); + static int mpls_reconfigure_range(struct mpls_domain *m, struct mpls_range *r, struct mpls_range_config *cf); + static void mpls_remove_range(struct mpls_range *r); + + + /* + * MPLS domain + */ + + list mpls_domains; + + void + mpls_init(void) + { + init_list(&mpls_domains); + } + + struct mpls_domain_config * + mpls_domain_config_new(struct symbol *s) + { + struct mpls_domain_config *mc = cfg_allocz(sizeof(struct mpls_domain_config)); + struct mpls_range_config *rc; + + cf_define_symbol(new_config, s, SYM_MPLS_DOMAIN, mpls_domain, mc); + mc->name = s->name; + init_list(&mc->ranges); + + /* Predefined static range */ + rc = mpls_range_config_new(mc, NULL); + rc->name = "static"; + rc->start = 16; + rc->length = 984; + mc->static_range = rc; + + /* Predefined dynamic range */ + rc = mpls_range_config_new(mc, NULL); + rc->name = "dynamic"; + rc->start = 1000; + rc->length = 9000; + mc->dynamic_range = rc; + + add_tail(&new_config->mpls_domains, &mc->n); + + return mc; + } + + void + mpls_domain_postconfig(struct mpls_domain_config *cf UNUSED) + { + /* Add label range non-intersection check */ + } + + static struct mpls_domain * + mpls_new_domain(struct mpls_domain_config *cf) + { - struct pool *p = rp_new(&root_pool, "MPLS domain"); ++ struct pool *p = rp_new(&root_pool, the_bird_domain.the_bird, "MPLS domain"); + struct mpls_domain *m = mb_allocz(p, sizeof(struct mpls_domain)); + + m->cf = cf; + m->name = cf->name; + m->pool = p; + + lmap_init(&m->labels, p); + lmap_set(&m->labels, 0); + + init_list(&m->ranges); + init_list(&m->handles); + + struct mpls_range_config *rc; + WALK_LIST(rc, cf->ranges) + mpls_new_range(m, rc); + + add_tail(&mpls_domains, &m->n); + cf->domain = m; + + return m; + } + + static struct mpls_domain * + mpls_find_domain_(list *l, const char *name) + { + struct mpls_domain *m; + + WALK_LIST(m, *l) + if (!strcmp(m->name, name)) + return m; + + return NULL; + } + + static int + mpls_reconfigure_domain(struct mpls_domain *m, struct mpls_domain_config *cf) + { + cf->domain = m; + m->cf->domain = NULL; + m->cf = cf; + m->name = cf->name; + + /* Reconfigure label ranges */ + list old_ranges; + init_list(&old_ranges); + add_tail_list(&old_ranges, &m->ranges); + init_list(&m->ranges); + + struct mpls_range_config *rc; + WALK_LIST(rc, cf->ranges) + { + struct mpls_range *r = mpls_find_range_(&old_ranges, rc->name); + + if (r && mpls_reconfigure_range(m, r, rc)) + { + rem_node(&r->n); + add_tail(&m->ranges, &r->n); + continue; + } + + mpls_new_range(m, rc); + } + + struct mpls_range *r, *r2; + WALK_LIST_DELSAFE(r, r2, old_ranges) + mpls_remove_range(r); + + add_tail_list(&m->ranges, &old_ranges); + + return 1; + } + + static void + mpls_free_domain(struct mpls_domain *m) + { + ASSERT(m->use_count == 0); + ASSERT(m->label_count == 0); + ASSERT(EMPTY_LIST(m->handles)); + + struct config *cfg = m->removed; + + m->cf->domain = NULL; + rem_node(&m->n); + rfree(m->pool); + + config_del_obstacle(cfg); + } + + static void + mpls_remove_domain(struct mpls_domain *m, struct config *cfg) + { + m->removed = cfg; + config_add_obstacle(cfg); + + if (!m->use_count) + mpls_free_domain(m); + } + + void + mpls_lock_domain(struct mpls_domain *m) + { + m->use_count++; + } + + void + mpls_unlock_domain(struct mpls_domain *m) + { + ASSERT(m->use_count > 0); + + m->use_count--; + if (!m->use_count && m->removed) + mpls_free_domain(m); + } + + void + mpls_preconfig(struct config *c) + { + init_list(&c->mpls_domains); + } + + void + mpls_commit(struct config *new, struct config *old) + { + list old_domains; + init_list(&old_domains); + add_tail_list(&old_domains, &mpls_domains); + init_list(&mpls_domains); + + struct mpls_domain_config *mc; + WALK_LIST(mc, new->mpls_domains) + { + struct mpls_domain *m = mpls_find_domain_(&old_domains, mc->name); + + if (m && mpls_reconfigure_domain(m, mc)) + { + rem_node(&m->n); + add_tail(&mpls_domains, &m->n); + continue; + } + + mpls_new_domain(mc); + } + + struct mpls_domain *m, *m2; + WALK_LIST_DELSAFE(m, m2, old_domains) + mpls_remove_domain(m, old); + + add_tail_list(&mpls_domains, &old_domains); + } + + + /* + * MPLS range + */ + + struct mpls_range_config * + mpls_range_config_new(struct mpls_domain_config *mc, struct symbol *s) + { + struct mpls_range_config *rc = cfg_allocz(sizeof(struct mpls_range_config)); + + if (s) + cf_define_symbol(new_config, s, SYM_MPLS_RANGE, mpls_range, rc); + + rc->domain = mc; + rc->name = s ? s->name : NULL; + rc->start = (uint) -1; + rc->length = (uint) -1; + + add_tail(&mc->ranges, &rc->n); + + return rc; + } + + static struct mpls_range * + mpls_new_range(struct mpls_domain *m, struct mpls_range_config *cf) + { + struct mpls_range *r = mb_allocz(m->pool, sizeof(struct mpls_range)); + + r->cf = cf; + r->name = cf->name; + r->lo = cf->start; + r->hi = cf->start + cf->length; + + add_tail(&m->ranges, &r->n); + cf->range = r; + + return r; + } + + static struct mpls_range * + mpls_find_range_(list *l, const char *name) + { + struct mpls_range *r; + + WALK_LIST(r, *l) + if (!strcmp(r->name, name)) + return r; + + return NULL; + } + + static int + mpls_reconfigure_range(struct mpls_domain *m UNUSED, struct mpls_range *r, struct mpls_range_config *cf) + { + if ((cf->start > r->lo) || (cf->start + cf->length < r->hi)) + return 0; + + cf->range = r; + r->cf->range = NULL; + r->cf = cf; + r->name = cf->name; + r->lo = cf->start; + r->hi = cf->start + cf->length; + + return 1; + } + + static void + mpls_free_range(struct mpls_range *r) + { + ASSERT(r->use_count == 0); + ASSERT(r->label_count == 0); + + r->cf->range = NULL; + rem_node(&r->n); + mb_free(r); + } + + static void + mpls_remove_range(struct mpls_range *r) + { + r->removed = 1; + + if (!r->use_count) + mpls_free_range(r); + } + + void + mpls_lock_range(struct mpls_range *r) + { + r->use_count++; + } + + void + mpls_unlock_range(struct mpls_range *r) + { + ASSERT(r->use_count > 0); + + r->use_count--; + if (!r->use_count && r->removed) + mpls_free_range(r); + } + + + /* + * MPLS handle + */ + + struct mpls_handle * + mpls_new_handle(struct mpls_domain *m, struct mpls_range *r) + { + struct mpls_handle *h = mb_allocz(m->pool, sizeof(struct mpls_handle)); + + h->range = r; + mpls_lock_range(h->range); + + add_tail(&m->handles, &h->n); + + return h; + } + + void + mpls_free_handle(struct mpls_domain *m UNUSED, struct mpls_handle *h) + { + ASSERT(h->label_count == 0); + + mpls_unlock_range(h->range); + rem_node(&h->n); + mb_free(h); + } + + + /* + * MPLS label + */ + + uint + mpls_new_label(struct mpls_domain *m, struct mpls_handle *h) + { + struct mpls_range *r = h->range; + uint n = lmap_first_zero_in_range(&m->labels, r->lo, r->hi); + + if (n >= r->hi) + return 0; + + m->label_count++; + r->label_count++; + h->label_count++; + + lmap_set(&m->labels, n); + return n; + } + + void + mpls_free_label(struct mpls_domain *m, struct mpls_handle *h, uint n) + { + struct mpls_range *r = h->range; + + ASSERT(lmap_test(&m->labels, n)); + lmap_clear(&m->labels, n); + + ASSERT(m->label_count); + m->label_count--; + + ASSERT(r->label_count); + r->label_count--; + + ASSERT(h->label_count); + h->label_count--; + } + + + /* + * MPLS channel + */ + + static void + mpls_channel_init(struct channel *C, struct channel_config *CC) + { + struct mpls_channel *c = (void *) C; + struct mpls_channel_config *cc = (void *) CC; + + c->domain = cc->domain->domain; + c->range = cc->range->range; + c->label_policy = cc->label_policy; + } + + static int + mpls_channel_start(struct channel *C) + { + struct mpls_channel *c = (void *) C; + + mpls_lock_domain(c->domain); + mpls_lock_range(c->range); + + return 0; + } + + /* + static void + mpls_channel_shutdown(struct channel *C) + { + struct mpls_channel *c = (void *) C; + + } + */ + + static void + mpls_channel_cleanup(struct channel *C) + { + struct mpls_channel *c = (void *) C; + + mpls_unlock_range(c->range); + mpls_unlock_domain(c->domain); + } + + static int + mpls_channel_reconfigure(struct channel *C, struct channel_config *CC, int *import_changed UNUSED, int *export_changed UNUSED) + { + struct mpls_channel *c = (void *) C; + struct mpls_channel_config *new = (void *) CC; + + if ((new->domain->domain != c->domain) || + (new->range->range != c->range) || + (new->label_policy != c->label_policy)) + return 0; + + return 1; + } + + void + mpls_channel_postconfig(struct channel_config *CC) + { + struct mpls_channel_config *cc = (void *) CC; + + if (!cc->domain) + cf_error("MPLS domain not specified"); + + if (!cc->range) + cc->range = (cc->label_policy == MPLS_POLICY_STATIC) ? + cc->domain->static_range : cc->domain->dynamic_range; + + if (cc->range->domain != cc->domain) + cf_error("MPLS label range from different MPLS domain"); + + if (!cc->c.table) + cf_error("Routing table not specified"); + } + + struct channel_class channel_mpls = { + .channel_size = sizeof(struct mpls_channel), + .config_size = sizeof(struct mpls_channel_config), + .init = mpls_channel_init, + .start = mpls_channel_start, + // .shutdown = mpls_channel_shutdown, + .cleanup = mpls_channel_cleanup, + .reconfigure = mpls_channel_reconfigure, + }; + + + /* + * MPLS FEC map + */ + + #define NET_KEY(fec) fec->net, fec->path_id, fec->hash + #define NET_NEXT(fec) fec->next_k + #define NET_EQ(n1,i1,h1,n2,i2,h2) h1 == h2 && i1 == i2 && net_equal(n1, n2) + #define NET_FN(n,i,h) h + + #define NET_REHASH mpls_net_rehash + #define NET_PARAMS /8, *2, 2, 2, 8, 24 + + -#define RTA_KEY(fec) fec->rta, fec->class_id, fec->hash ++#define RTA_KEY(fec) fec->rta + #define RTA_NEXT(fec) fec->next_k -#define RTA_EQ(r1,i1,h1,r2,i2,h2) h1 == h2 && r1 == r2 && i1 == i2 -#define RTA_FN(r,i,h) h ++#define RTA_EQ(r1,r2) r1 == r2 ++#define RTA_FN(r) r->hash_key + + #define RTA_REHASH mpls_rta_rehash + #define RTA_PARAMS /8, *2, 2, 2, 8, 24 + + + #define LABEL_KEY(fec) fec->label + #define LABEL_NEXT(fec) fec->next_l + #define LABEL_EQ(l1,l2) l1 == l2 + #define LABEL_FN(l) u32_hash(l) + + #define LABEL_REHASH mpls_label_rehash + #define LABEL_PARAMS /8, *2, 2, 2, 8, 24 + + + HASH_DEFINE_REHASH_FN(NET, struct mpls_fec) + HASH_DEFINE_REHASH_FN(RTA, struct mpls_fec) + HASH_DEFINE_REHASH_FN(LABEL, struct mpls_fec) + + + static void mpls_withdraw_fec(struct mpls_fec_map *m, struct mpls_fec *fec); -static rta * mpls_get_key_rta(struct mpls_fec_map *m, const rta *src); ++static struct ea_storage * mpls_get_key_attrs(struct mpls_fec_map *m, ea_list *src); + + struct mpls_fec_map * + mpls_fec_map_new(pool *pp, struct channel *C, uint rts) + { - struct pool *p = rp_new(pp, "MPLS FEC map"); ++ struct pool *p = rp_new(pp, the_bird_domain.the_bird, "MPLS FEC map"); + struct mpls_fec_map *m = mb_allocz(p, sizeof(struct mpls_fec_map)); + struct mpls_channel *c = (void *) C; + + m->pool = p; + m->channel = C; + + m->domain = c->domain; + mpls_lock_domain(m->domain); + + m->handle = mpls_new_handle(c->domain, c->range); + + /* net_hash and rta_hash are initialized on-demand */ + HASH_INIT(m->label_hash, m->pool, 4); + + m->mpls_rts = rts; - m->mpls_scope = SCOPE_UNIVERSE; + + return m; + } + + void + mpls_fec_map_free(struct mpls_fec_map *m) + { + /* Free stored rtas */ - if (m->rta_hash.data) ++ if (m->attrs_hash.data) + { - HASH_WALK(m->rta_hash, next_k, fec) ++ HASH_WALK(m->attrs_hash, next_k, fec) + { - rta_free(fec->rta); ++ ea_free(fec->rta->l); + fec->rta = NULL; + } + HASH_WALK_END; + } + + /* Free allocated labels */ + HASH_WALK(m->label_hash, next_l, fec) + { + if (fec->policy != MPLS_POLICY_STATIC) + mpls_free_label(m->domain, m->handle, fec->label); + } + HASH_WALK_END; + + mpls_free_handle(m->domain, m->handle); + mpls_unlock_domain(m->domain); + + rfree(m->pool); + } + + static slab * + mpls_slab(struct mpls_fec_map *m, uint type) + { + ASSERT(type <= NET_VPN6); + int pos = type ? (type - 1) : 0; + + if (!m->slabs[pos]) + m->slabs[pos] = sl_new(m->pool, sizeof(struct mpls_fec) + net_addr_length[pos + 1]); + + return m->slabs[pos]; + } + + struct mpls_fec * + mpls_find_fec_by_label(struct mpls_fec_map *m, u32 label) + { + return HASH_FIND(m->label_hash, LABEL, label); + } + + struct mpls_fec * + mpls_get_fec_by_label(struct mpls_fec_map *m, u32 label) + { + struct mpls_fec *fec = HASH_FIND(m->label_hash, LABEL, label); + + if (fec) + return fec; + + fec = sl_allocz(mpls_slab(m, 0)); + + fec->label = label; + fec->policy = MPLS_POLICY_STATIC; + + DBG("New FEC lab %u\n", fec->label); + + HASH_INSERT2(m->label_hash, LABEL, m->pool, fec); + + return fec; + } + + struct mpls_fec * + mpls_get_fec_by_net(struct mpls_fec_map *m, const net_addr *net, u32 path_id) + { + if (!m->net_hash.data) + HASH_INIT(m->net_hash, m->pool, 4); + + u32 hash = net_hash(net) ^ u32_hash(path_id); + struct mpls_fec *fec = HASH_FIND(m->net_hash, NET, net, path_id, hash); + + if (fec) + return fec; + + fec = sl_allocz(mpls_slab(m, net->type)); + + fec->hash = hash; + fec->path_id = path_id; + net_copy(fec->net, net); + + fec->label = mpls_new_label(m->domain, m->handle); + fec->policy = MPLS_POLICY_PREFIX; + + DBG("New FEC net %u\n", fec->label); + + HASH_INSERT2(m->net_hash, NET, m->pool, fec); + HASH_INSERT2(m->label_hash, LABEL, m->pool, fec); + + return fec; + } + + struct mpls_fec * -mpls_get_fec_by_rta(struct mpls_fec_map *m, const rta *src, u32 class_id) ++mpls_get_fec_by_destination(struct mpls_fec_map *m, ea_list *dest) + { - if (!m->rta_hash.data) - HASH_INIT(m->rta_hash, m->pool, 4); ++ if (!m->attrs_hash.data) ++ HASH_INIT(m->attrs_hash, m->pool, 4); + - rta *rta = mpls_get_key_rta(m, src); - u32 hash = rta->hash_key ^ u32_hash(class_id); - struct mpls_fec *fec = HASH_FIND(m->rta_hash, RTA, rta, class_id, hash); ++ struct ea_storage *rta = mpls_get_key_attrs(m, dest); ++ u32 hash = rta->hash_key; ++ struct mpls_fec *fec = HASH_FIND(m->attrs_hash, RTA, rta); + + if (fec) + { - rta_free(rta); ++ ea_free(rta->l); + return fec; + } + + fec = sl_allocz(mpls_slab(m, 0)); + + fec->hash = hash; - fec->class_id = class_id; + fec->rta = rta; + + fec->label = mpls_new_label(m->domain, m->handle); + fec->policy = MPLS_POLICY_AGGREGATE; + + DBG("New FEC rta %u\n", fec->label); + - HASH_INSERT2(m->rta_hash, RTA, m->pool, fec); ++ HASH_INSERT2(m->attrs_hash, RTA, m->pool, fec); + HASH_INSERT2(m->label_hash, LABEL, m->pool, fec); + + return fec; + } + + void + mpls_free_fec(struct mpls_fec_map *m, struct mpls_fec *fec) + { + if (fec->state != MPLS_FEC_DOWN) + mpls_withdraw_fec(m, fec); + + DBG("Free FEC %u\n", fec->label); + + mpls_free_label(m->domain, m->handle, fec->label); + HASH_REMOVE2(m->label_hash, LABEL, m->pool, fec); + + switch (fec->policy) + { + case MPLS_POLICY_STATIC: + break; + + case MPLS_POLICY_PREFIX: + HASH_REMOVE2(m->net_hash, NET, m->pool, fec); + break; + + case MPLS_POLICY_AGGREGATE: - rta_free(fec->rta); - HASH_REMOVE2(m->rta_hash, RTA, m->pool, fec); ++ ea_free(fec->rta->l); ++ HASH_REMOVE2(m->attrs_hash, RTA, m->pool, fec); + break; + + default: + bug("Unknown fec type"); + } + + sl_free(fec); + } + + static inline void mpls_lock_fec(struct mpls_fec_map *x UNUSED, struct mpls_fec *fec) + { if (fec) fec->uc++; } + + static inline void mpls_unlock_fec(struct mpls_fec_map *x, struct mpls_fec *fec) + { if (fec && !--fec->uc) mpls_free_fec(x, fec); } + -static inline void -mpls_damage_fec(struct mpls_fec_map *m UNUSED, struct mpls_fec *fec) ++struct mpls_fec_tmp_lock { ++ resource r; ++ struct mpls_fec_map *m; ++ struct mpls_fec *fec; ++}; ++ ++static void ++mpls_fec_tmp_lock_free(resource *r) + { - if (fec->state == MPLS_FEC_CLEAN) - fec->state = MPLS_FEC_DIRTY; ++ struct mpls_fec_tmp_lock *l = SKIP_BACK(struct mpls_fec_tmp_lock, r, r); ++ mpls_unlock_fec(l->m, l->fec); + } + -static rta * -mpls_get_key_rta(struct mpls_fec_map *m, const rta *src) ++static void ++mpls_fec_tmp_lock_dump(resource *r, unsigned indent UNUSED) + { - rta *a = allocz(RTA_MAX_SIZE); ++ struct mpls_fec_tmp_lock *l = SKIP_BACK(struct mpls_fec_tmp_lock, r, r); ++ debug("map=%p fec=%p label=%u", l->m, l->fec, l->fec->label); ++} + - a->source = m->mpls_rts; - a->scope = m->mpls_scope; ++static struct resclass mpls_fec_tmp_lock_class = { ++ .name = "Temporary MPLS FEC Lock", ++ .size = sizeof(struct mpls_fec_tmp_lock), ++ .free = mpls_fec_tmp_lock_free, ++ .dump = mpls_fec_tmp_lock_dump, ++}; + - if (!src->hostentry) - { - /* Just copy the nexthop */ - a->dest = src->dest; - nexthop_link(a, &src->nh); - } - else - { - /* Keep the hostentry */ - a->hostentry = src->hostentry; ++static void ++mpls_lock_fec_tmp(struct mpls_fec_map *m, struct mpls_fec *fec) ++{ ++ if (!fec) ++ return; + - /* Keep the original labelstack */ - const u32 *labels = &src->nh.label[src->nh.labels - src->nh.labels_orig]; - a->nh.labels = a->nh.labels_orig = src->nh.labels_orig; - memcpy(a->nh.label, labels, src->nh.labels_orig * sizeof(u32)); - } ++ fec->uc++; + - return rta_lookup(a); ++ struct mpls_fec_tmp_lock *l = ralloc(tmp_res.pool, &mpls_fec_tmp_lock_class); ++ l->m = m; ++ l->fec = fec; + } + -static void -mpls_announce_fec(struct mpls_fec_map *m, struct mpls_fec *fec, const rta *src) ++static inline void ++mpls_damage_fec(struct mpls_fec_map *m UNUSED, struct mpls_fec *fec) + { - rta *a = allocz(RTA_MAX_SIZE); ++ if (fec->state == MPLS_FEC_CLEAN) ++ fec->state = MPLS_FEC_DIRTY; ++} + - a->source = m->mpls_rts; - a->scope = m->mpls_scope; ++static struct ea_storage * ++mpls_get_key_attrs(struct mpls_fec_map *m, ea_list *src) ++{ ++ EA_LOCAL_LIST(4) ea = { ++ .l.flags = EALF_SORTED, ++ }; + - if (!src->hostentry) - { - /* Just copy the nexthop */ - a->dest = src->dest; - nexthop_link(a, &src->nh); - } - else - { - const u32 *labels = &src->nh.label[src->nh.labels - src->nh.labels_orig]; - mpls_label_stack ms; ++ uint last_id = 0; ++ #define PUT_ATTR(cls) do { \ ++ ASSERT_DIE(last_id < (cls)->id); \ ++ last_id = (cls)->id; \ ++ eattr *a = ea_find_by_class(src, (cls)); \ ++ if (a) ea.a[ea.l.count++] = *a; \ ++ } while (0) + - /* Reconstruct the original labelstack */ - ms.len = src->nh.labels_orig; - memcpy(ms.stack, labels, src->nh.labels_orig * sizeof(u32)); ++ PUT_ATTR(&ea_gen_nexthop); ++ PUT_ATTR(&ea_gen_hostentry); ++ ea.a[ea.l.count++] = EA_LITERAL_EMBEDDED(&ea_gen_source, 0, m->mpls_rts); ++ PUT_ATTR(&ea_gen_mpls_class); + ++ return ea_get_storage(ea_lookup(&ea.l, 0)); ++} ++ ++static void ++mpls_announce_fec(struct mpls_fec_map *m, struct mpls_fec *fec, ea_list *src) ++{ ++ /* Check existence of hostentry */ ++ const struct eattr *heea = ea_find_by_class(src, &ea_gen_hostentry); ++ if (heea) { + /* The same hostentry, but different dependent table */ - struct hostentry *s = src->hostentry; - rta_set_recursive_next_hop(m->channel->table, a, s->owner, s->addr, s->link, &ms); ++ struct hostentry_adata *head = SKIP_BACK(struct hostentry_adata, ad, heea->u.ad); ++ struct hostentry *he = head->he; ++ ea_set_hostentry(&src, m->channel->table, he->owner, he->addr, he->link, ++ HOSTENTRY_LABEL_COUNT(head), head->labels); + } + + net_addr_mpls n = NET_ADDR_MPLS(fec->label); + - rte *e = rte_get_temp(rta_lookup(a), m->channel->proto->main_source); - e->pflags = 0; ++ rte e = { ++ .src = m->channel->proto->main_source, ++ .attrs = src, ++ }; + + fec->state = MPLS_FEC_CLEAN; - rte_update2(m->channel, (net_addr *) &n, e, m->channel->proto->main_source); ++ rte_update(m->channel, (net_addr *) &n, &e, m->channel->proto->main_source); + } + + static void + mpls_withdraw_fec(struct mpls_fec_map *m, struct mpls_fec *fec) + { + net_addr_mpls n = NET_ADDR_MPLS(fec->label); + + fec->state = MPLS_FEC_DOWN; - rte_update2(m->channel, (net_addr *) &n, NULL, m->channel->proto->main_source); ++ rte_update(m->channel, (net_addr *) &n, NULL, m->channel->proto->main_source); + } + + static void -mpls_apply_fec(rte *r, struct mpls_fec *fec, linpool *lp) ++mpls_apply_fec(rte *r, struct mpls_fec *fec) + { - struct ea_list *ea = lp_allocz(lp, sizeof(struct ea_list) + 2 * sizeof(eattr)); - - rta *old_attrs = r->attrs; - - if (rta_is_cached(old_attrs)) - r->attrs = rta_do_cow(r->attrs, lp); - - *ea = (struct ea_list) { - .next = r->attrs->eattrs, - .flags = EALF_SORTED, - .count = 2, - }; - - ea->attrs[0] = (struct eattr) { - .id = EA_MPLS_LABEL, - .type = EAF_TYPE_INT, - .u.data = fec->label, - }; - - ea->attrs[1] = (struct eattr) { - .id = EA_MPLS_POLICY, - .type = EAF_TYPE_INT, - .u.data = fec->policy, - }; - - r->attrs->eattrs = ea; - - if (rta_is_cached(old_attrs)) - { - r->attrs = rta_lookup(r->attrs); - rta_free(old_attrs); - } ++ ea_set_attr_u32(&r->attrs, &ea_gen_mpls_label, 0, fec->label); ++ ea_set_attr_u32(&r->attrs, &ea_gen_mpls_policy, 0, fec->policy); + } + + + void -mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r, linpool *lp, struct mpls_fec **locked_fec) ++mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r) + { - ASSERT(!(r->flags & REF_COW)); - + struct mpls_fec *fec = NULL; + - + /* Select FEC for route */ - uint policy = ea_get_int(r->attrs->eattrs, EA_MPLS_POLICY, 0); ++ uint policy = ea_get_int(r->attrs, &ea_gen_mpls_policy, 0); + switch (policy) + { + case MPLS_POLICY_NONE: + return; + + case MPLS_POLICY_STATIC:; - uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0); ++ uint label = ea_get_int(r->attrs, &ea_gen_mpls_label, 0); + + if (label < 16) + return; + + fec = mpls_get_fec_by_label(m, label); + mpls_damage_fec(m, fec); + break; + + case MPLS_POLICY_PREFIX: + fec = mpls_get_fec_by_net(m, n, r->src->private_id); + mpls_damage_fec(m, fec); + break; + - case MPLS_POLICY_AGGREGATE:; - uint class = ea_get_int(r->attrs->eattrs, EA_MPLS_CLASS, 0); - fec = mpls_get_fec_by_rta(m, r->attrs, class); ++ case MPLS_POLICY_AGGREGATE: ++ fec = mpls_get_fec_by_destination(m, r->attrs); + break; + + default: + log(L_WARN "Route %N has invalid MPLS policy %u", n, policy); + return; + } + + /* Temporarily lock FEC */ - mpls_lock_fec(m, fec); - *locked_fec = fec; ++ mpls_lock_fec_tmp(m, fec); + + /* Apply FEC label to route */ - mpls_apply_fec(r, fec, lp); ++ mpls_apply_fec(r, fec); + + /* Announce MPLS rule for new/updated FEC */ + if (fec->state != MPLS_FEC_CLEAN) + mpls_announce_fec(m, fec, r->attrs); + } + -void -mpls_handle_rte_cleanup(struct mpls_fec_map *m, struct mpls_fec **locked_fec) ++static inline struct mpls_fec_tmp_lock ++mpls_rte_get_fec_lock(const rte *r) + { - /* Unlock temporarily locked FEC from mpls_handle_rte() */ - if (*locked_fec) - { - mpls_unlock_fec(m, *locked_fec); - *locked_fec = NULL; - } -} ++ struct mpls_fec_tmp_lock mt = { ++ .m = SKIP_BACK(struct proto, sources, r->src->owner)->mpls_map, ++ }; + -void -mpls_rte_insert(net *n UNUSED, rte *r) -{ - struct proto *p = r->src->proto; - struct mpls_fec_map *m = p->mpls_map; ++ if (!mt.m) ++ return mt; + - uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0); ++ uint label = ea_get_int(r->attrs, &ea_gen_mpls_label, 0); + if (label < 16) - return; ++ return mt; + - struct mpls_fec *fec = mpls_find_fec_by_label(m, label); - if (!fec) - return; - - mpls_lock_fec(m, fec); ++ mt.fec = mpls_find_fec_by_label(mt.m, label); ++ return mt; + } + + void -mpls_rte_remove(net *n UNUSED, rte *r) ++mpls_rte_preimport(rte *new, const rte *old) + { - struct proto *p = r->src->proto; - struct mpls_fec_map *m = p->mpls_map; ++ struct mpls_fec_tmp_lock new_mt = {}, old_mt = {}; + - uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0); - if (label < 16) - return; ++ if (new) ++ new_mt = mpls_rte_get_fec_lock(new); + - struct mpls_fec *fec = mpls_find_fec_by_label(m, label); - if (!fec) ++ if (old) ++ old_mt = mpls_rte_get_fec_lock(old); ++ ++ if (new_mt.fec == old_mt.fec) + return; + - mpls_unlock_fec(m, fec); ++ if (new_mt.fec) ++ mpls_lock_fec(new_mt.m, new_mt.fec); ++ ++ if (old_mt.fec) ++ mpls_unlock_fec(old_mt.m, old_mt.fec); + } ++ ++struct ea_class ea_gen_mpls_policy = { ++ .name = "mpls_policy", ++ .type = T_ENUM_MPLS_POLICY, ++}; ++ ++struct ea_class ea_gen_mpls_class = { ++ .name = "mpls_class", ++ .type = T_INT, ++}; ++ ++struct ea_class ea_gen_mpls_label = { ++ .name = "mpls_label", ++ .type = T_INT, ++}; diff --cc nest/mpls.h index 000000000,a84ede14a..2d188d02f mode 000000,100644..100644 --- a/nest/mpls.h +++ b/nest/mpls.h @@@ -1,0 -1,170 +1,166 @@@ + /* + * BIRD Internet Routing Daemon -- MPLS Structures + * + * (c) 2022 Ondrej Zajicek + * (c) 2022 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + + #ifndef _BIRD_MPLS_H_ + #define _BIRD_MPLS_H_ + + #include "nest/bird.h" + #include "lib/bitmap.h" + #include "lib/hash.h" + #include "nest/route.h" + #include "nest/protocol.h" + + + #define MPLS_POLICY_NONE 0 + #define MPLS_POLICY_STATIC 1 + #define MPLS_POLICY_PREFIX 2 + #define MPLS_POLICY_AGGREGATE 3 + + #define MPLS_FEC_DOWN 0 + #define MPLS_FEC_CLEAN 1 + #define MPLS_FEC_DIRTY 2 + + + struct mpls_domain_config { + node n; /* Node in config.mpls_domains */ + struct mpls_domain *domain; /* Our instance */ + const char *name; + + list ranges; /* List of label ranges (struct mpls_range_config) */ + struct mpls_range_config *static_range; /* Default static label range */ + struct mpls_range_config *dynamic_range; /* Default dynamic label range */ + }; + + struct mpls_domain { + node n; /* Node in global list of MPLS domains (mpls_domains) */ + struct mpls_domain_config *cf; /* Our config */ + const char *name; + pool *pool; /* Pool for the domain and associated objects */ + + struct lmap labels; /* Bitmap of allocated labels */ + uint label_count; /* Number of allocated labels */ + uint use_count; /* Reference counter */ + + struct config *removed; /* Deconfigured, waiting for zero use_count, + while keeping config obstacle */ + + list ranges; /* List of label ranges (struct mpls_range) */ + list handles; /* List of label handles (struct mpls_handle) */ + }; + + struct mpls_range_config { + node n; /* Node in mpls_domain_config.ranges */ + struct mpls_range *range; /* Our instance */ + struct mpls_domain_config *domain; /* Parent MPLS domain */ + const char *name; + + uint start; /* Label range start, (uint) -1 for undefined */ + uint length; /* Label range length, (uint) -1 for undefined */ + }; + + struct mpls_range { + node n; /* Node in mpls_domain.ranges */ + struct mpls_range_config *cf; /* Our config */ + const char *name; + + uint lo, hi; /* Label range interval */ + uint label_count; /* Number of allocated labels */ + uint use_count; /* Reference counter */ + u8 removed; /* Deconfigured, waiting for zero use_count */ + }; + + struct mpls_handle { + node n; /* Node in mpls_domain.handles */ + + struct mpls_range *range; /* Associated range, keeping reference */ + uint label_count; /* Number of allocated labels */ + }; + + + void mpls_init(void); + struct mpls_domain_config * mpls_domain_config_new(struct symbol *s); + void mpls_domain_postconfig(struct mpls_domain_config *cf); + struct mpls_range_config * mpls_range_config_new(struct mpls_domain_config *m, struct symbol *s); + void mpls_preconfig(struct config *c); + void mpls_commit(struct config *new, struct config *old); + uint mpls_new_label(struct mpls_domain *m, struct mpls_handle *h); + void mpls_free_label(struct mpls_domain *m, struct mpls_handle *h, uint n); + + static inline struct mpls_domain_config *cf_default_mpls_domain(struct config *cfg) + { return EMPTY_LIST(cfg->mpls_domains) ? NULL : HEAD(cfg->mpls_domains); } + + + struct mpls_channel_config { + struct channel_config c; + + struct mpls_domain_config *domain; + struct mpls_range_config *range; + + uint label_policy; + }; + + struct mpls_channel { + struct channel c; + + struct mpls_domain *domain; + struct mpls_range *range; + + uint label_policy; + }; + + + void mpls_channel_postconfig(struct channel_config *CF); + extern struct channel_class channel_mpls; + + + struct mpls_fec { + u32 label; /* Label for FEC */ + u32 hash; /* Hash for primary key (net / rta) */ + u32 uc; /* Number of LSPs for FEC */ + union { /* Extension part of key */ + u32 path_id; /* Source path_id */ - u32 class_id; /* Aaggregation class */ + }; + + u8 state; /* FEC state (MPLS_FEC_*) */ + u8 policy; /* Label policy (MPLS_POLICY_*) */ + + struct mpls_fec *next_k; /* Next in mpls_fec.net_hash/rta_hash */ + struct mpls_fec *next_l; /* Next in mpls_fec.label_hash */ + union { /* Primary key */ - struct rta *rta; ++ struct ea_storage *rta; + net_addr net[0]; + }; + }; + + struct mpls_fec_map { + pool *pool; /* Pool for FEC map */ + slab *slabs[4]; /* Slabs for FEC allocation */ + HASH(struct mpls_fec) net_hash; /* Hash table for MPLS_POLICY_PREFIX FECs */ - HASH(struct mpls_fec) rta_hash; /* Hash table for MPLS_POLICY_AGGREGATE FECs */ ++ HASH(struct mpls_fec) attrs_hash; /* Hash table for MPLS_POLICY_AGGREGATE FECs */ + HASH(struct mpls_fec) label_hash; /* Hash table for FEC lookup by label */ + + struct channel *channel; /* MPLS channel for FEC announcement */ + struct mpls_domain *domain; /* MPLS domain, keeping reference */ + struct mpls_handle *handle; /* Handle for allocation of labels */ + + u8 mpls_rts; /* Source value used for MPLS routes (RTS_*) */ - u8 mpls_scope; /* Scope value used for MPLS routes () */ + }; + + + struct mpls_fec_map *mpls_fec_map_new(pool *p, struct channel *c, uint rts); + void mpls_fec_map_free(struct mpls_fec_map *m); + struct mpls_fec *mpls_find_fec_by_label(struct mpls_fec_map *x, u32 label); + struct mpls_fec *mpls_get_fec_by_label(struct mpls_fec_map *m, u32 label); + struct mpls_fec *mpls_get_fec_by_net(struct mpls_fec_map *m, const net_addr *net, u32 path_id); -struct mpls_fec *mpls_get_fec_by_rta(struct mpls_fec_map *m, const rta *src, u32 class_id); ++struct mpls_fec *mpls_get_fec_by_destination(struct mpls_fec_map *m, ea_list *dest); + void mpls_free_fec(struct mpls_fec_map *x, struct mpls_fec *fec); -void mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r, linpool *lp, struct mpls_fec **locked_fec); -void mpls_handle_rte_cleanup(struct mpls_fec_map *m, struct mpls_fec **locked_fec); -void mpls_rte_insert(net *n UNUSED, rte *r); -void mpls_rte_remove(net *n UNUSED, rte *r); ++void mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r); ++void mpls_rte_preimport(rte *new, const rte *old); + + #endif diff --cc nest/proto.c index bbf41ab14,701952fff..9be4daee5 --- a/nest/proto.c +++ b/nest/proto.c @@@ -1283,10 -765,10 +1284,10 @@@ channel_config_new(const struct channel if (!net_val_match(net_type, proto->protocol->channel_mask)) cf_error("Unsupported channel type"); - if (proto->net_type && (net_type != proto->net_type)) + if (proto->net_type && (net_type != proto->net_type) && (net_type != NET_MPLS)) cf_error("Different channel type"); - tab = new_config->def_tables[net_type]; + tab = rt_get_default_table(new_config, net_type); } if (!cc) @@@ -1502,21 -956,80 +1503,86 @@@ proto_configure_channel(struct proto *p return 1; } + /** + * proto_setup_mpls_map - automatically setup FEC map for protocol + * @p: affected protocol + * @rts: RTS_* value for generated MPLS routes + * @hooks: whether to update rte_insert / rte_remove hooks + * + * Add, remove or reconfigure MPLS FEC map of the protocol @p, depends on + * whether MPLS channel exists, and setup rte_insert / rte_remove hooks with + * default MPLS handlers. It is a convenience function supposed to be called + * from the protocol start and configure hooks, after reconfiguration of + * channels. For shutdown, use proto_shutdown_mpls_map(). If caller uses its own + * rte_insert / rte_remove hooks, it is possible to disable updating hooks and + * doing that manually. + */ + void -proto_setup_mpls_map(struct proto *p, uint rts, int hooks) ++proto_setup_mpls_map(struct proto *p, uint rts) + { + struct mpls_fec_map *m = p->mpls_map; + struct channel *c = p->mpls_channel; + + if (!m && c) + { + /* + * Note that when called from a protocol start hook, it is called before + * mpls_channel_start(). But FEC map locks MPLS domain internally so it does + * not depend on lock from MPLS channel. + */ + p->mpls_map = mpls_fec_map_new(p->pool, c, rts); + } + else if (m && !c) + { + /* + * Note that for reconfiguration, it is called after the MPLS channel has + * been already removed. But removal of active MPLS channel would trigger + * protocol restart anyways. + */ + mpls_fec_map_free(m); + p->mpls_map = NULL; + } + else if (m && c) + { + // mpls_fec_map_reconfigure(m, c); + } - - if (hooks) - { - p->rte_insert = p->mpls_map ? mpls_rte_insert : NULL; - p->rte_remove = p->mpls_map ? mpls_rte_remove : NULL; - } + } + ++ + /** + * proto_shutdown_mpls_map - automatically shutdown FEC map for protocol + * @p: affected protocol + * @hooks: whether to update rte_insert / rte_remove hooks + * + * Remove MPLS FEC map of the protocol @p during protocol shutdown. + */ + void -proto_shutdown_mpls_map(struct proto *p, int hooks) ++proto_shutdown_mpls_map(struct proto *p) + { + struct mpls_fec_map *m = p->mpls_map; + + if (!m) + return; + + mpls_fec_map_free(m); + p->mpls_map = NULL; ++} ++ +static void +proto_cleanup(struct proto *p) +{ + CALL(p->proto->cleanup, p); - if (hooks) + if (p->pool) { - p->rte_insert = NULL; - p->rte_remove = NULL; + rp_free(p->pool); + p->pool = NULL; } + + p->active = 0; + proto_log_state_change(p); + + proto_rethink_goal(p); } static void @@@ -1524,20 -1037,11 +1590,21 @@@ proto_loop_stopped(void *ptr { struct proto *p = ptr; - if (p->do_start) - { - if_feed_baby(p); - p->do_start = 0; - } + ASSERT_DIE(birdloop_inside(&main_birdloop)); + ASSERT_DIE(p->loop != &main_birdloop); + + p->pool = NULL; /* is freed by birdloop_free() */ + birdloop_free(p->loop); + p->loop = &main_birdloop; + + proto_cleanup(p); +} + ++ +static void +proto_event(void *ptr) +{ + struct proto *p = ptr; if (p->do_stop) { diff --cc nest/protocol.h index 4831d2374,81139c33f..e2e3c7498 --- a/nest/protocol.h +++ b/nest/protocol.h @@@ -157,12 -172,11 +158,14 @@@ struct proto list channels; /* List of channels to rtables (struct channel) */ struct channel *main_channel; /* Primary channel */ struct rte_src *main_source; /* Primary route source */ + struct rte_owner sources; /* Route source owner structure */ struct iface *vrf; /* Related VRF instance, NULL if global */ + TLIST_LIST(proto_neigh) neighbors; /* List of neighbor structures */ + struct iface_subscription iface_sub; /* Interface notification subscription */ + struct channel *mpls_channel; /* MPLS channel, when used */ + struct mpls_fec_map *mpls_map; /* Maps protocol routes to FECs / labels */ - const char *name; /* Name of this instance (== cf->name) */ + const char *name; /* Name of this instance (== cf->name) */ u32 debug; /* Debugging flags */ u32 mrtdump; /* MRTDump flags */ uint active_channels; /* Number of active channels */ @@@ -677,16 -622,21 +680,20 @@@ struct channel struct channel_config *proto_cf_find_channel(struct proto_config *p, uint net_type); static inline struct channel_config *proto_cf_main_channel(struct proto_config *pc) { return proto_cf_find_channel(pc, pc->net_type); } + static inline struct channel_config *proto_cf_mpls_channel(struct proto_config *pc) + { return proto_cf_find_channel(pc, NET_MPLS); } -struct channel *proto_find_channel_by_table(struct proto *p, struct rtable *t); +struct channel *proto_find_channel_by_table(struct proto *p, rtable *t); struct channel *proto_find_channel_by_name(struct proto *p, const char *n); struct channel *proto_add_channel(struct proto *p, struct channel_config *cf); void proto_remove_channel(struct proto *p, struct channel *c); int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf); -void proto_setup_mpls_map(struct proto *p, uint rts, int hooks); -void proto_shutdown_mpls_map(struct proto *p, int hooks); ++void proto_setup_mpls_map(struct proto *p, uint rts); ++void proto_shutdown_mpls_map(struct proto *p); void channel_set_state(struct channel *c, uint state); -void channel_setup_in_table(struct channel *c); -void channel_setup_out_table(struct channel *c); -void channel_schedule_reload(struct channel *c); +void channel_schedule_reload(struct channel *c, struct channel_import_request *cir); +int channel_import_request_prefilter(struct channel_import_request *cir_head, const net_addr *n); static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); } static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); } diff --cc nest/route.h index fe100a366,0e502b99e..610bc21e3 --- a/nest/route.h +++ b/nest/route.h @@@ -158,79 -195,35 +158,80 @@@ struct rtable_private struct f_trie *trie_old; /* Old prefix trie waiting to be freed */ u32 trie_lock_count; /* Prefix trie locked by walks */ u32 trie_old_lock_count; /* Old prefix trie locked by walks */ + struct tbf rl_pipe; /* Rate limiting token buffer for pipe collisions */ - list subscribers; /* Subscribers for notifications */ - struct timer *settle_timer; /* Settle time for notifications */ - list flowspec_links; /* List of flowspec links, src for NET_IPx and dst for NET_FLOWx */ struct f_trie *flowspec_trie; /* Trie for evaluation of flowspec notifications */ + // struct mpls_domain *mpls_domain; /* Label allocator for MPLS */ +}; + +/* The final union private-public rtable structure */ +typedef union rtable { + struct { + RTABLE_PUBLIC; + }; + struct rtable_private priv; } rtable; -struct rt_subscription { - node n; - rtable *tab; - void (*hook)(struct rt_subscription *b); - void *data; -}; +#define RT_IS_LOCKED(tab) DOMAIN_IS_LOCKED(rtable, (tab)->lock) -struct rt_flowspec_link { - node n; - rtable *src; - rtable *dst; - u32 uc; -}; +#define RT_LOCK(tab) ({ LOCK_DOMAIN(rtable, (tab)->lock); &(tab)->priv; }) +#define RT_UNLOCK(tab) UNLOCK_DOMAIN(rtable, (tab)->lock) +#define RT_PRIV(tab) ({ ASSERT_DIE(RT_IS_LOCKED((tab))); &(tab)->priv; }) +#define RT_PUB(tab) SKIP_BACK(rtable, priv, tab) + +#define RT_LOCKED(tpub, tpriv) for (struct rtable_private *tpriv = RT_LOCK(tpub); tpriv; RT_UNLOCK(tpriv), (tpriv = NULL)) +#define RT_LOCKED_IF_NEEDED(_tpub, _tpriv) for ( \ + struct rtable_private *_al = RT_IS_LOCKED(_tpub) ? &(_tpub)->priv : NULL, *_tpriv = _al ?: RT_LOCK(_tpub); \ + _tpriv; \ + _al ?: RT_UNLOCK(_tpriv), (_tpriv = NULL)) + +#define RT_RETURN(tpriv, ...) do { RT_UNLOCK(tpriv); return __VA_ARGS__; } while (0) + +#define RT_PRIV_SAME(tpriv, tpub) (&(tpub)->priv == (tpriv)) + +/* Flags for birdloop_flag() */ +#define RTF_CLEANUP 1 +#define RTF_NHU 2 +#define RTF_EXPORT 4 +#define RTF_DELETE 8 + +extern struct rt_cork { + _Atomic uint active; + event_list queue; + event run; +} rt_cork; + +static inline void rt_cork_acquire(void) +{ + atomic_fetch_add_explicit(&rt_cork.active, 1, memory_order_acq_rel); +} + +static inline void rt_cork_release(void) +{ + if (atomic_fetch_sub_explicit(&rt_cork.active, 1, memory_order_acq_rel) == 1) + { + synchronize_rcu(); + ev_send(&global_work_list, &rt_cork.run); + } +} + +static inline int rt_cork_check(event *e) +{ + rcu_read_lock(); + + int corked = (atomic_load_explicit(&rt_cork.active, memory_order_acquire) > 0); + if (corked) + ev_send(&rt_cork.queue, e); + + rcu_read_unlock(); + + return corked; +} -#define NHU_CLEAN 0 -#define NHU_SCHEDULED 1 -#define NHU_RUNNING 2 -#define NHU_DIRTY 3 typedef struct network { - struct rte *routes; /* Available routes for this network */ + struct rte_storage *routes; /* Available routes for this network */ + struct rt_pending_export *first, *last; struct fib_node n; /* FIB flags reserved for kernel syncer */ } net; @@@ -709,19 -417,339 +710,21 @@@ struct rt_show_data_rtable * rt_show_ad #define RSEM_NOEXPORT 3 /* Routes rejected by export filter */ #define RSEM_EXPORTED 4 /* Routes marked in export map */ -/* - * 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 */ - struct nexthop *next; - byte flags; - byte weight; - byte labels_orig; /* Number of labels before hostentry was applied */ - byte labels; /* Number of all labels */ - u32 label[0]; -}; - -#define RNF_ONLINK 0x1 /* Gateway is onlink regardless of IP ranges */ - - -struct rte_src { - struct rte_src *next; /* Hash chain */ - struct proto *proto; /* Protocol the source is based on */ - u64 private_id; /* Private ID, assigned by the protocol */ - u32 global_id; /* Globally unique ID of the source */ - unsigned uc; /* Use count */ -}; - - -typedef struct rta { - struct rta *next, **pprev; /* Hash chain */ - u32 uc; /* Use count */ - u32 hash_key; /* Hash over important fields */ - struct ea_list *eattrs; /* Extended Attribute chain */ - struct hostentry *hostentry; /* Hostentry for recursive next-hops */ - ip_addr from; /* Advertising router */ - u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */ - u16 cached:1; /* Are attributes cached? */ - u16 source:7; /* Route source (RTS_...) */ - u16 scope:4; /* Route scope (SCOPE_... -- see ip.h) */ - u16 dest:4; /* Route destination type (RTD_...) */ - word pref; - struct nexthop nh; /* Next hop */ -} rta; - -#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_AGGREGATED 16 /* Aggregated route */ -#define RTS_MAX 17 - -#define RTD_NONE 0 /* Undefined next hop */ -#define RTD_UNICAST 1 /* Next hop is neighbor router */ -#define RTD_BLACKHOLE 2 /* Silently drop packets */ -#define RTD_UNREACHABLE 3 /* Reject as unreachable */ -#define RTD_PROHIBIT 4 /* Administratively prohibited */ -#define RTD_MAX 5 - -#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other - protocol-specific metric is availabe */ - - -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] : "???"; } - -/* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */ -static inline int rte_is_reachable(rte *r) -{ return r->attrs->dest == RTD_UNICAST; } - - -/* - * Extended Route Attributes - */ - -typedef struct eattr { - word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */ - byte flags; /* Protocol-dependent flags */ - byte type:5; /* Attribute type */ - 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 */ - union { - uintptr_t data; - const struct adata *ptr; /* Attribute data elsewhere */ - } u; -} eattr; - - -#define EA_CODE(proto,id) (((proto) << 8) | (id)) -#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_MPLS_LABEL EA_CODE(PROTOCOL_NONE, 1) -#define EA_MPLS_POLICY EA_CODE(PROTOCOL_NONE, 2) -#define EA_MPLS_CLASS EA_CODE(PROTOCOL_NONE, 3) - -#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 */ -#define EA_BIT_GET(ea) ((ea) >> 24) - -#define EAF_TYPE_MASK 0x1f /* Mask with this to get type */ -#define EAF_TYPE_INT 0x01 /* 32-bit unsigned integer number */ -#define EAF_TYPE_OPAQUE 0x02 /* Opaque byte string (not filterable) */ -#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_BITFIELD 0x09 /* 32-bit embedded bitfield */ -#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_LC_SET 0x12 /* Set of triplets of u32's - large community list */ -#define EAF_TYPE_IFACE 0x16 /* Interface pointer stored in adata */ -#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */ -#define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */ - -typedef struct adata { - uint length; /* Length of data */ - byte data[0]; -} adata; - -extern const adata null_adata; /* adata of length 0 */ - -static inline struct adata * -lp_alloc_adata(struct linpool *pool, uint len) -{ - struct adata *ad = lp_alloc(pool, sizeof(struct adata) + len); - ad->length = len; - return ad; -} - -static inline int adata_same(const struct adata *a, const struct adata *b) -{ return (a->length == b->length && !memcmp(a->data, b->data, a->length)); } - - -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; - -#define EALF_SORTED 1 /* Attributes are sorted by code */ -#define EALF_BISECT 2 /* Use interval bisection for searching */ -#define EALF_CACHED 4 /* Attributes belonging to cached rta */ - -struct rte_src *rt_find_source(struct proto *p, u32 id); -struct rte_src *rt_get_source(struct proto *p, u32 id); -static inline void rt_lock_source(struct rte_src *src) { src->uc++; } -static inline void rt_unlock_source(struct rte_src *src) { src->uc--; } -void rt_prune_sources(void); - -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 */ +/* Host entry: Resolve hook for recursive nexthops */ +extern struct ea_class ea_gen_hostentry; +struct hostentry_adata { + adata ad; + struct hostentry *he; + u32 labels[0]; }; -eattr *ea_find(ea_list *, unsigned ea); -eattr *ea_walk(struct ea_walk_state *s, uint id, uint max); -uintptr_t ea_get_int(ea_list *, unsigned ea, uintptr_t def); -void ea_dump(ea_list *); -void ea_sort(ea_list *); /* Sort entries in all sub-lists */ -unsigned ea_scan(ea_list *); /* How many bytes do we need for merged ea_list */ -void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffer */ -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); - -#define ea_normalize(ea) do { \ - if (ea->next) { \ - ea_list *t = alloca(ea_scan(ea)); \ - ea_merge(ea, t); \ - ea = t; \ - } \ - ea_sort(ea); \ - if (ea->count == 0) \ - ea = NULL; \ -} while(0) \ - -struct ea_one_attr_list { - ea_list l; - eattr a; -}; ++#define HOSTENTRY_LABEL_COUNT(head) (head->ad.length + sizeof(struct adata) - sizeof(struct hostentry_adata)) / sizeof(u32) + -static inline eattr * -ea_set_attr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, uintptr_t val) -{ - struct ea_one_attr_list *ea = lp_alloc(pool, sizeof(*ea)); - *ea = (struct ea_one_attr_list) { - .l.flags = EALF_SORTED, - .l.count = 1, - .l.next = *to, - - .a.id = id, - .a.type = type, - .a.flags = flags, - }; - - if (type & EAF_EMBEDDED) - ea->a.u.data = val; - else - ea->a.u.ptr = (struct adata *) val; - - *to = &ea->l; - - return &ea->a; -} - -static inline void -ea_unset_attr(ea_list **to, struct linpool *pool, _Bool local, uint code) -{ - struct ea_one_attr_list *ea = lp_alloc(pool, sizeof(*ea)); - *ea = (struct ea_one_attr_list) { - .l.flags = EALF_SORTED, - .l.count = 1, - .l.next = *to, - .a.id = code, - .a.fresh = local, - .a.originated = local, - .a.undef = 1, - }; - - *to = &ea->l; -} - -static inline void -ea_set_attr_u32(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, u32 val) -{ ea_set_attr(to, pool, id, flags, type, (uintptr_t) val); } - -static inline void -ea_set_attr_ptr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, struct adata *val) -{ ea_set_attr(to, pool, id, flags, type, (uintptr_t) val); } - -static inline void -ea_set_attr_data(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, void *data, uint len) -{ - struct adata *a = lp_alloc_adata(pool, len); - memcpy(a->data, data, len); - ea_set_attr(to, pool, id, flags, type, (uintptr_t) a); -} - - -#define NEXTHOP_MAX_SIZE (sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK) - -static inline size_t nexthop_size(const struct nexthop *nh) -{ return sizeof(struct nexthop) + sizeof(u32)*nh->labels; } -int nexthop__same(struct nexthop *x, struct nexthop *y); /* Compare multipath nexthops */ -static inline int nexthop_same(struct nexthop *x, struct nexthop *y) -{ return (x == y) || nexthop__same(x, y); } -struct nexthop *nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp); -struct nexthop *nexthop_sort(struct nexthop *x); -static inline void nexthop_link(struct rta *a, const struct nexthop *from) -{ memcpy(&a->nh, from, nexthop_size(from)); } -void nexthop_insert(struct nexthop **n, struct nexthop *y); -int nexthop_is_sorted(struct nexthop *x); - -void rta_init(void); -static inline size_t rta_size(const rta *a) { return sizeof(rta) + sizeof(u32)*a->nh.labels; } -#define RTA_MAX_SIZE (sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK) -rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */ -static inline int rta_is_cached(rta *r) { return r->cached; } -static inline rta *rta_clone(rta *r) { r->uc++; return r; } -void rta__free(rta *r); -static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); } -rta *rta_do_cow(rta *o, linpool *lp); -static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; } -void rta_dump(rta *); -void rta_dump_all(void); -void rta_show(struct cli *, rta *); - -u32 rt_get_igp_metric(rte *rt); -struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep); -void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls); - -static inline void -rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls) -{ - rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls); -} - -/* - * rta_set_recursive_next_hop() acquires hostentry from hostcache and fills - * rta->hostentry field. New hostentry has zero use count. Cached rta locks its - * hostentry (increases its use count), uncached rta does not lock it. Hostentry - * with zero use count is removed asynchronously during host cache update, - * therefore it is safe to hold such hostentry temorarily. Hostentry holds a - * lock for a 'source' rta, mainly to share multipath nexthops. - * - * There is no need to hold a lock for hostentry->dep table, because that table - * contains routes responsible for that hostentry, and therefore is non-empty if - * given hostentry has non-zero use count. If the hostentry has zero use count, - * the entry is removed before dep is referenced. - * - * The protocol responsible for routes with recursive next hops should hold a - * lock for a 'source' table governing that routes (argument tab to - * rta_set_recursive_next_hop()), because its routes reference hostentries - * (through rta) related to the governing table. When all such routes are - * removed, rtas are immediately removed achieving zero uc. Then the 'source' - * table lock could be immediately released, although hostentries may still - * exist - they will be freed together with the 'source' table. - */ - -static inline void rt_lock_hostentry(struct hostentry *he) { if (he) he->uc++; } -static inline void rt_unlock_hostentry(struct hostentry *he) { if (he) he->uc--; } - -int rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, int interior); +void +ea_set_hostentry(ea_list **to, rtable *dep, rtable *tab, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum]); +void ea_show_hostentry(const struct adata *ad, byte *buf, uint size); +void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad); /* * Default protocol preferences diff --cc nest/rt-attr.c index 53111696d,5b86a4188..32723ded8 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@@ -1701,20 -1370,6 +1701,25 @@@ rta_init(void rta_alloc_hash(); rte_src_init(); + ea_class_init(); + + RTA_UNLOCK; + + /* These attributes are required to be first for nice "show route" output */ + ea_register_init(&ea_gen_nexthop); + ea_register_init(&ea_gen_hostentry); + + /* Other generic route attributes */ + ea_register_init(&ea_gen_preference); + ea_register_init(&ea_gen_igp_metric); + ea_register_init(&ea_gen_from); + ea_register_init(&ea_gen_source); + ea_register_init(&ea_gen_flowspec_valid); ++ ++ /* MPLS route attributes */ ++ ea_register_init(&ea_gen_mpls_policy); ++ ea_register_init(&ea_gen_mpls_class); ++ ea_register_init(&ea_gen_mpls_label); } /* diff --cc nest/rt-show.c index 5d43317ac,265d5c447..00d2aa5c2 --- a/nest/rt-show.c +++ b/nest/rt-show.c @@@ -38,19 -42,14 +38,19 @@@ rt_show_rte(struct cli *c, byte *ia, rt { byte from[IPA_MAX_TEXT_LENGTH+8]; byte tm[TM_DATETIME_BUFFER_SIZE], info[256]; - rta *a = e->attrs; - int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0; - void (*get_route_info)(struct rte *, byte *buf); - struct nexthop *nh; + ea_list *a = e->attrs; + int sync_error = d->tab->kernel ? krt_get_sync_error(d->tab->kernel, e) : 0; + void (*get_route_info)(const rte *, byte *buf); - eattr *nhea = net_type_match(e->net, NB_DEST) ? ++ const eattr *nhea = net_type_match(e->net, NB_DEST) ? + ea_find(a, &ea_gen_nexthop) : NULL; + struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL; + int dest = nhad ? (NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest) : RTD_NONE; + int flowspec_valid = net_is_flow(e->net) ? rt_get_flowspec_valid(e) : FLOWSPEC_UNKNOWN; tm_format_time(tm, &config->tf_route, e->lastmod); - if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw)) - bsprintf(from, " from %I", a->from); + ip_addr a_from = ea_get_ip(a, &ea_gen_from, IPA_NONE); + if (ipa_nonzero(a_from) && (!nhad || !ipa_equal(a_from, nhad->nh.gw))) + bsprintf(from, " from %I", a_from); else from[0] = 0; @@@ -62,41 -61,49 +62,41 @@@ if (get_route_info) get_route_info(e, info); else - bsprintf(info, " (%d)", a->pref); + bsprintf(info, " (%d)", rt_get_preference(e)); if (d->last_table != d->tab) - rt_show_table(c, d); - - cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest), - e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info); - - if (a->dest == RTD_UNICAST) - for (nh = &(a->nh); nh; nh = nh->next) - { - char mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls; - char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : ""; - char weight[16] = ""; - - if (nh->labels) - { - lsp += bsprintf(lsp, " mpls %d", nh->label[0]); - for (int i=1;ilabels; i++) - lsp += bsprintf(lsp, "/%d", nh->label[i]); - } - *lsp = '\0'; + rt_show_table(d); - eattr *heea; - if (a->nh.next) - bsprintf(weight, " weight %d", nh->weight + 1); ++ const eattr *heea; + struct hostentry_adata *had = NULL; + if (!net_is_flow(e->net) && (dest == RTD_NONE) && (heea = ea_find(a, &ea_gen_hostentry))) + had = (struct hostentry_adata *) heea->u.ptr; - if (ipa_nonzero(nh->gw)) - cli_printf(c, -1007, "\tvia %I on %s%s%s%s", - nh->gw, nh->iface->name, mpls, onlink, weight); - else - cli_printf(c, -1007, "\tdev %s%s%s", - nh->iface->name, mpls, onlink, weight); - } + cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, + net_is_flow(e->net) ? flowspec_valid_name(flowspec_valid) : had ? "recursive" : rta_dest_name(dest), + e->src->owner->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info); if (d->verbose) - rta_show(c, a); + { + ea_show_list(c, a); + cli_printf(c, -1008, "\tInternal route handling values: %luL %uG %uS id %u", + e->src->private_id, e->src->global_id, e->stale_cycle, e->id); + } + else if (dest == RTD_UNICAST) + ea_show_nexthop_list(c, nhad); + else if (had) + { + char hetext[256]; + ea_show_hostentry(&had->ad, hetext, sizeof hetext); + cli_printf(c, -1007, "\t%s", hetext); + } } static void -rt_show_net(struct cli *c, net *n, struct rt_show_data *d) +rt_show_net(struct rt_show_data *d, const net_addr *n, const rte **feed, uint count) { - rte *e, *ee; + struct cli *c = d->cli; - byte ia[NET_MAX_TEXT_LENGTH+1]; + byte ia[NET_MAX_TEXT_LENGTH+16+1]; struct channel *ec = d->tab->export_channel; /* The Clang static analyzer complains that ec may be NULL. @@@ -105,11 -112,12 +105,12 @@@ int first = 1; int first_show = 1; - int last_label = 0; ++ uint last_label = 0; int pass = 0; - for (e = n->routes; e; e = e->next) + for (uint i = 0; i < count; i++) { - if (rte_is_filtered(e) != d->filtered) + if (!d->tab->prefilter && (rte_is_filtered(feed[i]) != d->filtered)) continue; d->rt_counter++; @@@ -186,13 -188,21 +187,21 @@@ if (d->stats < 2) { - if (first_show) - net_format(n, ia, sizeof(ia)); - int label = (int) ea_get_int(e->attrs->eattrs, EA_MPLS_LABEL, (uint) -1); ++ uint label = ea_get_int(e.attrs, &ea_gen_mpls_label, ~0U); + + if (first_show || (last_label != label)) + { - if (label < 0) - net_format(n->n.addr, ia, sizeof(ia)); ++ if (!~label) ++ net_format(n, ia, sizeof(ia)); + else - bsnprintf(ia, sizeof(ia), "%N mpls %d", n->n.addr, label); ++ bsnprintf(ia, sizeof(ia), "%N mpls %d", n, label); + } else ia[0] = 0; - rt_show_rte(c, ia, e, d, (e->net->routes == ee)); + rt_show_rte(c, ia, &e, d, !d->tab->prefilter && !i); first_show = 0; + last_label = label; } d->show_counter++; diff --cc nest/rt-table.c index 94576191b,31c59ccb3..e074c0bf9 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@@ -1850,237 -1453,62 +1851,240 @@@ rte_recalculate(struct rtable_private * /* The fourth (empty) case - suboptimal route was removed, nothing to do */ } - if (new) + if (new_stored) { new->lastmod = current_time(); + new->id = hmap_first_zero(&table->id_map); + hmap_set(&table->id_map, new->id); + } - if (!old) - { - new->id = hmap_first_zero(&table->id_map); - hmap_set(&table->id_map, new->id); - } + /* Log the route change */ + if (new_ok) + rt_rte_trace_in(D_ROUTES, req, &new_stored->rte, new_stored == net->routes ? "added [best]" : "added"); + else if (old_ok) + { + if (old != old_best) + rt_rte_trace_in(D_ROUTES, req, old, "removed"); + else if (net->routes && rte_is_ok(&net->routes->rte)) + rt_rte_trace_in(D_ROUTES, req, old, "removed [replaced]"); else - new->id = old->id; + rt_rte_trace_in(D_ROUTES, req, old, "removed [sole]"); } + else + if (req->trace_routes & D_ROUTES) + log(L_TRACE "%s > ignored %N %s->%s", req->name, net->n.addr, old ? "filtered" : "none", new ? "filtered" : "none"); - /* Log the route change */ - if ((c->debug & D_ROUTES) || (p->debug & D_ROUTES)) + /* Propagate the route change */ + rte_announce(table, net, new_stored, old_stored, + net->routes, old_best_stored); + + return 1; +} + +int +channel_preimport(struct rt_import_request *req, rte *new, const rte *old) +{ + struct channel *c = SKIP_BACK(struct channel, in_req, req); + + if (new && !old) + if (CHANNEL_LIMIT_PUSH(c, RX)) + return 0; + + if (!new && old) + CHANNEL_LIMIT_POP(c, RX); + + int new_in = new && !rte_is_filtered(new); + int old_in = old && !rte_is_filtered(old); ++ ++ int verdict = 1; + + if (new_in && !old_in) + if (CHANNEL_LIMIT_PUSH(c, IN)) + if (c->in_keep & RIK_REJECTED) - { + new->flags |= REF_FILTERED; - return 1; - } + else - return 0; ++ verdict = 0; + + if (!new_in && old_in) + CHANNEL_LIMIT_POP(c, IN); + - return 1; ++ mpls_rte_preimport(new_in ? new : NULL, old_in ? old : NULL); ++ ++ return verdict; +} + +void +rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) +{ + if (!c->in_req.hook) + { + log(L_WARN "%s.%s: Called rte_update without import hook", c->proto->name, c->name); + return; + } + + ASSERT(c->channel_state == CS_UP); + + /* The import reloader requires prefilter routes to be the first layer */ + if (new && (c->in_keep & RIK_PREFILTER)) + if (ea_is_cached(new->attrs) && !new->attrs->next) + new->attrs = ea_clone(new->attrs); + else + new->attrs = ea_lookup(new->attrs, 0); + + const struct filter *filter = c->in_filter; + struct channel_import_stats *stats = &c->import_stats; + + if (new) { - if (new_ok) - rte_trace(c, new, '>', new == net->routes ? "added [best]" : "added"); - else if (old_ok) + new->net = n; + + int fr; + + stats->updates_received++; + if ((filter == FILTER_REJECT) || + ((fr = f_run(filter, new, 0)) > F_ACCEPT)) { - if (old != old_best) - rte_trace(c, old, '>', "removed"); - else if (rte_is_ok(net->routes)) - rte_trace(c, old, '>', "removed [replaced]"); + stats->updates_filtered++; + channel_rte_trace_in(D_FILTERS, c, new, "filtered out"); + + if (c->in_keep & RIK_REJECTED) + new->flags |= REF_FILTERED; else - rte_trace(c, old, '>', "removed [sole]"); + new = NULL; + } + ++ if (new && c->proto->mpls_map) ++ mpls_handle_rte(c->proto->mpls_map, n, new); ++ + if (new) + if (net_is_flow(n)) + rt_flowspec_resolve_rte(new, c); + else + rt_next_hop_resolve_rte(new); + + if (new && !rte_validate(c, new)) + { + channel_rte_trace_in(D_FILTERS, c, new, "invalid"); + stats->updates_invalid++; + new = NULL; } - } + else + stats->withdraws_received++; - /* Propagate the route change */ - rte_announce(table, RA_UNDEF, net, new, old, net->routes, old_best); + rte_import(&c->in_req, n, new, src); + + /* Now the route attributes are kept by the in-table cached version + * and we may drop the local handle */ + if (new && (c->in_keep & RIK_PREFILTER)) + { + /* There may be some updates on top of the original attribute block */ + ea_list *a = new->attrs; + while (a->next) + a = a->next; - if (!net->routes && - (table->gc_counter++ >= table->config->gc_threshold)) - rt_kick_prune_timer(table); + ea_free(a); + } - if (old_ok && p->rte_remove) - p->rte_remove(net, old); - if (new_ok && p->rte_insert) - p->rte_insert(net, new); +} - if (old) +void +rte_import(struct rt_import_request *req, const net_addr *n, rte *new, struct rte_src *src) +{ + struct rt_import_hook *hook = req->hook; + if (!hook) + { + log(L_WARN "%s: Called rte_import without import hook", req->name); + return; + } + + RT_LOCKED(hook->table, tab) + { + net *nn; + if (new) { - if (!new) - hmap_clear(&table->id_map, old->id); + /* Use the actual struct network, not the dummy one */ + nn = net_get(tab, n); + new->net = nn->n.addr; + new->sender = hook; - rte_free_quick(old); + /* Set the stale cycle */ + new->stale_cycle = hook->stale_set; } + else if (!(nn = net_find(tab, n))) + { + req->hook->stats.withdraws_ignored++; + if (req->trace_routes & D_ROUTES) + log(L_TRACE "%s > ignored %N withdraw", req->name, n); + RT_RETURN(tab); + } + + /* Recalculate the best route */ + if (rte_recalculate(tab, hook, nn, new, src)) + ev_send(req->list, &hook->announce_event); + } } -static int rte_update_nest_cnt; /* Nesting counter to allow recursive updates */ +/* Check rtable for best route to given net whether it would be exported do p */ +int +rt_examine(rtable *tp, net_addr *a, struct channel *c, const struct filter *filter) +{ + rte rt = {}; -static inline void -rte_update_lock(void) + RT_LOCKED(tp, t) + { + net *n = net_find(t, a); + if (n) + rt = RTE_COPY_VALID(n->routes); + } + + if (!rt.src) + return 0; + + int v = c->proto->preexport ? c->proto->preexport(c, &rt) : 0; + if (v == RIC_PROCESS) + v = (f_run(filter, &rt, FF_SILENT) <= F_ACCEPT); + + return v > 0; +} + +static void +rt_table_export_done(void *hh) { - rte_update_nest_cnt++; + struct rt_table_export_hook *hook = hh; + struct rt_export_request *req = hook->h.req; + void (*stopped)(struct rt_export_request *) = hook->h.stopped; + rtable *t = SKIP_BACK(rtable, priv.exporter, hook->table); + + RT_LOCKED(t, tab) + { + DBG("Export hook %p in table %s finished uc=%u\n", hook, tab->name, tab->use_count); + + /* Drop pending exports */ + rt_export_used(&tab->exporter, hook->h.req->name, "stopped"); + + /* Do the common code; this frees the hook */ + rt_export_stopped(&hook->h); + } + + /* Report the channel as stopped. */ + CALL(stopped, req); + + /* Unlock the table; this may free it */ + rt_unlock_table(t); +} + +void +rt_export_stopped(struct rt_export_hook *hook) +{ + /* Unlink from the request */ + hook->req->hook = NULL; + + /* Unlist */ + rem_node(&hook->n); + + /* Free the hook itself together with its pool */ + rp_free(hook->pool); } static inline void @@@ -3469,35 -2445,22 +3473,32 @@@ rt_postconfig(struct config *c */ void -rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls) +ea_set_hostentry(ea_list **to, rtable *dep, rtable *src, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum]) +{ - struct { - struct adata ad; - struct hostentry *he; - u32 labels[0]; - } *head = (void *) tmp_alloc_adata(sizeof *head + sizeof(u32) * lnum - sizeof(struct adata)); ++ struct hostentry_adata *head = (struct hostentry_adata *) tmp_alloc_adata( ++ sizeof *head + sizeof(u32) * lnum - sizeof(struct adata)); + + RT_LOCKED(src, tab) + head->he = rt_get_hostentry(tab, gw, ll, dep); + memcpy(head->labels, labels, lnum * sizeof(u32)); + + ea_set_attr(to, EA_LITERAL_DIRECT_ADATA( + &ea_gen_hostentry, 0, &head->ad)); +} + + +static void +rta_apply_hostentry(struct rtable_private *tab UNUSED, ea_list **to, struct hostentry_adata *head) { - a->hostentry = he; - a->dest = he->dest; - a->igp_metric = he->igp_metric; + struct hostentry *he = head->he; + u32 *labels = head->labels; + u32 lnum = (u32 *) (head->ad.data + head->ad.length) - labels; + + ea_set_attr_u32(to, &ea_gen_igp_metric, 0, he->igp_metric); - if (a->dest != RTD_UNICAST) + if (!he->src) { - /* No nexthop */ -no_nexthop: - a->nh = (struct nexthop) {}; - if (mls) - { /* Store the label stack for later changes */ - a->nh.labels_orig = a->nh.labels = mls->len; - memcpy(a->nh.label, mls->stack, mls->len * sizeof(u32)); - } + ea_set_dest(to, 0, RTD_UNREACHABLE); return; } diff --cc sysdep/unix/main.c index f0ed85e25,0d7788bb4..f9ffbd015 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@@ -894,17 -886,17 +895,18 @@@ main(int argc, char **argv dmalloc_debug(0x2f03d00); #endif - parse_args(argc, argv); - log_switch(1, NULL, NULL); - - random_init(); + /* Prepare necessary infrastructure */ + the_bird_lock(); + times_update(); resource_init(); - timer_init(); + random_init(); + + birdloop_init(); olock_init(); - io_init(); rt_init(); + io_init(); if_init(); + mpls_init(); // roa_init(); config_init(); diff --cc test/bt-utils.c index 53ee92d2b,a0353a210..56124e1f7 --- a/test/bt-utils.c +++ b/test/bt-utils.c @@@ -60,12 -59,14 +61,13 @@@ bt_bird_init(void { if(bt_verbose) log_init_debug(""); - log_switch(bt_verbose != 0, NULL, NULL); olock_init(); - timer_init(); - io_init(); rt_init(); + log_switch(1, NULL, NULL); + io_init(); if_init(); + mpls_init(); config_init(); protos_build();