a7f23f581f
Based on the patch from Alexander V. Chernikov. Extended to support almost all protocols. Uses 'protocol bgp NAME from TEMPLATE { ... }' syntax.
207 lines
4.8 KiB
C
207 lines
4.8 KiB
C
/*
|
|
* BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
|
|
*
|
|
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
|
|
*
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
*/
|
|
|
|
/**
|
|
* DOC: Pipe
|
|
*
|
|
* The Pipe protocol is very simple. It just connects to two routing tables
|
|
* using proto_add_announce_hook() and whenever it receives a rt_notify()
|
|
* about a change in one of the tables, it converts it to a rte_update()
|
|
* in the other one.
|
|
*
|
|
* To avoid pipe loops, Pipe keeps a `being updated' flag in each routing
|
|
* table.
|
|
*/
|
|
|
|
#undef LOCAL_DEBUG
|
|
|
|
#include "nest/bird.h"
|
|
#include "nest/iface.h"
|
|
#include "nest/protocol.h"
|
|
#include "nest/route.h"
|
|
#include "conf/conf.h"
|
|
#include "filter/filter.h"
|
|
#include "lib/string.h"
|
|
|
|
#include "pipe.h"
|
|
|
|
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 proto *src;
|
|
|
|
net *nn;
|
|
rte *e;
|
|
rta a;
|
|
|
|
if (!new && !old)
|
|
return;
|
|
|
|
if (dest->pipe_busy)
|
|
{
|
|
log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
|
|
n->n.prefix, n->n.pxlen, dest->name);
|
|
return;
|
|
}
|
|
nn = net_get(dest, n->n.prefix, n->n.pxlen);
|
|
if (new)
|
|
{
|
|
memcpy(&a, new->attrs, sizeof(rta));
|
|
|
|
if (p->mode == PIPE_OPAQUE)
|
|
{
|
|
a.proto = &p->p;
|
|
a.source = RTS_PIPE;
|
|
}
|
|
|
|
a.aflags = 0;
|
|
a.eattrs = attrs;
|
|
a.hostentry = NULL;
|
|
e = rte_get_temp(&a);
|
|
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;
|
|
}
|
|
|
|
src = new->attrs->proto;
|
|
}
|
|
else
|
|
{
|
|
e = NULL;
|
|
src = old->attrs->proto;
|
|
}
|
|
|
|
src_table->pipe_busy = 1;
|
|
rte_update(dest, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e);
|
|
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;
|
|
|
|
if (pp == P)
|
|
return -1; /* Avoid local loops automatically */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pipe_reload_routes(struct 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);
|
|
return 1;
|
|
}
|
|
|
|
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 the peer table, unlock is handled in pipe_cleanup() */
|
|
rt_lock_table(p->peer);
|
|
|
|
/* Connect the protocol also to the peer routing table. */
|
|
a = proto_add_announce_hook(P, p->peer);
|
|
|
|
return PS_UP;
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
return P;
|
|
}
|
|
|
|
static void
|
|
pipe_postconfig(struct proto_config *C)
|
|
{
|
|
struct pipe_config *c = (struct pipe_config *) C;
|
|
|
|
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");
|
|
}
|
|
|
|
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;
|
|
|
|
if ((o->peer->table != n->peer->table) || (o->mode != n->mode))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
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;
|
|
|
|
bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer->name);
|
|
}
|
|
|
|
|
|
struct protocol proto_pipe = {
|
|
name: "Pipe",
|
|
template: "pipe%d",
|
|
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,
|
|
};
|