]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Channels - explicit links between protocols and tables
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 26 Jan 2016 10:48:58 +0000 (11:48 +0100)
committerJan Moskyto Matejka <mq@ucw.cz>
Mon, 1 Feb 2016 09:28:50 +0000 (10:28 +0100)
The patch adds support for channels, structures connecting protocols and
tables and handling most interactions between them. The documentation is
missing yet.

37 files changed:
conf/conf.c
conf/conf.h
conf/confbase.Y
lib/birdlib.h
lib/lists.h
lib/net.c
lib/net.h
nest/config.Y
nest/iface.c
nest/neighbor.c
nest/proto.c
nest/proto.sgml
nest/protocol.h
nest/route.h
nest/rt-dev.c
nest/rt-dev.h
nest/rt-table.c
proto/bfd/bfd.c
proto/bgp/bgp.c
proto/bgp/config.Y
proto/ospf/config.Y
proto/ospf/ospf.c
proto/ospf/rt.c
proto/pipe/config.Y
proto/pipe/pipe.c
proto/pipe/pipe.h
proto/radv/config.Y
proto/radv/radv.c
proto/radv/radv.h
proto/rip/config.Y
proto/rip/rip.c
proto/static/config.Y
proto/static/static.c
sysdep/linux/netlink.c
sysdep/unix/krt.Y
sysdep/unix/krt.c
sysdep/unix/krt.h

index f66e356176d21c7dae53e38b9301fcddbe02cbc4..50e5944b27f4b141ec29af0642afbbdd32964654 100644 (file)
@@ -136,12 +136,14 @@ config_parse(struct config *c)
   protos_preconfig(c);
   rt_preconfig(c);
   cf_parse();
-  protos_postconfig(c);
+
   if (EMPTY_LIST(c->protos))
     cf_error("No protocol is specified in the config file");
-  /* XXXX */
+
+  /*
   if (!c->router_id)
     cf_error("Router ID must be configured manually");
+  */
 
   done = 1;
 
index 1e142babc21756ca868ef42c77ef9c9671debc06..1c4f7fcec0ea4728012b13aa0d66e43eab82c7b8 100644 (file)
@@ -24,7 +24,7 @@ struct config {
 
   int mrtdump_file;                    /* Configured MRTDump file (sysdep, fd in unix) */
   char *syslog_name;                   /* Name used for syslog (NULL -> no syslog) */
-  struct rtable_config *master_rtc;    /* Configuration of master routing table */
+  struct rtable_config *def_tables[NET_MAX]; /* Default routing tables for each network */
   struct iface_patt *router_id_from;   /* Configured list of router ID iface patterns */
 
   u32 router_id;                       /* Our Router ID */
index 94a20fe71097c11513e87522a4cb1eb9cb361d68..22aee77075d1b477deb2b758834f72e2bbfe34b7 100644 (file)
@@ -46,6 +46,7 @@ CF_DECLS
   struct symbol *s;
   char *t;
   struct rtable_config *r;
+  struct channel_config *cc;
   struct f_inst *x;
   struct filter *f;
   struct f_tree *e;
@@ -61,6 +62,7 @@ CF_DECLS
   bird_clock_t time;
   struct f_prefix px;
   struct proto_spec ps;
+  struct channel_limit cl;
   struct timeformat *tf;
 }
 
index 9ffe0070c9ef7e84a8cf279ed527a52bfbd99f22..ece50dc253051100d525894f841e4b2bf73cfb8b 100644 (file)
@@ -34,6 +34,7 @@
 #define ABS(a)   ((a)>=0 ? (a) : -(a))
 #define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a))
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
+#define CALL(fn, args...) ({ if (fn) fn(args); })
 
 static inline int uint_cmp(uint i1, uint i2)
 { return (int)(i1 > i2) - (int)(i1 < i2); }
index 80a4dc93731e3b0835fbe6bea9e3c53726dd81f0..d75f033d7216945a937aee292f2e41572a2e7b31 100644 (file)
@@ -39,7 +39,10 @@ typedef struct list {                        /* In fact two overlayed nodes */
 #define WALK_LIST2(n,nn,list,pos) \
   for(nn=(list).head; NODE_VALID(nn) && (n=SKIP_BACK(typeof(*n),pos,nn)); nn=nn->next)
 #define WALK_LIST_DELSAFE(n,nxt,list) \
-     for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt)
+  for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt)
+#define WALK_LIST2_DELSAFE(n,nn,nxt,list,pos) \
+  for(nn=HEAD(list); (nxt=nn->next) && (n=SKIP_BACK(typeof(*n),pos,nn)); nn=nxt)
+
 /* WALK_LIST_FIRST supposes that called code removes each processed node */
 #define WALK_LIST_FIRST(n,list) \
      while(n=HEAD(list), (NODE (n))->next)
index d03a03b71900976250de10db6bdcd0b3384e6369..71fbe6ffb13c2d1e805203737999d872c34da184 100644 (file)
--- a/lib/net.c
+++ b/lib/net.c
@@ -4,6 +4,13 @@
 #include "lib/net.h"
 
 
+const char * const net_label[] = {
+  [NET_IP4] = "ipv4",
+  [NET_IP6] = "ipv6",
+  [NET_VPN4] = "vpn4",
+  [NET_VPN6] = "vpn6"
+};
+
 const u16 net_addr_length[] = {
   [NET_IP4] = sizeof(net_addr_ip4),
   [NET_IP6] = sizeof(net_addr_ip6),
index dd6e597a62b7ed7efd4af4c1b91467635dfcaab0..fbce2811ce3df49a647f189405d27dcaa17c77f9 100644 (file)
--- a/lib/net.h
+++ b/lib/net.h
 #define NET_ROA6       6
 #define NET_MAX                7
 
+#define NB_IP4         (1 << NET_IP4)
+#define NB_IP6         (1 << NET_IP6)
+#define NB_VPN4                (1 << NET_VPN4)
+#define NB_VPN6                (1 << NET_VPN6)
+
+#define NB_IP          (NB_IP4 | NB_IP6)
+#define NB_ANY         0xffffffff
+
+
 typedef struct net_addr {
   u8 type;
   u8 pxlen;
@@ -88,6 +97,7 @@ typedef union net_addr_union {
 } net_addr_union;
 
 
+extern const char * const net_label[];
 extern const u16 net_addr_length[];
 extern const u8  net_max_prefix_length[];
 extern const u16 net_max_text_length[];
@@ -149,6 +159,13 @@ static inline void net_fill_ip_host(net_addr *a, ip_addr prefix)
     net_fill_ip6(a, ipa_to_ip6(prefix), IP6_MAX_PREFIX_LENGTH);
 }
 
+
+static inline int net_val_match(u8 type, u32 mask)
+{ return !!((1 << type) & mask); }
+
+static inline int net_type_match(const net_addr *a, u32 mask)
+{ return net_val_match(a->type, mask); }
+
 static inline int net_is_ip(const net_addr *a)
 { return (a->type == NET_IP4) || (a->type == NET_IP6); }
 
index 769822f35f130dc423d1d1829433a09b13f09015..6bb686c3b0d8e40468c94ee121bde7ef20169a7a 100644 (file)
@@ -17,6 +17,7 @@ CF_HDR
 CF_DEFINES
 
 static struct proto_config *this_proto;
+static struct channel_config *this_channel;
 static struct iface_patt *this_ipatt;
 static struct iface_patt_node *this_ipn;
 /* static struct roa_table_config *this_roa_table; */
@@ -49,6 +50,15 @@ get_passwords(void)
   return rv;
 }
 
+static void
+proto_postconfig(void)
+{
+  CALL(this_proto->protocol->postconfig, this_proto);
+  this_channel = NULL;
+  this_proto = NULL;
+}
+
+
 #define DIRECT_CFG ((struct rt_dev_config *) this_proto)
 
 CF_DECLS
@@ -76,9 +86,10 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
 %type <s> optsym
 %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 table_type table_sorted tos
+%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 table_sorted tos
 %type <ps> proto_patt proto_patt2
-%type <g> limit_spec
+%type <cc> channel_start proto_channel
+%type <cl> limit_spec
 
 CF_GRAMMAR
 
@@ -115,7 +126,7 @@ listen_opts:
  | listen_opts listen_opt
  ;
 
-listen_opt: 
+listen_opt:
    ADDRESS ipa { new_config->listen_bgp_addr = $2; }
  | PORT expr { new_config->listen_bgp_port = $2; }
  | V6ONLY { new_config->listen_bgp_flags = 0; }
@@ -128,13 +139,10 @@ CF_ADDTO(conf, gr_opts)
 gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
 
 
-/* Creation of routing tables */
+/* Network types (for tables, channels) */
 
-CF_ADDTO(conf, table)
-
-table_type:
-   /* empty */ { $$ = NET_IP4; }
- | IPV4 { $$ = NET_IP4; }
+net_type:
+   IPV4 { $$ = NET_IP4; }
  | IPV6 { $$ = NET_IP6; }
  | VPN4 { $$ = NET_VPN4; }
  | VPN6 { $$ = NET_VPN6; }
@@ -142,21 +150,27 @@ table_type:
  | ROA6 { $$ = NET_ROA6; }
  ;
 
+
+/* Creation of routing tables */
+
+CF_ADDTO(conf, table)
+
 table_sorted:
           { $$ = 0; }
  | SORTED { $$ = 1; }
  ;
 
-table: table_type TABLE SYM table_sorted {
+table: net_type TABLE SYM table_sorted {
    struct rtable_config *cf;
    cf = rt_new_table($3, $1);
    cf->sorted = $4;
    }
  ;
 
+
 /* Definition of protocols */
 
-CF_ADDTO(conf, proto)
+CF_ADDTO(conf, proto { proto_postconfig(); })
 
 proto_start:
    PROTOCOL { $$ = SYM_PROTO; }
@@ -194,24 +208,62 @@ proto_name:
 
 proto_item:
    /* EMPTY */
- | PREFERENCE expr {
-     if ($2 < 0 || $2 > 0xFFFF) cf_error("Invalid preference");
-     this_proto->preference = $2;
-   }
  | DISABLED bool { this_proto->disabled = $2; }
  | DEBUG debug_mask { this_proto->debug = $2; }
  | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
- | IMPORT imexport { this_proto->in_filter = $2; }
- | EXPORT imexport { this_proto->out_filter = $2; }
- | RECEIVE LIMIT limit_spec { this_proto->rx_limit = $3; }
- | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
- | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
- | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; }
- | TABLE rtable { this_proto->table = $2; }
  | ROUTER ID idval { this_proto->router_id = $3; }
  | DESCRIPTION text { this_proto->dsc = $2; }
  ;
 
+
+channel_start: net_type
+{
+  $$ = this_channel = channel_config_new(NULL, $1, this_proto);
+};
+
+channel_item:
+   TABLE rtable {
+     if (this_channel->net_type && ($2->addr_type != this_channel->net_type))
+       cf_error("Incompatible table type");
+     this_channel->table = $2;
+   }
+ | IMPORT imexport { this_channel->in_filter = $2; }
+ | EXPORT imexport { this_channel->out_filter = $2; }
+ | RECEIVE LIMIT limit_spec { this_channel->rx_limit = $3; }
+ | IMPORT LIMIT limit_spec { this_channel->in_limit = $3; }
+ | EXPORT LIMIT limit_spec { this_channel->out_limit = $3; }
+ | PREFERENCE expr { this_channel->preference = $2; check_u16($2); }
+ | IMPORT KEEP FILTERED bool { this_channel->in_keep_filtered = $4; }
+ ;
+
+channel_opts:
+   /* empty */
+ | channel_opts channel_item ';'
+ ;
+
+channel_opt_list:
+   /* empty */
+ | '{' channel_opts '}'
+ ;
+
+channel_end:
+{
+  if (!this_channel->table)
+    cf_error("Routing table not specified");
+
+  this_channel = NULL;
+};
+
+proto_channel: channel_start channel_opt_list channel_end;
+
+
+rtable:
+   SYM {
+     if ($1->class != SYM_TABLE) cf_error("Table expected");
+     $$ = $1->def;
+   }
+ ;
+
 imexport:
    FILTER filter { $$ = $2; }
  | where_filter
@@ -228,20 +280,8 @@ limit_action:
  ;
 
 limit_spec:
-   expr limit_action {
-     struct proto_limit *l = cfg_allocz(sizeof(struct proto_limit));
-     l->limit = $1;
-     l->action = $2;
-     $$ = l;
-   }
- | OFF { $$ = NULL; }
- ;
-
-rtable:
-   SYM {
-     if ($1->class != SYM_TABLE) cf_error("Table name expected");
-     $$ = $1->def;
-   }
+   expr limit_action { $$ = (struct channel_limit){ .limit = $1, $$.action = $2 }; }
+ | OFF { $$ = (struct channel_limit){}; }
  ;
 
 CF_ADDTO(conf, debug_default)
@@ -315,6 +355,7 @@ dev_proto_start: proto_start DIRECT {
 dev_proto:
    dev_proto_start proto_name '{'
  | dev_proto proto_item ';'
+ | dev_proto proto_channel ';'
  | dev_proto dev_iface_patt ';'
  ;
 
index e9f55f0ac2046aab7de055ea4d8bccd9b5bce40f..00af5052926ff8343cafbaf812224fd6648c073a 100644 (file)
@@ -138,7 +138,7 @@ if_copy(struct iface *to, struct iface *from)
 static inline void
 ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
 {
-  if (p->ifa_notify)
+  if (p->ifa_notify && (p->proto_state != PS_DOWN))
     {
       if (p->debug & D_IFACES)
        log(L_TRACE "%s < %s address %N on interface %s %s",
@@ -155,7 +155,7 @@ ifa_notify_change_(unsigned c, struct ifa *a)
 
   DBG("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip);
 
-  WALK_LIST(p, active_proto_list)
+  WALK_LIST(p, proto_list)
     ifa_send_notify(p, c, a);
 }
 
@@ -174,7 +174,7 @@ ifa_notify_change(unsigned c, struct ifa *a)
 static inline void
 if_send_notify(struct proto *p, unsigned c, struct iface *i)
 {
-  if (p->if_notify)
+  if (p->if_notify && (p->proto_state != PS_DOWN))
     {
       if (p->debug & D_IFACES)
        log(L_TRACE "%s < interface %s %s", p->name, i->name,
@@ -215,7 +215,7 @@ if_notify_change(unsigned c, struct iface *i)
        ifa_notify_change_(IF_CHANGE_DOWN, a);
       }
 
-  WALK_LIST(p, active_proto_list)
+  WALK_LIST(p, proto_list)
     if_send_notify(p, c, i);
 
   if (c & IF_CHANGE_UP)
index 74c3c6640453608e4de313be90a7d7d7e3833ad5..69f0942362b1a67ecacb16be55d48cf803655a14 100644 (file)
@@ -239,7 +239,7 @@ neigh_up(neighbor *n, struct iface *i, int scope, struct ifa *a)
   rem_node(&n->n);
   add_tail(&neigh_hash_table[neigh_hash(n->proto, &n->addr)], &n->n);
   DBG("Waking up sticky neighbor %I\n", n->addr);
-  if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
+  if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
     n->proto->neigh_notify(n);
 }
 
@@ -252,7 +252,7 @@ neigh_down(neighbor *n)
     n->iface = NULL;
   n->ifa = NULL;
   n->scope = -1;
-  if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
+  if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
     n->proto->neigh_notify(n);
   rem_node(&n->n);
   if (n->flags & NEF_STICKY)
@@ -333,7 +333,7 @@ neigh_if_link(struct iface *i)
   WALK_LIST_DELSAFE(x, y, i->neighbors)
     {
       neighbor *n = SKIP_BACK(neighbor, if_n, x);
-      if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
+      if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
        n->proto->neigh_notify(n);
     }
 }
index d04da333c6731809acabb21dce1e834ac64b2181..08786e5683b9c5a7493b8e705816eea787f06d7b 100644 (file)
 #include "filter/filter.h"
 
 pool *proto_pool;
+list  proto_list;
 
 static list protocol_list;
-static list proto_list;
 
 #define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0)
 
-list active_proto_list;
-static list inactive_proto_list;
-static list initial_proto_list;
-static list flush_proto_list;
-static struct proto *initial_device_proto;
-
-static event *proto_flush_event;
 static timer *proto_shutdown_timer;
 static timer *gr_wait_timer;
 
@@ -46,199 +39,634 @@ static int graceful_restart_state;
 static u32 graceful_restart_locks;
 
 static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
-static char *c_states[] = { "HUNGRY", "???", "HAPPY", "FLUSHING" };
 
-static void proto_flush_loop(void *);
+extern struct protocol proto_unix_iface;
+
 static void proto_shutdown_loop(struct timer *);
 static void proto_rethink_goal(struct proto *p);
-static void proto_want_export_up(struct proto *p);
-static void proto_fell_down(struct proto *p);
 static char *proto_state_name(struct proto *p);
+static void channel_verify_limits(struct channel *c);
+static void channel_reset_limit(struct channel_limit *l);
 
-static void
-proto_relink(struct proto *p)
-{
-  list *l = NULL;
 
-  switch (p->core_state)
-    {
-    case FS_HUNGRY:
-      l = &inactive_proto_list;
-      break;
-    case FS_HAPPY:
-      l = &active_proto_list;
-      break;
-    case FS_FLUSHING:
-      l = &flush_proto_list;
-      break;
-    default:
-      ASSERT(0);
-    }
+static inline int proto_is_done(struct proto *p)
+{ return (p->proto_state == PS_DOWN) && (p->active_channels == 0); }
 
-  rem_node(&p->n);
-  add_tail(l, &p->n);
-}
+static inline int channel_is_active(struct channel *c)
+{ return (c->channel_state == CS_START) || (c->channel_state == CS_UP); }
 
 static void
 proto_log_state_change(struct proto *p)
 {
   if (p->debug & D_STATES)
+  {
+    char *name = proto_state_name(p);
+    if (name != p->last_state_name_announced)
     {
-      char *name = proto_state_name(p);
-      if (name != p->last_state_name_announced)
-       {
-         p->last_state_name_announced = name;
-         PD(p, "State changed to %s", proto_state_name(p));
-       }
+      p->last_state_name_announced = name;
+      PD(p, "State changed to %s", proto_state_name(p));
     }
+  }
   else
     p->last_state_name_announced = NULL;
 }
 
 
-/**
- * proto_new - create a new protocol instance
- * @c: protocol configuration
- * @size: size of protocol data structure (each protocol instance is represented by
- * a structure starting with generic part [struct &proto] and continued
- * with data specific to the protocol)
- *
- * When a new configuration has been read in, the core code starts
- * initializing all the protocol instances configured by calling their
- * init() hooks with the corresponding instance configuration. The initialization
- * code of the protocol is expected to create a new instance according to the
- * configuration by calling this function and then modifying the default settings
- * to values wanted by the protocol.
- */
-void *
-proto_new(struct proto_config *c, unsigned size)
+struct channel_config *
+proto_cf_find_channel(struct proto_config *pc, uint net_type)
 {
-  struct protocol *pr = c->protocol;
-  struct proto *p = mb_allocz(proto_pool, size);
-
-  p->cf = c;
-  p->debug = c->debug;
-  p->mrtdump = c->mrtdump;
-  p->name = c->name;
-  p->preference = c->preference;
-  p->disabled = c->disabled;
-  p->proto = pr;
-  p->table = c->table->table;
-  p->hash_key = random_u32();
-  c->proto = p;
-  return p;
+  struct channel_config *cc;
+
+  WALK_LIST(cc, pc->channels)
+    if (cc->net_type == net_type)
+      return cc;
+
+  return NULL;
 }
 
-static void
-proto_init_instance(struct proto *p)
+/**
+ * proto_find_channel_by_table - find channel connected to a routing table
+ * @p: protocol instance
+ * @t: routing table
+ *
+ * Returns pointer to channel or NULL
+ */
+struct channel *
+proto_find_channel_by_table(struct proto *p, struct rtable *t)
 {
-  /* Here we cannot use p->cf->name since it won't survive reconfiguration */
-  p->pool = rp_new(proto_pool, p->proto->name);
-  p->attn = ev_new(p->pool);
-  p->attn->data = p;
+  struct channel *c;
 
-  if (graceful_restart_state == GRS_INIT)
-    p->gr_recovery = 1;
+  WALK_LIST(c, p->channels)
+    if (c->table == t)
+      return c;
 
-  if (! p->proto->multitable)
-    rt_lock_table(p->table);
+  return NULL;
 }
 
-extern pool *rt_table_pool;
 /**
- * proto_add_announce_hook - connect protocol to a routing table
+ * proto_add_channel - connect protocol to a routing table
  * @p: protocol instance
- * @t: routing table to connect to
- * @stats: per-table protocol statistics
- *
- * This function creates a connection between the protocol instance @p and the
- * routing table @t, making the protocol hear all changes in the table.
+ * @cf: channel configuration
  *
- * The announce hook is linked in the protocol ahook list. Announce hooks are
- * allocated from the routing table resource pool and when protocol accepts
- * routes also in the table ahook list. The are linked to the table ahook list
- * and unlinked from it depending on export_state (in proto_want_export_up() and
- * proto_want_export_down()) and they are automatically freed after the protocol
- * is flushed (in proto_fell_down()).
+ * This function creates a channel between the protocol instance @p and the
+ * routing table specified in the configuration @cf, making the protocol hear
+ * all changes in the table and allowing the protocol to update routes in the
+ * table.
  *
- * Unless you want to listen to multiple routing tables (as the Pipe protocol
- * does), you needn't to worry about this function since the connection to the
- * protocol's primary routing table is initialized automatically by the core
- * code.
+ * The channel is linked in the protocol channel list and when active also in
+ * the table channel list. Channels are allocated from the global resource pool
+ * (@proto_pool) and they are automatically freed when the protocol is removed.
  */
-struct announce_hook *
-proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats)
+
+struct channel *
+proto_add_channel(struct proto *p, struct channel_config *cf)
+{
+  struct channel *c = mb_allocz(proto_pool, cf->channel->channel_size);
+
+  c->name = cf->name;
+  c->channel = cf->channel;
+  c->proto = p;
+  c->table = cf->table->table;
+
+  c->in_filter = cf->in_filter;
+  c->out_filter = cf->out_filter;
+  c->rx_limit = cf->rx_limit;
+  c->in_limit = cf->in_limit;
+  c->out_limit = cf->out_limit;
+
+  c->net_type = cf->net_type;
+  c->ra_mode = cf->ra_mode;
+  c->preference = cf->preference;
+  c->merge_limit = cf->merge_limit;
+  c->in_keep_filtered = cf->in_keep_filtered;
+
+  c->channel_state = CS_DOWN;
+  c->export_state = ES_DOWN;
+  c->last_state_change = now;
+  c->reloadable = 1;
+
+  CALL(c->channel->init, c, cf);
+
+  add_tail(&p->channels, &c->n);
+
+  PD(p, "Channel %s connected to table %s", c->name, c->table->name);
+
+  return c;
+}
+
+void
+proto_remove_channel(struct proto *p, struct channel *c)
+{
+  ASSERT(c->channel_state == CS_DOWN);
+
+  PD(p, "Channel %s removed", c->name);
+
+  rem_node(&c->n);
+  mb_free(c);
+}
+
+
+static void
+proto_start_channels(struct proto *p)
+{
+  struct channel *c;
+  WALK_LIST(c, p->channels)
+    if (!c->disabled)
+      channel_set_state(c, CS_UP);
+}
+
+static void
+proto_pause_channels(struct proto *p)
 {
-  struct announce_hook *h;
+  struct channel *c;
+  WALK_LIST(c, p->channels)
+    if (!c->disabled && channel_is_active(c))
+      channel_set_state(c, CS_START);
+}
 
-  DBG("Connecting protocol %s to table %s\n", p->name, t->name);
-  PD(p, "Connected to table %s", t->name);
+static void
+proto_stop_channels(struct proto *p)
+{
+  struct channel *c;
+  WALK_LIST(c, p->channels)
+    if (!c->disabled && channel_is_active(c))
+      channel_set_state(c, CS_FLUSHING);
+}
 
-  h = mb_allocz(rt_table_pool, sizeof(struct announce_hook));
-  h->table = t;
-  h->proto = p;
-  h->stats = stats;
+static void
+proto_remove_channels(struct proto *p)
+{
+  struct channel *c;
+  WALK_LIST_FIRST(c, p->channels)
+    proto_remove_channel(p, c);
+}
+
+static void
+channel_schedule_feed(struct channel *c, int initial)
+{
+  // DBG("%s: Scheduling meal\n", p->name);
+  ASSERT(c->channel_state == CS_UP);
 
-  h->next = p->ahooks;
-  p->ahooks = h;
+  c->export_state = ES_FEEDING;
+  c->refeeding = !initial;
 
-  if (p->rt_notify && (p->export_state != ES_DOWN))
-    add_tail(&t->hooks, &h->n);
-  return h;
+  ev_schedule(c->feed_event);
+}
+
+static void
+channel_feed_loop(void *ptr)
+{
+  struct channel *c = ptr;
+
+  if (c->export_state != ES_FEEDING)
+    return;
+
+  if (!c->feed_active)
+    if (c->proto->feed_begin)
+      c->proto->feed_begin(c, !c->refeeding);
+
+  // DBG("Feeding protocol %s continued\n", p->name);
+  if (!rt_feed_channel(c))
+  {
+    ev_schedule(c->feed_event);
+    return;
+  }
+
+  // DBG("Feeding protocol %s finished\n", p->name);
+  c->export_state = ES_READY;
+  // proto_log_state_change(p);
+
+  if (c->proto->feed_end)
+    c->proto->feed_end(c);
+}
+
+
+static void
+channel_start_export(struct channel *c)
+{
+  ASSERT(c->channel_state == CS_UP);
+  ASSERT(c->export_state == ES_DOWN);
+
+  channel_schedule_feed(c, 1); /* Sets ES_FEEDING */
+}
+
+static void
+channel_stop_export(struct channel *c)
+{
+  /* Need to abort feeding */
+  if (c->export_state == ES_FEEDING)
+    rt_feed_channel_abort(c);
+
+  c->export_state = ES_DOWN;
+}
+
+static void
+channel_do_start(struct channel *c)
+{
+  rt_lock_table(c->table);
+  add_tail(&c->table->channels, &c->table_node);
+  c->proto->active_channels++;
+
+  c->feed_event = ev_new(c->proto->pool);
+  c->feed_event->data = c;
+  c->feed_event->hook = channel_feed_loop;
+
+  channel_reset_limit(&c->rx_limit);
+  channel_reset_limit(&c->in_limit);
+  channel_reset_limit(&c->out_limit);
+
+  CALL(c->channel->start, c);
+}
+
+static void
+channel_do_flush(struct channel *c)
+{
+  rt_schedule_prune(c->table);
+
+  c->gr_wait = 0;
+  if (c->gr_lock)
+    channel_graceful_restart_unlock(c);
+
+  CALL(c->channel->shutdown, c);
+}
+
+static void
+channel_do_down(struct channel *c)
+{
+  rem2_node(&c->table_node);
+  rt_unlock_table(c->table);
+  c->proto->active_channels--;
+
+  if ((c->stats.imp_routes + c->stats.filt_routes) != 0)
+    log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name);
+
+  memset(&c->stats, 0, sizeof(struct proto_stats));
+
+  /* Schedule protocol shutddown */
+  if (proto_is_done(c->proto))
+    ev_schedule(c->proto->event);
+}
+
+void
+channel_set_state(struct channel *c, uint state)
+{
+  uint cs = c->channel_state;
+  uint es = c->export_state;
+
+  DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]);
+  if (state == cs)
+    return;
+
+  c->channel_state = state;
+  c->last_state_change = now;
+
+  switch (state)
+  {
+  case CS_START:
+    ASSERT(cs == CS_DOWN || cs == CS_UP);
+
+    if (cs == CS_DOWN)
+      channel_do_start(c);
+
+    if (es != ES_DOWN)
+      channel_stop_export(c);
+
+    break;
+
+  case CS_UP:
+    ASSERT(cs == CS_DOWN || cs == CS_START);
+
+    if (cs == CS_DOWN)
+      channel_do_start(c);
+
+    if (!c->gr_wait)
+      channel_start_export(c);
+
+    break;
+
+  case CS_FLUSHING:
+    ASSERT(cs == CS_START || cs == CS_UP);
+
+    if (es != ES_DOWN)
+      channel_stop_export(c);
+
+    channel_do_flush(c);
+    break;
+
+  case CS_DOWN:
+    ASSERT(cs == CS_FLUSHING);
+
+    channel_do_down(c);
+    break;
+
+  default:
+    ASSERT(0);
+  }
+  // XXXX proto_log_state_change(c);
 }
 
 /**
- * proto_find_announce_hook - find announce hooks
- * @p: protocol instance
- * @t: routing table
+ * channel_request_feeding - request feeding routes to the channel
+ * @c: given channel
  *
- * Returns pointer to announce hook or NULL
+ * Sometimes it is needed to send again all routes to the channel. This is
+ * called feeding and can be requested by this function. This would cause
+ * channel export state transition to ES_FEEDING (during feeding) and when
+ * completed, it will switch back to ES_READY. This function can be called
+ * even when feeding is already running, in that case it is restarted.
  */
-struct announce_hook *
-proto_find_announce_hook(struct proto *p, struct rtable *t)
+void
+channel_request_feeding(struct channel *c)
 {
-  struct announce_hook *a;
+  ASSERT(c->channel_state == CS_UP);
 
-  for (a = p->ahooks; a; a = a->next)
-    if (a->table == t)
-      return a;
+  /* Do nothing if we are still waiting for feeding */
+  if (c->export_state == ES_DOWN)
+    return;
 
-  return NULL;
+  /* If we are already feeding, we want to restart it */
+  if (c->export_state == ES_FEEDING)
+  {
+    /* Unless feeding is in initial state */
+    if (!c->feed_active)
+       return;
+
+    rt_feed_channel_abort(c);
+  }
+
+  channel_reset_limit(&c->out_limit);
+
+  /* Hack: reset exp_routes during refeed, and do not decrease it later */
+  c->stats.exp_routes = 0;
+
+  channel_schedule_feed(c, 0); /* Sets ES_FEEDING */
+  // proto_log_state_change(c);
+}
+
+static inline int
+channel_reloadable(struct channel *c)
+{
+  return c->proto->reload_routes && c->reloadable;
 }
 
 static void
-proto_link_ahooks(struct proto *p)
+channel_request_reload(struct channel *c)
 {
-  struct announce_hook *h;
+  ASSERT(c->channel_state == CS_UP);
+  // ASSERT(channel_reloadable(c));
+
+  c->proto->reload_routes(c);
 
-  if (p->rt_notify)
-    for(h=p->ahooks; h; h=h->next)
-      add_tail(&h->table->hooks, &h->n);
+  /*
+   * Should this be done before reload_routes() hook?
+   * Perhaps, but routes are updated asynchronously.
+   */
+  channel_reset_limit(&c->rx_limit);
+  channel_reset_limit(&c->in_limit);
 }
 
-static void
-proto_unlink_ahooks(struct proto *p)
+const struct channel_class channel_basic = {
+  .channel_size = sizeof(struct channel),
+  .config_size = sizeof(struct channel_config)
+};
+
+void *
+channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto)
+{
+  struct channel_config *cf = NULL;
+  struct rtable_config *tab = NULL;
+  const char *name = NULL;
+
+  if (net_type)
+  {
+    if (!net_val_match(net_type, proto->protocol->channel_mask))
+      cf_error("Unsupported channel type");
+
+    if (proto->net_type && (net_type != proto->net_type))
+      cf_error("Different channel type");
+
+    tab = new_config->def_tables[net_type];
+    name = net_label[net_type];
+  }
+
+  if (!cc)
+    cc = &channel_basic;
+
+  cf = cfg_allocz(cc->config_size);
+  cf->name = name;
+  cf->channel = cc;
+  cf->table = tab;
+  cf->out_filter = FILTER_REJECT;
+
+  cf->net_type = net_type;
+  cf->ra_mode = RA_OPTIMAL;
+  cf->preference = proto->protocol->preference;
+
+  add_tail(&proto->channels, &cf->n);
+
+  return cf;
+}
+
+struct channel_config *
+channel_copy_config(struct channel_config *src, struct proto_config *proto)
+{
+  struct channel_config *dst = cfg_alloc(src->channel->config_size);
+
+  memcpy(dst, src, src->channel->config_size);
+  add_tail(&proto->channels, &dst->n);
+  CALL(src->channel->copy_config, dst, src);
+
+  return dst;
+}
+
+
+static int reconfigure_type;  /* Hack to propagate type info to channel_reconfigure() */
+
+int
+channel_reconfigure(struct channel *c, struct channel_config *cf)
+{
+  /* FIXME: better handle these changes, also handle in_keep_filtered */
+  if ((c->table != cf->table->table) || (c->ra_mode != cf->ra_mode))
+    return 0;
+
+  int import_changed = !filter_same(c->in_filter, cf->in_filter);
+  int export_changed = !filter_same(c->out_filter, cf->out_filter);
+
+  if (c->preference != cf->preference)
+    import_changed = 1;
+
+  if (c->merge_limit != cf->merge_limit)
+    export_changed = 1;
+
+  /* Reconfigure channel fields */
+  c->in_filter = cf->in_filter;
+  c->out_filter = cf->out_filter;
+  c->rx_limit = cf->rx_limit;
+  c->in_limit = cf->in_limit;
+  c->out_limit = cf->out_limit;
+
+  // c->ra_mode = cf->ra_mode;
+  c->merge_limit = cf->merge_limit;
+  c->preference = cf->preference;
+  c->in_keep_filtered = cf->in_keep_filtered;
+
+  channel_verify_limits(c);
+
+  CALL(c->channel->reconfigure, c, cf);
+
+  /* If the channel is not open, it has no routes and we cannot reload it anyways */
+  if (c->channel_state != CS_UP)
+    return 1;
+
+  if (reconfigure_type == RECONFIG_SOFT)
+  {
+    if (import_changed)
+      log(L_INFO "Channel %s.%s changed import", c->proto->name, c->name);
+
+    if (export_changed)
+      log(L_INFO "Channel %s.%s changed export", c->proto->name, c->name);
+
+    return 1;
+  }
+
+  /* Route reload may be not supported */
+  if (import_changed && !channel_reloadable(c))
+    return 0;
+
+  if (import_changed || export_changed)
+    log(L_INFO "Reloading channel %s.%s", c->proto->name, c->name);
+
+  if (import_changed)
+    channel_request_reload(c);
+
+  if (export_changed)
+    channel_request_feeding(c);
+
+  return 1;
+}
+
+
+int
+proto_configure_channel(struct proto *p, struct channel **pc, struct channel_config *cf)
 {
-  struct announce_hook *h;
+  struct channel *c = *pc;
 
-  if (p->rt_notify)
-    for(h=p->ahooks; h; h=h->next)
-      rem_node(&h->n);
+  if (!c && cf)
+  {
+    *pc = proto_add_channel(p, cf);
+  }
+  else if (c && !cf)
+  {
+    if (c->channel_state != CS_DOWN)
+    {
+      log(L_INFO "Cannot remove channel %s.%s", c->proto->name, c->name);
+      return 0;
+    }
+
+    proto_remove_channel(p, c);
+    *pc = NULL;
+  }
+  else if (c && cf)
+  {
+    if (!channel_reconfigure(c, cf))
+    {
+      log(L_INFO "Cannot reconfigure channel %s.%s", c->proto->name, c->name);
+      return 0;
+    }
+  }
+
+  return 1;
 }
 
+
 static void
-proto_free_ahooks(struct proto *p)
+proto_event(void *ptr)
 {
-  struct announce_hook *h, *hn;
+  struct proto *p = ptr;
+
+  if (p->do_start)
+  {
+    if_feed_baby(p);
+    p->do_start = 0;
+  }
 
-  for(h = p->ahooks; h; h = hn)
+  if (p->do_stop)
   {
-    hn = h->next;
-    mb_free(h);
+    if (p->proto == &proto_unix_iface)
+      if_flush_ifaces(p);
+    p->do_stop = 0;
   }
 
-  p->ahooks = NULL;
-  p->main_ahook = NULL;
+  if (proto_is_done(p))
+  {
+    if (p->proto->cleanup)
+      p->proto->cleanup(p);
+
+    p->active = 0;
+    proto_log_state_change(p);
+    proto_rethink_goal(p);
+  }
+}
+
+
+/**
+ * proto_new - create a new protocol instance
+ * @c: protocol configuration
+ *
+ * When a new configuration has been read in, the core code starts
+ * initializing all the protocol instances configured by calling their
+ * init() hooks with the corresponding instance configuration. The initialization
+ * code of the protocol is expected to create a new instance according to the
+ * configuration by calling this function and then modifying the default settings
+ * to values wanted by the protocol.
+ */
+void *
+proto_new(struct proto_config *cf)
+{
+  struct proto *p = mb_allocz(proto_pool, cf->protocol->proto_size);
+
+  p->cf = cf;
+  p->debug = cf->debug;
+  p->mrtdump = cf->mrtdump;
+  p->name = cf->name;
+  p->proto = cf->protocol;
+  p->net_type = cf->net_type;
+  p->disabled = cf->disabled;
+  p->hash_key = random_u32();
+  cf->proto = p;
+
+  init_list(&p->channels);
+
+  return p;
+}
+
+static struct proto *
+proto_init(struct proto_config *c, node *n)
+{
+  struct protocol *pr = c->protocol;
+  struct proto *p = pr->init(c);
+
+  p->proto_state = PS_DOWN;
+  p->last_state_change = now;
+  insert_node(&p->n, n);
+
+  p->event = ev_new(proto_pool);
+  p->event->hook = proto_event;
+  p->event->data = p;
+
+  PD(p, "Initializing%s", p->disabled ? " [disabled]" : "");
+
+  return p;
+}
+
+static void
+proto_start(struct proto *p)
+{
+  /* Here we cannot use p->cf->name since it won't survive reconfiguration */
+  p->pool = rp_new(proto_pool, p->proto->name);
+
+  if (graceful_restart_state == GRS_INIT)
+    p->gr_recovery = 1;
 }
 
 
@@ -263,22 +691,24 @@ proto_free_ahooks(struct proto *p)
 void *
 proto_config_new(struct protocol *pr, int class)
 {
-  struct proto_config *c = cfg_allocz(pr->config_size);
+  struct proto_config *cf = cfg_allocz(pr->config_size);
 
   if (class == SYM_PROTO)
-    add_tail(&new_config->protos, &c->n);
-  c->global = new_config;
-  c->protocol = pr;
-  c->name = pr->name;
-  c->preference = pr->preference;
-  c->class = class;
-  c->out_filter = FILTER_REJECT;
-  c->table = c->global->master_rtc;
-  c->debug = new_config->proto_default_debug;
-  c->mrtdump = new_config->proto_default_mrtdump;
-  return c;
+    add_tail(&new_config->protos, &cf->n);
+
+  cf->global = new_config;
+  cf->protocol = pr;
+  cf->name = pr->name;
+  cf->class = class;
+  cf->debug = new_config->proto_default_debug;
+  cf->mrtdump = new_config->proto_default_mrtdump;
+
+  init_list(&cf->channels);
+
+  return cf;
 }
 
+
 /**
  * proto_copy_config - copy a protocol configuration
  * @dest: destination protocol configuration
@@ -293,6 +723,7 @@ proto_config_new(struct protocol *pr, int class)
 void
 proto_copy_config(struct proto_config *dest, struct proto_config *src)
 {
+  struct channel_config *cc;
   node old_node;
   int old_class;
   char *old_name;
@@ -305,7 +736,7 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src)
 
   DBG("Copying configuration from %s to %s\n", src->name, dest->name);
 
-  /* 
+  /*
    * Copy struct proto_config here. Keep original node, class and name.
    * protocol-specific config copy is handled by protocol copy_config() hook
    */
@@ -314,12 +745,17 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src)
   old_class = dest->class;
   old_name = dest->name;
 
-  memcpy(dest, src, sizeof(struct proto_config));
+  memcpy(dest, src, src->protocol->config_size);
 
   dest->n = old_node;
   dest->class = old_class;
   dest->name = old_name;
+  init_list(&dest->channels);
 
+  WALK_LIST(cc, src->channels)
+    channel_copy_config(cc, dest);
+
+  /* FIXME: allow for undefined copy_config */
   dest->protocol->copy_config(dest, src);
 }
 
@@ -339,66 +775,15 @@ protos_preconfig(struct config *c)
   init_list(&c->protos);
   DBG("Protocol preconfig:");
   WALK_LIST(p, protocol_list)
-    {
-      DBG(" %s", p->name);
-      p->name_counter = 0;
-      if (p->preconfig)
-       p->preconfig(p, c);
-    }
-  DBG("\n");
-}
-
-/**
- * protos_postconfig - post-configuration processing
- * @c: new configuration
- *
- * This function calls the postconfig() hooks of all protocol
- * instances specified in configuration @c. The hooks are not
- * called for protocol templates.
- */
-void
-protos_postconfig(struct config *c)
-{
-  struct proto_config *x;
-  struct protocol *p;
-
-  DBG("Protocol postconfig:");
-  WALK_LIST(x, c->protos)
-    {
-      DBG(" %s", x->name);
-
-      p = x->protocol;
-      if (p->postconfig)
-       p->postconfig(x);
-    }
+  {
+    DBG(" %s", p->name);
+    p->name_counter = 0;
+    if (p->preconfig)
+      p->preconfig(p, c);
+  }
   DBG("\n");
 }
 
-extern struct protocol proto_unix_iface;
-
-static struct proto *
-proto_init(struct proto_config *c)
-{
-  struct protocol *p = c->protocol;
-  struct proto *q = p->init(c);
-
-  q->proto_state = PS_DOWN;
-  q->core_state = FS_HUNGRY;
-  q->export_state = ES_DOWN;
-  q->last_state_change = now;
-
-  add_tail(&initial_proto_list, &q->n);
-
-  if (p == &proto_unix_iface)
-    initial_device_proto = q;
-
-  add_tail(&proto_list, &q->glob_node);
-  PD(q, "Initializing%s", q->disabled ? " [disabled]" : "");
-  return q;
-}
-
-int proto_reconfig_type;  /* Hack to propagate type info to pipe reconfigure hook */
-
 static int
 proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type)
 {
@@ -408,74 +793,23 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
 
   /* If there is a too big change in core attributes, ... */
   if ((nc->protocol != oc->protocol) ||
-      (nc->disabled != p->disabled) ||
-      (nc->table->table != oc->table->table))
+      (nc->net_type != oc->net_type) ||
+      (nc->disabled != p->disabled))
+
     return 0;
 
+  p->name = nc->name;
   p->debug = nc->debug;
   p->mrtdump = nc->mrtdump;
-  proto_reconfig_type = type;
+  reconfigure_type = type;
 
   /* Execute protocol specific reconfigure hook */
-  if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc)))
+  if (!p->proto->reconfigure || !p->proto->reconfigure(p, nc))
     return 0;
 
   DBG("\t%s: same\n", oc->name);
   PD(p, "Reconfigured");
   p->cf = nc;
-  p->name = nc->name;
-  p->preference = nc->preference;
-
-
-  /* Multitable protocols handle rest in their reconfigure hooks */
-  if (p->proto->multitable)
-    return 1;
-
-  /* Update filters and limits in the main announce hook
-     Note that this also resets limit state */
-  if (p->main_ahook)
-    {  
-      struct announce_hook *ah = p->main_ahook;
-      ah->in_filter = nc->in_filter;
-      ah->out_filter = nc->out_filter;
-      ah->rx_limit = nc->rx_limit;
-      ah->in_limit = nc->in_limit;
-      ah->out_limit = nc->out_limit;
-      ah->in_keep_filtered = nc->in_keep_filtered;
-      proto_verify_limits(ah);
-    }
-
-  /* Update routes when filters changed. If the protocol in not UP,
-     it has no routes and we can ignore such changes */
-  if ((p->proto_state != PS_UP) || (type == RECONFIG_SOFT))
-    return 1;
-
-  int import_changed = ! filter_same(nc->in_filter, oc->in_filter);
-  int export_changed = ! filter_same(nc->out_filter, oc->out_filter);
-
-  /* We treat a change in preferences by reimporting routes */
-  if (nc->preference != oc->preference)
-    import_changed = 1;
-
-  if (import_changed || export_changed)
-    log(L_INFO "Reloading protocol %s", p->name);
-
-  /* If import filter changed, call reload hook */
-  if (import_changed && ! (p->reload_routes && p->reload_routes(p)))
-    {
-      /* Now, the protocol is reconfigured. But route reload failed
-        and we have to do regular protocol restart. */
-      log(L_INFO "Restarting protocol %s", p->name);
-      p->disabled = 1;
-      p->down_code = PDC_CF_RESTART;
-      proto_rethink_goal(p);
-      p->disabled = 0;
-      proto_rethink_goal(p);
-      return 1;
-    }
-
-  if (export_changed)
-    proto_request_feeding(p);
 
   return 1;
 }
@@ -512,85 +846,94 @@ void
 protos_commit(struct config *new, struct config *old, int force_reconfig, int type)
 {
   struct proto_config *oc, *nc;
-  struct proto *p, *n;
   struct symbol *sym;
+  struct proto *p;
+  node *n;
+
 
   DBG("protos_commit:\n");
   if (old)
+  {
+    WALK_LIST(oc, old->protos)
     {
-      WALK_LIST(oc, old->protos)
-       {
-         p = oc->proto;
-         sym = cf_find_symbol(new, oc->name);
-         if (sym && sym->class == SYM_PROTO && !new->shutdown)
-           {
-             /* Found match, let's check if we can smoothly switch to new configuration */
-             /* No need to check description */
-             nc = sym->def;
-             nc->proto = p;
-
-             /* We will try to reconfigure protocol p */
-             if (! force_reconfig && proto_reconfigure(p, oc, nc, type))
-               continue;
-
-             /* Unsuccessful, we will restart it */
-             if (!p->disabled && !nc->disabled)
-               log(L_INFO "Restarting protocol %s", p->name);
-             else if (p->disabled && !nc->disabled)
-               log(L_INFO "Enabling protocol %s", p->name);
-             else if (!p->disabled && nc->disabled)
-               log(L_INFO "Disabling protocol %s", p->name);
-
-             p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
-             p->cf_new = nc;
-           }
-         else if (!new->shutdown)
-           {
-             log(L_INFO "Removing protocol %s", p->name);
-             p->down_code = PDC_CF_REMOVE;
-             p->cf_new = NULL;
-           }
-         else /* global shutdown */
-           {
-             p->down_code = PDC_CMD_SHUTDOWN;
-             p->cf_new = NULL;
-           }
-
-         p->reconfiguring = 1;
-         config_add_obstacle(old);
-         proto_rethink_goal(p);
-       }
+      p = oc->proto;
+      sym = cf_find_symbol(new, oc->name);
+      if (sym && sym->class == SYM_PROTO && !new->shutdown)
+      {
+       /* Found match, let's check if we can smoothly switch to new configuration */
+       /* No need to check description */
+       nc = sym->def;
+       nc->proto = p;
+
+       /* We will try to reconfigure protocol p */
+       if (! force_reconfig && proto_reconfigure(p, oc, nc, type))
+         continue;
+
+       /* Unsuccessful, we will restart it */
+       if (!p->disabled && !nc->disabled)
+         log(L_INFO "Restarting protocol %s", p->name);
+       else if (p->disabled && !nc->disabled)
+         log(L_INFO "Enabling protocol %s", p->name);
+       else if (!p->disabled && nc->disabled)
+         log(L_INFO "Disabling protocol %s", p->name);
+
+       p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
+       p->cf_new = nc;
+      }
+      else if (!new->shutdown)
+      {
+       log(L_INFO "Removing protocol %s", p->name);
+       p->down_code = PDC_CF_REMOVE;
+       p->cf_new = NULL;
+      }
+      else /* global shutdown */
+      {
+       p->down_code = PDC_CMD_SHUTDOWN;
+       p->cf_new = NULL;
+      }
+
+      p->reconfiguring = 1;
+      config_add_obstacle(old);
+      proto_rethink_goal(p);
     }
+  }
+
+  struct proto *first_dev_proto = NULL;
 
+  n = NODE &(proto_list.head);
   WALK_LIST(nc, new->protos)
     if (!nc->proto)
-      {
-       if (old)                /* Not a first-time configuration */
-         log(L_INFO "Adding protocol %s", nc->name);
-       proto_init(nc);
-      }
-  DBG("\tdone\n");
+    {
+      /* Not a first-time configuration */
+      if (old)
+       log(L_INFO "Adding protocol %s", nc->name);
+
+      p = proto_init(nc, n);
+      n = NODE p;
+
+      if (p->proto == &proto_unix_iface)
+       first_dev_proto = p;
+    }
+    else
+      n = NODE nc->proto;
 
   DBG("Protocol start\n");
 
   /* Start device protocol first */
-  if (initial_device_proto)
-  {
-    proto_rethink_goal(initial_device_proto);
-    initial_device_proto = NULL;
-  }
+  if (first_dev_proto)
+    proto_rethink_goal(first_dev_proto);
 
   /* Determine router ID for the first time - it has to be here and not in
      global_commit() because it is postponed after start of device protocol */
   if (!config->router_id)
-    {
-      config->router_id = if_choose_router_id(config->router_id_from, 0);
-      if (!config->router_id)
-       die("Cannot determine router ID, please configure it manually");
-    }
+  {
+    config->router_id = if_choose_router_id(config->router_id_from, 0);
+    if (!config->router_id)
+      die("Cannot determine router ID, please configure it manually");
+  }
 
-  /* Start all other protocols */
-  WALK_LIST_DELSAFE(p, n, initial_proto_list)
+  /* Start all new protocols */
+  WALK_LIST_DELSAFE(p, n, proto_list)
     proto_rethink_goal(p);
 }
 
@@ -600,19 +943,21 @@ proto_rethink_goal(struct proto *p)
   struct protocol *q;
   byte goal;
 
-  if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
-    {
-      struct proto_config *nc = p->cf_new;
-      DBG("%s has shut down for reconfiguration\n", p->name);
-      p->cf->proto = NULL;
-      config_del_obstacle(p->cf->global);
-      rem_node(&p->n);
-      rem_node(&p->glob_node);
-      mb_free(p);
-      if (!nc)
-       return;
-      p = proto_init(nc);
-    }
+  if (p->reconfiguring && !p->active)
+  {
+    struct proto_config *nc = p->cf_new;
+    node *n = p->n.prev;
+    DBG("%s has shut down for reconfiguration\n", p->name);
+    p->cf->proto = NULL;
+    config_del_obstacle(p->cf->global);
+    proto_remove_channels(p);
+    rem_node(&p->n);
+    rfree(p->event);
+    mb_free(p);
+    if (!nc)
+      return;
+    p = proto_init(nc, n);
+  }
 
   /* Determine what state we want to reach */
   if (p->disabled || p->reconfiguring)
@@ -621,25 +966,27 @@ proto_rethink_goal(struct proto *p)
     goal = PS_UP;
 
   q = p->proto;
-  if (goal == PS_UP)                   /* Going up */
+  if (goal == PS_UP)
+  {
+    if (!p->active)
     {
-      if (p->proto_state == PS_DOWN && p->core_state == FS_HUNGRY)
-       {
-         DBG("Kicking %s up\n", p->name);
-         PD(p, "Starting");
-         proto_init_instance(p);
-         proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
-       }
+      /* Going up */
+      DBG("Kicking %s up\n", p->name);
+      PD(p, "Starting");
+      proto_start(p);
+      proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
     }
-  else                                         /* Going down */
+  }
+  else
+  {
+    if (p->proto_state == PS_START || p->proto_state == PS_UP)
     {
-      if (p->proto_state == PS_START || p->proto_state == PS_UP)
-       {
-         DBG("Kicking %s down\n", p->name);
-         PD(p, "Shutting down");
-         proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
-       }
+      /* Going down */
+      DBG("Kicking %s down\n", p->name);
+      PD(p, "Shutting down");
+      proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
     }
+  }
 }
 
 
@@ -661,13 +1008,14 @@ proto_rethink_goal(struct proto *p)
  * When graceful restart recovery need is detected during initialization, then
  * enabled protocols are marked with @gr_recovery flag before start. Such
  * protocols then decide how to proceed with graceful restart, participation is
- * voluntary. Protocols could lock the recovery by proto_graceful_restart_lock()
- * (stored in @gr_lock flag), which means that they want to postpone the end of
- * the recovery until they converge and then unlock it. They also could set
- * @gr_wait before advancing to %PS_UP, which means that the core should defer
- * route export to that protocol until the end of the recovery. This should be
- * done by protocols that expect their neigbors to keep the proper routes
- * (kernel table, BGP sessions with BGP graceful restart capability).
+ * voluntary. Protocols could lock the recovery for each channel by function
+ * channel_graceful_restart_lock() (starte stored in @gr_lock flag), which means
+ * that they want to postpone the end of the recovery until they converge and
+ * then unlock it. They also could set @gr_wait before advancing to %PS_UP,
+ * which means that the core should defer route export to that channel until
+ * the end of the recovery. This should be done by protocols that expect their
+ * neigbors to keep the proper routes (kernel table, BGP sessions with BGP
+ * graceful restart capability).
  *
  * The graceful restart recovery is finished when either all graceful restart
  * locks are unlocked or when graceful restart wait timer fires.
@@ -705,10 +1053,10 @@ graceful_restart_init(void)
   log(L_INFO "Graceful restart started");
 
   if (!graceful_restart_locks)
-    {
-      graceful_restart_done(NULL);
-      return;
-    }
+  {
+    graceful_restart_done(NULL);
+    return;
+  }
 
   graceful_restart_state = GRS_ACTIVE;
   gr_wait_timer = tm_new(proto_pool);
@@ -728,30 +1076,30 @@ graceful_restart_init(void)
 static void
 graceful_restart_done(struct timer *t UNUSED)
 {
-  struct proto *p;
-  node *n;
-
   log(L_INFO "Graceful restart done");
   graceful_restart_state = GRS_DONE;
 
-  WALK_LIST2(p, n, proto_list, glob_node)
-    {
-      if (!p->gr_recovery)
-       continue;
+  struct proto *p;
+  WALK_LIST(p, proto_list)
+  {
+    if (!p->gr_recovery)
+      continue;
 
+    struct channel *c;
+    WALK_LIST(c, p->channels)
+    {
       /* Resume postponed export of routes */
-      if ((p->proto_state == PS_UP) && p->gr_wait)
-      {
-       proto_want_export_up(p);
-       proto_log_state_change(p);
-      }
+      if ((c->channel_state == CS_UP) && c->gr_wait)
+       channel_start_export(c);
 
       /* Cleanup */
-      p->gr_recovery = 0;
-      p->gr_wait = 0;
-      p->gr_lock = 0;
+      c->gr_wait = 0;
+      c->gr_lock = 0;
     }
 
+    p->gr_recovery = 0;
+  }
+
   graceful_restart_locks = 0;
 }
 
@@ -762,17 +1110,17 @@ graceful_restart_show_status(void)
     return;
 
   cli_msg(-24, "Graceful restart recovery in progress");
-  cli_msg(-24, "  Waiting for %d protocols to recover", graceful_restart_locks);
+  cli_msg(-24, "  Waiting for %d channels to recover", graceful_restart_locks);
   cli_msg(-24, "  Wait timer is %d/%d", tm_remains(gr_wait_timer), config->gr_wait);
 }
 
 /**
- * proto_graceful_restart_lock - lock graceful restart by protocol
- * @p: protocol instance
+ * channel_graceful_restart_lock - lock graceful restart by channel
+ * @p: channel instance
  *
  * This function allows a protocol to postpone the end of graceful restart
  * recovery until it converges. The lock is removed when the protocol calls
- * proto_graceful_restart_unlock() or when the protocol is stopped.
+ * channel_graceful_restart_unlock() or when the channel is closed.
  *
  * The function have to be called during the initial phase of graceful restart
  * recovery and only for protocols that are part of graceful restart (i.e. their
@@ -780,32 +1128,32 @@ graceful_restart_show_status(void)
  * hooks.
  */
 void
-proto_graceful_restart_lock(struct proto *p)
+channel_graceful_restart_lock(struct channel *c)
 {
   ASSERT(graceful_restart_state == GRS_INIT);
-  ASSERT(p->gr_recovery);
+  ASSERT(c->proto->gr_recovery);
 
-  if (p->gr_lock)
+  if (c->gr_lock)
     return;
 
-  p->gr_lock = 1;
+  c->gr_lock = 1;
   graceful_restart_locks++;
 }
 
 /**
- * proto_graceful_restart_unlock - unlock graceful restart by protocol
- * @p: protocol instance
+ * channel_graceful_restart_unlock - unlock graceful restart by channel
+ * @p: channel instance
  *
- * This function unlocks a lock from proto_graceful_restart_lock(). It is also
+ * This function unlocks a lock from channel_graceful_restart_lock(). It is also
  * automatically called when the lock holding protocol went down.
  */
 void
-proto_graceful_restart_unlock(struct proto *p)
+channel_graceful_restart_unlock(struct channel *c)
 {
-  if (!p->gr_lock)
+  if (!c->gr_lock)
     return;
 
-  p->gr_lock = 0;
+  c->gr_lock = 0;
   graceful_restart_locks--;
 
   if ((graceful_restart_state == GRS_ACTIVE) && !graceful_restart_locks)
@@ -826,34 +1174,26 @@ proto_graceful_restart_unlock(struct proto *p)
 void
 protos_dump_all(void)
 {
-  struct proto *p;
-  struct announce_hook *a;
-
   debug("Protocols:\n");
 
-  WALK_LIST(p, active_proto_list)
+  struct proto *p;
+  WALK_LIST(p, proto_list)
+  {
+    debug("  protocol %s state %s\n", p->name, p_states[p->proto_state]);
+
+    struct channel *c;
+    WALK_LIST(c, p->channels)
     {
-      debug("  protocol %s state %s/%s\n", p->name,
-           p_states[p->proto_state], c_states[p->core_state]);
-      for (a = p->ahooks; a; a = a->next)
-       {
-         debug("\tTABLE %s\n", a->table->name);
-         if (a->in_filter)
-           debug("\tInput filter: %s\n", filter_name(a->in_filter));
-         if (a->out_filter != FILTER_REJECT)
-           debug("\tOutput filter: %s\n", filter_name(a->out_filter));
-       }
-      if (p->disabled)
-       debug("\tDISABLED\n");
-      else if (p->proto->dump)
-       p->proto->dump(p);
+      debug("\tTABLE %s\n", c->table->name);
+      if (c->in_filter)
+       debug("\tInput filter: %s\n", filter_name(c->in_filter));
+      if (c->out_filter)
+       debug("\tOutput filter: %s\n", filter_name(c->out_filter));
     }
-  WALK_LIST(p, inactive_proto_list)
-    debug("  inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]);
-  WALK_LIST(p, initial_proto_list)
-    debug("  initial %s\n", p->name);
-  WALK_LIST(p, flush_proto_list)
-    debug("  flushing %s\n", p->name);
+
+    if (p->proto->dump && (p->proto_state != PS_DOWN))
+      p->proto->dump(p);
+  }
 }
 
 /**
@@ -890,12 +1230,9 @@ extern void bfd_init_all(void);
 void
 protos_build(void)
 {
-  init_list(&protocol_list);
   init_list(&proto_list);
-  init_list(&active_proto_list);
-  init_list(&inactive_proto_list);
-  init_list(&initial_proto_list);
-  init_list(&flush_proto_list);
+  init_list(&protocol_list);
+
   proto_build(&proto_device);
 #ifdef CONFIG_RADV
   proto_build(&proto_radv);
@@ -921,136 +1258,10 @@ protos_build(void)
 #endif
 
   proto_pool = rp_new(&root_pool, "Protocols");
-  proto_flush_event = ev_new(proto_pool);
-  proto_flush_event->hook = proto_flush_loop;
   proto_shutdown_timer = tm_new(proto_pool);
   proto_shutdown_timer->hook = proto_shutdown_loop;
 }
 
-static void
-proto_feed_more(void *P)
-{
-  struct proto *p = P;
-
-  if (p->export_state != ES_FEEDING)
-    return;
-
-  DBG("Feeding protocol %s continued\n", p->name);
-  if (rt_feed_baby(p))
-    {
-      DBG("Feeding protocol %s finished\n", p->name);
-      p->export_state = ES_READY;
-      proto_log_state_change(p);
-
-      if (p->feed_end)
-       p->feed_end(p);
-    }
-  else
-    {
-      p->attn->hook = proto_feed_more;
-      ev_schedule(p->attn);            /* Will continue later... */
-    }
-}
-
-static void
-proto_feed_initial(void *P)
-{
-  struct proto *p = P;
-
-  if (p->export_state != ES_FEEDING)
-    return;
-
-  DBG("Feeding protocol %s\n", p->name);
-
-  if_feed_baby(p);
-  proto_feed_more(P);
-}
-
-static void
-proto_schedule_feed(struct proto *p, int initial)
-{
-  DBG("%s: Scheduling meal\n", p->name);
-
-  p->export_state = ES_FEEDING;
-  p->refeeding = !initial;
-
-  p->attn->hook = initial ? proto_feed_initial : proto_feed_more;
-  ev_schedule(p->attn);
-
-  if (p->feed_begin)
-    p->feed_begin(p, initial);
-}
-
-/*
- * Flushing loop is responsible for flushing routes and protocols
- * after they went down. It runs in proto_flush_event. At the start of
- * one round, protocols waiting to flush are marked in
- * proto_schedule_flush_loop(). At the end of the round (when routing
- * table flush is complete), marked protocols are flushed and a next
- * round may start.
- */
-
-static int flush_loop_state;   /* 1 -> running */
-
-static void
-proto_schedule_flush_loop(void)
-{
-  struct proto *p;
-  struct announce_hook *h;
-
-  if (flush_loop_state)
-    return;
-  flush_loop_state = 1;
-
-  WALK_LIST(p, flush_proto_list)
-  {
-    p->flushing = 1;
-    for (h=p->ahooks; h; h=h->next)
-      rt_mark_for_prune(h->table);
-  }
-
-  ev_schedule(proto_flush_event);
-}
-
-static void
-proto_flush_loop(void *unused UNUSED)
-{
-  struct proto *p;
-
-  if (! rt_prune_loop())
-    {
-      /* Rtable pruning is not finished */
-      ev_schedule(proto_flush_event);
-      return;
-    }
-
-  rt_prune_sources();
-
- again:
-  WALK_LIST(p, flush_proto_list)
-    if (p->flushing)
-      {
-       /* This will flush interfaces in the same manner
-          like rt_prune_all() flushes routes */
-       if (p->proto == &proto_unix_iface)
-         if_flush_ifaces(p);
-
-       DBG("Flushing protocol %s\n", p->name);
-       p->flushing = 0;
-       p->core_state = FS_HUNGRY;
-       proto_relink(p);
-       proto_log_state_change(p);
-       if (p->proto_state == PS_DOWN)
-         proto_fell_down(p);
-       goto again;
-      }
-
-  /* This round finished, perhaps there will be another one */
-  flush_loop_state = 0;
-  if (!EMPTY_LIST(flush_proto_list))
-    proto_schedule_flush_loop();
-}
-
 
 /* Temporary hack to propagate restart to BGP */
 int proto_restart;
@@ -1060,19 +1271,19 @@ proto_shutdown_loop(struct timer *t UNUSED)
 {
   struct proto *p, *p_next;
 
-  WALK_LIST_DELSAFE(p, p_next, active_proto_list)
+  WALK_LIST_DELSAFE(p, p_next, proto_list)
     if (p->down_sched)
-      {
-       proto_restart = (p->down_sched == PDS_RESTART);
+    {
+      proto_restart = (p->down_sched == PDS_RESTART);
 
-       p->disabled = 1;
+      p->disabled = 1;
+      proto_rethink_goal(p);
+      if (proto_restart)
+      {
+       p->disabled = 0;
        proto_rethink_goal(p);
-       if (proto_restart)
-         {
-           p->disabled = 0;
-           proto_rethink_goal(p);
-         }
       }
+    }
 }
 
 static inline void
@@ -1091,50 +1302,8 @@ proto_schedule_down(struct proto *p, byte restart, byte code)
 }
 
 
-/**
- * proto_request_feeding - request feeding routes to the protocol
- * @p: given protocol 
- *
- * Sometimes it is needed to send again all routes to the
- * protocol. This is called feeding and can be requested by this
- * function. This would cause protocol export state transition
- * to ES_FEEDING (during feeding) and when completed, it will
- * switch back to ES_READY. This function can be called even
- * when feeding is already running, in that case it is restarted.
- */
-void
-proto_request_feeding(struct proto *p)
-{
-  ASSERT(p->proto_state == PS_UP);
-
-  /* Do nothing if we are still waiting for feeding */
-  if (p->export_state == ES_DOWN)
-    return;
-
-  /* If we are already feeding, we want to restart it */
-  if (p->export_state == ES_FEEDING)
-    {
-      /* Unless feeding is in initial state */
-      if (p->attn->hook == proto_feed_initial)
-       return;
-
-      rt_feed_baby_abort(p);
-    }
-
-  /* FIXME: This should be changed for better support of multitable protos */
-  struct announce_hook *ah;
-  for (ah = p->ahooks; ah; ah = ah->next)
-    proto_reset_limit(ah->out_limit);
-
-  /* Hack: reset exp_routes during refeed, and do not decrease it later */
-  p->stats.exp_routes = 0;
-
-  proto_schedule_feed(p, 0);
-  proto_log_state_change(p);
-}
-
 static const char *
-proto_limit_name(struct proto_limit *l)
+channel_limit_name(struct channel_limit *l)
 {
   const char *actions[] = {
     [PLA_WARN] = "warn",
@@ -1147,22 +1316,22 @@ proto_limit_name(struct proto_limit *l)
 }
 
 /**
- * proto_notify_limit: notify about limit hit and take appropriate action
- * @ah: announce hook
+ * channel_notify_limit: notify about limit hit and take appropriate action
+ * @c: channel
  * @l: limit being hit
  * @dir: limit direction (PLD_*)
- * @rt_count: the number of routes 
+ * @rt_count: the number of routes
  *
  * The function is called by the route processing core when limit @l
  * is breached. It activates the limit and tooks appropriate action
  * according to @l->action.
  */
 void
-proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count)
+channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count)
 {
   const char *dir_name[PLD_MAX] = { "receive", "import" , "export" };
   const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
-  struct proto *p = ah->proto;
+  struct proto *p = c->proto;
 
   if (l->state == PLS_BLOCKED)
     return;
@@ -1170,147 +1339,112 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32
   /* For warning action, we want the log message every time we hit the limit */
   if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit)))
     log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
-       p->name, dir_name[dir], l->limit, proto_limit_name(l));
+       p->name, dir_name[dir], l->limit, channel_limit_name(l));
 
   switch (l->action)
-    {
-    case PLA_WARN:
-      l->state = PLS_ACTIVE;
-      break;
-
-    case PLA_BLOCK:
-      l->state = PLS_BLOCKED;
-      break;
-
-    case PLA_RESTART:
-    case PLA_DISABLE:
-      l->state = PLS_BLOCKED;
-      if (p->proto_state == PS_UP)
-       proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
-      break;
-    }
+  {
+  case PLA_WARN:
+    l->state = PLS_ACTIVE;
+    break;
+
+  case PLA_BLOCK:
+    l->state = PLS_BLOCKED;
+    break;
+
+  case PLA_RESTART:
+  case PLA_DISABLE:
+    l->state = PLS_BLOCKED;
+    if (p->proto_state == PS_UP)
+      proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
+    break;
+  }
 }
 
-void
-proto_verify_limits(struct announce_hook *ah)
+static void
+channel_verify_limits(struct channel *c)
 {
-  struct proto_limit *l;
-  struct proto_stats *stats = ah->stats;
-  u32 all_routes = stats->imp_routes + stats->filt_routes;
+  struct channel_limit *l;
+  u32 all_routes = c->stats.imp_routes + c->stats.filt_routes;
 
-  l = ah->rx_limit;
-  if (l && (all_routes > l->limit))
-    proto_notify_limit(ah, l, PLD_RX, all_routes);
+  l = &c->rx_limit;
+  if (l->action && (all_routes > l->limit))
+    channel_notify_limit(c, l, PLD_RX, all_routes);
 
-  l = ah->in_limit;
-  if (l && (stats->imp_routes > l->limit))
-    proto_notify_limit(ah, l, PLD_IN, stats->imp_routes);
+  l = &c->in_limit;
+  if (l->action && (c->stats.imp_routes > l->limit))
+    channel_notify_limit(c, l, PLD_IN, c->stats.imp_routes);
 
-  l = ah->out_limit;
-  if (l && (stats->exp_routes > l->limit))
-    proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes);
+  l = &c->out_limit;
+  if (l->action && (c->stats.exp_routes > l->limit))
+    channel_notify_limit(c, l, PLD_OUT, c->stats.exp_routes);
 }
 
-
-static void
-proto_want_core_up(struct proto *p)
+static inline void
+channel_reset_limit(struct channel_limit *l)
 {
-  ASSERT(p->core_state == FS_HUNGRY);
-
-  if (!p->proto->multitable)
-    {
-      p->main_source = rt_get_source(p, 0);
-      rt_lock_source(p->main_source);
-
-      /* Connect protocol to routing table */
-      p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats);
-      p->main_ahook->in_filter = p->cf->in_filter;
-      p->main_ahook->out_filter = p->cf->out_filter;
-      p->main_ahook->rx_limit = p->cf->rx_limit;
-      p->main_ahook->in_limit = p->cf->in_limit;
-      p->main_ahook->out_limit = p->cf->out_limit;
-      p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered;
-
-      proto_reset_limit(p->main_ahook->rx_limit);
-      proto_reset_limit(p->main_ahook->in_limit);
-      proto_reset_limit(p->main_ahook->out_limit);
-    }
-
-  p->core_state = FS_HAPPY;
-  proto_relink(p);
+  if (l->action)
+    l->state = PLS_INITIAL;
 }
 
-static void
-proto_want_export_up(struct proto *p)
+static inline void
+proto_do_start(struct proto *p)
 {
-  ASSERT(p->core_state == FS_HAPPY);
-  ASSERT(p->export_state == ES_DOWN);
-
-  proto_link_ahooks(p);
-  proto_schedule_feed(p, 1); /* Sets ES_FEEDING */
+  p->active = 1;
+  p->do_start = 1;
+  ev_schedule(p->event);
 }
 
 static void
-proto_want_export_down(struct proto *p)
+proto_do_up(struct proto *p)
 {
-  ASSERT(p->export_state != ES_DOWN);
-
-  /* Need to abort feeding */
-  if (p->export_state == ES_FEEDING)
-    rt_feed_baby_abort(p);
+  if (!p->main_source)
+  {
+    p->main_source = rt_get_source(p, 0);
+    rt_lock_source(p->main_source);
+  }
 
-  p->export_state = ES_DOWN;
-  proto_unlink_ahooks(p);
+  proto_start_channels(p);
 }
 
-static void
-proto_want_core_down(struct proto *p)
+static inline void
+proto_do_pause(struct proto *p)
 {
-  ASSERT(p->core_state == FS_HAPPY);
-  ASSERT(p->export_state == ES_DOWN);
-
-  p->core_state = FS_FLUSHING;
-  proto_relink(p);
-  proto_schedule_flush_loop();
-
-  if (!p->proto->multitable)
-    {
-      rt_unlock_source(p->main_source);
-      p->main_source = NULL;
-    }
+  proto_pause_channels(p);
 }
 
 static void
-proto_falling_down(struct proto *p)
+proto_do_stop(struct proto *p)
 {
+  p->down_sched = 0;
   p->gr_recovery = 0;
-  p->gr_wait = 0;
-  if (p->gr_lock)
-    proto_graceful_restart_unlock(p);
-}
-
-static void
-proto_fell_down(struct proto *p)
-{
-  DBG("Protocol %s down\n", p->name);
-
-  u32 all_routes = p->stats.imp_routes + p->stats.filt_routes;
-  if (all_routes != 0)
-    log(L_ERR "Protocol %s is down but still has %d routes", p->name, all_routes);
 
-  bzero(&p->stats, sizeof(struct proto_stats));
-  proto_free_ahooks(p);
+  p->do_stop = 1;
+  ev_schedule(p->event);
 
-  if (! p->proto->multitable)
-    rt_unlock_table(p->table);
+  if (p->main_source)
+  {
+    rt_unlock_source(p->main_source);
+    p->main_source = NULL;
+  }
 
-  if (p->proto->cleanup)
-    p->proto->cleanup(p);
+  proto_stop_channels(p);
+}
 
-  proto_rethink_goal(p);
+static void
+proto_do_down(struct proto *p)
+{
+  p->down_code = 0;
+  neigh_prune();
+  rfree(p->pool);
+  p->pool = NULL;
+
+  /* Shutdown is finished in the protocol event */
+  if (proto_is_done(p))
+    ev_schedule(p->event);
 }
 
 
+
 /**
  * proto_notify_state - notify core about protocol state change
  * @p: protocol the state of which has changed
@@ -1326,78 +1460,53 @@ proto_fell_down(struct proto *p)
  * it should be used at tail positions of protocol callbacks.
  */
 void
-proto_notify_state(struct proto *p, unsigned ps)
+proto_notify_state(struct proto *p, uint state)
 {
-  unsigned ops = p->proto_state;
-  unsigned cs = p->core_state;
-  unsigned es = p->export_state;
+  uint ps = p->proto_state;
 
-  DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]);
-  if (ops == ps)
+  DBG("%s reporting state transition %s -> %s\n", p->name, p_states[ps], p_states[state]);
+  if (state == ps)
     return;
 
-  p->proto_state = ps;
+  p->proto_state = state;
   p->last_state_change = now;
 
-  switch (ps)
-    {
-    case PS_START:
-      ASSERT(ops == PS_DOWN || ops == PS_UP);
-      ASSERT(cs == FS_HUNGRY || cs == FS_HAPPY);
-
-      if (es != ES_DOWN)
-       proto_want_export_down(p);
-      break;
-
-    case PS_UP:
-      ASSERT(ops == PS_DOWN || ops == PS_START);
-      ASSERT(cs == FS_HUNGRY || cs == FS_HAPPY);
-      ASSERT(es == ES_DOWN);
-
-      if (cs == FS_HUNGRY)
-       proto_want_core_up(p);
-      if (!p->gr_wait)
-       proto_want_export_up(p);
-      break;
-
-    case PS_STOP:
-      ASSERT(ops == PS_START || ops == PS_UP);
-
-      p->down_sched = 0;
-
-      if (es != ES_DOWN)
-       proto_want_export_down(p);
-      if (cs == FS_HAPPY)
-       proto_want_core_down(p);
-      proto_falling_down(p);
-      break;
-
-    case PS_DOWN:
-      p->down_code = 0;
-      p->down_sched = 0;
-
-      if (es != ES_DOWN)
-       proto_want_export_down(p);
-      if (cs == FS_HAPPY)
-       proto_want_core_down(p);
-      if (ops != PS_STOP)
-       proto_falling_down(p);
-
-      neigh_prune(); // FIXME convert neighbors to resource?
-      rfree(p->pool);
-      p->pool = NULL;
-
-      if (cs == FS_HUNGRY)             /* Shutdown finished */
-       {
-         proto_log_state_change(p);
-         proto_fell_down(p);
-         return;                       /* The protocol might have ceased to exist */
-       }
-      break;
-
-    default:
-      bug("%s: Invalid state %d", p->name, ps);
-    }
+  switch (state)
+  {
+  case PS_START:
+    ASSERT(ps == PS_DOWN || ps == PS_UP);
+
+    if (ps == PS_DOWN)
+      proto_do_start(p);
+    else
+      proto_do_pause(p);
+    break;
+
+  case PS_UP:
+    ASSERT(ps == PS_DOWN || ps == PS_START);
+
+    if (ps == PS_DOWN)
+      proto_do_start(p);
+
+    proto_do_up(p);
+    break;
+
+  case PS_STOP:
+    ASSERT(ps == PS_START || ps == PS_UP);
+
+    proto_do_stop(p);
+    break;
+
+  case PS_DOWN:
+    if (ps != PS_STOP)
+      proto_do_stop(p);
+
+    proto_do_down(p);
+    break;
+
+  default:
+    bug("%s: Invalid state %d", p->name, ps);
+  }
 
   proto_log_state_change(p);
 }
@@ -1409,82 +1518,73 @@ proto_notify_state(struct proto *p, unsigned ps)
 static char *
 proto_state_name(struct proto *p)
 {
-#define P(x,y) ((x << 4) | y)
-  switch (P(p->proto_state, p->core_state))
-    {
-    case P(PS_DOWN, FS_HUNGRY):                return "down";
-    case P(PS_START, FS_HUNGRY):
-    case P(PS_START, FS_HAPPY):                return "start";
-    case P(PS_UP, FS_HAPPY):
-      switch (p->export_state)
-       {
-       case ES_DOWN:                   return "wait";
-       case ES_FEEDING:                return "feed";
-       case ES_READY:                  return "up";
-       default:                        return "???";
-       }
-    case P(PS_STOP, FS_HUNGRY):
-    case P(PS_STOP, FS_FLUSHING):      return "stop";
-    case P(PS_DOWN, FS_FLUSHING):      return "flush";
-    default:                           return "???";
-    }
-#undef P
+  switch (p->proto_state)
+  {
+  case PS_DOWN:                return p->active ? "flush" : "down";
+  case PS_START:       return "start";
+  case PS_UP:          return "up";
+  case PS_STOP:                return "stop";
+  default:             return "???";
+  }
 }
 
 static void
-proto_show_stats(struct proto_stats *s, int in_keep_filtered)
+channel_show_stats(struct channel *c)
 {
-  if (in_keep_filtered)
-    cli_msg(-1006, "  Routes:         %u imported, %u filtered, %u exported, %u preferred", 
-           s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes);
+  struct proto_stats *s = &c->stats;
+
+  if (c->in_keep_filtered)
+    cli_msg(-1006, "    Routes:         %u imported, %u filtered, %u exported",
+           s->imp_routes, s->filt_routes, s->exp_routes);
   else
-    cli_msg(-1006, "  Routes:         %u imported, %u exported, %u preferred", 
-           s->imp_routes, s->exp_routes, s->pref_routes);
+    cli_msg(-1006, "    Routes:         %u imported, %u exported",
+           s->imp_routes, s->exp_routes);
 
-  cli_msg(-1006, "  Route change stats:     received   rejected   filtered    ignored   accepted");
-  cli_msg(-1006, "    Import updates:     %10u %10u %10u %10u %10u",
+  cli_msg(-1006, "    Route change stats:     received   rejected   filtered    ignored   accepted");
+  cli_msg(-1006, "      Import updates:     %10u %10u %10u %10u %10u",
          s->imp_updates_received, s->imp_updates_invalid,
          s->imp_updates_filtered, s->imp_updates_ignored,
          s->imp_updates_accepted);
-  cli_msg(-1006, "    Import withdraws:   %10u %10u        --- %10u %10u",
+  cli_msg(-1006, "      Import withdraws:   %10u %10u        --- %10u %10u",
          s->imp_withdraws_received, s->imp_withdraws_invalid,
          s->imp_withdraws_ignored, s->imp_withdraws_accepted);
-  cli_msg(-1006, "    Export updates:     %10u %10u %10u        --- %10u",
+  cli_msg(-1006, "      Export updates:     %10u %10u %10u        --- %10u",
          s->exp_updates_received, s->exp_updates_rejected,
          s->exp_updates_filtered, s->exp_updates_accepted);
-  cli_msg(-1006, "    Export withdraws:   %10u        ---        ---        --- %10u",
+  cli_msg(-1006, "      Export withdraws:   %10u        ---        ---        --- %10u",
          s->exp_withdraws_received, s->exp_withdraws_accepted);
 }
 
 void
-proto_show_limit(struct proto_limit *l, const char *dsc)
+channel_show_limit(struct channel_limit *l, const char *dsc)
 {
-  if (!l)
+  if (!l->action)
     return;
 
-  cli_msg(-1006, "  %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : "");
-  cli_msg(-1006, "    Action:       %s", proto_limit_name(l));
+  cli_msg(-1006, "    %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : "");
+  cli_msg(-1006, "      Action:       %s", channel_limit_name(l));
 }
 
 void
-proto_show_basic_info(struct proto *p)
+channel_show_info(struct channel *c)
 {
-  // cli_msg(-1006, "  Table:          %s", p->table->name);
-  cli_msg(-1006, "  Preference:     %d", p->preference);
-  cli_msg(-1006, "  Input filter:   %s", filter_name(p->cf->in_filter));
-  cli_msg(-1006, "  Output filter:  %s", filter_name(p->cf->out_filter));
+  cli_msg(-1006, "  Channel %s", c->name);
+  cli_msg(-1006, "    Table:          %s", c->table->name);
+  cli_msg(-1006, "    Preference:     %d", c->preference);
+  cli_msg(-1006, "    Input filter:   %s", filter_name(c->in_filter));
+  cli_msg(-1006, "    Output filter:  %s", filter_name(c->out_filter));
 
   if (graceful_restart_state == GRS_ACTIVE)
-    cli_msg(-1006, "  GR recovery:   %s%s",
-           p->gr_lock ? " pending" : "",
-           p->gr_wait ? " waiting" : "");
+    cli_msg(-1006, "    GR recovery:   %s%s",
+           c->gr_lock ? " pending" : "",
+           c->gr_wait ? " waiting" : "");
 
-  proto_show_limit(p->cf->rx_limit, "Receive limit:");
-  proto_show_limit(p->cf->in_limit, "Import limit:");
-  proto_show_limit(p->cf->out_limit, "Export limit:");
+  channel_show_limit(&c->rx_limit, "Receive limit:");
+  channel_show_limit(&c->in_limit, "Import limit:");
+  channel_show_limit(&c->out_limit, "Export limit:");
 
-  if (p->proto_state != PS_DOWN)
-    proto_show_stats(&p->stats, p->cf->in_keep_filtered);
+  if (c->channel_state != CS_DOWN)
+    channel_show_stats(c);
 }
 
 void
@@ -1503,34 +1603,39 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt)
   cli_msg(-1002, "%-8s %-8s %-8s %-5s  %-10s  %s",
          p->name,
          p->proto->name,
-         p->table->name,
+         p->main_channel ? p->main_channel->table->name : "---",
          proto_state_name(p),
          tbuf,
          buf);
+
   if (verbose)
+  {
+    if (p->cf->dsc)
+      cli_msg(-1006, "  Description:    %s", p->cf->dsc);
+    if (p->cf->router_id)
+      cli_msg(-1006, "  Router ID:      %R", p->cf->router_id);
+
+    if (p->proto->show_proto_info)
+      p->proto->show_proto_info(p);
+    else
     {
-      if (p->cf->dsc)
-       cli_msg(-1006, "  Description:    %s", p->cf->dsc);
-      if (p->cf->router_id)
-       cli_msg(-1006, "  Router ID:      %R", p->cf->router_id);
-
-      if (p->proto->show_proto_info)
-       p->proto->show_proto_info(p);
-      else
-       proto_show_basic_info(p);
-
-      cli_msg(-1006, "");
+      struct channel *c;
+      WALK_LIST(c, p->channels)
+       channel_show_info(c);
     }
+
+    cli_msg(-1006, "");
+  }
 }
 
 void
 proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
 {
   if (p->disabled)
-    {
-      cli_msg(-8, "%s: already disabled", p->name);
-      return;
-    }
+  {
+    cli_msg(-8, "%s: already disabled", p->name);
+    return;
+  }
 
   log(L_INFO "Disabling protocol %s", p->name);
   p->disabled = 1;
@@ -1543,10 +1648,10 @@ void
 proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
 {
   if (!p->disabled)
-    {
-      cli_msg(-10, "%s: already enabled", p->name);
-      return;
-    }
+  {
+    cli_msg(-10, "%s: already enabled", p->name);
+    return;
+  }
 
   log(L_INFO "Enabling protocol %s", p->name);
   p->disabled = 0;
@@ -1558,10 +1663,10 @@ void
 proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
 {
   if (p->disabled)
-    {
-      cli_msg(-8, "%s: already disabled", p->name);
-      return;
-    }
+  {
+    cli_msg(-8, "%s: already disabled", p->name);
+    return;
+  }
 
   log(L_INFO "Restarting protocol %s", p->name);
   p->disabled = 1;
@@ -1575,41 +1680,38 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
 void
 proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED)
 {
+  struct channel *c;
+
   if (p->disabled)
-    {
-      cli_msg(-8, "%s: already disabled", p->name);
-      return;
-    }
+  {
+    cli_msg(-8, "%s: already disabled", p->name);
+    return;
+  }
 
   /* If the protocol in not UP, it has no routes */
   if (p->proto_state != PS_UP)
     return;
 
+  /* All channels must support reload */
+  if (dir != CMD_RELOAD_OUT)
+    WALK_LIST(c, p->channels)
+      if (!channel_reloadable(c))
+      {
+       cli_msg(-8006, "%s: reload failed", p->name);
+       return;
+      }
+
   log(L_INFO "Reloading protocol %s", p->name);
 
   /* re-importing routes */
   if (dir != CMD_RELOAD_OUT)
-    {
-      if (! (p->reload_routes && p->reload_routes(p)))
-       {
-         cli_msg(-8006, "%s: reload failed", p->name);
-         return;
-       }
-
-      /*
-       * Should be done before reload_routes() hook?
-       * Perhaps, but these hooks work asynchronously.
-       */
-      if (!p->proto->multitable)
-       {
-         proto_reset_limit(p->main_ahook->rx_limit);
-         proto_reset_limit(p->main_ahook->in_limit);
-       }
-    }
+    WALK_LIST(c, p->channels)
+      channel_request_reload(c);
 
   /* re-exporting routes */
   if (dir != CMD_RELOAD_IN)
-    proto_request_feeding(p);
+    WALK_LIST(c, p->channels)
+      channel_request_feeding(c);
 
   cli_msg(-15, "%s: reloading", p->name);
 }
@@ -1630,10 +1732,10 @@ static void
 proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int), uint arg)
 {
   if (s->class != SYM_PROTO)
-    {
-      cli_msg(9002, "%s is not a protocol", s->name);
-      return;
-    }
+  {
+    cli_msg(9002, "%s is not a protocol", s->name);
+    return;
+  }
 
   cmd(((struct proto_config *)s->def)->proto, arg, 0);
   cli_msg(0, "");
@@ -1642,16 +1744,12 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int)
 static void
 proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint arg)
 {
+  struct proto *p;
   int cnt = 0;
 
-  node *nn;
-  WALK_LIST(nn, proto_list)
-    {
-      struct proto *p = SKIP_BACK(struct proto, glob_node, nn);
-
-      if (!patt || patmatch(patt, p->name))
-       cmd(p, arg, cnt++);
-    }
+  WALK_LIST(p, proto_list)
+    if (!patt || patmatch(patt, p->name))
+      cmd(p, arg, cnt++);
 
   if (!cnt)
     cli_msg(8003, "No protocols match");
@@ -1678,25 +1776,27 @@ proto_get_named(struct symbol *sym, struct protocol *pr)
   struct proto *p, *q;
 
   if (sym)
-    {
-      if (sym->class != SYM_PROTO)
-       cf_error("%s: Not a protocol", sym->name);
-      p = ((struct proto_config *)sym->def)->proto;
-      if (!p || p->proto != pr)
-       cf_error("%s: Not a %s protocol", sym->name, pr->name);
-    }
+  {
+    if (sym->class != SYM_PROTO)
+      cf_error("%s: Not a protocol", sym->name);
+
+    p = ((struct proto_config *) sym->def)->proto;
+    if (!p || p->proto != pr)
+      cf_error("%s: Not a %s protocol", sym->name, pr->name);
+  }
   else
-    {
-      p = NULL;
-      WALK_LIST(q, active_proto_list)
-       if (q->proto == pr)
-         {
-           if (p)
-             cf_error("There are multiple %s protocols running", pr->name);
-           p = q;
-         }
-      if (!p)
-       cf_error("There is no %s protocol running", pr->name);
-    }
+  {
+    p = NULL;
+    WALK_LIST(q, proto_list)
+      if ((q->proto == pr) && (q->proto_state != PS_DOWN))
+      {
+       if (p)
+         cf_error("There are multiple %s protocols running", pr->name);
+       p = q;
+      }
+    if (!p)
+      cf_error("There is no %s protocol running", pr->name);
+  }
+
   return p;
 }
index 1d4c31a70d13b98ed2e2ca123da560f9b0c56bba..53da78b8d516f740d0f2aacb5c471f1b7517d782 100644 (file)
@@ -69,23 +69,6 @@ its state by calling the <func/proto_notify_state/ function.
 
 <p>At any time, the core code can ask the protocol to shut itself down by calling its stop() hook.
 
-<p>The <em/core state machine/ takes care of the core view of protocol state.
-The states are traversed according to changes of the protocol state machine, but
-sometimes the transitions are delayed if the core needs to finish some actions
-(for example sending of new routes to the protocol) before proceeding to the
-new state. There are the following core states:
-
-<descrip>
-       <tag/FS_HUNGRY/ The protocol is down, it doesn't have any routes and
-       doesn't want them.
-       <tag/FS_FEEDING/ The protocol has reached the <tt/PS_UP/ state, but
-       we are still busy sending the initial set of routes to it.
-       <tag/FS_HAPPY/ The protocol is up and has complete routing information.
-       <tag/FS_FLUSHING/ The protocol is shutting down (it's in either <tt/PS_STOP/
-       or <tt/PS_DOWN/ state) and we're flushing all of its routes from the
-       routing tables.
-</descrip>
-
 <sect1>Functions of the protocol module
 
 <p>The protocol module provides the following functions:
index 8c49154ffc656d11c932bd21b04c0007cb93af9c..41e31a8078ce11ec304321e6bce097301a027e1e 100644 (file)
@@ -11,7 +11,9 @@
 
 #include "lib/lists.h"
 #include "lib/resource.h"
+#include "lib/event.h"
 #include "lib/timer.h"
+#include "nest/route.h"
 #include "conf/conf.h"
 
 struct iface;
@@ -22,13 +24,16 @@ struct neighbor;
 struct rta;
 struct network;
 struct proto_config;
+struct channel_limit;
+struct channel_config;
 struct config;
 struct proto;
-struct event;
+struct channel;
 struct ea_list;
 struct eattr;
 struct symbol;
 
+
 /*
  *     Routing Protocol
  */
@@ -39,9 +44,10 @@ struct protocol {
   char *template;                      /* Template for automatic generation of names */
   int name_counter;                    /* Counter for automatic name generation */
   int attr_class;                      /* Attribute class known to this protocol */
-  int multitable;                      /* Protocol handles all announce hooks itself */
   uint preference;                     /* Default protocol preference */
-  uint config_size;                    /* Size of protocol config */
+  uint channel_mask;                   /* Mask of accepted channel types (NB_*) */
+  uint proto_size;                     /* Size of protocol data structure */
+  uint config_size;                    /* Size of protocol config data structure */
 
   void (*preconfig)(struct protocol *, struct config *);       /* Just before configuring */
   void (*postconfig)(struct proto_config *);                   /* After configuring each instance */
@@ -62,7 +68,6 @@ struct protocol {
 void protos_build(void);
 void proto_build(struct protocol *);
 void protos_preconfig(struct config *);
-void protos_postconfig(struct config *);
 void protos_commit(struct config *new, struct config *old, int force_restart, int type);
 void protos_dump_all(void);
 
@@ -90,16 +95,12 @@ struct proto_config {
   char *name;
   char *dsc;
   int class;                           /* SYM_PROTO or SYM_TEMPLATE */
+  u8 net_type;                         /* Protocol network type (NET_*), 0 for undefined */
+  u8 disabled;                         /* Protocol enabled/disabled by default */
   u32 debug, mrtdump;                  /* Debugging bitfields, both use D_* constants */
-  unsigned preference, disabled;       /* Generic parameters */
-  int in_keep_filtered;                        /* Routes rejected in import filter are kept */
   u32 router_id;                       /* Protocol specific router ID */
-  struct rtable_config *table;         /* Table we're attached to */
-  struct filter *in_filter, *out_filter; /* Attached filters */
-  struct proto_limit *rx_limit;                /* Limit for receiving routes from protocol
-                                          (relevant when in_keep_filtered is active) */
-  struct proto_limit *in_limit;                /* Limit for importing routes from protocol */
-  struct proto_limit *out_limit;       /* Limit for exporting routes to protocol */
+
+  list channels;                       /* List of channel configs (struct channel_config) */
 
   /* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
 
@@ -111,7 +112,6 @@ 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 that are preferred, sum over all routing tables */
   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 */
@@ -133,36 +133,34 @@ struct proto_stats {
 };
 
 struct proto {
-  node n;                              /* Node in *_proto_list */
-  node glob_node;                      /* Node in global proto_list */
+  node n;                              /* 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) */
   pool *pool;                          /* Pool containing local objects */
-  struct event *attn;                  /* "Pay attention" event */
+  event *event;                                /* Protocol event */
+
+  list channels;                       /* List of channels to rtables (struct channel) */
+  struct channel *main_channel;                /* Primary channel */
+  struct rte_src *main_source;         /* Primary route source */
 
   char *name;                          /* Name of this instance (== cf->name) */
   u32 debug;                           /* Debugging flags */
   u32 mrtdump;                         /* MRTDump flags */
-  unsigned preference;                 /* Default route preference */
-  byte accept_ra_types;                        /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */
+  uint active_channels;                        /* Number of active channels */
+  byte net_type;                       /* Protocol network type (NET_*), 0 for undefined */
   byte disabled;                       /* Manually disabled */
   byte proto_state;                    /* Protocol state machine (PS_*, see below) */
-  byte core_state;                     /* Core state machine (FS_*, see below) */
-  byte export_state;                   /* Route export state (ES_*, see below) */
+  byte active;                         /* From PS_START to cleanup after PS_STOP */
+  byte do_start;                       /* Start actions are scheduled */
+  byte do_stop;                                /* Stop actions are scheduled */
   byte reconfiguring;                  /* We're shutting down due to reconfiguration */
-  byte refeeding;                      /* We are refeeding (valid only if export_state == ES_FEEDING) */
-  byte flushing;                       /* Protocol is flushed in current flush loop round */
   byte gr_recovery;                    /* Protocol should participate in graceful restart recovery */
-  byte gr_lock;                                /* Graceful restart mechanism should wait for this proto */
-  byte gr_wait;                                /* Route export to protocol is postponed until graceful restart */
   byte down_sched;                     /* Shutdown is scheduled for later (PDS_*) */
   byte down_code;                      /* Reason for shutdown (PDC_* codes) */
-  byte merge_limit;                    /* Maximal number of nexthops for RA_MERGED */
   u32 hash_key;                                /* Random key used for hashing of neighbors */
   bird_clock_t last_state_change;      /* Time of last state transition */
   char *last_state_name_announced;     /* Last state name we've announced to the user */
-  struct proto_stats stats;            /* Current protocol statistics */
 
   /*
    *   General protocol hooks:
@@ -177,11 +175,11 @@ struct proto {
    *                   It can construct a new rte, add private attributes and
    *                   decide whether the route shall be imported: 1=yes, -1=no,
    *                   0=process it through the import filter set by the user.
-   *      reload_routes   Request protocol to reload all its routes to the core
+   *      reload_routes   Request channel to reload all its routes to the core
    *                   (using rte_update()). Returns: 0=reload cannot be done,
    *                   1= reload is scheduled and will happen (asynchronously).
-   *      feed_begin   Notify protocol about beginning of route feeding.
-   *      feed_end     Notify protocol about finish of route feeding.
+   *      feed_begin   Notify channel about beginning of route feeding.
+   *      feed_end     Notify channel about finish of route feeding.
    */
 
   void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
@@ -191,9 +189,9 @@ struct proto {
   struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
   void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs);
   int (*import_control)(struct proto *, struct rte **rt, struct ea_list **attrs, struct linpool *pool);
-  int (*reload_routes)(struct proto *);
-  void (*feed_begin)(struct proto *, int initial);
-  void (*feed_end)(struct proto *);
+  void (*reload_routes)(struct channel *);
+  void (*feed_begin)(struct channel *, int initial);
+  void (*feed_end)(struct channel *);
 
   /*
    *   Routing entry hooks (called only for routes belonging to this protocol):
@@ -213,14 +211,6 @@ struct proto {
   void (*rte_insert)(struct network *, struct rte *);
   void (*rte_remove)(struct network *, struct rte *);
 
-  struct rtable *table;                        /* Our primary routing table */
-  struct rte_src *main_source;         /* Primary route source */
-  struct announce_hook *main_ahook;    /* Primary announcement hook */
-  struct announce_hook *ahooks;                /* Announcement hooks for this protocol */
-
-  struct fib_iterator *feed_iterator;  /* Routing table iterator used during protocol feeding */
-  struct announce_hook *feed_ahook;    /* Announce hook we currently feed */
-
   /* Hic sunt protocol-specific data */
 };
 
@@ -244,25 +234,20 @@ struct proto_spec {
 #define PDC_OUT_LIMIT_HIT      0x23    /* Route export limit reached */
 
 
-void *proto_new(struct proto_config *, unsigned size);
+void *proto_new(struct proto_config *);
 void *proto_config_new(struct protocol *, int class);
 void proto_copy_config(struct proto_config *dest, struct proto_config *src);
-void proto_request_feeding(struct proto *p);
-
-static inline void
-proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned size)
-{ memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); }
 
 void graceful_restart_recovery(void);
 void graceful_restart_init(void);
 void graceful_restart_show_status(void);
-void proto_graceful_restart_lock(struct proto *p);
-void proto_graceful_restart_unlock(struct proto *p);
+void channel_graceful_restart_lock(struct channel *c);
+void channel_graceful_restart_unlock(struct channel *c);
 
 #define DEFAULT_GR_WAIT        240
 
-void proto_show_limit(struct proto_limit *l, const char *dsc);
-void proto_show_basic_info(struct proto *p);
+void channel_show_limit(struct channel_limit *l, const char *dsc);
+void channel_show_info(struct channel *c);
 
 void proto_cmd_show(struct proto *, uint, int);
 void proto_cmd_disable(struct proto *, uint, int);
@@ -285,7 +270,10 @@ proto_get_router_id(struct proto_config *pc)
   return pc->router_id ? pc->router_id : pc->global->router_id;
 }
 
-extern list active_proto_list;
+/* Moved from route.h to avoid dependency conflicts */
+static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_channel, net, new, p->main_source); }
+
+extern list proto_list;
 
 /*
  *  Each protocol instance runs two different state machines:
@@ -361,16 +349,6 @@ void proto_notify_state(struct proto *p, unsigned state);
  *     as a result of received ROUTE-REFRESH request).
  */
 
-#define FS_HUNGRY      0
-#define FS_FEEDING     1       /* obsolete */
-#define FS_HAPPY       2
-#define FS_FLUSHING    3
-
-
-#define ES_DOWN                0
-#define ES_FEEDING     1
-#define ES_READY       2
-
 
 
 /*
@@ -413,6 +391,7 @@ extern struct proto_config *cf_dev_proto;
 #define PLD_OUT                2       /* Export limit */
 #define PLD_MAX                3
 
+#define PLA_NONE       0       /* No limit */
 #define PLA_WARN       1       /* Issue log warning */
 #define PLA_BLOCK      2       /* Block new routes */
 #define PLA_RESTART    4       /* Force protocol restart */
@@ -422,42 +401,176 @@ extern struct proto_config *cf_dev_proto;
 #define PLS_ACTIVE     1       /* Limit was hit */
 #define PLS_BLOCKED    2       /* Limit is active and blocking new routes */
 
-struct proto_limit {
+struct channel_limit {
   u32 limit;                   /* Maximum number of prefixes */
-  byte action;                 /* Action to take (PLA_*) */
-  byte state;                  /* State of limit (PLS_*) */
+  u8 action;                   /* Action to take (PLA_*) */
+  u8 state;                    /* State of limit (PLS_*) */
 };
 
-void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count);
-void proto_verify_limits(struct announce_hook *ah);
-
-static inline void
-proto_reset_limit(struct proto_limit *l)
-{
-  if (l)
-    l->state = PLS_INITIAL;
-}
+void channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count);
 
 
 /*
- *     Route Announcement Hook
+ *     Channels
  */
 
-struct announce_hook {
+struct channel_class {
+  uint channel_size;                   /* Size of channel data structure */
+  uint config_size;                    /* Size of channel config data structure */
+
+  struct channel * (*init)(struct channel *, struct channel_config *); /* Create new instance */
+  int (*reconfigure)(struct channel *, struct channel_config *);       /* Try to reconfigure instance, returns success */
+  int (*start)(struct channel *);      /* Start the instance */
+  int (*shutdown)(struct channel *);   /* Stop the instance */
+
+  void (*copy_config)(struct channel_config *, struct channel_config *); /* Copy config from given channel instance */
+#if 0
+  void (*preconfig)(struct protocol *, struct config *);       /* Just before configuring */
+  void (*postconfig)(struct proto_config *);                   /* After configuring each instance */
+
+
+  void (*dump)(struct proto *);                        /* Debugging dump */
+  void (*dump_attrs)(struct rte *);            /* Dump protocol-dependent attributes */
+  void (*cleanup)(struct proto *);             /* Called after shutdown when protocol became hungry/down */
+  void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
+  void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); /* Get route information (for `show route' command) */
+  int (*get_attr)(struct eattr *, byte *buf, int buflen);      /* ASCIIfy dynamic attribute (returns GA_*) */
+  void (*show_proto_info)(struct proto *);     /* Show protocol info (for `show protocols all' command) */
+
+#endif
+};
+
+struct channel_config {
   node n;
-  struct rtable *table;
+  const char *name;
+  const struct channel_class *channel;
+
+  struct rtable_config *table;         /* Table we're attached to */
+  struct filter *in_filter, *out_filter; /* Attached filters */
+  struct channel_limit rx_limit;       /* Limit for receiving routes from protocol
+                                          (relevant when in_keep_filtered is active) */
+  struct channel_limit in_limit;       /* Limit for importing routes from protocol */
+  struct channel_limit out_limit;      /* Limit for exporting routes to protocol */
+
+  u8 net_type;                         /* Routing table network type (NET_*), 0 for undefined */
+  u8 ra_mode;                          /* Mode of received route advertisements (RA_*) */
+  u16 preference;                      /* Default route preference */
+  u8 merge_limit;                      /* Maximal number of nexthops for RA_MERGED */
+  u8 in_keep_filtered;                 /* Routes rejected in import filter are kept */
+};
+
+struct channel {
+  node n;                              /* Node in proto->channels */
+  node table_node;                     /* Node in table->channels */
+
+  const char *name;                    /* Channel name (may be NULL) */
+  const struct channel_class *channel;
   struct proto *proto;
+
+  struct rtable *table;
   struct filter *in_filter;            /* Input filter */
   struct filter *out_filter;           /* Output filter */
-  struct proto_limit *rx_limit;                /* Receive limit (for in_keep_filtered) */
-  struct proto_limit *in_limit;                /* Input limit */
-  struct proto_limit *out_limit;       /* Output limit */
-  struct proto_stats *stats;           /* Per-table protocol statistics */
-  struct announce_hook *next;          /* Next hook for the same protocol */
-  int in_keep_filtered;                        /* Routes rejected in import filter are kept */
+  struct channel_limit rx_limit;       /* Receive limit (for in_keep_filtered) */
+  struct channel_limit in_limit;       /* Input limit */
+  struct channel_limit out_limit;      /* Output limit */
+
+  struct event *feed_event;            /* Event responsible for feeding */
+  struct fib_iterator feed_fit;                /* Routing table iterator used during feeding */
+  struct proto_stats stats;            /* Per-channel protocol statistics */
+
+  u8 net_type;                         /* Routing table network type (NET_*), 0 for undefined */
+  u8 ra_mode;                          /* Mode of received route advertisements (RA_*) */
+  u16 preference;                      /* Default route preference */
+  u8 merge_limit;                      /* Maximal number of nexthops for RA_MERGED */
+  u8 in_keep_filtered;                 /* Routes rejected in import filter are kept */
+  u8 disabled;
+
+  u8 channel_state;
+  u8 export_state;                     /* Route export state (ES_*, see below) */
+  u8 feed_active;
+  u8 flush_active;
+  u8 refeeding;                                /* We are refeeding (valid only if export_state == ES_FEEDING) */
+  u8 reloadable;                       /* Hook reload_routes() is allowed on the channel */
+  u8 gr_lock;                          /* Graceful restart mechanism should wait for this channel */
+  u8 gr_wait;                          /* Route export to channel is postponed until graceful restart */
+
+  bird_clock_t last_state_change;      /* Time of last state transition */
 };
 
-struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats);
-struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable *t);
+
+/*
+ * Channel states
+ *
+ * CS_DOWN - The initial and the final state of a channel. There is no route
+ * exchange between the protocol and the table. Channel is not counted as
+ * active. Channel keeps a ptr to the table, but do not lock the table and is
+ * not linked in the table. Generally, new closed channels are created in
+ * protocols' init() hooks. The protocol is expected to explicitly activate its
+ * channels (by calling channel_init() or channel_open()).
+ *
+ * CS_START - The channel as a connection between the protocol and the table is
+ * initialized (counted as active by the protocol, linked in the table and keeps
+ * the table locked), but there is no current route exchange. There still may be
+ * routes associated with the channel in the routing table if the channel falls
+ * to CS_START from CS_UP. Generally, channels are initialized in protocols'
+ * start() hooks when going to PS_START.
+ *
+ * CS_UP - The channel is initialized and the route exchange is allowed. Note
+ * that even in CS_UP state, route export may still be down (ES_DOWN) by the
+ * core decision (e.g. waiting for table convergence after graceful restart).
+ * I.e., the protocol decides to open the channel but the core decides to start
+ * route export. Route import (caused by rte_update() from the protocol) is not
+ * restricted by that and is on volition of the protocol. Generally, channels
+ * are opened in protocols' start() hooks when going to PS_UP.
+ *
+ * CS_FLUSHING - The transitional state between initialized channel and closed
+ * channel. The channel is still initialized, but no route exchange is allowed.
+ * Instead, the associated table is running flush loop to remove routes imported
+ * through the channel. After that, the channel changes state to CS_DOWN and
+ * is detached from the table (the table is unlocked and the channel is unlinked
+ * from it). Unlike other states, the CS_FLUSHING state is not explicitly
+ * entered or left by the protocol. A protocol may request to close a channel
+ * (by calling channel_close()), which causes the channel to change state to
+ * CS_FLUSHING and later to CS_DOWN. Also note that channels are closed
+ * automatically by the core when the protocol is going down.
+ *
+ * Allowed transitions:
+ *
+ * CS_DOWN     -> CS_START / CS_UP
+ * CS_START    -> CS_UP / CS_FLUSHING
+ * CS_UP       -> CS_START / CS_FLUSHING
+ * CS_FLUSHING -> CS_DOWN (automatic)
+ */
+
+#define CS_DOWN                0
+#define CS_START       1
+#define CS_UP          2
+#define CS_FLUSHING    3
+
+#define ES_DOWN                0
+#define ES_FEEDING     1
+#define ES_READY       2
+
+
+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)
+{ struct channel_config *cc = HEAD(pc->channels); return NODE_VALID(cc) ? cc : NULL; }
+
+struct channel *proto_find_channel_by_table(struct proto *p, struct rtable *t);
+struct channel *proto_add_channel(struct proto *p, struct channel_config *cf);
+int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf);
+
+void channel_set_state(struct channel *c, uint state);
+
+/*
+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); }
+static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
+*/
+
+void channel_request_feeding(struct channel *c);
+void *channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto);
+int channel_reconfigure(struct channel *c, struct channel_config *cf);
+
 
 #endif
index eba3d9b051ecd9ef87941ec3facef9c81568cce2..b68f614f42b1ec5c5f0997c15376075586b8c495 100644 (file)
 #include "lib/lists.h"
 #include "lib/resource.h"
 #include "lib/timer.h"
-#include "nest/protocol.h"
+//#include "nest/protocol.h"
 
+struct ea_list;
 struct protocol;
 struct proto;
+struct rte_src;
 struct symbol;
 struct filter;
 struct cli;
@@ -57,8 +59,8 @@ struct fib {
   uint hash_order;                     /* Binary logarithm of hash_size */
   uint hash_shift;                     /* 32 - hash_order */
   uint addr_type;                      /* Type of address data stored in fib (NET_*) */
-  uint node_size;      /* XXXX */
-  uint node_offset;    /* XXXX */
+  uint node_size;                      /* FIB node size, 0 for nonuniform */
+  uint node_offset;                    /* Offset of fib_node struct inside of user data */
   uint entries;                                /* Number of entries */
   uint entries_min, entries_max;       /* Entry count limits (else start rehashing) */
   fib_init_fn init;                    /* Constructor */
@@ -146,7 +148,7 @@ typedef struct rtable {
   node n;                              /* Node in list of all tables */
   struct fib fib;
   char *name;                          /* Name of this table */
-  list hooks;                          /* List of announcement hooks */
+  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 */
@@ -159,7 +161,6 @@ typedef struct rtable {
   struct event *rt_event;              /* Routing table event */
   int gc_counter;                      /* Number of operations since last GC */
   bird_clock_t gc_time;                        /* Time of last GC */
-  byte gc_scheduled;                   /* GC is scheduled */
   byte prune_state;                    /* Table prune state, 1 -> scheduled, 2-> running */
   byte hcu_scheduled;                  /* Hostcache update is scheduled */
   byte nhu_state;                      /* Next Hop Update state */
@@ -167,10 +168,6 @@ typedef struct rtable {
   struct fib_iterator nhu_fit;         /* Next Hop Update FIB iterator */
 } rtable;
 
-#define RPS_NONE       0
-#define RPS_SCHEDULED  1
-#define RPS_RUNNING    2
-
 typedef struct network {
   struct rte *routes;                  /* Available routes for this network */
   struct fib_node n;                   /* FIB flags reserved for kernel syncer */
@@ -206,7 +203,7 @@ struct hostentry {
 typedef struct rte {
   struct rte *next;
   net *net;                            /* Network this RTE belongs to */
-  struct announce_hook *sender;                /* Announce hook used to send the route to the routing table */
+  struct channel *sender;              /* Channel used to send the route to the routing table */
   struct rta *attrs;                   /* Attributes of this route */
   byte flags;                          /* Flags (REF_...) */
   byte pflags;                         /* Protocol-specific flags */
@@ -279,13 +276,14 @@ static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) f
 
 rte *rte_find(net *net, struct rte_src *src);
 rte *rte_get_temp(struct rta *);
-void rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src);
-static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_ahook, net, new, p->main_source); }
+void rte_update2(struct channel *c, net *net, rte *new, struct rte_src *src);
+/* rte_update() moved to protocol.h to avoid dependency conflicts */
 void rte_discard(rtable *tab, rte *old);
 int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
-rte *rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, struct ea_list **tmpa, int silent);
-void rt_refresh_begin(rtable *t, struct announce_hook *ah);
-void rt_refresh_end(rtable *t, struct announce_hook *ah);
+rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, int silent);
+void rt_refresh_begin(rtable *t, struct channel *c);
+void rt_refresh_end(rtable *t, struct channel *c);
+void rt_schedule_prune(rtable *t);
 void rte_dump(rte *);
 void rte_free(rte *);
 rte *rte_do_cow(rte *);
@@ -293,19 +291,10 @@ static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r
 rte *rte_cow_rta(rte *r, linpool *lp);
 void rt_dump(rtable *);
 void rt_dump_all(void);
-int rt_feed_baby(struct proto *p);
-void rt_feed_baby_abort(struct proto *p);
-int rt_prune_loop(void);
+int rt_feed_channel(struct channel *c);
+void rt_feed_channel_abort(struct channel *c);
 struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
 
-static inline void
-rt_mark_for_prune(rtable *tab)
-{
-  if (tab->prune_state == RPS_RUNNING)
-    fit_get(&tab->fib, &tab->prune_fit);
-
-  tab->prune_state = RPS_SCHEDULED;
-}
 
 struct rt_show_data {
   net_addr *addr;
@@ -315,6 +304,7 @@ struct rt_show_data {
   struct fib_iterator fit;
   struct proto *show_protocol;
   struct proto *export_protocol;
+  struct channel *export_channel;
   int export_mode, primary_only, filtered;
   struct config *running_on_config;
   int net_counter, rt_counter, show_counter;
@@ -561,7 +551,6 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX];
 #define DEF_PREF_OSPF          150     /* OSPF intra-area, inter-area and type 1 external routes */
 #define DEF_PREF_RIP           120     /* RIP */
 #define DEF_PREF_BGP           100     /* BGP */
-#define DEF_PREF_PIPE          70      /* Routes piped from other tables */
 #define DEF_PREF_INHERITED     10      /* Routes inherited from other routing daemons */
 
 /*
index f94933d5256b4b4583f2eee6f8528cd5d8ebd245..17737853a7537b53a2e0df3df96d96a27f5630e9 100644 (file)
 #include "lib/resource.h"
 #include "lib/string.h"
 
+
 static void
-dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
+dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
 {
-  struct rt_dev_config *P = (void *) p->cf;
+  struct rt_dev_proto *p = (void *) P;
+  struct rt_dev_config *cf = (void *) P->cf;
+  struct channel *c;
 
-  if (!EMPTY_LIST(P->iface_list) &&
-      !iface_patt_find(&P->iface_list, ad->iface, ad->iface->addr))
+  if (!EMPTY_LIST(cf->iface_list) &&
+      !iface_patt_find(&cf->iface_list, ad->iface, ad->iface->addr))
     /* Empty list is automagically treated as "*" */
     return;
 
@@ -40,12 +43,22 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
   if (ad->scope <= SCOPE_LINK)
     return;
 
-  if (c & IF_CHANGE_DOWN)
+  if (ad->prefix.type == NET_IP4)
+    c = p->ip4_channel;
+  else if (ad->prefix.type == NET_IP6)
+    c = p->ip6_channel;
+  else
+    return;
+
+  if (!c)
+    return;
+
+  if (flags & IF_CHANGE_DOWN)
     {
       net *n;
 
       DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip);
-      n = net_find(p->table, &ad->prefix);
+      n = net_find(c->table, &ad->prefix);
       if (!n)
        {
          DBG("dev_if_notify: device shutdown: prefix not found\n");
@@ -53,10 +66,10 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
        }
 
       /* Use iface ID as local source ID */
-      struct rte_src *src = rt_get_source(p, ad->iface->index);
-      rte_update2(p->main_ahook, n, NULL, src);
+      struct rte_src *src = rt_get_source(P, ad->iface->index);
+      rte_update2(c, n, NULL, src);
     }
-  else if (c & IF_CHANGE_UP)
+  else if (flags & IF_CHANGE_UP)
     {
       rta *a;
       net *n;
@@ -65,7 +78,7 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
       DBG("dev_if_notify: %s:%I going up\n", ad->iface->name, ad->ip);
 
       /* Use iface ID as local source ID */
-      struct rte_src *src = rt_get_source(p, ad->iface->index);
+      struct rte_src *src = rt_get_source(P, ad->iface->index);
 
       rta a0 = {
        .src = src,
@@ -77,37 +90,51 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
       };
 
       a = rta_lookup(&a0);
-      n = net_get(p->table, &ad->prefix);
+      n = net_get(c->table, &ad->prefix);
       e = rte_get_temp(a);
       e->net = n;
       e->pflags = 0;
-      rte_update2(p->main_ahook, n, e, src);
+      rte_update2(c, n, e, src);
     }
 }
 
 static struct proto *
-dev_init(struct proto_config *c)
+dev_init(struct proto_config *CF)
 {
-  struct proto *p = proto_new(c, sizeof(struct proto));
+  struct proto *P = proto_new(CF);
+  struct rt_dev_proto *p = (void *) P;
+  // struct rt_dev_config *cf = (void *) CF;
 
-  p->ifa_notify = dev_ifa_notify;
-  return p;
+  proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4));
+  proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
+
+  P->ifa_notify = dev_ifa_notify;
+
+  return P;
 }
 
 static int
-dev_reconfigure(struct proto *p, struct proto_config *new)
+dev_reconfigure(struct proto *P, struct proto_config *CF)
 {
-  struct rt_dev_config *o = (struct rt_dev_config *) p->cf;
-  struct rt_dev_config *n = (struct rt_dev_config *) new;
+  struct rt_dev_proto *p = (void *) P;
+  struct rt_dev_config *o = (void *) P->cf;
+  struct rt_dev_config *n = (void *) CF;
+
+  if (!iface_patts_equal(&o->iface_list, &n->iface_list, NULL))
+    return 0;
+
+  return
+    proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) &&
+    proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
 
-  return iface_patts_equal(&o->iface_list, &n->iface_list, NULL);
+  return 1;
 }
 
 static void
 dev_copy_config(struct proto_config *dest, struct proto_config *src)
 {
-  struct rt_dev_config *d = (struct rt_dev_config *) dest;
-  struct rt_dev_config *s = (struct rt_dev_config *) src;
+  struct rt_dev_config *d = (void *) dest;
+  struct rt_dev_config *s = (void *) src;
 
   /*
    * We copy iface_list as ifaces can be shared by more direct protocols.
@@ -120,7 +147,9 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
 struct protocol proto_device = {
   .name =              "Direct",
   .template =          "direct%d",
-  .preference =        DEF_PREF_DIRECT,
+  .preference =                DEF_PREF_DIRECT,
+  .channel_mask =      NB_IP,
+  .proto_size =                sizeof(struct rt_dev_proto),
   .config_size =       sizeof(struct rt_dev_config),
   .init =              dev_init,
   .reconfigure =       dev_reconfigure,
index c36d074238c99f0cf48fa55f4e2078923100ef3f..c901233620ae73afdf3561b48c4d621579e3b508 100644 (file)
@@ -14,4 +14,10 @@ struct rt_dev_config {
   list iface_list;             /* list of struct iface_patt */
 };
 
+struct rt_dev_proto {
+  struct proto p;
+  struct channel *ip4_channel;
+  struct channel *ip6_channel;
+};
+
 #endif
index f164ecd9987114b47c03aef65d63191bd575ec09..88f60bdfa8fb67c94e2c593639870f7cac0b88de 100644 (file)
@@ -55,9 +55,7 @@ static void rt_free_hostcache(rtable *tab);
 static void rt_notify_hostcache(rtable *tab, net *net);
 static void rt_update_hostcache(rtable *tab);
 static void rt_next_hop_update(rtable *tab);
-static inline int rt_prune_table(rtable *tab);
-static inline void rt_schedule_gc(rtable *tab);
-static inline void rt_schedule_prune(rtable *tab);
+static inline void rt_prune_table(rtable *tab);
 
 
 static inline struct ea_list *
@@ -230,7 +228,7 @@ rte_get_temp(rta *a)
 
   e->attrs = a;
   e->flags = 0;
-  e->pref = a->src->proto->preference;
+  e->pref = 0;
   return e;
 }
 
@@ -349,11 +347,11 @@ rte_trace_out(uint flag, struct proto *p, rte *e, char *msg)
 }
 
 static rte *
-export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, int silent)
+export_filter(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, int silent)
 {
-  struct proto *p = ah->proto;
-  struct filter *filter = ah->out_filter;
-  struct proto_stats *stats = ah->stats;
+  struct proto *p = c->proto;
+  struct filter *filter = c->out_filter;
+  struct proto_stats *stats = &c->stats;
   ea_list *tmpb = NULL;
   rte *rt;
   int v;
@@ -409,10 +407,10 @@ export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa,
 }
 
 static void
-do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
+do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
 {
-  struct proto *p = ah->proto;
-  struct proto_stats *stats = ah->stats;
+  struct proto *p = c->proto;
+  struct proto_stats *stats = &c->stats;
 
 
   /*
@@ -442,11 +440,11 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm
    * also non-new updates (contrary to import blocking).
    */
 
-  struct proto_limit *l = ah->out_limit;
-  if (l && new)
+  struct channel_limit *l = &c->out_limit;
+  if (l->action && new)
     {
       if ((!old || refeed) && (stats->exp_routes >= l->limit))
-       proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes);
+       channel_notify_limit(c, l, PLD_OUT, stats->exp_routes);
 
       if (l->state == PLS_BLOCKED)
        {
@@ -483,25 +481,24 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm
        rte_trace_out(D_ROUTES, p, old, "removed");
     }
   if (!new)
-    p->rt_notify(p, ah->table, net, NULL, old, NULL);
+    p->rt_notify(p, c->table, net, NULL, old, NULL);
   else if (tmpa)
     {
       ea_list *t = tmpa;
       while (t->next)
        t = t->next;
       t->next = new->attrs->eattrs;
-      p->rt_notify(p, ah->table, net, new, old, tmpa);
+      p->rt_notify(p, c->table, net, new, old, tmpa);
       t->next = NULL;
     }
   else
-    p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
+    p->rt_notify(p, c->table, net, new, old, new->attrs->eattrs);
 }
 
 static void
-rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int refeed)
+rt_notify_basic(struct channel *c, net *net, rte *new0, rte *old0, int refeed)
 {
-  struct proto *p = ah->proto;
-  struct proto_stats *stats = ah->stats;
+  struct proto *p = c->proto;
 
   rte *new = new0;
   rte *old = old0;
@@ -510,9 +507,9 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int re
   ea_list *tmpa = NULL;
 
   if (new)
-    stats->exp_updates_received++;
+    c->stats.exp_updates_received++;
   else
-    stats->exp_withdraws_received++;
+    c->stats.exp_withdraws_received++;
 
   /*
    * This is a tricky part - we don't know whether route 'old' was
@@ -535,10 +532,10 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int re
    */
 
   if (new)
-    new = export_filter(ah, new, &new_free, &tmpa, 0);
+    new = export_filter(c, new, &new_free, &tmpa, 0);
 
   if (old && !refeed)
-    old = export_filter(ah, old, &old_free, NULL, 1);
+    old = export_filter(c, old, &old_free, NULL, 1);
 
   if (!new && !old)
   {
@@ -555,13 +552,13 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int re
 
 #ifdef CONFIG_PIPE
     if ((p->proto == &proto_pipe) && !new0 && (p != old0->sender->proto))
-      p->rt_notify(p, ah->table, net, NULL, old0, NULL);
+      p->rt_notify(p, c->table, net, NULL, old0, NULL);
 #endif
 
     return;
   }
 
-  do_rt_notify(ah, net, new, old, tmpa, refeed);
+  do_rt_notify(c, net, new, old, tmpa, refeed);
 
   /* Discard temporary rte's */
   if (new_free)
@@ -571,10 +568,9 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int re
 }
 
 static void
-rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old, int feed)
+rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_changed, rte *before_old, int feed)
 {
-  // struct proto *p = ah->proto;
-  struct proto_stats *stats = ah->stats;
+  // struct proto *p = c->proto;
 
   rte *r;
   rte *new_best = NULL;
@@ -592,14 +588,14 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
      was not valid, caller must use NULL for both old_changed and before_old. */
 
   if (new_changed)
-    stats->exp_updates_received++;
+    c->stats.exp_updates_received++;
   else
-    stats->exp_withdraws_received++;
+    c->stats.exp_withdraws_received++;
 
   /* First, find the new_best route - first accepted by filters */
   for (r=net->routes; rte_is_valid(r); r=r->next)
     {
-      if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
+      if (new_best = export_filter(c, r, &new_free, &tmpa, 0))
        break;
 
       /* Note if we walked around the position of old_changed route */
@@ -650,7 +646,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
 
   /* First case */
   if (old_meet)
-    if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
+    if (old_best = export_filter(c, old_changed, &old_free, NULL, 1))
       goto found;
 
   /* Second case */
@@ -668,18 +664,18 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
   /* Fourth case */
   for (r=r->next; rte_is_valid(r); r=r->next)
     {
-      if (old_best = export_filter(ah, r, &old_free, NULL, 1))
+      if (old_best = export_filter(c, r, &old_free, NULL, 1))
        goto found;
 
       if (r == before_old)
-       if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
+       if (old_best = export_filter(c, old_changed, &old_free, NULL, 1))
          goto found;
     }
 
   /* Implicitly, old_best is NULL and new_best is non-NULL */
 
  found:
-  do_rt_notify(ah, net, new_best, old_best, tmpa, (feed == 2));
+  do_rt_notify(c, net, new_best, old_best, tmpa, (feed == 2));
 
   /* Discard temporary rte's */
   if (new_free)
@@ -698,9 +694,9 @@ mpnh_merge_rta(struct mpnh *nhs, rta *a, int max)
 }
 
 rte *
-rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tmpa, int silent)
+rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, int silent)
 {
-  // struct proto *p = ah->proto;
+  // struct proto *p = c->proto;
   struct mpnh *nhs = NULL;
   rte *best0, *best, *rt0, *rt, *tmp;
 
@@ -710,7 +706,7 @@ rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tm
   if (!rte_is_valid(best0))
     return NULL;
 
-  best = export_filter(ah, best0, rt_free, tmpa, silent);
+  best = export_filter(c, best0, rt_free, tmpa, silent);
 
   if (!best || !rte_is_reachable(best))
     return best;
@@ -720,13 +716,13 @@ rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tm
     if (!rte_mergable(best0, rt0))
       continue;
 
-    rt = export_filter(ah, rt0, &tmp, NULL, 1);
+    rt = export_filter(c, rt0, &tmp, NULL, 1);
 
     if (!rt)
       continue;
 
     if (rte_is_reachable(rt))
-      nhs = mpnh_merge_rta(nhs, rt->attrs, ah->proto->merge_limit);
+      nhs = mpnh_merge_rta(nhs, rt->attrs, c->merge_limit);
 
     if (tmp)
       rte_free(tmp);
@@ -734,7 +730,7 @@ rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tm
 
   if (nhs)
   {
-    nhs = mpnh_merge_rta(nhs, best->attrs, ah->proto->merge_limit);
+    nhs = mpnh_merge_rta(nhs, best->attrs, c->merge_limit);
 
     if (nhs->next)
     {
@@ -752,10 +748,10 @@ rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tm
 
 
 static void
-rt_notify_merged(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed,
+rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed,
                 rte *new_best, rte*old_best, int refeed)
 {
-  // struct proto *p = ah->proto;
+  // struct proto *p = c->proto;
 
   rte *new_best_free = NULL;
   rte *old_best_free = NULL;
@@ -773,31 +769,31 @@ rt_notify_merged(struct announce_hook *ah, net *net, rte *new_changed, rte *old_
   if ((new_best == old_best) && !refeed)
   {
     new_changed = rte_mergable(new_best, new_changed) ?
-      export_filter(ah, new_changed, &new_changed_free, NULL, 1) : NULL;
+      export_filter(c, new_changed, &new_changed_free, NULL, 1) : NULL;
 
     old_changed = rte_mergable(old_best, old_changed) ?
-      export_filter(ah, old_changed, &old_changed_free, NULL, 1) : NULL;
+      export_filter(c, old_changed, &old_changed_free, NULL, 1) : NULL;
 
     if (!new_changed && !old_changed)
       return;
   }
 
   if (new_best)
-    ah->stats->exp_updates_received++;
+    c->stats.exp_updates_received++;
   else
-    ah->stats->exp_withdraws_received++;
+    c->stats.exp_withdraws_received++;
 
   /* Prepare new merged route */
   if (new_best)
-    new_best = rt_export_merged(ah, net, &new_best_free, &tmpa, 0);
+    new_best = rt_export_merged(c, net, &new_best_free, &tmpa, 0);
 
   /* Prepare old merged route (without proper merged next hops) */
   /* There are some issues with running filter on old route - see rt_notify_basic() */
   if (old_best && !refeed)
-    old_best = export_filter(ah, old_best, &old_best_free, NULL, 1);
+    old_best = export_filter(c, old_best, &old_best_free, NULL, 1);
 
   if (new_best || old_best)
-    do_rt_notify(ah, net, new_best, old_best, tmpa, refeed);
+    do_rt_notify(c, net, new_best, old_best, tmpa, refeed);
 
   /* Discard temporary rte's */
   if (new_best_free)
@@ -858,28 +854,22 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old,
   if (!old && !new)
     return;
 
-  if (type == RA_OPTIMAL)
-    {
-      if (new)
-       new->attrs->src->proto->stats.pref_routes++;
-      if (old)
-       old->attrs->src->proto->stats.pref_routes--;
+  if ((type == RA_OPTIMAL) && tab->hostcache)
+    rt_notify_hostcache(tab, net);
 
-      if (tab->hostcache)
-       rt_notify_hostcache(tab, net);
-    }
-
-  struct announce_hook *a;
-  WALK_LIST(a, tab->hooks)
+  struct channel *c; node *n;
+  WALK_LIST2(c, n, tab->channels, table_node)
     {
-      ASSERT(a->proto->export_state != ES_DOWN);
-      if (a->proto->accept_ra_types == type)
+      if (c->export_state == ES_DOWN)
+       continue;
+
+      if (c->ra_mode == type)
        if (type == RA_ACCEPTED)
-         rt_notify_accepted(a, net, new, old, before_old, 0);
+         rt_notify_accepted(c, net, new, old, before_old, 0);
        else if (type == RA_MERGED)
-         rt_notify_merged(a, net, new, old, new_best, old_best, 0);
+         rt_notify_merged(c, net, new, old, new_best, old_best, 0);
        else
-         rt_notify_basic(a, net, new, old, 0);
+         rt_notify_basic(c, net, new, old, 0);
     }
 }
 
@@ -943,11 +933,11 @@ rte_same(rte *x, rte *y)
 static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
 
 static void
-rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
+rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
 {
-  struct proto *p = ah->proto;
-  struct rtable *table = ah->table;
-  struct proto_stats *stats = ah->stats;
+  struct proto *p = c->proto;
+  struct rtable *table = c->table;
+  struct proto_stats *stats = &c->stats;
   static struct tbf rl_pipe = TBF_DEFAULT_LOG_LIMITS;
   rte *before_old = NULL;
   rte *old_best = net->routes;
@@ -1011,13 +1001,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr
   int new_ok = rte_is_ok(new);
   int old_ok = rte_is_ok(old);
 
-  struct proto_limit *l = ah->rx_limit;
-  if (l && !old && new)
+  struct channel_limit *l = &c->rx_limit;
+  if (l->action && !old && new)
     {
       u32 all_routes = stats->imp_routes + stats->filt_routes;
 
       if (all_routes >= l->limit)
-       proto_notify_limit(ah, l, PLD_RX, all_routes);
+       channel_notify_limit(c, l, PLD_RX, all_routes);
 
       if (l->state == PLS_BLOCKED)
        {
@@ -1031,11 +1021,11 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr
        }
     }
 
-  l = ah->in_limit;
-  if (l && !old_ok && new_ok)
+  l = &c->in_limit;
+  if (l->action && !old_ok && new_ok)
     {
       if (stats->imp_routes >= l->limit)
-       proto_notify_limit(ah, l, PLD_IN, stats->imp_routes);
+       channel_notify_limit(c, l, PLD_IN, stats->imp_routes);
 
       if (l->state == PLS_BLOCKED)
        {
@@ -1049,13 +1039,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr
          stats->imp_updates_ignored++;
          rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
 
-         if (ah->in_keep_filtered)
+         if (c->in_keep_filtered)
            new->flags |= REF_FILTERED;
          else
            { rte_free_quick(new); new = NULL; }
 
          /* Note that old && !new could be possible when
-            ah->in_keep_filtered changed in the recent past. */
+            c->in_keep_filtered changed in the recent past. */
 
          if (!old && !new)
            return;
@@ -1188,7 +1178,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr
   if (!net->routes &&
       (table->gc_counter++ >= table->config->gc_max_ops) &&
       (table->gc_time + table->config->gc_min_time <= now))
-    rt_schedule_gc(table);
+    rt_schedule_prune(table);
 
   if (old_ok && p->rte_remove)
     p->rte_remove(net, old);
@@ -1237,7 +1227,7 @@ rte_unhide_dummy_routes(net *net, rte **dummy)
 /**
  * rte_update - enter a new update to a routing table
  * @table: table to be updated
- * @ah: pointer to table announce hook
+ * @c: channel doing the update
  * @net: network node
  * @p: protocol submitting the update
  * @src: protocol originating the update
@@ -1277,18 +1267,23 @@ rte_unhide_dummy_routes(net *net, rte **dummy)
  */
 
 void
-rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
+rte_update2(struct channel *c, net *net, rte *new, struct rte_src *src)
 {
-  struct proto *p = ah->proto;
-  struct proto_stats *stats = ah->stats;
-  struct filter *filter = ah->in_filter;
+  struct proto *p = c->proto;
+  struct proto_stats *stats = &c->stats;
+  struct filter *filter = c->in_filter;
   ea_list *tmpa = NULL;
   rte *dummy = NULL;
 
+  ASSERT(c->channel_state == CS_UP);
+
   rte_update_lock();
   if (new)
     {
-      new->sender = ah;
+      new->sender = c;
+
+      if (!new->pref)
+       new->pref = c->preference;
 
       stats->imp_updates_received++;
       if (!rte_validate(new))
@@ -1303,7 +1298,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
          stats->imp_updates_filtered++;
          rte_trace_in(D_FILTERS, p, new, "filtered out");
 
-         if (! ah->in_keep_filtered)
+         if (! c->in_keep_filtered)
            goto drop;
 
          /* new is a private copy, i could modify it */
@@ -1321,7 +1316,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
                  stats->imp_updates_filtered++;
                  rte_trace_in(D_FILTERS, p, new, "filtered out");
 
-                 if (! ah->in_keep_filtered)
+                 if (! c->in_keep_filtered)
                    goto drop;
 
                  new->flags |= REF_FILTERED;
@@ -1348,7 +1343,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
 
  recalc:
   rte_hide_dummy_routes(net, &dummy);
-  rte_recalculate(ah, net, new, src);
+  rte_recalculate(c, net, new, src);
   rte_unhide_dummy_routes(net, &dummy);
   rte_update_unlock();
   return;
@@ -1409,25 +1404,25 @@ rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter)
 /**
  * rt_refresh_begin - start a refresh cycle
  * @t: related routing table
- * @ah: related announce hook 
+ * @c related channel
  *
  * This function starts a refresh cycle for given routing table and announce
  * hook. The refresh cycle is a sequence where the protocol sends all its valid
  * routes to the routing table (by rte_update()). After that, all protocol
- * routes (more precisely routes with @ah as @sender) not sent during the
+ * routes (more precisely routes with @c as @sender) not sent during the
  * refresh cycle but still in the table from the past are pruned. This is
  * implemented by marking all related routes as stale by REF_STALE flag in
  * rt_refresh_begin(), then marking all related stale routes with REF_DISCARD
  * flag in rt_refresh_end() and then removing such routes in the prune loop.
  */
 void
-rt_refresh_begin(rtable *t, struct announce_hook *ah)
+rt_refresh_begin(rtable *t, struct channel *c)
 {
   FIB_WALK(&t->fib, net, n)
     {
       rte *e;
       for (e = n->routes; e; e = e->next)
-       if (e->sender == ah)
+       if (e->sender == c)
          e->flags |= REF_STALE;
     }
   FIB_WALK_END;
@@ -1436,13 +1431,13 @@ rt_refresh_begin(rtable *t, struct announce_hook *ah)
 /**
  * rt_refresh_end - end a refresh cycle
  * @t: related routing table
- * @ah: related announce hook 
+ * @c: related channel
  *
- * This function starts a refresh cycle for given routing table and announce
+ * This function ends a refresh cycle for given routing table and announce
  * hook. See rt_refresh_begin() for description of refresh cycles.
  */
 void
-rt_refresh_end(rtable *t, struct announce_hook *ah)
+rt_refresh_end(rtable *t, struct channel *c)
 {
   int prune = 0;
 
@@ -1450,7 +1445,7 @@ rt_refresh_end(rtable *t, struct announce_hook *ah)
     {
       rte *e;
       for (e = n->routes; e; e = e->next)
-       if ((e->sender == ah) && (e->flags & REF_STALE))
+       if ((e->sender == c) && (e->flags & REF_STALE))
          {
            e->flags |= REF_DISCARD;
            prune = 1;
@@ -1501,10 +1496,6 @@ rt_dump(rtable *t)
        rte_dump(e);
     }
   FIB_WALK_END;
-
-  struct announce_hook *a;
-  WALK_LIST(a, t->hooks)
-    debug("\tAnnounces routes to protocol %s\n", a->proto->name);
   debug("\n");
 }
 
@@ -1522,23 +1513,6 @@ rt_dump_all(void)
     rt_dump(t);
 }
 
-static inline void
-rt_schedule_prune(rtable *tab)
-{
-  rt_mark_for_prune(tab);
-  ev_schedule(tab->rt_event);
-}
-
-static inline void
-rt_schedule_gc(rtable *tab)
-{
-  if (tab->gc_scheduled)
-    return;
-
-  tab->gc_scheduled = 1;
-  ev_schedule(tab->rt_event);
-}
-
 static inline void
 rt_schedule_hcu(rtable *tab)
 {
@@ -1559,38 +1533,17 @@ rt_schedule_nhu(rtable *tab)
   tab->nhu_state |= 1;
 }
 
-
-static void
-rt_prune_nets(rtable *tab)
+void
+rt_schedule_prune(rtable *tab)
 {
-  struct fib_iterator fit;
-  int ncnt = 0, ndel = 0;
-
-#ifdef DEBUGGING
-  fib_check(&tab->fib);
-#endif
-
-  FIB_ITERATE_INIT(&fit, &tab->fib);
-again:
-  FIB_ITERATE_START(&tab->fib, &fit, net, n)
-    {
-      ncnt++;
-      if (!n->routes)          /* Orphaned FIB entry */
-       {
-         FIB_ITERATE_PUT(&fit);
-         fib_delete(&tab->fib, n);
-         ndel++;
-         goto again;
-       }
-    }
-  FIB_ITERATE_END;
-  DBG("Pruned %d of %d networks\n", ndel, ncnt);
+  if (tab->prune_state == 0)
+    ev_schedule(tab->rt_event);
 
-  tab->gc_counter = 0;
-  tab->gc_time = now;
-  tab->gc_scheduled = 0;
+  /* state change 0->1, 2->3 */
+  tab->prune_state |= 1;
 }
 
+
 static void
 rt_event(void *ptr)
 {
@@ -1603,18 +1556,7 @@ rt_event(void *ptr)
     rt_next_hop_update(tab);
 
   if (tab->prune_state)
-    if (!rt_prune_table(tab))
-      {
-       /* Table prune unfinished */
-       ev_schedule(tab->rt_event);
-       return;
-      }
-
-  if (tab->gc_scheduled)
-    {
-      rt_prune_nets(tab);
-      rt_prune_sources(); // FIXME this should be moved to independent event
-    }
+    rt_prune_table(tab);
 }
 
 void
@@ -1625,7 +1567,8 @@ rt_setup(pool *p, rtable *t, char *name, struct rtable_config *cf)
   t->config = cf;
   t->addr_type = cf ? cf->addr_type : NET_IP4;
   fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL);
-  init_list(&t->hooks);
+  init_list(&t->channels);
+
   if (cf)
     {
       t->rt_event = ev_new(p);
@@ -1652,24 +1595,47 @@ rt_init(void)
 }
 
 
-static int
-rt_prune_step(rtable *tab, int *limit)
+/**
+ * rt_prune_table - prune a routing table
+ *
+ * The prune loop scans routing tables and removes routes belonging to flushing
+ * protocols, discarded routes and also stale network entries. It is called from
+ * rt_event(). The event is rescheduled if the current iteration do not finish
+ * the table. The pruning is directed by the prune state (@prune_state),
+ * specifying whether the prune cycle is scheduled or running, and there
+ * is also a persistent pruning iterator (@prune_fit).
+ *
+ * The prune loop is used also for channel flushing. For this purpose, the
+ * channels to flush are marked before the iteration and notified after the
+ * iteration.
+ */
+static void
+rt_prune_table(rtable *tab)
 {
   struct fib_iterator *fit = &tab->prune_fit;
+  int limit = 512;
+
+  struct channel *c;
+  node *n, *x;
 
   DBG("Pruning route table %s\n", tab->name);
 #ifdef DEBUGGING
   fib_check(&tab->fib);
 #endif
 
-  if (tab->prune_state == RPS_NONE)
-    return 1;
+  if (tab->prune_state == 0)
+    return;
 
-  if (tab->prune_state == RPS_SCHEDULED)
-    {
-      FIB_ITERATE_INIT(fit, &tab->fib);
-      tab->prune_state = RPS_RUNNING;
-    }
+  if (tab->prune_state == 1)
+  {
+    /* Mark channels to flush */
+    WALK_LIST2(c, n, tab->channels, table_node)
+      if (c->channel_state == CS_FLUSHING)
+       c->flush_active = 1;
+
+    FIB_ITERATE_INIT(fit, &tab->fib);
+    tab->prune_state = 2;
+  }
 
 again:
   FIB_ITERATE_START(&tab->fib, fit, net, n)
@@ -1678,19 +1644,21 @@ again:
 
     rescan:
       for (e=n->routes; e; e=e->next)
-       if (e->sender->proto->flushing || (e->flags & REF_DISCARD))
+       if (e->sender->flush_active || (e->flags & REF_DISCARD))
          {
-           if (*limit <= 0)
+           if (limit <= 0)
              {
                FIB_ITERATE_PUT(fit);
-               return 0;
+               ev_schedule(tab->rt_event);
+               return;
              }
 
            rte_discard(tab, e);
-           (*limit)--;
+           limit--;
 
            goto rescan;
          }
+
       if (!n->routes)          /* Orphaned FIB entry */
        {
          FIB_ITERATE_PUT(fit);
@@ -1704,61 +1672,40 @@ again:
   fib_check(&tab->fib);
 #endif
 
-  tab->prune_state = RPS_NONE;
-  return 1;
-}
+  tab->gc_counter = 0;
+  tab->gc_time = now;
 
-/**
- * rt_prune_table - prune a routing table
- *
- * This function scans the routing table @tab and removes routes belonging to
- * flushing protocols, discarded routes and also stale network entries, in a
- * similar fashion like rt_prune_loop(). Returns 1 when all such routes are
- * pruned. Contrary to rt_prune_loop(), this function is not a part of the
- * protocol flushing loop, but it is called from rt_event() for just one routing
- * table.
- *
- * Note that rt_prune_table() and rt_prune_loop() share (for each table) the
- * prune state (@prune_state) and also the pruning iterator (@prune_fit).
- */
-static inline int
-rt_prune_table(rtable *tab)
-{
-  int limit = 512;
-  return rt_prune_step(tab, &limit);
-}
+  /* state change 2->0, 3->1 */
+  tab->prune_state &= 1;
 
-/**
- * rt_prune_loop - prune routing tables
- *
- * The prune loop scans routing tables and removes routes belonging to flushing
- * protocols, discarded routes and also stale network entries. Returns 1 when
- * all such routes are pruned. It is a part of the protocol flushing loop.
- */
-int
-rt_prune_loop(void)
-{
-  int limit = 512;
-  rtable *t;
+  if (tab->prune_state > 0)
+    ev_schedule(tab->rt_event);
 
-  WALK_LIST(t, routing_tables)
-    if (! rt_prune_step(t, &limit))
-      return 0;
+  /* FIXME: This should be handled in a better way */
+  rt_prune_sources();
 
-  return 1;
+  /* Close flushed channels */
+  WALK_LIST2_DELSAFE(c, n, x, tab->channels, table_node)
+    if (c->flush_active)
+      {
+       c->flush_active = 0;
+       channel_set_state(c, CS_DOWN);
+      }
+
+  return;
 }
 
 void
 rt_preconfig(struct config *c)
 {
-  struct symbol *s = cf_get_symbol("master");
-
   init_list(&c->tables);
-  c->master_rtc = rt_new_table(s, NET_IP4);
+
+  rt_new_table(cf_get_symbol("master4"), NET_IP4);
+  rt_new_table(cf_get_symbol("master6"), NET_IP6);
 }
 
 
-/* 
+/*
  * Some functions for handing internal next hop updates
  * triggered by rt_schedule_nhu().
  */
@@ -1914,7 +1861,9 @@ struct rtable_config *
 rt_new_table(struct symbol *s, uint addr_type)
 {
   /* Hack that allows to 'redefine' the master table */
-  if ((s->class == SYM_TABLE) && (s->def == new_config->master_rtc))
+  if ((s->class == SYM_TABLE) &&
+      (s->def == new_config->def_tables[addr_type]) &&
+      ((addr_type == NET_IP4) || (addr_type == NET_IP6)))
     return s->def;
 
   struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config));
@@ -1922,9 +1871,15 @@ rt_new_table(struct symbol *s, uint addr_type)
   cf_define_symbol(s, SYM_TABLE, c);
   c->name = s->name;
   c->addr_type = addr_type;
-  add_tail(&new_config->tables, &c->n);
   c->gc_max_ops = 1000;
   c->gc_min_time = 5;
+
+  add_tail(&new_config->tables, &c->n);
+
+  /* First table of each type is kept as default */
+  if (! new_config->def_tables[addr_type])
+    new_config->def_tables[addr_type] = c;
+
   return c;
 }
 
@@ -2029,48 +1984,42 @@ rt_commit(struct config *new, struct config *old)
 }
 
 static inline void
-do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
+do_feed_channel(struct channel *c, net *n, rte *e)
 {
   rte_update_lock();
-  if (type == RA_ACCEPTED)
-    rt_notify_accepted(h, n, e, NULL, NULL, p->refeeding ? 2 : 1);
-  else if (type == RA_MERGED)
-    rt_notify_merged(h, n, NULL, NULL, e, p->refeeding ? e : NULL, p->refeeding);
-  else
-    rt_notify_basic(h, n, e, p->refeeding ? e : NULL, p->refeeding);
+  if (c->ra_mode == RA_ACCEPTED)
+    rt_notify_accepted(c, n, e, NULL, NULL, c->refeeding ? 2 : 1);
+  else if (c->ra_mode == RA_MERGED)
+    rt_notify_merged(c, n, NULL, NULL, e, c->refeeding ? e : NULL, c->refeeding);
+  else /* RA_BASIC */
+    rt_notify_basic(c, n, e, c->refeeding ? e : NULL, c->refeeding);
   rte_update_unlock();
 }
 
 /**
- * rt_feed_baby - advertise routes to a new protocol
- * @p: protocol to be fed
+ * rt_feed_channel - advertise all routes to a channel
+ * @c: channel to be fed
  *
- * This function performs one pass of advertisement of routes to a newly
- * initialized protocol. It's called by the protocol code as long as it
- * has something to do. (We avoid transferring all the routes in single
- * pass in order not to monopolize CPU time.)
+ * This function performs one pass of advertisement of routes to a channel that
+ * is in the ES_FEEDING state. It is called by the protocol code as long as it
+ * has something to do. (We avoid transferring all the routes in single pass in
+ * order not to monopolize CPU time.)
  */
 int
-rt_feed_baby(struct proto *p)
+rt_feed_channel(struct channel *c)
 {
-  struct announce_hook *h;
-  struct fib_iterator *fit;
+  struct fib_iterator *fit = &c->feed_fit;
   int max_feed = 256;
 
-  if (!p->feed_ahook)                  /* Need to initialize first */
+  ASSERT(c->export_state == ES_FEEDING);
+
+  if (!c->feed_active)
     {
-      if (!p->ahooks)
-       return 1;
-      DBG("Announcing routes to new protocol %s\n", p->name);
-      p->feed_ahook = p->ahooks;
-      fit = p->feed_iterator = mb_alloc(p->pool, sizeof(struct fib_iterator));
-      goto next_hook;
+      FIB_ITERATE_INIT(fit, &c->table->fib);
+      c->feed_active = 1;
     }
-  fit = p->feed_iterator;
 
-again:
-  h = p->feed_ahook;
-  FIB_ITERATE_START(&h->table->fib, fit, net, n)
+  FIB_ITERATE_START(&c->table->fib, fit, net, n)
     {
       rte *e = n->routes;
       if (max_feed <= 0)
@@ -2079,68 +2028,60 @@ again:
          return 0;
        }
 
-      /* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */
+      /* FIXME: perhaps we should change feed for RA_ACCEPTED to not use 'new' */
 
-      if ((p->accept_ra_types == RA_OPTIMAL) ||
-         (p->accept_ra_types == RA_ACCEPTED) ||
-         (p->accept_ra_types == RA_MERGED))
+      if ((c->ra_mode == RA_OPTIMAL) ||
+         (c->ra_mode == RA_ACCEPTED) ||
+         (c->ra_mode == RA_MERGED))
        if (rte_is_valid(e))
          {
-           if (p->export_state != ES_FEEDING)
-             return 1;  /* In the meantime, the protocol fell down. */
+           /* In the meantime, the protocol may fell down */
+           if (c->export_state != ES_FEEDING)
+             goto done;
 
-           do_feed_baby(p, p->accept_ra_types, h, n, e);
+           do_feed_channel(c, n, e);
            max_feed--;
          }
 
-      if (p->accept_ra_types == RA_ANY)
+      if (c->ra_mode == RA_ANY)
        for(e = n->routes; e; e = e->next)
          {
-           if (p->export_state != ES_FEEDING)
-             return 1;  /* In the meantime, the protocol fell down. */
+           /* In the meantime, the protocol may fell down */
+           if (c->export_state != ES_FEEDING)
+             goto done;
 
            if (!rte_is_valid(e))
              continue;
 
-           do_feed_baby(p, RA_ANY, h, n, e);
+           do_feed_channel(c, n, e);
            max_feed--;
          }
     }
   FIB_ITERATE_END;
-  p->feed_ahook = h->next;
-  if (!p->feed_ahook)
-    {
-      mb_free(p->feed_iterator);
-      p->feed_iterator = NULL;
-      return 1;
-    }
 
-next_hook:
-  h = p->feed_ahook;
-  FIB_ITERATE_INIT(fit, &h->table->fib);
-  goto again;
+done:
+  c->feed_active = 0;
+  return 1;
 }
 
 /**
  * rt_feed_baby_abort - abort protocol feeding
- * @p: protocol
+ * @c: channel
  *
- * This function is called by the protocol code when the protocol
- * stops or ceases to exist before the last iteration of rt_feed_baby()
- * has finished.
+ * This function is called by the protocol code when the protocol stops or
+ * ceases to exist during the feeding.
  */
 void
-rt_feed_baby_abort(struct proto *p)
+rt_feed_channel_abort(struct channel *c)
 {
-  if (p->feed_ahook)
+  if (c->feed_active)
     {
-      /* Unlink the iterator and exit */
-      fit_get(&p->feed_ahook->table->fib, p->feed_iterator);
-      p->feed_ahook = NULL;
+      /* Unlink the iterator */
+      fit_get(&c->table->fib, &c->feed_fit);
+      c->feed_active = 0;
     }
 }
 
-
 static inline unsigned
 ptr_hash(void *ptr)
 {
@@ -2517,21 +2458,12 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
   rte *e, *ee;
   byte ia[NET_MAX_TEXT_LENGTH+1];
   struct ea_list *tmpa;
-  struct announce_hook *a = NULL;
+  struct channel *ec = d->export_channel;
   int first = 1;
   int pass = 0;
 
   bsprintf(ia, "%N", n->n.addr);
 
-  if (d->export_mode)
-    {
-      if (! d->export_protocol->rt_notify)
-       return;
-
-      a = proto_find_announce_hook(d->export_protocol, d->table);
-      if (!a)
-       return;
-    }
 
   for (e = n->routes; e; e = e->next)
     {
@@ -2550,10 +2482,10 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
       tmpa = make_tmp_attrs(e, rte_update_pool);
 
       /* Special case for merged export */
-      if ((d->export_mode == RSEM_EXPORT) && (d->export_protocol->accept_ra_types == RA_MERGED))
+      if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
         {
          rte *rt_free;
-         e = rt_export_merged(a, n, &rt_free, &tmpa, 1);
+         e = rt_export_merged(ec, n, &rt_free, &tmpa, 1);
          pass = 1;
 
          if (!e)
@@ -2564,7 +2496,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
          struct proto *ep = d->export_protocol;
          int ic = ep->import_control ? ep->import_control(ep, &e, &tmpa, rte_update_pool) : 0;
 
-         if (ep->accept_ra_types == RA_OPTIMAL || ep->accept_ra_types == RA_MERGED)
+         if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
            pass = 1;
 
          if (ic < 0)
@@ -2578,12 +2510,12 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
               * command may change the export filter and do not update routes.
               */
              int do_export = (ic > 0) ||
-               (f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
+               (f_run(ec->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
 
              if (do_export != (d->export_mode == RSEM_EXPORT))
                goto skip;
 
-             if ((d->export_mode == RSEM_EXPORT) && (ep->accept_ra_types == RA_ACCEPTED))
+             if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_ACCEPTED))
                pass = 1;
            }
        }
@@ -2612,6 +2544,15 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
     }
 }
 
+static struct channel *
+rt_show_export_channel(struct rt_show_data *d)
+{
+  if (! d->export_protocol->rt_notify)
+    return NULL;
+
+  return proto_find_channel_by_table(d->export_protocol, d->table);
+}
+
 static void
 rt_show_cont(struct cli *c)
 {
@@ -2624,18 +2565,19 @@ rt_show_cont(struct cli *c)
   struct fib *fib = &d->table->fib;
   struct fib_iterator *it = &d->fit;
 
-  FIB_ITERATE_START(fib, it, net, n)
+  if (d->export_mode)
     {
-      if (d->running_on_config && d->running_on_config != config)
-       {
-         cli_printf(c, 8004, "Stopped due to reconfiguration");
-         goto done;
-       }
-      if (d->export_protocol && (d->export_protocol->export_state == ES_DOWN))
-       {
-         cli_printf(c, 8005, "Protocol is down");
+      /* Ensure we have current export channel */
+      d->export_channel = rt_show_export_channel(d);
+      if (!d->export_channel || (d->export_channel->export_state == ES_DOWN))
+        {
+         cli_printf(c, 8005, "Channel is down");
          goto done;
        }
+    }
+
+  FIB_ITERATE_START(fib, it, net, n)
+    {
       if (!max--)
        {
          FIB_ITERATE_PUT(it);
@@ -2661,15 +2603,29 @@ rt_show_cleanup(struct cli *c)
   fit_get(&d->table->fib, &d->fit);
 }
 
+static inline rtable *
+rt_show_get_table(struct proto *p)
+{
+  /* FIXME: Use a better way to handle multi-channel protocols */
+
+  if (p->main_channel)
+    return p->main_channel->table;
+
+  if (!EMPTY_LIST(p->channels))
+    return ((struct channel *) HEAD(p->channels))->table;
+
+  return NULL;
+}
+
 void
 rt_show(struct rt_show_data *d)
 {
   net *n;
 
   /* Default is either a master table or a table related to a respective protocol */
-  if (!d->table && d->export_protocol) d->table = d->export_protocol->table;
-  if (!d->table && d->show_protocol) d->table = d->show_protocol->table;
-  if (!d->table) d->table = config->master_rtc->table;
+  if (!d->table && d->export_protocol) d->table = rt_show_get_table(d->export_protocol);
+  if (!d->table && d->show_protocol) d->table = rt_show_get_table(d->show_protocol);
+  if (!d->table) d->table = config->def_tables[NET_IP4]->table; /* FIXME: iterate through all tables ? */
 
   /* Filtered routes are neither exported nor have sensible ordering */
   if (d->filtered && (d->export_mode || d->primary_only))
@@ -2684,6 +2640,17 @@ rt_show(struct rt_show_data *d)
     }
   else
     {
+      if (d->export_mode)
+        {
+         /* Find channel associated with the export protocol */
+         d->export_channel = rt_show_export_channel(d);
+         if (!d->export_channel || (d->export_channel->export_state == ES_DOWN))
+           {
+             cli_msg(8005, "Channel is down");
+             return;
+           }
+       }
+
       if (d->show_for)
        n = net_route(d->table, d->addr);
       else
index 110bf9319e2c875eb8ddf0044ca4e00064208815..62752e21e5f2105efb764a7b07358951f16c9495 100644 (file)
@@ -952,7 +952,7 @@ bfd_init_all(void)
 static struct proto *
 bfd_init(struct proto_config *c)
 {
-  struct proto *p = proto_new(c, sizeof(struct bfd_proto));
+  struct proto *p = proto_new(c);
 
   p->neigh_notify = bfd_neigh_notify;
 
@@ -1118,6 +1118,7 @@ bfd_show_sessions(struct proto *P)
 struct protocol proto_bfd = {
   .name =              "BFD",
   .template =          "bfd%d",
+  .proto_size =                sizeof(struct bfd_proto),
   .config_size =       sizeof(struct bfd_config),
   .init =              bfd_init,
   .start =             bfd_start,
index f549b0ed9aafadf9d39de68bbc341494c91a5ff7..cb5b108c7f5a7297e3a8df93845706bd5d99b760 100644 (file)
@@ -1372,7 +1372,6 @@ static void
 bgp_copy_config(struct proto_config *dest, struct proto_config *src)
 {
   /* Just a shallow copy */
-  proto_copy_rest(dest, src, sizeof(struct bgp_config));
 }
 
 
index 85b93a6b9e5f8dc6de62a11bb9236354b5f857a8..614ef08cd3ea29d56eeb469d0813bcf185c281a1 100644 (file)
@@ -112,12 +112,6 @@ bgp_proto:
  | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
  | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
  | bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; }
- | bgp_proto ROUTE LIMIT expr ';' {
-     this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit));
-     this_proto->in_limit->limit = $4;
-     this_proto->in_limit->action = PLA_RESTART;
-     log(L_WARN "%s: Route limit option is deprecated, use import limit", this_proto->name);
-   }
  | bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
  | bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
  | bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; }
index 881ec7818d26d13e802e8beeadfd09ffa4b81394..297774b5a39b840aec33ae151f894ce13fb7bf9e 100644 (file)
@@ -68,6 +68,10 @@ ospf_proto_finish(void)
   if (EMPTY_LIST(cf->area_list))
     cf_error( "No configured areas in OSPF");
 
+  /* Define default channel */
+  if (EMPTY_LIST(this_proto->channels))
+    channel_config_new(NULL, this_proto->net_type, this_proto);
+
   int areano = 0;
   int backbone = 0;
   int nssa = 0;
@@ -84,7 +88,7 @@ ospf_proto_finish(void)
   cf->abr = areano > 1;
 
   /* Route export or NSSA translation (RFC 3101 3.1) */
-  cf->asbr = (this_proto->out_filter != FILTER_REJECT) || (nssa && cf->abr);
+  cf->asbr = (proto_cf_main_channel(this_proto)->out_filter != FILTER_REJECT) || (nssa && cf->abr);
 
   if (cf->abr && !backbone)
   {
@@ -145,14 +149,16 @@ ospf_variant:
  | OSPF3 { $$ = 0; }
  ;
 
-ospf_proto_start: proto_start ospf_variant {
-     this_proto = proto_config_new(&proto_ospf, $1);
-     init_list(&OSPF_CFG->area_list);
-     init_list(&OSPF_CFG->vlink_list);
-     OSPF_CFG->tick = OSPF_DEFAULT_TICK;
-     OSPF_CFG->ospf2 = $2;
-  }
- ;
+ospf_proto_start: proto_start ospf_variant
+{
+  this_proto = proto_config_new(&proto_ospf, $1);
+  this_proto->net_type = $2 ? NET_IP4 : NET_IP6;
+
+  init_list(&OSPF_CFG->area_list);
+  init_list(&OSPF_CFG->vlink_list);
+  OSPF_CFG->tick = OSPF_DEFAULT_TICK;
+  OSPF_CFG->ospf2 = $2;
+};
 
 ospf_proto:
    ospf_proto_start proto_name '{'
@@ -161,6 +167,7 @@ ospf_proto:
 
 ospf_proto_item:
    proto_item
+ | proto_channel
  | RFC1583COMPAT bool { OSPF_CFG->rfc1583 = $2; }
  | STUB ROUTER bool { OSPF_CFG->stub_router = $3; }
  | ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; }
index 1c128794fdea245937bde73db421d4667e2dbbd6..d074600a12c6d821b85987f9e3aafee6f4423543 100644 (file)
 static int ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool);
 static struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
 static void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs);
-static int ospf_reload_routes(struct proto *P);
+static void ospf_reload_routes(struct channel *C);
 static int ospf_rte_better(struct rte *new, struct rte *old);
 static int ospf_rte_same(struct rte *new, struct rte *old);
 static void ospf_disp(timer *timer);
@@ -297,15 +297,16 @@ ospf_dump(struct proto *P)
 }
 
 static struct proto *
-ospf_init(struct proto_config *c)
+ospf_init(struct proto_config *CF)
 {
-  struct ospf_config *oc = (struct ospf_config *) c;
-  struct proto *P = proto_new(c, sizeof(struct ospf_proto));
+  struct ospf_config *cf = (struct ospf_config *) CF;
+  struct proto *P = proto_new(CF);
+
+  P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
 
-  P->accept_ra_types = RA_OPTIMAL;
   P->rt_notify = ospf_rt_notify;
   P->if_notify = ospf_if_notify;
-  P->ifa_notify = oc->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3;
+  P->ifa_notify = cf->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3;
   P->import_control = ospf_import_control;
   P->reload_routes = ospf_reload_routes;
   P->make_tmp_attrs = ospf_make_tmp_attrs;
@@ -389,17 +390,16 @@ ospf_schedule_rtcalc(struct ospf_proto *p)
   p->calcrt = 1;
 }
 
-static int
-ospf_reload_routes(struct proto *P)
+static void
+ospf_reload_routes(struct channel *C)
 {
-  struct ospf_proto *p = (struct ospf_proto *) P;
+  struct ospf_proto *p = (struct ospf_proto *) C->proto;
 
-  if (p->calcrt != 2)
-    OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload");
+  if (p->calcrt == 2)
+    return;
 
+  OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload");
   p->calcrt = 2;
-
-  return 1;
 }
 
 
@@ -637,17 +637,17 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
  * nonbroadcast network, cost of interface, etc.
  */
 static int
-ospf_reconfigure(struct proto *P, struct proto_config *c)
+ospf_reconfigure(struct proto *P, struct proto_config *CF)
 {
   struct ospf_proto *p = (struct ospf_proto *) P;
   struct ospf_config *old = (struct ospf_config *) (P->cf);
-  struct ospf_config *new = (struct ospf_config *) c;
+  struct ospf_config *new = (struct ospf_config *) CF;
   struct ospf_area_config *nac;
   struct ospf_area *oa, *oax;
   struct ospf_iface *ifa, *ifx;
   struct ospf_iface_patt *ip;
 
-  if (proto_get_router_id(c) != p->router_id)
+  if (proto_get_router_id(CF) != p->router_id)
     return 0;
 
   if (p->ospf2 != new->ospf2)
@@ -659,6 +659,9 @@ ospf_reconfigure(struct proto *P, struct proto_config *c)
   if (old->abr != new->abr)
     return 0;
 
+  if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
+    return 0;
+
   p->stub_router = new->stub_router;
   p->merge_external = new->merge_external;
   p->asbr = new->asbr;
@@ -1465,6 +1468,8 @@ struct protocol proto_ospf = {
   .template =          "ospf%d",
   .attr_class =                EAP_OSPF,
   .preference =                DEF_PREF_OSPF,
+  .channel_mask =      NB_IP,
+  .proto_size =                sizeof(struct ospf_proto),
   .config_size =       sizeof(struct ospf_config),
   .init =              ospf_init,
   .dump =              ospf_dump,
index 21a3e300a8097258fb2fd46c5ba7669a9c450064..0855f21fb05adbcdf690409932105028186f96b3 100644 (file)
@@ -1973,7 +1973,7 @@ again1:
 
       if (reload || ort_changed(nf, &a0))
       {
-       net *ne = net_get(p->p.table, nf->fn.addr);
+       net *ne = net_get(p->p.main_channel->table, nf->fn.addr);
        rta *a = rta_lookup(&a0);
        rte *e = rte_get_temp(a);
 
@@ -1985,7 +1985,6 @@ again1:
        e->u.ospf.router_id = nf->old_rid = nf->n.rid;
        e->pflags = 0;
        e->net = ne;
-       e->pref = p->p.preference;
 
        DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
            a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
@@ -1998,7 +1997,7 @@ again1:
       rta_free(nf->old_rta);
       nf->old_rta = NULL;
 
-      net *ne = net_get(p->p.table, nf->fn.addr);
+      net *ne = net_get(p->p.main_channel->table, nf->fn.addr);
       rte_update(&p->p, ne, NULL);
     }
 
index 8daf2e7cf6bc90860015e0f1f3e7e640e3c12dc2..f51ee5751b552a858c51a7bb15930feaf034f6e8 100644 (file)
@@ -16,28 +16,25 @@ CF_DEFINES
 
 CF_DECLS
 
-CF_KEYWORDS(PIPE, PEER, TABLE, MODE, OPAQUE, TRANSPARENT)
+CF_KEYWORDS(PIPE, PEER, TABLE)
 
 CF_GRAMMAR
 
-CF_ADDTO(proto, pipe_proto '}')
+CF_ADDTO(proto, pipe_proto '}' { this_channel = NULL; } )
 
-pipe_proto_start: proto_start PIPE {
-     this_proto = proto_config_new(&proto_pipe, $1);
-     PIPE_CFG->mode = PIPE_TRANSPARENT;
-  }
- ;
+pipe_proto_start: proto_start PIPE
+{
+  this_proto = proto_config_new(&proto_pipe, $1);
+  this_channel = channel_config_new(NULL, 0, this_proto);
+  this_channel->in_filter = FILTER_ACCEPT;
+  this_channel->out_filter = FILTER_ACCEPT;
+};
 
 pipe_proto:
    pipe_proto_start proto_name '{'
  | pipe_proto proto_item ';'
- | pipe_proto PEER TABLE SYM ';' {
-     if ($4->class != SYM_TABLE)
-       cf_error("Routing table name expected");
-     PIPE_CFG->peer = $4->def;
-   }
- | pipe_proto MODE OPAQUE ';' { PIPE_CFG->mode = PIPE_OPAQUE; }
- | pipe_proto MODE TRANSPARENT ';' { PIPE_CFG->mode = PIPE_TRANSPARENT; }
+ | pipe_proto channel_item ';'
+ | pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; }
  ;
 
 CF_CODE
index 422858808662a588a5e894ccc5f43957cb885fa7..57db3e8bc54ae28aa73ca55dcc81eaef5ee75b67 100644 (file)
@@ -46,9 +46,8 @@
 static void
 pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
 {
-  struct pipe_proto *p = (struct pipe_proto *) P;
-  struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
-  rtable *dst_table = ah->table;
+  struct pipe_proto *p = (void *) P;
+  struct channel *dst = (src_table == p->pri->table) ? p->sec : p->pri;
   struct rte_src *src;
 
   net *nn;
@@ -58,24 +57,18 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
   if (!new && !old)
     return;
 
-  if (dst_table->pipe_busy)
+  if (dst->table->pipe_busy)
     {
       log(L_ERR "Pipe loop detected when sending %N to table %s",
-         n->n.addr, dst_table->name);
+         n->n.addr, dst->table->name);
       return;
     }
 
-  nn = net_get(dst_table, n->n.addr);
+  nn = net_get(dst->table, n->n.addr);
   if (new)
     {
       memcpy(&a, new->attrs, sizeof(rta));
 
-      if (p->mode == PIPE_OPAQUE)
-       {
-         a.src = P->main_source;
-         a.source = RTS_PIPE;
-       }
-
       a.aflags = 0;
       a.eattrs = attrs;
       a.hostentry = NULL;
@@ -83,13 +76,10 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
       e->net = nn;
       e->pflags = 0;
 
-      if (p->mode == PIPE_TRANSPARENT)
-       {
-         /* Copy protocol specific embedded attributes. */
-         memcpy(&(e->u), &(new->u), sizeof(e->u));
-         e->pref = new->pref;
-         e->pflags = new->pflags;
-       }
+      /* Copy protocol specific embedded attributes. */
+      memcpy(&(e->u), &(new->u), sizeof(e->u));
+      e->pref = new->pref;
+      e->pflags = new->pflags;
 
       src = a.src;
     }
@@ -100,7 +90,7 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
     }
 
   src_table->pipe_busy = 1;
-  rte_update2(ah, nn, e, src);
+  rte_update2(dst, nn, e, src);
   src_table->pipe_busy = 0;
 }
 
@@ -111,171 +101,117 @@ pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpo
 
   if (pp == P)
     return -1; /* Avoid local loops automatically */
+
   return 0;
 }
 
-static int
-pipe_reload_routes(struct proto *P)
+static void
+pipe_reload_routes(struct channel *C)
 {
-  struct pipe_proto *p = (struct pipe_proto *) P;
-
-  /*
-   * Because the pipe protocol feeds routes from both routing tables
-   * together, both directions are reloaded during refeed and 'reload
-   * out' command works like 'reload' command. For symmetry, we also
-   * request refeed when 'reload in' command is used.
-   */
-  proto_request_feeding(P);
+  struct pipe_proto *p = (void *) C->proto;
 
-  proto_reset_limit(P->main_ahook->in_limit);
-  proto_reset_limit(p->peer_ahook->in_limit);
-
-  return 1;
+  /* Route reload on one channel is just refeed on the other */
+  channel_request_feeding((C == p->pri) ? p->sec : p->pri);
 }
 
-static struct proto *
-pipe_init(struct proto_config *C)
-{
-  struct pipe_config *c = (struct pipe_config *) C;
-  struct proto *P = proto_new(C, sizeof(struct pipe_proto));
-  struct pipe_proto *p = (struct pipe_proto *) P;
 
-  p->mode = c->mode;
-  p->peer_table = c->peer->table;
-  P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY;
-  P->rt_notify = pipe_rt_notify;
-  P->import_control = pipe_import_control;
-  P->reload_routes = pipe_reload_routes;
-
-  return P;
-}
-
-static int
-pipe_start(struct proto *P)
+static void
+pipe_postconfig(struct proto_config *CF)
 {
-  struct pipe_config *cf = (struct pipe_config *) P->cf;
-  struct pipe_proto *p = (struct pipe_proto *) P;
+  struct pipe_config *cf = (void *) CF;
+  struct channel_config *cc = proto_cf_main_channel(CF);
 
-  /* Lock both tables, unlock is handled in pipe_cleanup() */
-  rt_lock_table(P->table);
-  rt_lock_table(p->peer_table);
+  if (!cc->table)
+    cf_error("Primary routing table not specified");
 
-  /* Going directly to PS_UP - prepare for feeding,
-     connect the protocol to both routing tables */
+  if (!cf->peer)
+    cf_error("Secondary routing table not specified");
 
-  P->main_ahook = proto_add_announce_hook(P, P->table, &P->stats);
-  P->main_ahook->out_filter = cf->c.out_filter;
-  P->main_ahook->in_limit = cf->c.in_limit;
-  proto_reset_limit(P->main_ahook->in_limit);
+  if (cc->table == cf->peer)
+    cf_error("Primary table and peer table must be different");
 
-  p->peer_ahook = proto_add_announce_hook(P, p->peer_table, &p->peer_stats);
-  p->peer_ahook->out_filter = cf->c.in_filter;
-  p->peer_ahook->in_limit = cf->c.out_limit;
-  proto_reset_limit(p->peer_ahook->in_limit);
+  if (cc->table->addr_type != cf->peer->addr_type)
+    cf_error("Primary table and peer table must have the same type");
 
-  if (p->mode == PIPE_OPAQUE)
-    {
-      P->main_source = rt_get_source(P, 0);
-      rt_lock_source(P->main_source);
-    }
+  if (cc->rx_limit.action)
+    cf_error("Pipe protocol does not support receive limits");
 
-  return PS_UP;
+  if (cc->in_keep_filtered)
+    cf_error("Pipe protocol prohibits keeping filtered routes");
 }
 
-static void
-pipe_cleanup(struct proto *P)
+static int
+pipe_configure_channels(struct pipe_proto *p, struct pipe_config *cf)
 {
-  struct pipe_proto *p = (struct pipe_proto *) P;
-
-  bzero(&P->stats, sizeof(struct proto_stats));
-  bzero(&p->peer_stats, sizeof(struct proto_stats));
-
-  P->main_ahook = NULL;
-  p->peer_ahook = NULL;
-
-  if (p->mode == PIPE_OPAQUE)
-    rt_unlock_source(P->main_source);
-  P->main_source = NULL;
-
-  rt_unlock_table(P->table);
-  rt_unlock_table(p->peer_table);
+  struct channel_config *cc = proto_cf_main_channel(&cf->c);
+
+  struct channel_config pri_cf = {
+    .name = "pri",
+    .channel = cc->channel,
+    .table = cc->table,
+    .out_filter = cc->out_filter,
+    .in_limit = cc->in_limit,
+    .ra_mode = RA_ANY
+  };
+
+  struct channel_config sec_cf = {
+    .name = "sec",
+    .channel = cc->channel,
+    .table = cf->peer,
+    .out_filter = cc->in_filter,
+    .in_limit = cc->out_limit,
+    .ra_mode = RA_ANY
+  };
+
+  return
+    proto_configure_channel(&p->p, &p->pri, &pri_cf) &&
+    proto_configure_channel(&p->p, &p->sec, &sec_cf);
 }
 
-static void
-pipe_postconfig(struct proto_config *C)
+static struct proto *
+pipe_init(struct proto_config *CF)
 {
-  struct pipe_config *c = (struct pipe_config *) C;
+  struct proto *P = proto_new(CF);
+  struct pipe_proto *p = (void *) P;
+  struct pipe_config *cf = (void *) CF;
 
-  if (!c->peer)
-    cf_error("Name of peer routing table not specified");
-  if (c->peer == C->table)
-    cf_error("Primary table and peer table must be different");
+  P->rt_notify = pipe_rt_notify;
+  P->import_control = pipe_import_control;
+  P->reload_routes = pipe_reload_routes;
 
-  if (C->in_keep_filtered)
-    cf_error("Pipe protocol prohibits keeping filtered routes");
-  if (C->rx_limit)
-    cf_error("Pipe protocol does not support receive limits");
-}
+  pipe_configure_channels(p, cf);
 
-extern int proto_reconfig_type;
+  return P;
+}
 
 static int
-pipe_reconfigure(struct proto *P, struct proto_config *new)
+pipe_reconfigure(struct proto *P, struct proto_config *CF)
 {
-  struct pipe_proto *p = (struct pipe_proto *)P;
-  struct proto_config *old = P->cf;
-  struct pipe_config *oc = (struct pipe_config *) old;
-  struct pipe_config *nc = (struct pipe_config *) new;
-
-  if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
-    return 0;
-
-  /* Update output filters in ahooks */
-  if (P->main_ahook)
-    {
-      P->main_ahook->out_filter = new->out_filter;
-      P->main_ahook->in_limit = new->in_limit;
-      proto_verify_limits(P->main_ahook);
-    }
-
-  if (p->peer_ahook)
-    {
-      p->peer_ahook->out_filter = new->in_filter;
-      p->peer_ahook->in_limit = new->out_limit;
-      proto_verify_limits(p->peer_ahook);
-    }
-
-  if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
-    return 1;
-
-  if ((new->preference != old->preference)
-      || ! filter_same(new->in_filter, old->in_filter)
-      || ! filter_same(new->out_filter, old->out_filter))
-    proto_request_feeding(P);
+  struct pipe_proto *p = (void *) P;
+  struct pipe_config *cf = (void *) CF;
 
-  return 1;
+  return pipe_configure_channels(p, cf);
 }
 
 static void
 pipe_copy_config(struct proto_config *dest, struct proto_config *src)
 {
   /* Just a shallow copy, not many items here */
-  proto_copy_rest(dest, src, sizeof(struct pipe_config));
 }
 
 static void
 pipe_get_status(struct proto *P, byte *buf)
 {
-  struct pipe_proto *p = (struct pipe_proto *) P;
+  struct pipe_proto *p = (void *) P;
 
-  bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name);
+  bsprintf(buf, "%s <=> %s", p->pri->table->name, p->sec->table->name);
 }
 
 static void
 pipe_show_stats(struct pipe_proto *p)
 {
-  struct proto_stats *s1 = &p->p.stats;
-  struct proto_stats *s2 = &p->peer_stats;
+  struct proto_stats *s1 = &p->pri->stats;
+  struct proto_stats *s2 = &p->sec->stats;
 
   /*
    * Pipe stats (as anything related to pipes) are a bit tricky. There
@@ -318,17 +254,16 @@ pipe_show_stats(struct pipe_proto *p)
 static void
 pipe_show_proto_info(struct proto *P)
 {
-  struct pipe_proto *p = (struct pipe_proto *) P;
-  struct pipe_config *cf = (struct pipe_config *) P->cf;
+  struct pipe_proto *p = (void *) P;
 
-  // cli_msg(-1006, "  Table:          %s", P->table->name);
-  // cli_msg(-1006, "  Peer table:     %s", p->peer_table->name);
-  cli_msg(-1006, "  Preference:     %d", P->preference);
-  cli_msg(-1006, "  Input filter:   %s", filter_name(cf->c.in_filter));
-  cli_msg(-1006, "  Output filter:  %s", filter_name(cf->c.out_filter));
+  cli_msg(-1006, "  Channel %s", "main");
+  cli_msg(-1006, "    Table:          %s", p->pri->table->name);
+  cli_msg(-1006, "    Peer table:     %s", p->sec->table->name);
+  cli_msg(-1006, "    Import filter:  %s", filter_name(p->sec->out_filter));
+  cli_msg(-1006, "    Export filter:  %s", filter_name(p->pri->out_filter));
 
-  proto_show_limit(cf->c.in_limit, "Import limit:");
-  proto_show_limit(cf->c.out_limit, "Export limit:");
+  channel_show_limit(&p->pri->in_limit, "Import limit:");
+  channel_show_limit(&p->sec->in_limit, "Export limit:");
 
   if (P->proto_state != PS_DOWN)
     pipe_show_stats(p);
@@ -338,13 +273,10 @@ pipe_show_proto_info(struct proto *P)
 struct protocol proto_pipe = {
   .name =              "Pipe",
   .template =          "pipe%d",
-  .multitable =                1,
-  .preference =                DEF_PREF_PIPE,
+  .proto_size =                sizeof(struct pipe_proto),
   .config_size =       sizeof(struct pipe_config),
   .postconfig =                pipe_postconfig,
   .init =              pipe_init,
-  .start =             pipe_start,
-  .cleanup =           pipe_cleanup,
   .reconfigure =       pipe_reconfigure,
   .copy_config =       pipe_copy_config,
   .get_status =        pipe_get_status,
index 50b3169815f4c93a5bab944b2eb89ecd55887a58..038c66667448b0f506ac7de32453b5d40adb70ca 100644 (file)
@@ -9,27 +9,15 @@
 #ifndef _BIRD_PIPE_H_
 #define _BIRD_PIPE_H_
 
-#define PIPE_OPAQUE 0
-#define PIPE_TRANSPARENT 1
-
 struct pipe_config {
   struct proto_config c;
   struct rtable_config *peer;          /* Table we're connected to */
-  int mode;                            /* PIPE_OPAQUE or PIPE_TRANSPARENT */
 };
 
 struct pipe_proto {
   struct proto p;
-  struct rtable *peer_table;
-  struct announce_hook *peer_ahook;    /* Announce hook for direction peer->primary */
-  struct proto_stats peer_stats;       /* Statistics for the direction peer->primary */
-  int mode;                            /* PIPE_OPAQUE or PIPE_TRANSPARENT */
+  struct channel *pri;
+  struct channel *sec;
 };
 
-
-extern struct protocol proto_pipe;
-
-static inline int proto_is_pipe(struct proto *p)
-{ return p->proto == &proto_pipe; }
-
 #endif
index 5e655de40cd15de4f5382cd29100290387201fa2..7ba232053c59791bdce4ebe34456044f8b0f70a7 100644 (file)
@@ -41,6 +41,7 @@ CF_ADDTO(proto, radv_proto)
 radv_proto_start: proto_start RADV
 {
   this_proto = proto_config_new(&proto_radv, $1);
+
   init_list(&RADV_CFG->patt_list);
   init_list(&RADV_CFG->pref_list);
   init_list(&RADV_CFG->rdnss_list);
@@ -49,14 +50,12 @@ radv_proto_start: proto_start RADV
 
 radv_proto_item:
    proto_item
+ | proto_channel
  | INTERFACE radv_iface
  | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); }
  | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); }
  | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); }
- | TRIGGER net_any {
-     RADV_CFG->trigger = $2;
-     RADV_CFG->trigger_valid = 1;
-   }
+ | TRIGGER net_ip6 { RADV_CFG->trigger = $2; }
  ;
 
 radv_proto_opts:
index 97713b9509c7b2b886aad7466cb9d08c249156d1..f9c0940f4c98e7975d1deed8359450a5250f7c50 100644 (file)
@@ -256,9 +256,16 @@ radv_ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
     radv_iface_notify(ifa, RA_EV_CHANGE);
 }
 
-static inline int radv_net_match_trigger(struct radv_config *cf, net *n)
+static inline int
+radv_trigger_valid(struct radv_config *cf)
 {
-  return cf->trigger_valid && net_equal(n->n.addr, cf->trigger);
+  return cf->trigger.type != 0;
+}
+
+static inline int
+radv_net_match_trigger(struct radv_config *cf, net *n)
+{
+  return radv_trigger_valid(cf) && net_equal(n->n.addr, &cf->trigger);
 }
 
 int
@@ -301,22 +308,35 @@ radv_check_active(struct proto_radv *ra)
 {
   struct radv_config *cf = (struct radv_config *) (ra->p.cf);
 
-  if (! cf->trigger_valid)
+  if (!radv_trigger_valid(cf))
     return 1;
 
-  return rt_examine(ra->p.table, cf->trigger, &ra->p, ra->p.cf->out_filter);
+  struct channel *c =ra->p.main_channel;
+  return rt_examine(c->table, &cf->trigger, &ra->p, c->out_filter);
+}
+
+static void
+radv_postconfig(struct proto_config *CF)
+{
+  // struct radv_config *cf = (void *) CF;
+
+  /* Define default channel */
+  if (EMPTY_LIST(CF->channels))
+    channel_config_new(NULL, NET_IP6, CF);
 }
 
 static struct proto *
-radv_init(struct proto_config *c)
+radv_init(struct proto_config *CF)
 {
-  struct proto *p = proto_new(c, sizeof(struct proto_radv));
+  struct proto *p = proto_new(CF);
+
+  p->main_channel = proto_add_channel(p, proto_cf_main_channel(CF));
 
-  p->accept_ra_types = RA_OPTIMAL;
   p->import_control = radv_import_control;
   p->rt_notify = radv_rt_notify;
   p->if_notify = radv_if_notify;
   p->ifa_notify = radv_ifa_notify;
+
   return p;
 }
 
@@ -327,7 +347,7 @@ radv_start(struct proto *p)
   struct radv_config *cf = (struct radv_config *) (p->cf);
 
   init_list(&(ra->iface_list));
-  ra->active = !cf->trigger_valid;
+  ra->active = !radv_trigger_valid(cf);
 
   return PS_UP;
 }
@@ -352,11 +372,11 @@ radv_shutdown(struct proto *p)
 }
 
 static int
-radv_reconfigure(struct proto *p, struct proto_config *c)
+radv_reconfigure(struct proto *p, struct proto_config *CF)
 {
   struct proto_radv *ra = (struct proto_radv *) p;
   // struct radv_config *old = (struct radv_config *) (p->cf);
-  struct radv_config *new = (struct radv_config *) c;
+  struct radv_config *new = (struct radv_config *) CF;
 
   /*
    * The question is why there is a reconfigure function for RAdv if
@@ -366,7 +386,10 @@ radv_reconfigure(struct proto *p, struct proto_config *c)
    * causing nodes to temporary remove their default routes.
    */
 
-  p->cf = c; /* radv_check_active() requires proper p->cf */
+  if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
+    return 0;
+
+  p->cf = CF; /* radv_check_active() requires proper p->cf */
   ra->active = radv_check_active(ra);
 
   struct iface *iface;
@@ -423,7 +446,10 @@ radv_get_status(struct proto *p, byte *buf)
 struct protocol proto_radv = {
   .name =              "RAdv",
   .template =          "radv%d",
+  .channel_mask =      NB_IP6,
+  .proto_size =                sizeof(struct proto_radv),
   .config_size =       sizeof(struct radv_config),
+  .postconfig =                radv_postconfig,
   .init =              radv_init,
   .start =             radv_start,
   .shutdown =          radv_shutdown,
index 755009d400c694e919b0b8990b74476178778fdd..f8aa421d788a07ac6411fb3e2b1889a3e525b878 100644 (file)
@@ -50,8 +50,7 @@ struct radv_config
   list rdnss_list;             /* Global list of RDNSS configs (struct radv_rdnss_config) */
   list dnssl_list;             /* Global list of DNSSL configs (struct radv_dnssl_config) */
 
-  net_addr *trigger;           /* Prefix of a trigger route, if defined */
-  u8 trigger_valid;            /* Whether a trigger route is defined */
+  net_addr trigger;            /* Prefix of a trigger route, if defined */
 };
 
 struct radv_iface_config
index d81c42d77257b33b13abcd8564de7c1a2a7f924b..79e57741a6d22e8917b912dcfd41c49e45ee1965 100644 (file)
@@ -52,17 +52,18 @@ rip_variant:
 rip_proto_start: proto_start rip_variant
 {
   this_proto = proto_config_new(&proto_rip, $1);
-  init_list(&RIP_CFG->patt_list);
+  this_proto->net_type = $2 ? NET_IP4 : NET_IP6;
 
+  init_list(&RIP_CFG->patt_list);
   RIP_CFG->rip2 = $2;
   RIP_CFG->infinity = RIP_DEFAULT_INFINITY;
-
   RIP_CFG->min_timeout_time = 60;
   RIP_CFG->max_garbage_time = 60;
 };
 
 rip_proto_item:
    proto_item
+ | proto_channel
  | ECMP bool           { RIP_CFG->ecmp = $2 ? RIP_DEFAULT_ECMP_LIMIT : 0; }
  | ECMP bool LIMIT expr        { RIP_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); }
  | INFINITY expr       { RIP_CFG->infinity = $2; }
index 7e5d6802e32d109cf3fdbe4b12f32b91389deea4..f020bc5b4c6edeb46f403ab652e6da94e882d4c7 100644 (file)
@@ -143,7 +143,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
   if (rt)
   {
     /* Update */
-    net *n = net_get(p->p.table, en->n.addr);
+    net *n = net_get(p->p.main_channel->table, en->n.addr);
 
     rta a0 = {
       .src = p->p.main_source,
@@ -212,7 +212,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
   else
   {
     /* Withdraw */
-    net *n = net_find(p->p.table, en->n.addr);
+    net *n = net_find(p->p.main_channel->table, en->n.addr);
     rte_update(&p->p, n, NULL);
   }
 }
@@ -1027,19 +1027,17 @@ rip_import_control(struct proto *P, struct rte **rt, struct ea_list **attrs, str
   return 0;
 }
 
-static int
-rip_reload_routes(struct proto *P)
+static void
+rip_reload_routes(struct channel *C)
 {
-  struct rip_proto *p = (struct rip_proto *) P;
+  struct rip_proto *p = (struct rip_proto *) C->proto;
 
   if (p->rt_reload)
-    return 1;
+    return;
 
   TRACE(D_EVENTS, "Scheduling route reload");
   p->rt_reload = 1;
   rip_kick_timer(p);
-
-  return 1;
 }
 
 static struct ea_list *
@@ -1070,12 +1068,23 @@ rip_rte_same(struct rte *new, struct rte *old)
 }
 
 
+static void
+rip_postconfig(struct proto_config *CF)
+{
+  // struct rip_config *cf = (void *) CF;
+
+  /* Define default channel */
+  if (EMPTY_LIST(CF->channels))
+    channel_config_new(NULL, CF->net_type, CF);
+}
+
 static struct proto *
-rip_init(struct proto_config *cfg)
+rip_init(struct proto_config *CF)
 {
-  struct proto *P = proto_new(cfg, sizeof(struct rip_proto));
+  struct proto *P = proto_new(CF);
+
+  P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
 
-  P->accept_ra_types = RA_OPTIMAL;
   P->if_notify = rip_if_notify;
   P->rt_notify = rip_rt_notify;
   P->neigh_notify = rip_neigh_notify;
@@ -1115,10 +1124,10 @@ rip_start(struct proto *P)
 }
 
 static int
-rip_reconfigure(struct proto *P, struct proto_config *c)
+rip_reconfigure(struct proto *P, struct proto_config *CF)
 {
   struct rip_proto *p = (void *) P;
-  struct rip_config *new = (void *) c;
+  struct rip_config *new = (void *) CF;
   // struct rip_config *old = (void *) (P->cf);
 
   if (new->rip2 != p->rip2)
@@ -1127,9 +1136,12 @@ rip_reconfigure(struct proto *P, struct proto_config *c)
   if (new->infinity != p->infinity)
     return 0;
 
+  if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
+    return 0;
+
   TRACE(D_EVENTS, "Reconfiguring");
 
-  p->p.cf = c;
+  p->p.cf = CF;
   p->ecmp = new->ecmp;
   rip_reconfigure_ifaces(p, new);
 
@@ -1270,7 +1282,10 @@ struct protocol proto_rip = {
   .template =          "rip%d",
   .attr_class =                EAP_RIP,
   .preference =                DEF_PREF_RIP,
+  .channel_mask =      NB_IP,
+  .proto_size =                sizeof(struct rip_proto),
   .config_size =       sizeof(struct rip_config),
+  .postconfig =                rip_postconfig,
   .init =              rip_init,
   .dump =              rip_dump,
   .start =             rip_start,
index 9e634803173bd0284e351d04fde0cada3030d5aa..86359f0bbd6caa2bbb6ac26e76d5907d235d0af0 100644 (file)
@@ -38,15 +38,16 @@ CF_GRAMMAR
 
 CF_ADDTO(proto, static_proto '}')
 
-static_proto_start: proto_start STATIC {
-     this_proto = proto_config_new(&proto_static, $1);
-     static_init_config((struct static_config *) this_proto);
-  }
- ;
+static_proto_start: proto_start STATIC
+{
+  this_proto = proto_config_new(&proto_static, $1);
+  static_init_config(STATIC_CFG);
+};
 
 static_proto:
    static_proto_start proto_name '{'
  | static_proto proto_item ';'
+ | static_proto proto_channel ';' { this_proto->net_type = $2->net_type; }
  | static_proto CHECK LINK bool ';' { STATIC_CFG->check_link = $4; }
  | static_proto IGP TABLE rtable ';' { STATIC_CFG->igp_table = $4; }
  | static_proto stat_route stat_route_opt_list ';' { static_route_finish(); }
index 809c29fbb85c4b9ede7ae7e49919e853ec82be8c..6239fccbb9bf0a91be72f69332751248c99d25cd 100644 (file)
@@ -54,7 +54,7 @@ static inline rtable *
 p_igp_table(struct proto *p)
 {
   struct static_config *cf = (void *) p->cf;
-  return cf->igp_table ? cf->igp_table->table : p->table;
+  return cf->igp_table ? cf->igp_table->table : p->main_channel->table;
 }
 
 static void
@@ -108,11 +108,11 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
     }
 
   if (r->dest == RTDX_RECURSIVE)
-    rta_set_recursive_next_hop(p->table, &a, p_igp_table(p), &r->via, &r->via);
+    rta_set_recursive_next_hop(p->main_channel->table, &a, p_igp_table(p), &r->via, &r->via);
 
   /* We skip rta_lookup() here */
 
-  n = net_get(p->table, r->net);
+  n = net_get(p->main_channel->table, r->net);
   e = rte_get_temp(&a);
   e->net = n;
   e->pflags = 0;
@@ -136,7 +136,7 @@ static_remove(struct proto *p, struct static_route *r)
     return;
 
   DBG("Removing static route %N via %I\n", r->net, r->via);
-  n = net_find(p->table, r->net);
+  n = net_find(p->main_channel->table, r->net);
   rte_update(p, n, NULL);
   r->installed = 0;
 }
@@ -309,6 +309,17 @@ static_shutdown(struct proto *p)
     r->installed = 0;
   }
 
+  /* Handle failure during channel reconfigure */
+  /* FIXME: This should be handled in a better way */
+  cf = (void *) p->cf_new;
+  if (cf)
+  {
+    WALK_LIST(r, cf->iface_routes)
+      r->installed = 0;
+    WALK_LIST(r, cf->other_routes)
+      r->installed = 0;
+  }
+
   return PS_DOWN;
 }
 
@@ -450,16 +461,40 @@ static_init_config(struct static_config *c)
   init_list(&c->other_routes);
 }
 
+static void
+static_postconfig(struct proto_config *CF)
+{
+  struct static_config *cf = (void *) CF;
+  struct static_route *r;
+
+  if (EMPTY_LIST(CF->channels))
+    cf_error("Channel not specified");
+
+
+  WALK_LIST(r, cf->iface_routes)
+    if (r->net->type != CF->net_type)
+      cf_error("Route %N incompatible with channel type", r->net);
+
+  WALK_LIST(r, cf->other_routes)
+    if (r->net->type != CF->net_type)
+      cf_error("Route %N incompatible with channel type", r->net);
+}
+
+
 static struct proto *
-static_init(struct proto_config *c)
+static_init(struct proto_config *CF)
 {
-  struct proto *p = proto_new(c, sizeof(struct proto));
+  struct proto *P = proto_new(CF);
+  // struct static_proto *p = (void *) P;
+  // struct static_config *cf = (void *) CF;
 
-  p->neigh_notify = static_neigh_notify;
-  p->if_notify = static_if_notify;
-  p->rte_mergable = static_rte_mergable;
+  P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
 
-  return p;
+  P->neigh_notify = static_neigh_notify;
+  P->if_notify = static_if_notify;
+  P->rte_mergable = static_rte_mergable;
+
+  return P;
 }
 
 static inline int
@@ -543,15 +578,18 @@ cf_igp_table(struct static_config *cf)
 }
 
 static int
-static_reconfigure(struct proto *p, struct proto_config *new)
+static_reconfigure(struct proto *p, struct proto_config *CF)
 {
   struct static_config *o = (void *) p->cf;
-  struct static_config *n = (void *) new;
+  struct static_config *n = (void *) CF;
   struct static_route *r;
 
   if (cf_igp_table(o) != cf_igp_table(n))
     return 0;
 
+  if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
+    return 0;
+
   /* Delete all obsolete routes and reset neighbor entries */
   WALK_LIST(r, o->iface_routes)
     static_match(p, r, n);
@@ -617,20 +655,19 @@ static_copy_config(struct proto_config *dest, struct proto_config *src)
   struct static_config *d = (struct static_config *) dest;
   struct static_config *s = (struct static_config *) src;
 
-  /* Shallow copy of everything */
-  proto_copy_rest(dest, src, sizeof(struct static_config));
-
   /* Copy route lists */
   static_copy_routes(&d->iface_routes, &s->iface_routes);
   static_copy_routes(&d->other_routes, &s->other_routes);
 }
 
-
 struct protocol proto_static = {
   .name =              "Static",
   .template =          "static%d",
   .preference =                DEF_PREF_STATIC,
+  .channel_mask =      NB_ANY,
+  .proto_size =                sizeof(struct proto),
   .config_size =       sizeof(struct static_config),
+  .postconfig =                static_postconfig,
   .init =              static_init,
   .dump =              static_dump,
   .start =             static_start,
index 530cb24035a175f9344a128c85d8edcb7e13c265..6240c177c4fb5e7b2e73df2a423126117d5694de 100644 (file)
@@ -1127,7 +1127,7 @@ nl_parse_route(struct nlmsghdr *h, int scan)
       src = KRT_SRC_ALIEN;
     }
 
-  net *net = net_get(p->p.table, &dst);
+  net *net = net_get(p->p.main_channel->table, &dst);
 
   rta ra = {
     .src= p->p.main_source,
index 80bf97ecb601920dd6e0c169fb31bdb2fbaf33db..1cd73502eee99cade5e414476ccb199b517c5057 100644 (file)
@@ -15,6 +15,16 @@ CF_DEFINES
 #define THIS_KRT ((struct krt_config *) this_proto)
 #define THIS_KIF ((struct kif_config *) this_proto)
 
+static void
+krt_set_merge_paths(struct channel_config *cc, uint merge, uint limit)
+{
+  if ((limit <= 0) || (limit > 255))
+    cf_error("Merge paths limit must be in range 1-255");
+
+  cc->ra_mode = merge ? RA_MERGED : RA_OPTIMAL;
+  cc->merge_limit = limit;
+}
+
 CF_DECLS
 
 CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTART, KRT_SOURCE, KRT_METRIC, MERGE, PATHS)
@@ -25,15 +35,18 @@ CF_GRAMMAR
 
 CF_ADDTO(proto, kern_proto '}')
 
-kern_proto_start: proto_start KERNEL { this_proto = krt_init_config($1); }
+kern_proto_start: proto_start KERNEL {
+     this_proto = krt_init_config($1);
+}
  ;
 
 CF_ADDTO(kern_proto, kern_proto_start proto_name '{')
-CF_ADDTO(kern_proto, kern_proto proto_item ';')
 CF_ADDTO(kern_proto, kern_proto kern_item ';')
 
 kern_item:
-   PERSIST bool { THIS_KRT->persist = $2; }
+   proto_item
+ | proto_channel { this_proto->net_type = $1->net_type; }
+ | PERSIST bool { THIS_KRT->persist = $2; }
  | SCAN TIME expr {
       /* Scan time of 0 means scan on startup only */
       THIS_KRT->scan_time = $3;
@@ -47,8 +60,8 @@ kern_item:
    }
  | DEVICE ROUTES bool { THIS_KRT->devroutes = $3; }
  | GRACEFUL RESTART bool { THIS_KRT->graceful_restart = $3; }
- | MERGE PATHS bool { THIS_KRT->merge_paths = $3 ? KRT_DEFAULT_ECMP_LIMIT : 0; }
- | MERGE PATHS bool LIMIT expr { THIS_KRT->merge_paths = $3 ? $5 : 0; if (($5 <= 0) || ($5 > 255)) cf_error("Merge paths limit must be in range 1-255"); }
+ | MERGE PATHS bool { krt_set_merge_paths(this_channel, $3, KRT_DEFAULT_ECMP_LIMIT); }
+ | MERGE PATHS bool LIMIT expr { krt_set_merge_paths(this_channel, $3, $5); }
  ;
 
 /* Kernel interface protocol */
@@ -59,11 +72,11 @@ kif_proto_start: proto_start DEVICE { this_proto = kif_init_config($1); }
  ;
 
 CF_ADDTO(kif_proto, kif_proto_start proto_name '{')
-CF_ADDTO(kif_proto, kif_proto proto_item ';')
 CF_ADDTO(kif_proto, kif_proto kif_item ';')
 
 kif_item:
-   SCAN TIME expr {
+   proto_item
+ | SCAN TIME expr {
       /* Scan time of 0 means scan on startup only */
       THIS_KIF->scan_time = $3;
    }
index a15d00e2d00963e10169b9413def61593d91aa3c..6af3a84ff35ef11a449563c32b736983ef3fa9b2 100644 (file)
@@ -170,7 +170,7 @@ kif_choose_primary(struct iface *i)
 static struct proto *
 kif_init(struct proto_config *c)
 {
-  struct kif_proto *p = proto_new(c, sizeof(struct kif_proto));
+  struct kif_proto *p = proto_new(c);
 
   kif_sys_init(p);
   return &p->p;
@@ -266,9 +266,6 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src)
   struct kif_config *d = (struct kif_config *) dest;
   struct kif_config *s = (struct kif_config *) src;
 
-  /* Shallow copy of everything (just scan_time currently) */
-  proto_copy_rest(dest, src, sizeof(struct kif_config));
-
   /* Copy primary addr list */
   cfg_copy_list(&d->primary, &s->primary, sizeof(struct kif_primary_item));
 
@@ -280,7 +277,7 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src)
 struct protocol proto_unix_iface = {
   .name =              "Device",
   .template =          "device%d",
-  .preference =                DEF_PREF_DIRECT,
+  .proto_size =                sizeof(struct kif_proto),
   .config_size =       sizeof(struct kif_config),
   .preconfig =         kif_preconfig,
   .init =              kif_init,
@@ -348,10 +345,9 @@ krt_learn_announce_update(struct krt_proto *p, rte *e)
   net *n = e->net;
   rta *aa = rta_clone(e->attrs);
   rte *ee = rte_get_temp(aa);
-  net *nn = net_get(p->p.table, n->n.addr);
+  net *nn = net_get(p->p.main_channel->table, n->n.addr);
   ee->net = nn;
   ee->pflags = 0;
-  ee->pref = p->p.preference;
   ee->u.krt = e->u.krt;
   rte_update(&p->p, nn, ee);
 }
@@ -359,7 +355,7 @@ krt_learn_announce_update(struct krt_proto *p, rte *e)
 static void
 krt_learn_announce_delete(struct krt_proto *p, net *n)
 {
-  n = net_find(p->p.table, n->n.addr);
+  n = net_find(p->p.main_channel->table, n->n.addr);
   rte_update(&p->p, n, NULL);
 }
 
@@ -575,7 +571,7 @@ krt_dump_attrs(rte *e)
 static void
 krt_flush_routes(struct krt_proto *p)
 {
-  struct rtable *t = p->p.table;
+  struct rtable *t = p->p.main_channel->table;
 
   KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
   FIB_WALK(&t->fib, net, n)
@@ -594,12 +590,12 @@ krt_flush_routes(struct krt_proto *p)
 static struct rte *
 krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa)
 {
-  struct announce_hook *ah = p->p.main_ahook;
-  struct filter *filter = ah->out_filter;
+  struct channel *c = p->p.main_channel;
+  struct filter *filter = c->out_filter;
   rte *rt;
 
-  if (p->p.accept_ra_types == RA_MERGED)
-    return rt_export_merged(ah, net, rt_free, tmpa, 1);
+  if (c->ra_mode == RA_MERGED)
+    return rt_export_merged(c, net, rt_free, tmpa, 1);
 
   rt = net->routes;
   *rt_free = NULL;
@@ -746,7 +742,7 @@ krt_got_route(struct krt_proto *p, rte *e)
 static void
 krt_prune(struct krt_proto *p)
 {
-  struct rtable *t = p->p.table;
+  struct rtable *t = p->p.main_channel->table;
 
   KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
   FIB_WALK(&t->fib, net, n)
@@ -1052,10 +1048,10 @@ krt_if_notify(struct proto *P, uint flags, struct iface *iface UNUSED)
     krt_scan_timer_kick(p);
 }
 
-static int
-krt_reload_routes(struct proto *P)
+static void
+krt_reload_routes(struct channel *C)
 {
-  struct krt_proto *p = (struct krt_proto *) P;
+  struct krt_proto *p = (void *) C->proto;
 
   /* Although we keep learned routes in krt_table, we rather schedule a scan */
 
@@ -1064,14 +1060,12 @@ krt_reload_routes(struct proto *P)
     p->reload = 1;
     krt_scan_timer_kick(p);
   }
-
-  return 1;
 }
 
 static void
-krt_feed_end(struct proto *P)
+krt_feed_end(struct channel *C)
 {
-  struct krt_proto *p = (struct krt_proto *) P;
+  struct krt_proto *p = (void *) C->proto;
 
   p->ready = 1;
   krt_scan_timer_kick(p);
@@ -1092,14 +1086,42 @@ krt_rte_same(rte *a, rte *b)
 
 struct krt_config *krt_cf;
 
+static void
+krt_preconfig(struct protocol *P UNUSED, struct config *c)
+{
+  krt_cf = NULL;
+  krt_sys_preconfig(c);
+}
+
+static void
+krt_postconfig(struct proto_config *CF)
+{
+  struct krt_config *cf = (void *) CF;
+
+  if (EMPTY_LIST(CF->channels))
+    cf_error("Channel not specified");
+
+#ifdef CONFIG_ALL_TABLES_AT_ONCE
+  if (krt_cf->scan_time != cf->scan_time)
+    cf_error("All kernel syncers must use the same table scan interval");
+#endif
+
+  struct rtable_config *tab = proto_cf_main_channel(CF)->table;
+  if (tab->krt_attached)
+    cf_error("Kernel syncer (%s) already attached to table %s", tab->krt_attached->name, tab->name);
+  tab->krt_attached = CF;
+
+  krt_sys_postconfig(cf);
+}
+
 static struct proto *
-krt_init(struct proto_config *C)
+krt_init(struct proto_config *CF)
 {
-  struct krt_proto *p = proto_new(C, sizeof(struct krt_proto));
-  struct krt_config *c = (struct krt_config *) C;
+  struct krt_proto *p = proto_new(CF);
+  // struct krt_config *cf = (void *) CF;
+
+  p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
 
-  p->p.accept_ra_types = c->merge_paths ? RA_MERGED : RA_OPTIMAL;
-  p->p.merge_limit = c->merge_paths;
   p->p.import_control = krt_import_control;
   p->p.rt_notify = krt_rt_notify;
   p->p.if_notify = krt_if_notify;
@@ -1118,7 +1140,7 @@ krt_start(struct proto *P)
 {
   struct krt_proto *p = (struct krt_proto *) P;
 
-  switch (p->p.table->addr_type)
+  switch (p->p.net_type)
   {
   case NET_IP4:        p->af = AF_INET; break;
   case NET_IP6:        p->af = AF_INET6; break;
@@ -1139,8 +1161,8 @@ krt_start(struct proto *P)
 
   krt_scan_timer_start(p);
 
-  if (P->gr_recovery && KRT_CF->graceful_restart)
-    P->gr_wait = 1;
+  if (p->p.gr_recovery && KRT_CF->graceful_restart)
+    p->p.main_channel->gr_wait = 1;
 
   return PS_UP;
 }
@@ -1169,40 +1191,19 @@ krt_shutdown(struct proto *P)
 }
 
 static int
-krt_reconfigure(struct proto *p, struct proto_config *new)
+krt_reconfigure(struct proto *p, struct proto_config *CF)
 {
-  struct krt_config *o = (struct krt_config *) p->cf;
-  struct krt_config *n = (struct krt_config *) new;
+  struct krt_config *o = (void *) p->cf;
+  struct krt_config *n = (void *) CF;
+
+  if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
+    return 0;
 
   if (!krt_sys_reconfigure((struct krt_proto *) p, n, o))
     return 0;
 
   /* persist, graceful restart need not be the same */
-  return o->scan_time == n->scan_time && o->learn == n->learn &&
-    o->devroutes == n->devroutes && o->merge_paths == n->merge_paths;
-}
-
-static void
-krt_preconfig(struct protocol *P UNUSED, struct config *c)
-{
-  krt_cf = NULL;
-  krt_sys_preconfig(c);
-}
-
-static void
-krt_postconfig(struct proto_config *C)
-{
-  struct krt_config *c = (struct krt_config *) C;
-
-#ifdef CONFIG_ALL_TABLES_AT_ONCE
-  if (krt_cf->scan_time != c->scan_time)
-    cf_error("All kernel syncers must use the same table scan interval");
-#endif
-
-  if (C->table->krt_attached)
-    cf_error("Kernel syncer (%s) already attached to table %s", C->table->krt_attached->name, C->table->name);
-  C->table->krt_attached = C;
-  krt_sys_postconfig(c);
+  return o->scan_time == n->scan_time && o->learn == n->learn && o->devroutes == n->devroutes;
 }
 
 struct proto_config *
@@ -1226,9 +1227,6 @@ krt_copy_config(struct proto_config *dest, struct proto_config *src)
   struct krt_config *d = (struct krt_config *) dest;
   struct krt_config *s = (struct krt_config *) src;
 
-  /* Shallow copy of everything */
-  proto_copy_rest(dest, src, sizeof(struct krt_config));
-
   /* Fix sysdep parts */
   krt_sys_copy_config(d, s);
 }
@@ -1257,6 +1255,8 @@ struct protocol proto_unix_kernel = {
   .template =          "kernel%d",
   .attr_class =                EAP_KRT,
   .preference =                DEF_PREF_INHERITED,
+  .channel_mask =      NB_IP,
+  .proto_size =                sizeof(struct krt_proto),
   .config_size =       sizeof(struct krt_config),
   .preconfig =         krt_preconfig,
   .postconfig =                krt_postconfig,
index f05dc37ea74c036da97a0778a0a73750cbb9b71d..e968ad570314942d0d889475bde038b64ed7d040 100644 (file)
@@ -49,7 +49,6 @@ struct krt_config {
   int learn;                   /* Learn routes from other sources */
   int devroutes;               /* Allow export of device routes */
   int graceful_restart;                /* Regard graceful restart recovery */
-  int merge_paths;             /* Exported routes are merged for ECMP */
 };
 
 struct krt_proto {