+
+/*
+ * 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_find_channel_by_name(struct proto *p, const char *n);
+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);
+void channel_setup_in_table(struct channel *c);
+void channel_schedule_reload(struct channel *c);
+
+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, const char *name, uint net_type, struct proto_config *proto);
+void *channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);
+int channel_reconfigure(struct channel *c, struct channel_config *cf);
+
+
+/* Moved from route.h to avoid dependency conflicts */
+static inline void rte_update(struct proto *p, const net_addr *n, rte *new) { rte_update2(p->main_channel, n, new, p->main_source); }
+
+static inline void
+rte_update3(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
+{
+ if (c->in_table && !rte_update_in(c, n, new, src))
+ return;
+
+ rte_update2(c, n, new, src);
+}
+