mirror of
				https://gitlab.labs.nic.cz/labs/bird.git
				synced 2024-05-11 16:54:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			191 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			4.4 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;
 | 
						|
      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 proto_fell_down() */
 | 
						|
  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 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 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);
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
struct protocol proto_pipe = {
 | 
						|
  name:		"Pipe",
 | 
						|
  template:	"pipe%d",
 | 
						|
  postconfig:	pipe_postconfig,
 | 
						|
  init:		pipe_init,
 | 
						|
  start:	pipe_start,
 | 
						|
  reconfigure:	pipe_reconfigure,
 | 
						|
  get_status:	pipe_get_status,
 | 
						|
};
 |