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:
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 */
--- /dev/null
+/*
+ * BIRD Internet Routing Daemon -- Routing data structures
+ *
+ * (c) 1998--2000 Martin Mares <mj@ucw.cz>
+ * (c) 2022 Maria Matejka <mq@jmq.cz>
+ *
+ * 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
--- /dev/null
+/*
+ * BIRD Internet Routing Daemon -- Internal Data Types
+ *
+ * (c) 2022 Maria Matejka <mq@jmq.cz>
+ *
+ * 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
- 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)
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)
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 <i32> idval
%type <f> imexport
%type <s> optproto
%type <ra> r_args
%type <sd> sym_args
- %type <i> proto_start debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type tos password_algorithm
-%type <i> 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 <i> 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 <ps> proto_patt proto_patt2
%type <cc> channel_start proto_channel
%type <cl> limit_spec
--- /dev/null
- | 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; }
+ /*
+ * BIRD Internet Routing Daemon -- MPLS Structures
+ *
+ * (c) 2022 Ondrej Zajicek <santiago@crfreenet.org>
+ * (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 <i> mpls_label_policy
+ %type <cc> 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 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
--- /dev/null
- struct pool *p = rp_new(&root_pool, "MPLS domain");
+ /*
+ * BIRD Internet Routing Daemon -- MPLS Structures
+ *
+ * (c) 2022 Ondrej Zajicek <santiago@crfreenet.org>
+ * (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)
+ {
-#define RTA_KEY(fec) fec->rta, fec->class_id, fec->hash
++ 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_EQ(r1,i1,h1,r2,i2,h2) h1 == h2 && r1 == r2 && i1 == i2
-#define RTA_FN(r,i,h) h
++#define RTA_KEY(fec) fec->rta
+ #define RTA_NEXT(fec) fec->next_k
-static rta * mpls_get_key_rta(struct mpls_fec_map *m, const rta *src);
++#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);
- struct pool *p = rp_new(pp, "MPLS FEC map");
++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)
+ {
- m->mpls_scope = SCOPE_UNIVERSE;
++ 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;
- if (m->rta_hash.data)
+
+ return m;
+ }
+
+ void
+ mpls_fec_map_free(struct mpls_fec_map *m)
+ {
+ /* Free stored rtas */
- HASH_WALK(m->rta_hash, next_k, fec)
++ if (m->attrs_hash.data)
+ {
- rta_free(fec->rta);
++ HASH_WALK(m->attrs_hash, next_k, fec)
+ {
-mpls_get_fec_by_rta(struct mpls_fec_map *m, const rta *src, u32 class_id)
++ 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 *
- if (!m->rta_hash.data)
- HASH_INIT(m->rta_hash, m->pool, 4);
++mpls_get_fec_by_destination(struct mpls_fec_map *m, ea_list *dest)
+ {
- 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);
++ if (!m->attrs_hash.data)
++ HASH_INIT(m->attrs_hash, m->pool, 4);
+
- rta_free(rta);
++ 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)
+ {
- fec->class_id = class_id;
++ ea_free(rta->l);
+ return fec;
+ }
+
+ fec = sl_allocz(mpls_slab(m, 0));
+
+ fec->hash = hash;
- HASH_INSERT2(m->rta_hash, RTA, m->pool, fec);
+ 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);
+
- rta_free(fec->rta);
- HASH_REMOVE2(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:
-static inline void
-mpls_damage_fec(struct mpls_fec_map *m UNUSED, struct mpls_fec *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); }
+
- if (fec->state == MPLS_FEC_CLEAN)
- fec->state = MPLS_FEC_DIRTY;
++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)
+ {
-static rta *
-mpls_get_key_rta(struct mpls_fec_map *m, const rta *src)
++ struct mpls_fec_tmp_lock *l = SKIP_BACK(struct mpls_fec_tmp_lock, r, r);
++ mpls_unlock_fec(l->m, l->fec);
+ }
+
- rta *a = allocz(RTA_MAX_SIZE);
++static void
++mpls_fec_tmp_lock_dump(resource *r, unsigned indent UNUSED)
+ {
- a->source = m->mpls_rts;
- a->scope = m->mpls_scope;
++ 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);
++}
+
- 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 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,
++};
+
- /* 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));
- }
++static void
++mpls_lock_fec_tmp(struct mpls_fec_map *m, struct mpls_fec *fec)
++{
++ if (!fec)
++ return;
+
- return rta_lookup(a);
++ fec->uc++;
+
-static void
-mpls_announce_fec(struct mpls_fec_map *m, struct mpls_fec *fec, const rta *src)
++ struct mpls_fec_tmp_lock *l = ralloc(tmp_res.pool, &mpls_fec_tmp_lock_class);
++ l->m = m;
++ l->fec = fec;
+ }
+
- rta *a = allocz(RTA_MAX_SIZE);
++static inline void
++mpls_damage_fec(struct mpls_fec_map *m UNUSED, struct mpls_fec *fec)
+ {
- a->source = m->mpls_rts;
- a->scope = m->mpls_scope;
++ if (fec->state == MPLS_FEC_CLEAN)
++ fec->state = MPLS_FEC_DIRTY;
++}
+
- 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;
++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,
++ };
+
- /* Reconstruct the original labelstack */
- ms.len = src->nh.labels_orig;
- memcpy(ms.stack, labels, src->nh.labels_orig * sizeof(u32));
++ 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)
+
- struct hostentry *s = src->hostentry;
- rta_set_recursive_next_hop(m->channel->table, a, s->owner, s->addr, s->link, &ms);
++ 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 */
- rte *e = rte_get_temp(rta_lookup(a), m->channel->proto->main_source);
- e->pflags = 0;
++ 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_update2(m->channel, (net_addr *) &n, e, m->channel->proto->main_source);
++ rte e = {
++ .src = m->channel->proto->main_source,
++ .attrs = src,
++ };
+
+ fec->state = MPLS_FEC_CLEAN;
- rte_update2(m->channel, (net_addr *) &n, NULL, 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;
-mpls_apply_fec(rte *r, struct mpls_fec *fec, linpool *lp)
++ rte_update(m->channel, (net_addr *) &n, NULL, m->channel->proto->main_source);
+ }
+
+ static void
- 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);
- }
++mpls_apply_fec(rte *r, struct mpls_fec *fec)
+ {
-mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r, linpool *lp, struct mpls_fec **locked_fec)
++ 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
- ASSERT(!(r->flags & REF_COW));
-
++mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r)
+ {
-
+ struct mpls_fec *fec = NULL;
+
- uint policy = ea_get_int(r->attrs->eattrs, EA_MPLS_POLICY, 0);
+ /* Select FEC for route */
- uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0);
++ uint policy = ea_get_int(r->attrs, &ea_gen_mpls_policy, 0);
+ switch (policy)
+ {
+ case MPLS_POLICY_NONE:
+ return;
+
+ case MPLS_POLICY_STATIC:;
- 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);
++ 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;
+
- mpls_lock_fec(m, fec);
- *locked_fec = fec;
++ 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_apply_fec(r, fec, lp);
++ mpls_lock_fec_tmp(m, fec);
+
+ /* Apply FEC label to route */
-void
-mpls_handle_rte_cleanup(struct mpls_fec_map *m, struct mpls_fec **locked_fec)
++ 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);
+ }
+
- /* Unlock temporarily locked FEC from mpls_handle_rte() */
- if (*locked_fec)
- {
- mpls_unlock_fec(m, *locked_fec);
- *locked_fec = NULL;
- }
-}
++static inline struct mpls_fec_tmp_lock
++mpls_rte_get_fec_lock(const rte *r)
+ {
-void
-mpls_rte_insert(net *n UNUSED, rte *r)
-{
- struct proto *p = r->src->proto;
- struct mpls_fec_map *m = p->mpls_map;
++ struct mpls_fec_tmp_lock mt = {
++ .m = SKIP_BACK(struct proto, sources, r->src->owner)->mpls_map,
++ };
+
- uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0);
++ if (!mt.m)
++ return mt;
+
- return;
++ uint label = ea_get_int(r->attrs, &ea_gen_mpls_label, 0);
+ if (label < 16)
- struct mpls_fec *fec = mpls_find_fec_by_label(m, label);
- if (!fec)
- return;
-
- mpls_lock_fec(m, fec);
++ return mt;
+
-mpls_rte_remove(net *n UNUSED, rte *r)
++ mt.fec = mpls_find_fec_by_label(mt.m, label);
++ return mt;
+ }
+
+ void
- struct proto *p = r->src->proto;
- struct mpls_fec_map *m = p->mpls_map;
++mpls_rte_preimport(rte *new, const rte *old)
+ {
- uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0);
- if (label < 16)
- return;
++ struct mpls_fec_tmp_lock new_mt = {}, old_mt = {};
+
- struct mpls_fec *fec = mpls_find_fec_by_label(m, label);
- if (!fec)
++ if (new)
++ new_mt = mpls_rte_get_fec_lock(new);
+
- mpls_unlock_fec(m, fec);
++ if (old)
++ old_mt = mpls_rte_get_fec_lock(old);
++
++ if (new_mt.fec == old_mt.fec)
+ return;
+
++ 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,
++};
--- /dev/null
- u32 class_id; /* Aaggregation class */
+ /*
+ * BIRD Internet Routing Daemon -- MPLS Structures
+ *
+ * (c) 2022 Ondrej Zajicek <santiago@crfreenet.org>
+ * (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 */
- struct rta *rta;
+ };
+
+ 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 */
- HASH(struct mpls_fec) rta_hash; /* Hash table for MPLS_POLICY_AGGREGATE FECs */
++ 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 */
- u8 mpls_scope; /* Scope value used for MPLS routes () */
++ 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_*) */
-struct mpls_fec *mpls_get_fec_by_rta(struct mpls_fec_map *m, const rta *src, u32 class_id);
+ };
+
+
+ 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);
-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);
++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);
++void mpls_rte_preimport(rte *new, const rte *old);
+
+ #endif
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)
return 1;
}
-proto_setup_mpls_map(struct proto *p, uint rts, int hooks)
+ /**
+ * 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
-
- if (hooks)
- {
- p->rte_insert = p->mpls_map ? mpls_rte_insert : NULL;
- p->rte_remove = p->mpls_map ? mpls_rte_remove : NULL;
- }
++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);
+ }
-proto_shutdown_mpls_map(struct proto *p, int hooks)
+ }
+
++
+ /**
+ * 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)
+ {
+ 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
{
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)
{
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 */
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); }
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;
#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
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);
}
/*
{
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;
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;i<nh->labels; 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.
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++;
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++;
/* 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
*/
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;
}
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();
{
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();