]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - nest/route.h
L3VPN: BGP/MPLS VPNs using MPLS backbone
[thirdparty/bird.git] / nest / route.h
index 791275195d9e2efb7d973ed1175658836b334da0..f83a5b33023f3478406a00a2378e99788c4a2c8b 100644 (file)
@@ -10,6 +10,7 @@
 #define _BIRD_ROUTE_H_
 
 #include "lib/lists.h"
+#include "lib/bitmap.h"
 #include "lib/resource.h"
 #include "lib/net.h"
 
@@ -18,7 +19,11 @@ struct protocol;
 struct proto;
 struct rte_src;
 struct symbol;
+struct timer;
+struct fib;
 struct filter;
+struct f_trie;
+struct f_trie_walk_state;
 struct cli;
 
 /*
@@ -36,7 +41,6 @@ struct cli;
 struct fib_node {
   struct fib_node *next;               /* Next in hash chain */
   struct fib_iterator *readers;                /* List of readers of this node */
-  byte flags;                          /* User-defined, will be removed */
   net_addr addr[0];
 };
 
@@ -48,7 +52,7 @@ struct fib_iterator {                 /* See lib/slists.h for an explanation */
   uint hash;
 };
 
-typedef void (*fib_init_fn)(void *);
+typedef void (*fib_init_fn)(struct fib *, void *);
 
 struct fib {
   pool *fib_pool;                      /* Pool holding all our data */
@@ -84,6 +88,8 @@ void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't
 struct fib_node *fit_get(struct fib *, struct fib_iterator *);
 void fit_put(struct fib_iterator *, struct fib_node *);
 void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos);
+void fit_put_end(struct fib_iterator *i);
+void fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src);
 
 
 #define FIB_WALK(fib, type, z) do {                            \
@@ -118,8 +124,12 @@ void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uin
 
 #define FIB_ITERATE_PUT_NEXT(it, fib) fit_put_next(fib, it, fn_, hpos_)
 
+#define FIB_ITERATE_PUT_END(it) fit_put_end(it)
+
 #define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it)
 
+#define FIB_ITERATE_COPY(dst, src, fib) fit_copy(fib, dst, src)
+
 
 /*
  *     Master Routing Tables. Generally speaking, each of them contains a FIB
@@ -138,19 +148,31 @@ struct rtable_config {
   struct rtable *table;
   struct proto_config *krt_attached;   /* Kernel syncer attached to this table */
   uint addr_type;                      /* Type of address data stored in table (NET_*) */
-  int gc_max_ops;                      /* Maximum number of operations before GC is run */
-  int gc_min_time;                     /* Minimum time between two consecutive GC runs */
+  uint gc_threshold;                   /* Maximum number of operations before GC is run */
+  uint gc_period;                      /* Approximate time between two consecutive GC runs */
   byte sorted;                         /* Routes of network are sorted according to rte_better() */
+  byte internal;                       /* Internal table of a protocol */
+  byte trie_used;                      /* Rtable has attached trie */
+  btime min_settle_time;               /* Minimum settle time for notifications */
+  btime max_settle_time;               /* Maximum settle time for notifications */
 };
 
 typedef struct rtable {
+  resource r;
   node n;                              /* Node in list of all tables */
+  pool *rp;                            /* Resource pool to allocate everything from, including itself */
   struct fib fib;
+  struct f_trie *trie;                 /* Trie of prefixes defined in fib */
   char *name;                          /* Name of this table */
   list channels;                       /* List of attached channels (struct channel) */
   uint addr_type;                      /* Type of address data stored in table (NET_*) */
   int pipe_busy;                       /* Pipe loop detection */
   int use_count;                       /* Number of protocols using this table */
+  u32 rt_count;                                /* Number of routes in the table */
+
+  byte internal;                       /* Internal table of a protocol */
+
+  struct hmap id_map;
   struct hostcache *hostcache;
   struct rtable_config *config;                /* Configuration of this table */
   struct config *deleted;              /* Table doesn't exist in current configuration,
@@ -158,15 +180,43 @@ typedef struct rtable {
                                         * obstacle from this routing table.
                                         */
   struct event *rt_event;              /* Routing table event */
+  struct timer *prune_timer;           /* Timer for periodic pruning / GC */
+  btime last_rt_change;                        /* Last time when route changed */
+  btime base_settle_time;              /* Start time of rtable settling interval */
   btime gc_time;                       /* Time of last GC */
-  int gc_counter;                      /* Number of operations since last GC */
+  uint gc_counter;                     /* Number of operations since last GC */
   byte prune_state;                    /* Table prune state, 1 -> scheduled, 2-> running */
+  byte prune_trie;                     /* Prune prefix trie during next table prune */
   byte hcu_scheduled;                  /* Hostcache update is scheduled */
   byte nhu_state;                      /* Next Hop Update state */
   struct fib_iterator prune_fit;       /* Rtable prune FIB iterator */
   struct fib_iterator nhu_fit;         /* Next Hop Update FIB iterator */
+  struct f_trie *trie_new;             /* New prefix trie defined during pruning */
+  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 */
+
+  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 */
 } rtable;
 
+struct rt_subscription {
+  node n;
+  rtable *tab;
+  void (*hook)(struct rt_subscription *b);
+  void *data;
+};
+
+struct rt_flowspec_link {
+  node n;
+  rtable *src;
+  rtable *dst;
+  u32 uc;
+};
+
 #define NHU_CLEAN      0
 #define NHU_SCHEDULED  1
 #define NHU_RUNNING    2
@@ -207,53 +257,20 @@ struct hostentry {
 typedef struct rte {
   struct rte *next;
   net *net;                            /* Network this RTE belongs to */
+  struct rte_src *src;                 /* Route source that created the route */
   struct channel *sender;              /* Channel used to send the route to the routing table */
   struct rta *attrs;                   /* Attributes of this route */
+  u32 id;                              /* Table specific route id */
   byte flags;                          /* Flags (REF_...) */
   byte pflags;                         /* Protocol-specific flags */
-  word pref;                           /* Route preference */
   btime lastmod;                       /* Last modified */
-  union {                              /* Protocol-dependent data (metrics etc.) */
-#ifdef CONFIG_RIP
-    struct {
-      struct iface *from;              /* Incoming iface */
-      u8 metric;                       /* RIP metric */
-      u16 tag;                         /* External route tag */
-    } rip;
-#endif
-#ifdef CONFIG_OSPF
-    struct {
-      u32 metric1, metric2;            /* OSPF Type 1 and Type 2 metrics */
-      u32 tag;                         /* External route tag */
-      u32 router_id;                   /* Router that originated this route */
-    } ospf;
-#endif
-#ifdef CONFIG_BGP
-    struct {
-      u8 suppressed;                   /* Used for deterministic MED comparison */
-    } bgp;
-#endif
-#ifdef CONFIG_BABEL
-    struct {
-      u16 seqno;                       /* Babel seqno */
-      u16 metric;                      /* Babel metric */
-      u64 router_id;                   /* Babel router id */
-    } babel;
-#endif
-    struct {                           /* Routes generated by krt sync (both temporary and inherited ones) */
-      s8 src;                          /* Alleged route source (see krt.h) */
-      u8 proto;                                /* Kernel source protocol ID */
-      u8 seen;                         /* Seen during last scan */
-      u8 best;                         /* Best route in network, propagated to core */
-      u32 metric;                      /* Kernel metric */
-    } krt;
-  } u;
 } rte;
 
 #define REF_COW                1               /* Copy this rte on write */
 #define REF_FILTERED   2               /* Route is rejected by import filter */
 #define REF_STALE      4               /* Route is stale in a refresh cycle */
 #define REF_DISCARD    8               /* Route is scheduled for discard */
+#define REF_MODIFY     16              /* Route is scheduled for modify */
 
 /* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
 static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_FILTERED); }
@@ -269,34 +286,46 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED);
 #define RA_ANY         3               /* Announcement of any route change */
 #define RA_MERGED      4               /* Announcement of optimal route merged with next ones */
 
-/* Return value of import_control() callback */
+/* Return value of preexport() callback */
 #define RIC_ACCEPT     1               /* Accepted by protocol */
 #define RIC_PROCESS    0               /* Process it through import filter */
 #define RIC_REJECT     -1              /* Rejected by protocol */
 #define RIC_DROP       -2              /* Silently dropped by protocol */
 
+extern list routing_tables;
 struct config;
 
 void rt_init(void);
 void rt_preconfig(struct config *);
+void rt_postconfig(struct config *);
 void rt_commit(struct config *new, struct config *old);
 void rt_lock_table(rtable *);
 void rt_unlock_table(rtable *);
-void rt_setup(pool *, rtable *, struct rtable_config *);
+struct f_trie * rt_lock_trie(rtable *tab);
+void rt_unlock_trie(rtable *tab, struct f_trie *trie);
+void rt_subscribe(rtable *tab, struct rt_subscription *s);
+void rt_unsubscribe(struct rt_subscription *s);
+void rt_flowspec_link(rtable *src, rtable *dst);
+void rt_flowspec_unlink(rtable *src, rtable *dst);
+rtable *rt_setup(pool *, struct rtable_config *);
+static inline void rt_shutdown(rtable *r) { rfree(r->rp); }
+
 static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
 static inline net *net_find_valid(rtable *tab, const net_addr *addr)
 { net *n = net_find(tab, addr); return (n && rte_is_valid(n->routes)) ? n : NULL; }
 static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) fib_get(&tab->fib, addr); }
-void *net_route(rtable *tab, const net_addr *n);
+net *net_get(rtable *tab, const net_addr *addr);
+net *net_route(rtable *tab, const net_addr *n);
 int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
 rte *rte_find(net *net, struct rte_src *src);
-rte *rte_get_temp(struct rta *);
+rte *rte_get_temp(struct rta *, struct rte_src *src);
 void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
 /* rte_update() moved to protocol.h to avoid dependency conflicts */
-int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
-rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, linpool *pool, int silent);
+int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
+rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent);
 void rt_refresh_begin(rtable *t, struct channel *c);
 void rt_refresh_end(rtable *t, struct channel *c);
+void rt_modify_stale(rtable *t, struct channel *c);
 void rt_schedule_prune(rtable *t);
 void rte_dump(rte *);
 void rte_free(rte *);
@@ -307,8 +336,28 @@ void rt_dump(rtable *);
 void rt_dump_all(void);
 int rt_feed_channel(struct channel *c);
 void rt_feed_channel_abort(struct channel *c);
+int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
+int rt_reload_channel(struct channel *c);
+void rt_reload_channel_abort(struct channel *c);
+void rt_prune_sync(rtable *t, int all);
+int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed);
 struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
 
+int rte_same(rte *x, rte *y);
+
+static inline int rt_is_ip(rtable *tab)
+{ return (tab->addr_type == NET_IP4) || (tab->addr_type == NET_IP6); }
+
+static inline int rt_is_vpn(rtable *tab)
+{ return (tab->addr_type == NET_VPN4) || (tab->addr_type == NET_VPN6); }
+
+static inline int rt_is_roa(rtable *tab)
+{ return (tab->addr_type == NET_ROA4) || (tab->addr_type == NET_ROA6); }
+
+static inline int rt_is_flow(rtable *tab)
+{ return (tab->addr_type == NET_FLOW4) || (tab->addr_type == NET_FLOW6); }
+
+
 /* Default limit for ECMP next hops, defined in sysdep code */
 extern const int rt_default_ecmp;
 
@@ -324,15 +373,19 @@ struct rt_show_data {
   struct rt_show_data_rtable *tab;     /* Iterator over table list */
   struct rt_show_data_rtable *last_table; /* Last table in output */
   struct fib_iterator fit;             /* Iterator over networks in table */
+  struct f_trie_walk_state *walk_state;        /* Iterator over networks in trie */
+  struct f_trie *walk_lock;            /* Locked trie for walking */
   int verbose, tables_defined_by;
-  struct filter *filter;
+  const struct filter *filter;
   struct proto *show_protocol;
   struct proto *export_protocol;
   struct channel *export_channel;
   struct config *running_on_config;
-  int export_mode, primary_only, filtered, stats, show_for;
+  struct krt_proto *kernel;
+  int export_mode, addr_mode, primary_only, filtered, stats;
 
   int table_open;                      /* Iteration (fit) is open */
+  int trie_walk;                       /* Current table is iterated using trie */
   int net_counter, rt_counter, show_counter, table_counter;
   int net_counter_last, rt_counter_last, show_counter_last;
 };
@@ -349,11 +402,17 @@ struct rt_show_data_rtable * rt_show_add_table(struct rt_show_data *d, rtable *t
 #define RSD_TDB_SET      0x1           /* internal: show empty tables */
 #define RSD_TDB_NMN      0x2           /* internal: need matching net */
 
+/* Value of addr_mode */
+#define RSD_ADDR_EQUAL 1               /* Exact query - show route <addr> */
+#define RSD_ADDR_FOR   2               /* Longest prefix match - show route for <addr> */
+#define RSD_ADDR_IN    3               /* Interval query - show route in <addr> */
+
 /* Value of export_mode in struct rt_show_data */
 #define RSEM_NONE      0               /* Export mode not used */
 #define RSEM_PREEXPORT 1               /* Routes ready for export, before filtering */
 #define RSEM_EXPORT    2               /* Routes accepted by export filter */
 #define RSEM_NOEXPORT  3               /* Routes rejected by export filter */
+#define RSEM_EXPORTED  4               /* Routes marked in export map */
 
 /*
  *     Route Attributes
@@ -381,7 +440,7 @@ struct nexthop {
 struct rte_src {
   struct rte_src *next;                        /* Hash chain */
   struct proto *proto;                 /* Protocol the source is based on */
-  u32 private_id;                      /* Private ID, assigned by the protocol */
+  u64 private_id;                      /* Private ID, assigned by the protocol */
   u32 global_id;                       /* Globally unique ID of the source */
   unsigned uc;                         /* Use count */
 };
@@ -392,18 +451,17 @@ typedef struct rta {
   u32 uc;                              /* Use count */
   u32 hash_key;                                /* Hash over important fields */
   struct ea_list *eattrs;              /* Extended Attribute chain */
-  struct rte_src *src;                 /* Route source that created the route */
   struct hostentry *hostentry;         /* Hostentry for recursive next-hops */
   ip_addr from;                                /* Advertising router */
   u32 igp_metric;                      /* IGP metric to next hop (for iBGP routes) */
-  u8 source;                           /* Route source (RTS_...) */
-  u8 scope;                            /* Route scope (SCOPE_... -- see ip.h) */
-  u8 dest;                             /* Route destination type (RTD_...) */
-  u8 aflags;
+  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_DUMMY 0                    /* Dummy route to be removed soon */
 #define RTS_STATIC 1                   /* Normal static route */
 #define RTS_INHERIT 2                  /* Route inherited from kernel */
 #define RTS_DEVICE 3                   /* Device route */
@@ -418,12 +476,10 @@ typedef struct rta {
 #define RTS_PIPE 12                    /* Inter-table wormhole */
 #define RTS_BABEL 13                   /* Babel route */
 #define RTS_RPKI 14                    /* Route Origin Authorization */
-
-
-#define RTC_UNICAST 0
-#define RTC_BROADCAST 1
-#define RTC_MULTICAST 2
-#define RTC_ANYCAST 3                  /* IPv6 Anycast */
+#define RTS_PERF 15                    /* Perf checker */
+#define RTS_L3VPN 16                   /* MPLS L3VPN */
+#define RTS_AGGREGATED 17              /* Aggregated route */
+#define RTS_MAX 18
 
 #define RTD_NONE 0                     /* Undefined next hop */
 #define RTD_UNICAST 1                  /* Next hop is neighbor router */
@@ -432,17 +488,11 @@ typedef struct rta {
 #define RTD_PROHIBIT 4                 /* Administratively prohibited */
 #define RTD_MAX 5
 
-                                       /* Flags for net->n.flags, used by kernel syncer */
-#define KRF_INSTALLED 0x80             /* This route should be installed in the kernel */
-#define KRF_SYNC_ERROR 0x40            /* Error during kernel table synchronization */
-
-#define RTAF_CACHED 1                  /* This is a cached rta */
-
 #define IGP_METRIC_UNKNOWN 0x80000000  /* Default igp_metric used when no other
                                           protocol-specific metric is availabe */
 
 
-const char * rta_dest_names[RTD_MAX];
+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] : "???"; }
@@ -457,33 +507,38 @@ static inline int rte_is_reachable(rte *r)
  */
 
 typedef struct eattr {
-  word id;                             /* EA_CODE(EAP_..., protocol-dependent ID) */
+  word id;                             /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */
   byte flags;                          /* Protocol-dependent flags */
-  byte type;                           /* Attribute type and several flags (EAF_...) */
+  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 {
-    u32 data;
-    struct adata *ptr;                 /* Attribute data elsewhere */
+    uintptr_t data;
+    const struct adata *ptr;           /* Attribute data elsewhere */
   } u;
 } eattr;
 
-#define EAP_GENERIC 0                  /* Generic attributes */
-#define EAP_BGP 1                      /* BGP attributes */
-#define EAP_RIP 2                      /* RIP */
-#define EAP_OSPF 3                     /* OSPF */
-#define EAP_KRT 4                      /* Kernel route attributes */
-#define EAP_BABEL 5                    /* Babel attributes */
-#define EAP_RADV 6                     /* Router advertisment attributes */
-#define EAP_MAX 7
 
 #define EA_CODE(proto,id) (((proto) << 8) | (id))
-#define EA_PROTO(ea) ((ea) >> 8)
 #define EA_ID(ea) ((ea) & 0xff)
+#define EA_PROTO(ea) ((ea) >> 8)
+#define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT)
+#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT)
+#define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT)
+
+const char *ea_custom_name(uint ea);
 
-#define EA_GEN_IGP_METRIC EA_CODE(EAP_GENERIC, 0)
+#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 */
@@ -495,18 +550,17 @@ typedef struct eattr {
 #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_UNDEF 0x1f            /* `force undefined' entry */
+#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) */
-#define EAF_ORIGINATED 0x20            /* The attribute has originated locally */
-#define EAF_FRESH 0x40                 /* An uncached attribute (e.g. modified in export filter) */
-#define EAF_TEMP 0x80                  /* A temporary attribute (the one stored in the tmp attr list) */
 
 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)
 {
@@ -515,7 +569,7 @@ lp_alloc_adata(struct linpool *pool, uint len)
   return ad;
 }
 
-static inline int adata_same(struct adata *a, struct adata *b)
+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)); }
 
 
@@ -545,7 +599,7 @@ struct ea_walk_state {
 
 eattr *ea_find(ea_list *, unsigned ea);
 eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
-int ea_get_int(ea_list *, unsigned ea, int def);
+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 */
@@ -553,29 +607,63 @@ void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffe
 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(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
+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;
+};
 
 static inline eattr *
 ea_set_attr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, uintptr_t val)
 {
-  ea_list *a = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
-  eattr *e = &a->attrs[0];
-
-  a->flags = EALF_SORTED;
-  a->count = 1;
-  a->next = *to;
-  *to = a;
+  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,
 
-  e->id = id;
-  e->type = type;
-  e->flags = flags;
+    .a.id = id,
+    .a.type = type,
+    .a.flags = flags,
+  };
 
   if (type & EAF_EMBEDDED)
-    e->u.data = (u32) val;
+    ea->a.u.data = val;
   else
-    e->u.ptr = (struct adata *) val;
+    ea->a.u.ptr = (struct adata *) val;
 
-  return e;
+  *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
@@ -603,7 +691,8 @@ int nexthop__same(struct nexthop *x, struct nexthop *y); /* Compare multipath ne
 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);
-static inline void nexthop_link(struct rta *a, struct nexthop *from)
+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);
@@ -612,7 +701,7 @@ 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->aflags & RTAF_CACHED; }
+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); }
@@ -620,8 +709,9 @@ 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 *, ea_list *);
+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);
 
@@ -656,8 +746,8 @@ rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr
 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);
 
-extern struct protocol *attr_class_to_protocol[EAP_MAX];
 
 /*
  *     Default protocol preferences
@@ -670,6 +760,8 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX];
 #define DEF_PREF_RIP           120     /* RIP */
 #define DEF_PREF_BGP           100     /* BGP */
 #define DEF_PREF_RPKI          100     /* RPKI */
+#define DEF_PREF_L3VPN_IMPORT   80     /* L3VPN import -> lower than BGP */
+#define DEF_PREF_L3VPN_EXPORT  120     /* L3VPN export -> higher than BGP */
 #define DEF_PREF_INHERITED     10      /* Routes inherited from other routing daemons */
 
 /*