pipe.c 9.1 KB
Newer Older
1 2 3
/*
 *	BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
 *
4
 *	(c) 1999--2000 Martin Mares <mj@ucw.cz>
5 6 7 8
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

9 10 11 12 13 14 15 16 17 18
/**
 * 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.
19 20 21 22 23 24 25 26 27 28 29
 *
 * 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.
30 31
 */

32
#undef LOCAL_DEBUG
33 34 35 36 37

#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
38
#include "nest/cli.h"
39 40
#include "conf/conf.h"
#include "filter/filter.h"
41
#include "lib/string.h"
42 43 44 45

#include "pipe.h"

static void
46
pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
47
{
48
  struct pipe_proto *p = (struct pipe_proto *) P;
49 50
  struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
  rtable *dst_table = ah->table;
51
  struct proto *src;
52

53 54 55 56
  net *nn;
  rte *e;
  rta a;

57 58 59
  if (!new && !old)
    return;

60
  if (dst_table->pipe_busy)
61 62
    {
      log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
63
	  n->n.prefix, n->n.pxlen, dst_table->name);
64 65
      return;
    }
66 67

  nn = net_get(dst_table, n->n.prefix, n->n.pxlen);
68 69 70
  if (new)
    {
      memcpy(&a, new->attrs, sizeof(rta));
71 72 73 74 75 76 77

      if (p->mode == PIPE_OPAQUE)
	{
	  a.proto = &p->p;
	  a.source = RTS_PIPE;
	}

78
      a.aflags = 0;
79
      a.eattrs = attrs;
80
      a.hostentry = NULL;
81 82
      e = rte_get_temp(&a);
      e->net = nn;
83
      e->pflags = 0;
84

85 86 87 88
      if (p->mode == PIPE_TRANSPARENT)
	{
	  /* Copy protocol specific embedded attributes. */
	  memcpy(&(e->u), &(new->u), sizeof(e->u));
89 90
	  e->pref = new->pref;
	  e->pflags = new->pflags;
91
	}
92 93

      src = new->attrs->proto;
94 95
    }
  else
96 97 98 99 100
    {
      e = NULL;
      src = old->attrs->proto;
    }

101
  src_table->pipe_busy = 1;
102
  rte_update2(ah, nn, e, (p->mode == PIPE_OPAQUE) ? &p->p : src);
103
  src_table->pipe_busy = 0;
104 105 106
}

static int
Martin Mareš's avatar
Martin Mareš committed
107
pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED)
108
{
109
  struct proto *pp = (*ee)->sender->proto;
110

111
  if (pp == P)
112 113 114 115
    return -1;	/* Avoid local loops automatically */
  return 0;
}

116 117 118 119 120 121 122 123 124 125 126 127 128
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;
}

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
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;
}

146 147 148 149 150
static int
pipe_start(struct proto *P)
{
  struct pipe_proto *p = (struct pipe_proto *) P;

151 152 153
  /* Lock both tables, unlock is handled in pipe_cleanup() */
  rt_lock_table(P->table);
  rt_lock_table(p->peer_table);
154

155 156
  /* Going directly to PS_UP - prepare for feeding,
     connect the protocol to both routing tables */
157

158 159 160 161
  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);
162 163 164 165

  return PS_UP;
}

166 167 168 169 170
static void
pipe_cleanup(struct proto *P)
{
  struct pipe_proto *p = (struct pipe_proto *) P;

171 172
  bzero(&P->stats, sizeof(struct proto_stats));
  bzero(&p->peer_stats, sizeof(struct proto_stats));
173

174 175
  P->main_ahook = NULL;
  p->peer_ahook = NULL;
176

177 178
  rt_unlock_table(P->table);
  rt_unlock_table(p->peer_table);
179 180 181 182 183 184 185 186 187 188 189 190 191
}

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");
}

192 193
extern int proto_reconfig_type;

194
static int
195
pipe_reconfigure(struct proto *P, struct proto_config *new)
196
{
197 198 199 200
  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;
201

202
  if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
203 204
    return 0;

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
  /* 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);

220
  return 1;
221 222
}

223 224 225 226 227 228 229 230 231 232 233 234
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;

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
  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);
295 296
}

297

298
struct protocol proto_pipe = {
299 300 301 302 303 304 305 306 307 308 309 310
  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
311
};