summaryrefslogtreecommitdiff
path: root/proto
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2012-03-15 11:58:08 +0100
committerOndrej Zajicek <santiago@crfreenet.org>2012-03-15 12:13:04 +0100
commitc0adf7e9fc0bb920175a639c6f56ed7b4190f3e4 (patch)
tree8bc6ac9e4c9288d7e7009a80a04d7278325ff05c /proto
parent46c1a583a5c1ea81e8d8f372bd7f614506a63938 (diff)
Better support for multitable protocols.
The nest-protocol interaction is changed to better handle multitable protocols. Multitable protocols now declare that by 'multitable' field, which tells nest that a protocol handles things related to proto-rtable interaction (table locking, announce hook adding, reconfiguration of filters) itself. Filters and stats are moved to announce hooks, a protocol could have different filters and stats to different tables. The patch is based on one from Alexander V. Chernikov, thanks.
Diffstat (limited to 'proto')
-rw-r--r--proto/bgp/bgp.c2
-rw-r--r--proto/pipe/pipe.c189
-rw-r--r--proto/pipe/pipe.h9
3 files changed, 150 insertions, 50 deletions
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 4d3c32fb..4dd4b7be 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1125,6 +1125,8 @@ bgp_show_proto_info(struct proto *P)
struct bgp_proto *p = (struct bgp_proto *) P;
struct bgp_conn *c = p->conn;
+ proto_show_basic_info(P);
+
cli_msg(-1006, " BGP state: %s", bgp_state_dsc(p));
cli_msg(-1006, " Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
cli_msg(-1006, " Neighbor AS: %u", p->remote_as);
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index fe8618b6..36b06d43 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -16,6 +16,17 @@
*
* To avoid pipe loops, Pipe keeps a `being updated' flag in each routing
* table.
+ *
+ * A pipe has two announce hooks, the first connected to the main
+ * table, the second connected to the peer table. When a new route is
+ * announced on the main table, it gets checked by an export filter in
+ * ahook 1, and, after that, it is announced to the peer table via
+ * rte_update(), an import filter in ahook 2 is called. When a new
+ * route is announced in the peer table, an export filter in ahook2
+ * and an import filter in ahook 1 are used. Oviously, there is no
+ * need in filtering the same route twice, so both import filters
+ * are set to accept, while user configured 'import' and 'export'
+ * filters are used as export filters in ahooks 2 and 1.
*/
#undef LOCAL_DEBUG
@@ -24,6 +35,7 @@
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
+#include "nest/cli.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "lib/string.h"
@@ -34,7 +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;
- rtable *dest = (src_table == P->table) ? p->peer : P->table; /* The other side of the pipe */
+ struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
+ rtable *dst_table = ah->table;
struct proto *src;
net *nn;
@@ -44,13 +57,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
if (!new && !old)
return;
- if (dest->pipe_busy)
+ if (dst_table->pipe_busy)
{
log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
- n->n.prefix, n->n.pxlen, dest->name);
+ n->n.prefix, n->n.pxlen, dst_table->name);
return;
}
- nn = net_get(dest, n->n.prefix, n->n.pxlen);
+
+ nn = net_get(dst_table, n->n.prefix, n->n.pxlen);
if (new)
{
memcpy(&a, new->attrs, sizeof(rta));
@@ -85,14 +99,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
}
src_table->pipe_busy = 1;
- rte_update(dest, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e);
+ rte_update2(ah, nn, e, (p->mode == PIPE_OPAQUE) ? &p->p : src);
src_table->pipe_busy = 0;
}
static int
pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED)
{
- struct proto *pp = (*ee)->sender;
+ struct proto *pp = (*ee)->sender->proto;
if (pp == P)
return -1; /* Avoid local loops automatically */
@@ -112,20 +126,39 @@ pipe_reload_routes(struct proto *P)
return 1;
}
+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)
{
struct pipe_proto *p = (struct pipe_proto *) P;
- struct announce_hook *a;
- /* Clean up the secondary stats */
- bzero(&p->peer_stats, sizeof(struct proto_stats));
+ /* Lock both tables, unlock is handled in pipe_cleanup() */
+ rt_lock_table(P->table);
+ rt_lock_table(p->peer_table);
- /* Lock the peer table, unlock is handled in pipe_cleanup() */
- rt_lock_table(p->peer);
+ /* Going directly to PS_UP - prepare for feeding,
+ connect the protocol to both routing tables */
- /* Connect the protocol also to the peer routing table. */
- a = proto_add_announce_hook(P, p->peer);
+ P->main_ahook = proto_add_announce_hook(P, P->table,
+ FILTER_ACCEPT, P->cf->out_filter, &P->stats);
+ p->peer_ahook = proto_add_announce_hook(P, p->peer_table,
+ FILTER_ACCEPT, P->cf->in_filter, &p->peer_stats);
return PS_UP;
}
@@ -134,24 +167,15 @@ static void
pipe_cleanup(struct proto *P)
{
struct pipe_proto *p = (struct pipe_proto *) P;
- rt_unlock_table(p->peer);
-}
-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;
+ bzero(&P->stats, sizeof(struct proto_stats));
+ bzero(&p->peer_stats, sizeof(struct proto_stats));
- p->peer = c->peer->table;
- p->mode = c->mode;
- 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;
+ P->main_ahook = NULL;
+ p->peer_ahook = NULL;
- return P;
+ rt_unlock_table(P->table);
+ rt_unlock_table(p->peer_table);
}
static void
@@ -165,16 +189,34 @@ pipe_postconfig(struct proto_config *C)
cf_error("Primary table and peer table must be different");
}
+extern int proto_reconfig_type;
+
static int
pipe_reconfigure(struct proto *P, struct proto_config *new)
{
- // struct pipe_proto *p = (struct pipe_proto *) P;
- struct pipe_config *o = (struct pipe_config *) P->cf;
- struct pipe_config *n = (struct pipe_config *) new;
+ 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 ((o->peer->table != n->peer->table) || (o->mode != n->mode))
+ 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;
+
+ if (p->peer_ahook)
+ p->peer_ahook->out_filter = new->in_filter;
+
+ 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);
+
return 1;
}
@@ -190,19 +232,80 @@ pipe_get_status(struct proto *P, byte *buf)
{
struct pipe_proto *p = (struct pipe_proto *) P;
- bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer->name);
+ bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_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;
+
+ /*
+ * Pipe stats (as anything related to pipes) are a bit tricky. There
+ * are two sets of stats - s1 for ahook to the primary routing and
+ * s2 for the ahook to the secondary routing table. The user point
+ * of view is that routes going from the primary routing table to
+ * the secondary routing table are 'exported', while routes going in
+ * the other direction are 'imported'.
+ *
+ * Each route going through a pipe is, technically, first exported
+ * to the pipe and then imported from that pipe and such operations
+ * are counted in one set of stats according to the direction of the
+ * route propagation. Filtering is done just in the first part
+ * (export). Therefore, we compose stats for one directon for one
+ * user direction from both import and export stats, skipping
+ * immediate and irrelevant steps (exp_updates_accepted,
+ * imp_updates_received, imp_updates_filtered, ...).
+ *
+ * Rule of thumb is that stats s1 have the correct 'polarity'
+ * (imp/exp), while stats s2 have switched 'polarity'.
+ */
+
+ cli_msg(-1006, " Routes: %u imported, %u exported",
+ s1->imp_routes, s2->imp_routes);
+ cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
+ cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
+ s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid,
+ s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
+ cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
+ s2->exp_withdraws_received, s1->imp_withdraws_invalid,
+ s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
+ cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
+ s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
+ s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
+ cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
+ s1->exp_withdraws_received, s2->imp_withdraws_invalid,
+ s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
+}
+
+static void
+pipe_show_proto_info(struct proto *P)
+{
+ struct pipe_proto *p = (struct pipe_proto *) 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(P->cf->in_filter));
+ cli_msg(-1006, " Output filter: %s", filter_name(P->cf->out_filter));
+
+ if (P->proto_state != PS_DOWN)
+ pipe_show_stats(p);
}
struct protocol proto_pipe = {
- name: "Pipe",
- template: "pipe%d",
- preference: DEF_PREF_PIPE,
- 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,
+ name: "Pipe",
+ template: "pipe%d",
+ multitable: 1,
+ preference: DEF_PREF_PIPE,
+ 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,
+ show_proto_info: pipe_show_proto_info
};
diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h
index fbd21291..50b31698 100644
--- a/proto/pipe/pipe.h
+++ b/proto/pipe/pipe.h
@@ -20,7 +20,8 @@ struct pipe_config {
struct pipe_proto {
struct proto p;
- struct rtable *peer;
+ 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 */
};
@@ -31,10 +32,4 @@ extern struct protocol proto_pipe;
static inline int proto_is_pipe(struct proto *p)
{ return p->proto == &proto_pipe; }
-static inline struct rtable * pipe_get_peer_table(struct proto *P)
-{ return ((struct pipe_proto *) P)->peer; }
-
-static inline struct proto_stats * pipe_get_peer_stats(struct proto *P)
-{ return &((struct pipe_proto *) P)->peer_stats; }
-
#endif