]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
tbf logging rate option merged from v2 kk-tbf-config2-v3
authorKaterina Kubecova <katerina.kubecova@nic.cz>
Thu, 9 May 2024 14:46:26 +0000 (16:46 +0200)
committerKaterina Kubecova <katerina.kubecova@nic.cz>
Thu, 9 May 2024 14:46:26 +0000 (16:46 +0200)
16 files changed:
1  2 
conf/confbase.Y
lib/birdlib.h
nest/config.Y
nest/proto.c
nest/protocol.h
nest/route.h
nest/rt-table.c
proto/babel/babel.c
proto/babel/babel.h
proto/babel/config.Y
proto/ospf/config.Y
proto/ospf/ospf.c
proto/ospf/ospf.h
proto/rip/config.Y
proto/rip/rip.c
proto/rip/rip.h

diff --cc conf/confbase.Y
index d1d3604beba8f5cb961f9fddfcbdbba10695f320,b29abf4c1950528a1b70a3d2def6487892d72f09..3a16cdbb790e224017fe42da928bc41cb66698be
@@@ -99,12 -95,15 +99,16 @@@ CF_DECL
    btime time;
    struct f_prefix px;
    struct proto_spec ps;
+   struct table_spec ts;
    struct channel_limit cl;
    struct timeformat *tf;
 -  mpls_label_stack *mls;
 +  struct settle_config settle;
 +  struct adata *ad;
    const struct adata *bs;
    struct aggr_item_node *ai;
+   struct logging_rate_targets *lrt;
+   struct tbf_config *tc;
+   enum tbf_targets tt;
  }
  
  %token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
diff --cc lib/birdlib.h
Simple merge
diff --cc nest/config.Y
index 3228b08ec45144abf4289547c5b776d2b1cda3b4,eb9098e8df82b29ebfe7abf18f0f00ee908c6c06..dfdde00a592e8e2f9af9af1ed085cd105fedb122
@@@ -164,8 -127,10 +165,10 @@@ CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LON
  CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS)
  CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
  CF_KEYWORDS(CHECK, LINK)
 -CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME, GC, THRESHOLD, PERIOD)
 +CF_KEYWORDS(CORK, SORTED, TRIE, MIN, MAX, ROA, ROUTE, REFRESH, SETTLE, TIME, GC, THRESHOLD, PERIOD)
  CF_KEYWORDS(MPLS_LABEL, MPLS_POLICY, MPLS_CLASS)
+ CF_KEYWORDS(LOGGING, RATE)
+ CF_KEYWORDS(PKT, LSA, RTE)
  
  /* 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)
@@@ -185,15 -150,18 +188,19 @@@ CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY
  %type <s> optproto
  %type <ra> r_args
  %type <sd> sym_args
 -%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 <ts> table_patt
  %type <cc> channel_start proto_channel
  %type <cl> limit_spec
  %type <net> r_args_for_val
  %type <net_ptr> r_args_for
  %type <t> channel_sym
  %type <c> channel_arg
 +%type <const_trie> partial_opt
+ %type <lrt> logging_rate_targets
+ %type <tc> logging_rate
+ %type <tt> tbf_target
  
  CF_GRAMMAR
  
@@@ -275,14 -245,11 +284,15 @@@ table_opt
         cf_error("Trie option not supported for %s table", net_label[this_table->addr_type]);
       this_table->trie_used = $2;
     }
 - | MIN SETTLE TIME expr_us { this_table->min_settle_time = $4; }
 - | MAX SETTLE TIME expr_us { this_table->max_settle_time = $4; }
   | GC THRESHOLD expr { this_table->gc_threshold = $3; }
   | GC PERIOD expr_us { this_table->gc_period = (uint) $3; if ($3 > 3600 S_) cf_error("GC period must be at most 3600 s"); }
 + | CORK THRESHOLD expr expr {
 +     if ($3 > $4) cf_error("Cork low threshold must be lower than the high threshold.");
 +     this_table->cork_threshold.low = $3;
 +     this_table->cork_threshold.high = $4; }
 + | EXPORT SETTLE TIME settle { this_table->export_settle = $4; }
 + | ROUTE REFRESH EXPORT SETTLE TIME settle { this_table->export_rr_settle = $6; }
+  | LOGGING RATE expr expr     { this_table->log_tbf_cf.rate = $3; this_table->log_tbf_cf.burst = $4; }
   ;
  
  table_opts:
@@@ -1027,6 -1020,19 +1077,13 @@@ proto_patt2
   | TEXT { $$.ptr = $1; $$.patt = 1; }
   ;
  
 -dynamic_attr: IGP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_GEN_IGP_METRIC); } ;
 -
 -dynamic_attr: MPLS_LABEL  { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_MPLS_LABEL); } ;
 -dynamic_attr: MPLS_POLICY { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_MPLS_POLICY, EA_MPLS_POLICY); } ;
 -dynamic_attr: MPLS_CLASS  { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_MPLS_CLASS); } ;
 -
+ table_patt:
+    CF_SYM_KNOWN { cf_assert_symbol($1, SYM_TABLE); $$.ptr = $1; $$.patt = 0; }
+  | ALL  { $$.ptr = NULL; $$.patt = 1; }
+  | TEXT { $$.ptr = $1; $$.patt = 1; }
+  ;
  CF_CODE
  
  CF_END
diff --cc nest/proto.c
Simple merge
diff --cc nest/protocol.h
index 2172ecf456184a92c8bcfcf4b29804e1e0e8ff03,25da85e7090bcc63b13d4284533326aa31f3f2d8..f531e766efb3936879fc6ab79c050f1995980ff8
@@@ -122,22 -136,42 +122,29 @@@ struct proto_config 
    /* Protocol-specific data follow... */
  };
  
 -struct proto_tbfs
 -{
 +struct channel_import_request {
 +  struct channel_import_request *next;                        /* Next in request chain */
 +  void (*done)(struct channel_import_request *);      /* Called when import finishes */
 +  const struct f_trie *trie;                          /* Reload only matching nets */
 +};
 +
++struct proto_tbfs {
+   /* Pointers to rate limiting logging structures */
+   struct tbf *pkt;
+   struct tbf *lsa;
+   struct tbf *rte;
+ };
 -/* Protocol statistics */
 -struct proto_stats {
 -  /* Import - from protocol to core */
 -  u32 imp_routes;             /* Number of routes successfully imported to the (adjacent) routing table */
 -  u32 filt_routes;            /* Number of routes rejected in import filter but kept in the routing table */
 -  u32 pref_routes;            /* Number of routes selected as best in the (adjacent) routing table */
 -  u32 imp_updates_received;   /* Number of route updates received */
 -  u32 imp_updates_invalid;    /* Number of route updates rejected as invalid */
 -  u32 imp_updates_filtered;   /* Number of route updates rejected by filters */
 -  u32 imp_updates_ignored;    /* Number of route updates rejected as already in route table */
 -  u32 imp_updates_accepted;   /* Number of route updates accepted and imported */
 -  u32 imp_withdraws_received; /* Number of route withdraws received */
 -  u32 imp_withdraws_invalid;  /* Number of route withdraws rejected as invalid */
 -  u32 imp_withdraws_ignored;  /* Number of route withdraws rejected as already not in route table */
 -  u32 imp_withdraws_accepted; /* Number of route withdraws accepted and processed */
 -
 -  /* Export - from core to protocol */
 -  u32 exp_routes;             /* Number of routes successfully exported to the protocol */
 -  u32 exp_updates_received;   /* Number of route updates received */
 -  u32 exp_updates_rejected;   /* Number of route updates rejected by protocol */
 -  u32 exp_updates_filtered;   /* Number of route updates rejected by filters */
 -  u32 exp_updates_accepted;   /* Number of route updates accepted and exported */
 -  u32 exp_withdraws_received; /* Number of route withdraws received */
 -  u32 exp_withdraws_accepted; /* Number of route withdraws accepted and processed */
 -};
 +#define TLIST_PREFIX proto
 +#define TLIST_TYPE struct proto
 +#define TLIST_ITEM n
 +#define TLIST_WANT_WALK
 +#define TLIST_WANT_ADD_TAIL
 +#define TLIST_WANT_ADD_AFTER
  
 +/* Protocol statistics */
  struct proto {
 -  node n;                             /* Node in global proto_list */
 +  TLIST_DEFAULT_NODE;                 /* Node in global proto_list */
    struct protocol *proto;             /* Protocol */
    struct proto_config *cf;            /* Configuration data */
    struct proto_config *cf_new;                /* Configuration we want to switch to after shutdown (NULL=delete) */
diff --cc nest/route.h
index 66e1d030fd46d2015cb9453a862e32ffa51b13e5,53bccdd4c014aefb8a84a7094831ccc9550804f4..9b105c05fa009dbc7aa7238deb79c8a03843a659
@@@ -65,71 -152,28 +65,74 @@@ struct rtable_config 
    uint gc_period;                     /* Approximate time between two consecutive GC runs */
    u32 debug;                          /* Debugging flags (D_*) */
    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 */
 +  struct rt_cork_threshold cork_threshold;    /* Cork threshold values */
 +  struct settle_config export_settle; /* Export announcement settler */
 +  struct settle_config export_rr_settle;/* Export announcement settler config valid when any
 +                                         route refresh is running */
+   btime min_settle_time;              /* Minimum settle time for notifications */
+   btime max_settle_time;              /* Maximum settle time for notifications */
+   struct tbf_config log_tbf_cf;               /* Config logging rate for rtable */
  };
  
 -typedef struct rtable {
 -  resource r;
 -  node n;                             /* Node in list of all tables */
 +struct rt_export_hook;
 +struct rt_export_request;
 +struct rt_exporter;
 +
 +struct rt_exporter_class {
 +  void (*start)(struct rt_exporter *, struct rt_export_request *);
 +  void (*stop)(struct rt_export_hook *);
 +  void (*done)(void *_rt_export_hook);
 +};
 +
 +struct rt_exporter {
 +  const struct rt_exporter_class *class;
 +  pool *rp;
 +  list hooks;                         /* Registered route export hooks */
 +  uint addr_type;                     /* Type of address data exported (NET_*) */
 +};
 +
 +struct rt_table_exporter {
 +  struct rt_exporter e;
 +  list pending;                               /* List of packed struct rt_pending_export */
 +
 +  struct rt_pending_export *first;    /* First export to announce */
 +  u64 next_seq;                               /* The next export will have this ID */
 +};
 +
 +extern uint rtable_max_id;
 +
 +/* The public part of rtable structure */
 +#define RTABLE_PUBLIC \
 +    resource r;                                                                                       \
 +    node n;                           /* Node in list of all tables */                        \
 +    char *name;                               /* Name of this table */                                \
 +    uint addr_type;                   /* Type of address data stored in table (NET_*) */      \
 +    uint id;                          /* Integer table ID for fast lookup */                  \
 +    DOMAIN(rtable) lock;              /* Lock to take to access the private parts */          \
 +    struct rtable_config *config;     /* Configuration of this table */                       \
 +    struct birdloop *loop;            /* Service thread */                                    \
 +    netindex_hash *netindex;          /* Prefix index for this table */                       \
 +
 +/* The complete rtable structure */
 +struct rtable_private {
 +  /* Once more the public part */
 +  struct { RTABLE_PUBLIC; };
 +  struct rtable_private **locked_at;
 +
 +  /* Here the private items not to be accessed without locking */
    pool *rp;                           /* Resource pool to allocate everything from, including itself */
 -  struct fib fib;
 +  struct slab *rte_slab;              /* Slab to allocate route objects */
 +  struct network *routes;             /* Actual route objects in the table */
 +  u32 routes_block_size;              /* Size of the route object pointer block */
    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_*) */
 -  u32 debug;                          /* Debugging flags (D_*) */
 -  int pipe_busy;                      /* Pipe loop detection */
    int use_count;                      /* Number of protocols using this table */
    u32 rt_count;                               /* Number of routes in the table */
 +  u32 net_count;                      /* Number of nets in the table */
 +  u32 debug;                          /* Debugging flags (D_*) */
  
 -  byte internal;                      /* Internal table of a protocol */
 +  list imports;                               /* Registered route importers */
 +  struct rt_table_exporter exporter;  /* Exporter API structure */
  
    struct hmap id_map;
    struct hostcache *hostcache;
    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 */
+   struct tbf log_tbf;                 /* Actual logging rate for rtable (might be changed in cmd) */
 +};
 +
 +/* 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 the lock cleanup function */
 +LOBJ_UNLOCK_CLEANUP(rtable, rtable);
 +
 +#define RT_IS_LOCKED(tab)     LOBJ_IS_LOCKED((tab), rtable)
 +#define RT_LOCKED(tab, tp)    LOBJ_LOCKED((tab), tp, rtable, rtable)
 +
 +#define RT_LOCK_SIMPLE(tab)   LOBJ_LOCK_SIMPLE((tab), rtable)
 +#define RT_UNLOCK_SIMPLE(tab) LOBJ_UNLOCK_SIMPLE((tab), rtable)
 +
 +#define RT_UNLOCKED_TEMPORARILY(tab, tp)      LOBJ_UNLOCKED_TEMPORARILY((tab), tp, rtable, rtable)
 +
 +#define RT_PUB(tab)   SKIP_BACK(rtable, priv, tab)
 +
 +/* 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;
 +}
 +
 +
 +typedef struct network {
 +  struct rte_storage *routes;         /* Available routes for this network */
 +  struct rt_pending_export *first, *last;
 +} net;
 +
 +struct rte_storage {
 +  struct rte_storage *next;           /* Next in chain */
 +  union {
 +    struct {
 +      RTE_IN_TABLE_WRITABLE;
 +    };
 +    const struct rte rte;                     /* Route data */
 +  };
  };
  
 -struct rt_flowspec_link {
 +#define RTE_COPY(r)           ((r) ? (r)->rte : (rte) {})
 +#define RTE_COPY_VALID(r)     (((r) && (rte_is_valid(&(r)->rte))) ? (r)->rte : (rte) {})
 +#define RTE_OR_NULL(r)                ((r) ? &((r)->rte) : NULL)
 +#define RTE_VALID_OR_NULL(r)  (((r) && (rte_is_valid(&(r)->rte))) ? &((r)->rte) : NULL)
 +
 +#define RTES_WRITE(r)         (((r) != ((struct rte_storage *) 0)) ? ((struct rte *) &(r)->rte) : NULL)
 +
 +#define RTE_GET_NETINDEX(e) NET_TO_INDEX((e)->net)
 +
 +/* Table-channel connections */
 +
 +struct rt_prefilter {
 +  union {
 +    const struct f_trie *trie;
 +    const net_addr *addr;     /* Network prefilter address */
 +    int (*hook)(const struct rt_prefilter *, const net_addr *);
 +  };
 +                              /* Network prefilter mode (TE_ADDR_*) */
 +  enum {
 +    TE_ADDR_NONE = 0,         /* No address matching */
 +    TE_ADDR_EQUAL,            /* Exact query - show route <addr> */
 +    TE_ADDR_FOR,              /* Longest prefix match - show route for <addr> */
 +    TE_ADDR_IN,                       /* Interval query - show route in <addr> */
 +    TE_ADDR_TRIE,             /* Query defined by trie */
 +    TE_ADDR_HOOK,             /* Query processed by supplied custom hook */
 +  } mode;
 +} PACKED;
 +
 +struct rt_import_request {
 +  struct rt_import_hook *hook;                /* The table part of importer */
 +  char *name;
 +  u8 trace_routes;
 +
 +  event_list *list;                   /* Where to schedule announce events */
 +
 +  void (*dump_req)(struct rt_import_request *req);
 +  void (*log_state_change)(struct rt_import_request *req, u8 state);
 +  /* Preimport is called when the @new route is just-to-be inserted, replacing @old.
 +   * Return a route (may be different or modified in-place) to continue or NULL to withdraw. */
 +  int (*preimport)(struct rt_import_request *req, struct rte *new, const struct rte *old);
 +};
 +
 +struct rt_import_hook {
    node n;
 -  rtable *src;
 -  rtable *dst;
 -  u32 uc;
 +  rtable *table;                      /* The connected table */
 +  struct rt_import_request *req;      /* The requestor */
 +
 +  struct rt_import_stats {
 +    /* Import - from protocol to core */
 +    u32 pref;                         /* Number of routes selected as best in the (adjacent) routing table */
 +    u32 updates_ignored;              /* Number of route updates rejected as already in route table */
 +    u32 updates_accepted;             /* Number of route updates accepted and imported */
 +    u32 withdraws_ignored;            /* Number of route withdraws rejected as already not in route table */
 +    u32 withdraws_accepted;           /* Number of route withdraws accepted and processed */
 +  } stats;
 +
 +  u64 flush_seq;                      /* Table export seq when the channel announced flushing */
 +  btime last_state_change;            /* Time of last state transition */
 +
 +  u8 import_state;                    /* IS_* */
 +  u8 stale_set;                               /* Set this stale_cycle to imported routes */
 +  u8 stale_valid;                     /* Routes with this stale_cycle and bigger are considered valid */
 +  u8 stale_pruned;                    /* Last prune finished when this value was set at stale_valid */
 +  u8 stale_pruning;                   /* Last prune started when this value was set at stale_valid */
 +
 +  void (*stopped)(struct rt_import_request *);        /* Stored callback when import is stopped */
 +  event announce_event;                       /* This event announces table updates */
  };
  
 -#define NHU_CLEAN     0
 -#define NHU_SCHEDULED 1
 -#define NHU_RUNNING   2
 -#define NHU_DIRTY     3
 +struct rt_pending_export {
 +  struct rt_pending_export * _Atomic next;    /* Next export for the same destination */
 +  struct rte_storage *new, *new_best, *old, *old_best;
 +  u64 seq;                            /* Sequential ID (table-local) of the pending export */
 +};
  
 -typedef struct network {
 -  struct rte *routes;                 /* Available routes for this network */
 -  struct fib_node n;                  /* FIB flags reserved for kernel syncer */
 -} net;
 +struct rt_export_request {
 +  struct rt_export_hook *hook;                /* Table part of the export */
 +  char *name;
 +  u8 trace_routes;
 +  uint feed_block_size;                       /* How many routes to feed at once */
 +  struct rt_prefilter prefilter;
 +
 +  event_list *list;                   /* Where to schedule export events */
 +  pool *pool;                         /* Pool to use for allocations */
 +
 +  /* There are two methods of export. You can either request feeding every single change
 +   * or feeding the whole route feed. In case of regular export, &export_one is preferred.
 +   * Anyway, when feeding, &export_bulk is preferred, falling back to &export_one.
 +   * Thus, for RA_OPTIMAL, &export_one is only set,
 +   *     for RA_MERGED and RA_ACCEPTED, &export_bulk is only set
 +   *     and for RA_ANY, both are set to accomodate for feeding all routes but receiving single changes
 +   */
 +  void (*export_one)(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
 +  void (*export_bulk)(struct rt_export_request *req, const net_addr *net,
 +      struct rt_pending_export *rpe, struct rt_pending_export *last,
 +      const rte **feed, uint count);
 +
 +  void (*mark_seen)(struct rt_export_request *req, struct rt_pending_export *rpe);
 +
 +  void (*dump_req)(struct rt_export_request *req);
 +  void (*log_state_change)(struct rt_export_request *req, u8);
 +};
  
 -struct hostcache {
 -  slab *slab;                         /* Slab holding all hostentries */
 -  struct hostentry **hash_table;      /* Hash table for hostentries */
 -  unsigned hash_order, hash_shift;
 -  unsigned hash_max, hash_min;
 -  unsigned hash_items;
 -  linpool *lp;                                /* Linpool for trie */
 -  struct f_trie *trie;                        /* Trie of prefixes that might affect hostentries */
 -  list hostentries;                   /* List of all hostentries */
 -  byte update_hostcache;
 +static inline int rt_prefilter_net(const struct rt_prefilter *p, const net_addr *n)
 +{
 +  switch (p->mode)
 +  {
 +    case TE_ADDR_NONE:        return 1;
 +    case TE_ADDR_IN:  return net_in_netX(n, p->addr);
 +    case TE_ADDR_EQUAL:       return net_equal(n, p->addr);
 +    case TE_ADDR_FOR: return net_in_netX(p->addr, n);
 +    case TE_ADDR_TRIE:        return trie_match_net(p->trie, n);
 +    case TE_ADDR_HOOK:        return p->hook(p, n);
 +  }
 +
 +  bug("Crazy prefilter application attempt failed wildly.");
 +}
 +
 +struct rt_export_hook {
 +  node n;
 +  struct rt_exporter *table;          /* The connected table */
 +
 +  pool *pool;
 +
 +  struct rt_export_request *req;      /* The requestor */
 +
 +  struct rt_export_stats {
 +    /* Export - from core to protocol */
 +    u32 updates_received;             /* Number of route updates received */
 +    u32 withdraws_received;           /* Number of route withdraws received */
 +  } stats;
 +
 +  btime last_state_change;            /* Time of last state transition */
 +
 +  _Atomic u8 export_state;            /* Route export state (TES_*, see below) */
 +  struct event event;                 /* Event running all the export operations */
 +
 +  struct bmap seq_map;                        /* Keep track which exports were already procesed */
 +
 +  void (*stopped)(struct rt_export_request *);        /* Stored callback when export is stopped */
  };
  
 -struct hostentry {
 -  node ln;
 -  ip_addr addr;                               /* IP address of host, part of key */
 -  ip_addr link;                               /* (link-local) IP address of host, used as gw
 -                                         if host is directly attached */
 -  rtable *tab;                                /* Dependent table, part of key */
 -  rtable *owner;                      /* Nexthop owner table */
 -  struct hostentry *next;             /* Next in hash chain */
 -  unsigned hash_key;                  /* Hash key */
 -  unsigned uc;                                /* Use count */
 -  struct rta *src;                    /* Source rta entry */
 -  byte dest;                          /* Chosen route destination type (RTD_...) */
 -  byte nexthop_linkable;              /* Nexthop list is completely non-device */
 -  u32 igp_metric;                     /* Chosen route IGP metric */
 +struct rt_table_export_hook {
 +  union {
 +    struct rt_export_hook h;
 +    struct {                          /* Overriding the parent structure beginning */
 +      node _n;
 +      struct rt_table_exporter *table;
 +    };
 +  };
 +  
 +  union {
 +    u32 feed_index;                           /* Routing table iterator used during feeding */
 +    struct {
 +      struct f_trie_walk_state *walk_state;   /* Iterator over networks in trie */
 +      struct f_trie *walk_lock;                       /* Locked trie for walking */
 +      union {                                 /* Last net visited but not processed */
 +      net_addr walk_last;
 +      net_addr_ip4 walk_last_ip4;
 +      net_addr_ip6 walk_last_ip6;
 +      };
 +    };
 +  };
 +
 +  struct rt_pending_export *_Atomic last_export;/* Last export processed */
 +  struct rt_pending_export *rpe_next; /* Next pending export to process */
 +
 +  u8 refeed_pending;                  /* Refeeding and another refeed is scheduled */
 +  u8 feed_type;                               /* Which feeding method is used (TFT_*, see below) */
 +
  };
  
 -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 */
 -  btime lastmod;                      /* Last modified */
 -} rte;
 +#define TIS_DOWN      0
 +#define TIS_UP                1
 +#define TIS_STOP      2
 +#define TIS_FLUSHING  3
 +#define TIS_WAITING   4
 +#define TIS_CLEARED   5
 +#define TIS_MAX               6
 +
 +#define TES_DOWN      0
 +#define TES_HUNGRY    1
 +#define TES_FEEDING   2
 +#define TES_READY     3
 +#define TES_STOP      4
 +#define TES_MAX               5
 +
 +
 +#define TFT_FIB               1
 +#define TFT_TRIE      2
 +#define TFT_HASH      3
 +
 +void rt_request_import(rtable *tab, struct rt_import_request *req);
 +void rt_request_export(rtable *tab, struct rt_export_request *req);
 +void rt_request_export_other(struct rt_exporter *tab, struct rt_export_request *req);
  
 -#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 */
 +void rt_stop_import(struct rt_import_request *, void (*stopped)(struct rt_import_request *));
 +void rt_stop_export(struct rt_export_request *, void (*stopped)(struct rt_export_request *));
  
 -/* 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); }
 +const char *rt_import_state_name(u8 state);
 +const char *rt_export_state_name(u8 state);
  
 -/* Route just has REF_FILTERED flag */
 -static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); }
 +static inline u8 rt_import_get_state(struct rt_import_hook *ih) { return ih ? ih->import_state : TIS_DOWN; }
 +static inline u8 rt_export_get_state(struct rt_export_hook *eh) { return eh ? eh->export_state : TES_DOWN; }
  
 +u8 rt_set_export_state(struct rt_export_hook *hook, u32 expected_mask, u8 state);
 +
 +void rte_import(struct rt_import_request *req, const net_addr *net, rte *new, struct rte_src *src);
 +
 +/*
 + * For table export processing
 + */
 +
 +/* Get next rpe. If src is given, it must match. */
 +struct rt_pending_export *rpe_next(struct rt_pending_export *rpe, struct rte_src *src);
 +
 +/* Walk all rpe's */
 +#define RPE_WALK(first, it, src) \
 +  for (struct rt_pending_export *it = (first); it; it = rpe_next(it, (src)))
 +
 +/* Mark the pending export processed */
 +void rpe_mark_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe);
 +
 +#define rpe_mark_seen_all(hook, first, last, src) do { \
 +  RPE_WALK((first), _rpe, (src)) { \
 +    rpe_mark_seen((hook), _rpe); \
 +    if (_rpe == last) break; \
 +  }} while (0)
 +
 +/* Get pending export seen status */
 +int rpe_get_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe);
 +
 +/*
 + * For rt_export_hook and rt_exporter inheritance
 + */
 +
 +void rt_init_export(struct rt_exporter *re, struct rt_export_hook *hook);
 +struct rt_export_hook *rt_alloc_export(struct rt_exporter *re, pool *pool, uint size);
 +void rt_stop_export_common(struct rt_export_hook *hook);
 +void rt_export_stopped(struct rt_export_hook *hook);
 +void rt_exporter_init(struct rt_exporter *re);
 +
 +/*
 + * Channel export hooks. To be refactored out.
 + */
 +
 +int channel_preimport(struct rt_import_request *req, rte *new, const rte *old);
 +
 +void channel_reload_export_bulk(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *first, struct rt_pending_export *last, const rte **feed, uint count);
 +
 +void rt_notify_optimal(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
 +void rt_notify_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
 +void rt_feed_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *first, struct rt_pending_export *last, const rte **feed, uint count);
 +void rt_notify_accepted(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *first, struct rt_pending_export *last, const rte **feed, uint count);
 +void rt_notify_merged(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *first, struct rt_pending_export *last, const rte **feed, uint count);
 +
 +void channel_rpe_mark_seen(struct channel *c, struct rt_pending_export *rpe);
  
  /* Types of route announcement, also used as flags */
  #define RA_UNDEF      0               /* Undefined RA type */
@@@ -706,22 -419,348 +710,24 @@@ struct rt_show_data_rtable * rt_show_ad
  #define RSEM_NOEXPORT 3               /* Routes rejected by export filter */
  #define RSEM_EXPORTED 4               /* Routes marked in export map */
  
 -/*
 - *    Route Attributes
 - *
 - *    Beware: All standard BGP attributes must be represented here instead
 - *    of making them local to the route. This is needed to ensure proper
 - *    construction of BGP route attribute lists.
 - */
 -
 -/* Nexthop structure */
 -struct nexthop {
 -  ip_addr gw;                         /* Next hop */
 -  struct iface *iface;                        /* Outgoing interface */
 -  struct nexthop *next;
 -  byte flags;
 -  byte weight;
 -  byte labels_orig;                   /* Number of labels before hostentry was applied */
 -  byte labels;                                /* Number of all labels */
 -  u32 label[0];
 -};
 -
 -#define RNF_ONLINK            0x1     /* Gateway is onlink regardless of IP ranges */
 -
 -
 -struct rte_src {
 -  struct rte_src *next;                       /* Hash chain */
 -  struct proto *proto;                        /* Protocol the source is based on */
 -  u64 private_id;                     /* Private ID, assigned by the protocol */
 -  u32 global_id;                      /* Globally unique ID of the source */
 -  unsigned uc;                                /* Use count */
 -};
 -
 -
 -typedef struct rta {
 -  struct rta *next, **pprev;          /* Hash chain */
 -  u32 uc;                             /* Use count */
 -  u32 hash_key;                               /* Hash over important fields */
 -  struct ea_list *eattrs;             /* Extended Attribute chain */
 -  struct hostentry *hostentry;                /* Hostentry for recursive next-hops */
 -  ip_addr from;                               /* Advertising router */
 -  u32 igp_metric;                     /* IGP metric to next hop (for iBGP routes) */
 -  u16 cached:1;                               /* Are attributes cached? */
 -  u16 source:7;                               /* Route source (RTS_...) */
 -  u16 scope:4;                                /* Route scope (SCOPE_... -- see ip.h) */
 -  u16 dest:4;                         /* Route destination type (RTD_...) */
 -  word pref;
 -  struct nexthop nh;                  /* Next hop */
 -} rta;
 -
 -#define RTS_STATIC 1                  /* Normal static route */
 -#define RTS_INHERIT 2                 /* Route inherited from kernel */
 -#define RTS_DEVICE 3                  /* Device route */
 -#define RTS_STATIC_DEVICE 4           /* Static device route */
 -#define RTS_REDIRECT 5                        /* Learned via redirect */
 -#define RTS_RIP 6                     /* RIP route */
 -#define RTS_OSPF 7                    /* OSPF route */
 -#define RTS_OSPF_IA 8                 /* OSPF inter-area route */
 -#define RTS_OSPF_EXT1 9                       /* OSPF external route type 1 */
 -#define RTS_OSPF_EXT2 10              /* OSPF external route type 2 */
 -#define RTS_BGP 11                    /* BGP route */
 -#define RTS_PIPE 12                   /* Inter-table wormhole */
 -#define RTS_BABEL 13                  /* Babel route */
 -#define RTS_RPKI 14                   /* Route Origin Authorization */
 -#define RTS_PERF 15                   /* Perf checker */
 -#define RTS_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 */
 -#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 EA_DATA_ALIGN 4                       /* Alignment of adata in attribute cache */
 -
 -#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 0x14           /* Interface pointer stored in adata */
 -#define EAF_TYPE_STRING 0x16          /* Text string */
 -#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 */
 -};
 -
 -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;
 +/* 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];
  };
  
 -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,
 -  };
 +#define HOSTENTRY_LABEL_COUNT(head)   (head->ad.length + sizeof(struct adata) - sizeof(struct hostentry_adata)) / sizeof(u32)
  
 -  if (type & EAF_EMBEDDED)
 -    ea->a.u.data = val;
 -  else
 -    ea->a.u.ptr = (struct adata *) val;
 +void
 +ea_set_hostentry(ea_list **to, rtable *dep, rtable *tab, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum]);
  
 -  *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); }
 -int nexthop_equal_(struct nexthop *x, struct nexthop *y); /* Compare multipath nexthops, ignore labels_orig */
 -static inline int nexthop_equal(struct nexthop *x, struct nexthop *y)
 -{ return (x == y) || nexthop_equal_(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 *);
 +void ea_show_hostentry(const struct adata *ad, byte *buf, uint size);
 +void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad);
  
 -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 table_logging_cmd(struct table_spec ts, struct tbf_config *rate);
  /*
   *    Default protocol preferences
   */
diff --cc nest/rt-table.c
index 79f74d24a2d758b0f4bce9a465a411b284582a05,81104673c6394e7607a14be3a362ff5e67f71eef..44e775aefbcb8f03e33cb7c50201e5f576b54a82
  #include "lib/string.h"
  #include "lib/alloca.h"
  #include "lib/flowspec.h"
 +#include "lib/idm.h"
 +#include "lib/netindex_private.h"
+ #include "nest/cli.h"
  
  #ifdef CONFIG_BGP
  #include "proto/bgp/bgp.h"
@@@ -2815,12 -1960,10 +2816,14 @@@ rt_setup(pool *pp, struct rtable_confi
    t->config = cf;
    t->addr_type = cf->addr_type;
    t->debug = cf->debug;
 +  t->id = idm_alloc(&rtable_idm);
 +  if (t->id >= rtable_max_id)
 +    rtable_max_id = t->id + 1;
+   t->log_tbf.cf.rate = cf->log_tbf_cf.rate;
+   t->log_tbf.cf.burst = cf->log_tbf_cf.burst;
  
 -  fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL);
 +  t->netindex = rt_global_netindex_hash;
 +  t->routes = mb_allocz(p, (t->routes_block_size = 128) * sizeof(net));
  
    if (cf->trie_used)
    {
@@@ -4082,23 -2733,9 +4085,25 @@@ rt_reconfigure(struct rtable_private *t
    tab->name = new->name;
    tab->config = new;
    tab->debug = new->debug;
+   tab->log_tbf.cf.rate = new->log_tbf_cf.rate;
+   tab->log_tbf.cf.burst = new->log_tbf_cf.burst;
  
 +  if (tab->hostcache)
 +    tab->hostcache->req.trace_routes = new->debug;
 +
 +  struct rt_table_export_hook *hook; node *n;
 +  WALK_LIST2(hook, n, tab->exporter.e.hooks, h.n)
 +    if (hook->h.req->export_one == rt_flowspec_export_one)
 +      hook->h.req->trace_routes = new->debug;
 +
 +  tab->cork_threshold = new->cork_threshold;
 +
 +  if (new->cork_threshold.high != old->cork_threshold.high)
 +    rt_check_cork_high(tab);
 +
 +  if (new->cork_threshold.low != old->cork_threshold.low)
 +    rt_check_cork_low(tab);
 +
    return 1;
  }
  
@@@ -4852,6 -3483,50 +4857,50 @@@ rt_get_hostentry(struct rtable_private 
    return he;
  }
  
 -cmd_logging_rate(rtable *table, struct tbf_config *rate)
+ void
 -  table->log_tbf.cf.rate = rate->rate;
 -  table->log_tbf.cf.burst = rate->burst;
++cmd_logging_rate(rtable *tp, struct tbf_config *rate)
+ {
++  RT_LOCKED(tp, table)
++  {
++    table->log_tbf.cf.rate = rate->rate;
++    table->log_tbf.cf.burst = rate->burst;
++  }
+ }
+ void
+ table_logging_cmd(struct table_spec ts, struct tbf_config *rate)
+ {
+   if (ts.patt)
+   {
+     const char *patt = (void *) ts.ptr;
+     int cnt = 0;
+     rtable *t;
+     node *n;
+     WALK_LIST2(t, n, routing_tables, n)
+       if (!ts.ptr || patmatch(patt, t->name))
+       {
+         cmd_logging_rate(t, rate);
+         cnt++;
+       }
+     if (!cnt)
+       cli_msg(8003, "No tables match");
+     else
+       cli_msg(0, "");
+   }
+   else
+   {
+     const struct symbol *s = (struct symbol*) ts.ptr;
+     if (s->table->table)
+     {
+       cmd_logging_rate(s->table->table, rate);
+       cli_msg(0, "");
+     }
+     else
+       cli_msg(9002, "%s does not exist", s->name);
+   }
+ }
  
  /*
   *  Documentation for functions declared inline in route.h
Simple merge
Simple merge
index 6a7c071f951751e0a3050199670625ffd4bd512e,23fd7f792cbe0a3b4857ef271ac0717a3e328bd1..769fa32ad082f25b05a27c93207fca0f82465a41
@@@ -24,9 -24,10 +24,10 @@@ CF_DECL
  
  CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
        TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
 -      NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
 +      NEXT, HOP, IPV4, IPV6, SHOW, INTERFACES, NEIGHBORS,
        ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
-       EXTENDED, TUNNEL, RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS, COST, DELAY)
+       EXTENDED, TUNNEL, RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS, COST, DELAY,
+       PKT, LOGGING, RATE, BURST)
  
  CF_GRAMMAR
  
Simple merge
Simple merge
Simple merge
index 6520844236a520a40e4457bbc26f6a9537aff65c,6c424253384ba7991cf0fa3105bc53e1363ea4a9..8facea2cf1b030809a9d0f3a501f560fb707c5ac
@@@ -37,7 -38,7 +37,7 @@@ CF_KEYWORDS(RIP, NG, ECMP, LIMIT, WEIGH
            PASSIVE, VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO,
            TIME, BFD, AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5,
            TTL, SECURITY, RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK,
-           DEMAND, CIRCUIT)
 -          DEMAND, CIRCUIT, RIP_METRIC, RIP_TAG, PKT, RTE, LOGGING, RATE, BURST)
++          DEMAND, CIRCUIT, PKT, RTE, LOGGING, RATE, BURST)
  
  %type <i> rip_variant rip_auth
  
diff --cc proto/rip/rip.c
Simple merge
diff --cc proto/rip/rip.h
Simple merge