}
-/**
- * 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 channel *c;
+ WALK_LIST(c, p->channels)
+ if (!c->disabled && channel_is_active(c))
+ channel_set_state(c, CS_START);
+}
+
+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);
+}
+
+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);
+
+ c->export_state = ES_FEEDING;
+ c->refeeding = !initial;
+
+ 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;
++ c->stats.exp_routes = 0;
+}
+
+static void
+channel_do_start(struct channel *c)
{
- struct announce_hook *h;
+ 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);
+}
- DBG("Connecting protocol %s to table %s\n", p->name, t->name);
- PD(p, "Connected to table %s", t->name);
+static void
+channel_do_down(struct channel *c)
+{
- rem2_node(&c->table_node);
++ rem_node(&c->table_node);
+ rt_unlock_table(c->table);
+ c->proto->active_channels--;
- h = mb_allocz(rt_table_pool, sizeof(struct announce_hook));
- h->table = t;
- h->proto = p;
- h->stats = stats;
+ 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);
- h->next = p->ahooks;
- p->ahooks = h;
+ memset(&c->stats, 0, sizeof(struct proto_stats));
- if (p->rt_notify && (p->export_state != ES_DOWN))
- add_tail(&t->hooks, &h->n);
- return h;
+ /* 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 channel %s state transition %s -> %s\n", c->proto->name, c->name, cs_states[cs], cs_states[state]);
+ 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 && c->proto->rt_notify)
+ 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);
}
/**