Statistics
| Branch: | Revision:

iof-bird-daemon / proto / pipe / pipe.c @ 094d2bdb

History | View | Annotate | Download (10 KB)

1 55e06729 Martin Mares
/*
2
 *        BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
3
 *
4 50fe90ed Martin Mares
 *        (c) 1999--2000 Martin Mares <mj@ucw.cz>
5 55e06729 Martin Mares
 *
6
 *        Can be freely distributed and used under the terms of the GNU GPL.
7
 */
8
9 3b31c538 Martin Mares
/**
10
 * DOC: Pipe
11
 *
12
 * The Pipe protocol is very simple. It just connects to two routing tables
13
 * using proto_add_announce_hook() and whenever it receives a rt_notify()
14
 * about a change in one of the tables, it converts it to a rte_update()
15
 * in the other one.
16
 *
17
 * To avoid pipe loops, Pipe keeps a `being updated' flag in each routing
18
 * table.
19 c0adf7e9 Ondrej Zajicek
 *
20
 * A pipe has two announce hooks, the first connected to the main
21
 * table, the second connected to the peer table. When a new route is
22
 * announced on the main table, it gets checked by an export filter in
23
 * ahook 1, and, after that, it is announced to the peer table via
24
 * rte_update(), an import filter in ahook 2 is called. When a new
25
 * route is announced in the peer table, an export filter in ahook2
26
 * and an import filter in ahook 1 are used. Oviously, there is no
27 ebecb6f6 Ondrej Zajicek
 * need in filtering the same route twice, so both import filters are
28
 * set to accept, while user configured 'import' and 'export' filters
29
 * are used as export filters in ahooks 2 and 1. Route limits are
30
 * handled similarly, but on the import side of ahooks.
31 3b31c538 Martin Mares
 */
32
33 6b9fa320 Martin Mares
#undef LOCAL_DEBUG
34 55e06729 Martin Mares
35
#include "nest/bird.h"
36
#include "nest/iface.h"
37
#include "nest/protocol.h"
38
#include "nest/route.h"
39 c0adf7e9 Ondrej Zajicek
#include "nest/cli.h"
40 55e06729 Martin Mares
#include "conf/conf.h"
41
#include "filter/filter.h"
42 3daf783f Martin Mares
#include "lib/string.h"
43 55e06729 Martin Mares
44
#include "pipe.h"
45
46
static void
47 dca75fd7 Ondrej Zajicek
pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
48 55e06729 Martin Mares
{
49 dca75fd7 Ondrej Zajicek
  struct pipe_proto *p = (struct pipe_proto *) P;
50 c0adf7e9 Ondrej Zajicek
  struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
51
  rtable *dst_table = ah->table;
52 094d2bdb Ondrej Zajicek
  struct rte_src *src;
53 dca75fd7 Ondrej Zajicek
54 55e06729 Martin Mares
  net *nn;
55
  rte *e;
56
  rta a;
57
58 23ac9e9a Ondrej Zajicek
  if (!new && !old)
59
    return;
60
61 c0adf7e9 Ondrej Zajicek
  if (dst_table->pipe_busy)
62 55e06729 Martin Mares
    {
63
      log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
64 c0adf7e9 Ondrej Zajicek
          n->n.prefix, n->n.pxlen, dst_table->name);
65 55e06729 Martin Mares
      return;
66
    }
67 c0adf7e9 Ondrej Zajicek
68
  nn = net_get(dst_table, n->n.prefix, n->n.pxlen);
69 55e06729 Martin Mares
  if (new)
70
    {
71
      memcpy(&a, new->attrs, sizeof(rta));
72 2d45e09f Ondrej Zajicek
73
      if (p->mode == PIPE_OPAQUE)
74
        {
75 094d2bdb Ondrej Zajicek
          a.src = P->main_source;
76 2d45e09f Ondrej Zajicek
          a.source = RTS_PIPE;
77
        }
78
79 55e06729 Martin Mares
      a.aflags = 0;
80 02bd064a Martin Mares
      a.eattrs = attrs;
81 cfe34a31 Ondrej Zajicek
      a.hostentry = NULL;
82 55e06729 Martin Mares
      e = rte_get_temp(&a);
83
      e->net = nn;
84 0781e9c6 Ondrej Zajicek
      e->pflags = 0;
85 23ac9e9a Ondrej Zajicek
86 2d45e09f Ondrej Zajicek
      if (p->mode == PIPE_TRANSPARENT)
87
        {
88
          /* Copy protocol specific embedded attributes. */
89
          memcpy(&(e->u), &(new->u), sizeof(e->u));
90 0781e9c6 Ondrej Zajicek
          e->pref = new->pref;
91
          e->pflags = new->pflags;
92 2d45e09f Ondrej Zajicek
        }
93 23ac9e9a Ondrej Zajicek
94 094d2bdb Ondrej Zajicek
      src = a.src;
95 55e06729 Martin Mares
    }
96
  else
97 23ac9e9a Ondrej Zajicek
    {
98
      e = NULL;
99 094d2bdb Ondrej Zajicek
      src = old->attrs->src;
100 23ac9e9a Ondrej Zajicek
    }
101
102 11787b84 Ondrej Zajicek
  src_table->pipe_busy = 1;
103 094d2bdb Ondrej Zajicek
  rte_update2(ah, nn, e, src);
104 11787b84 Ondrej Zajicek
  src_table->pipe_busy = 0;
105 55e06729 Martin Mares
}
106
107
static int
108 662faa4a Martin Mares
pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED)
109 55e06729 Martin Mares
{
110 c0adf7e9 Ondrej Zajicek
  struct proto *pp = (*ee)->sender->proto;
111 55e06729 Martin Mares
112 dca75fd7 Ondrej Zajicek
  if (pp == P)
113 55e06729 Martin Mares
    return -1;        /* Avoid local loops automatically */
114
  return 0;
115
}
116
117
static int
118 ea7ada38 Ondrej Zajicek
pipe_reload_routes(struct proto *P)
119
{
120 ebecb6f6 Ondrej Zajicek
  struct pipe_proto *p = (struct pipe_proto *) P;
121
122 ea7ada38 Ondrej Zajicek
  /*
123
   * Because the pipe protocol feeds routes from both routing tables
124
   * together, both directions are reloaded during refeed and 'reload
125
   * out' command works like 'reload' command. For symmetry, we also
126
   * request refeed when 'reload in' command is used.
127
   */
128
  proto_request_feeding(P);
129 ebecb6f6 Ondrej Zajicek
130 7d0a31de Ondrej Zajicek
  proto_reset_limit(P->main_ahook->in_limit);
131
  proto_reset_limit(p->peer_ahook->in_limit);
132 ebecb6f6 Ondrej Zajicek
133 ea7ada38 Ondrej Zajicek
  return 1;
134
}
135
136 c0adf7e9 Ondrej Zajicek
static struct proto *
137
pipe_init(struct proto_config *C)
138
{
139
  struct pipe_config *c = (struct pipe_config *) C;
140
  struct proto *P = proto_new(C, sizeof(struct pipe_proto));
141
  struct pipe_proto *p = (struct pipe_proto *) P;
142
143
  p->mode = c->mode;
144
  p->peer_table = c->peer->table;
145
  P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY;
146
  P->rt_notify = pipe_rt_notify;
147
  P->import_control = pipe_import_control;
148
  P->reload_routes = pipe_reload_routes;
149
150
  return P;
151
}
152
153 ea7ada38 Ondrej Zajicek
static int
154 55e06729 Martin Mares
pipe_start(struct proto *P)
155
{
156 ebecb6f6 Ondrej Zajicek
  struct pipe_config *cf = (struct pipe_config *) P->cf;
157 55e06729 Martin Mares
  struct pipe_proto *p = (struct pipe_proto *) P;
158
159 c0adf7e9 Ondrej Zajicek
  /* Lock both tables, unlock is handled in pipe_cleanup() */
160
  rt_lock_table(P->table);
161
  rt_lock_table(p->peer_table);
162 11787b84 Ondrej Zajicek
163 c0adf7e9 Ondrej Zajicek
  /* Going directly to PS_UP - prepare for feeding,
164
     connect the protocol to both routing tables */
165 55e06729 Martin Mares
166 ebecb6f6 Ondrej Zajicek
  P->main_ahook = proto_add_announce_hook(P, P->table, &P->stats);
167
  P->main_ahook->out_filter = cf->c.out_filter;
168
  P->main_ahook->in_limit = cf->c.in_limit;
169 7d0a31de Ondrej Zajicek
  proto_reset_limit(P->main_ahook->in_limit);
170 ebecb6f6 Ondrej Zajicek
171
  p->peer_ahook = proto_add_announce_hook(P, p->peer_table, &p->peer_stats);
172
  p->peer_ahook->out_filter = cf->c.in_filter;
173 d9b77cc2 Ondrej Zajicek
  p->peer_ahook->in_limit = cf->c.out_limit;
174 7d0a31de Ondrej Zajicek
  proto_reset_limit(p->peer_ahook->in_limit);
175 55e06729 Martin Mares
176 094d2bdb Ondrej Zajicek
  if (p->mode == PIPE_OPAQUE)
177
    {
178
      P->main_source = rt_get_source(P, 0);
179
      rt_lock_source(P->main_source);
180
    }
181
182 55e06729 Martin Mares
  return PS_UP;
183
}
184
185 cfe34a31 Ondrej Zajicek
static void
186
pipe_cleanup(struct proto *P)
187
{
188
  struct pipe_proto *p = (struct pipe_proto *) P;
189
190 c0adf7e9 Ondrej Zajicek
  bzero(&P->stats, sizeof(struct proto_stats));
191
  bzero(&p->peer_stats, sizeof(struct proto_stats));
192 55e06729 Martin Mares
193 c0adf7e9 Ondrej Zajicek
  P->main_ahook = NULL;
194
  p->peer_ahook = NULL;
195 dca75fd7 Ondrej Zajicek
196 094d2bdb Ondrej Zajicek
  if (p->mode == PIPE_OPAQUE)
197
    rt_unlock_source(P->main_source);
198
  P->main_source = NULL;
199
200 c0adf7e9 Ondrej Zajicek
  rt_unlock_table(P->table);
201
  rt_unlock_table(p->peer_table);
202 55e06729 Martin Mares
}
203
204
static void
205
pipe_postconfig(struct proto_config *C)
206
{
207
  struct pipe_config *c = (struct pipe_config *) C;
208
209
  if (!c->peer)
210
    cf_error("Name of peer routing table not specified");
211
  if (c->peer == C->table)
212
    cf_error("Primary table and peer table must be different");
213
}
214
215 c0adf7e9 Ondrej Zajicek
extern int proto_reconfig_type;
216
217 0ec90e9f Martin Mares
static int
218 ea7ada38 Ondrej Zajicek
pipe_reconfigure(struct proto *P, struct proto_config *new)
219 0ec90e9f Martin Mares
{
220 c0adf7e9 Ondrej Zajicek
  struct pipe_proto *p = (struct pipe_proto *)P;
221
  struct proto_config *old = P->cf;
222
  struct pipe_config *oc = (struct pipe_config *) old;
223
  struct pipe_config *nc = (struct pipe_config *) new;
224 0ec90e9f Martin Mares
225 c0adf7e9 Ondrej Zajicek
  if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
226 ea7ada38 Ondrej Zajicek
    return 0;
227
228 c0adf7e9 Ondrej Zajicek
  /* Update output filters in ahooks */
229
  if (P->main_ahook)
230 ebecb6f6 Ondrej Zajicek
    {
231
      P->main_ahook->out_filter = new->out_filter;
232
      P->main_ahook->in_limit = new->in_limit;
233
    }
234 c0adf7e9 Ondrej Zajicek
235
  if (p->peer_ahook)
236 ebecb6f6 Ondrej Zajicek
    {
237
      p->peer_ahook->out_filter = new->in_filter;
238 d9b77cc2 Ondrej Zajicek
      p->peer_ahook->in_limit = new->out_limit;
239 ebecb6f6 Ondrej Zajicek
    }
240 c0adf7e9 Ondrej Zajicek
241
  if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
242
    return 1;
243
  
244
  if ((new->preference != old->preference)
245
      || ! filter_same(new->in_filter, old->in_filter)
246
      || ! filter_same(new->out_filter, old->out_filter))
247
    proto_request_feeding(P);
248
249 ea7ada38 Ondrej Zajicek
  return 1;
250 0ec90e9f Martin Mares
}
251
252 a7f23f58 Ondrej Zajicek
static void
253
pipe_copy_config(struct proto_config *dest, struct proto_config *src)
254
{
255
  /* Just a shallow copy, not many items here */
256
  proto_copy_rest(dest, src, sizeof(struct pipe_config));
257
}
258
259
static void
260
pipe_get_status(struct proto *P, byte *buf)
261
{
262
  struct pipe_proto *p = (struct pipe_proto *) P;
263
264 c0adf7e9 Ondrej Zajicek
  bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name);
265
}
266
267
static void
268
pipe_show_stats(struct pipe_proto *p)
269
{
270
  struct proto_stats *s1 = &p->p.stats;
271
  struct proto_stats *s2 = &p->peer_stats;
272
273
  /*
274
   * Pipe stats (as anything related to pipes) are a bit tricky. There
275
   * are two sets of stats - s1 for ahook to the primary routing and
276
   * s2 for the ahook to the secondary routing table. The user point
277
   * of view is that routes going from the primary routing table to
278
   * the secondary routing table are 'exported', while routes going in
279
   * the other direction are 'imported'.
280
   *
281
   * Each route going through a pipe is, technically, first exported
282
   * to the pipe and then imported from that pipe and such operations
283
   * are counted in one set of stats according to the direction of the
284
   * route propagation. Filtering is done just in the first part
285
   * (export). Therefore, we compose stats for one directon for one
286
   * user direction from both import and export stats, skipping
287
   * immediate and irrelevant steps (exp_updates_accepted,
288
   * imp_updates_received, imp_updates_filtered, ...).
289
   *
290
   * Rule of thumb is that stats s1 have the correct 'polarity'
291
   * (imp/exp), while stats s2 have switched 'polarity'.
292
   */
293
294
  cli_msg(-1006, "  Routes:         %u imported, %u exported", 
295
          s1->imp_routes, s2->imp_routes);
296
  cli_msg(-1006, "  Route change stats:     received   rejected   filtered    ignored   accepted");
297
  cli_msg(-1006, "    Import updates:     %10u %10u %10u %10u %10u",
298
          s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid,
299
          s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
300
  cli_msg(-1006, "    Import withdraws:   %10u %10u        --- %10u %10u",
301
          s2->exp_withdraws_received, s1->imp_withdraws_invalid,
302
          s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
303
  cli_msg(-1006, "    Export updates:     %10u %10u %10u %10u %10u",
304
          s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
305
          s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
306
  cli_msg(-1006, "    Export withdraws:   %10u %10u        --- %10u %10u",
307
          s1->exp_withdraws_received, s2->imp_withdraws_invalid,
308
          s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
309
}
310
311
static void
312
pipe_show_proto_info(struct proto *P)
313
{
314
  struct pipe_proto *p = (struct pipe_proto *) P;
315 ebecb6f6 Ondrej Zajicek
  struct pipe_config *cf = (struct pipe_config *) P->cf;
316 c0adf7e9 Ondrej Zajicek
317
  // cli_msg(-1006, "  Table:          %s", P->table->name);
318
  // cli_msg(-1006, "  Peer table:     %s", p->peer_table->name);
319
  cli_msg(-1006, "  Preference:     %d", P->preference);
320 ebecb6f6 Ondrej Zajicek
  cli_msg(-1006, "  Input filter:   %s", filter_name(cf->c.in_filter));
321
  cli_msg(-1006, "  Output filter:  %s", filter_name(cf->c.out_filter));
322
323
  proto_show_limit(cf->c.in_limit, "Import limit:");
324 d9b77cc2 Ondrej Zajicek
  proto_show_limit(cf->c.out_limit, "Export limit:");
325 c0adf7e9 Ondrej Zajicek
326
  if (P->proto_state != PS_DOWN)
327
    pipe_show_stats(p);
328 a7f23f58 Ondrej Zajicek
}
329
330 9db74169 Ondrej Zajicek
331 55e06729 Martin Mares
struct protocol proto_pipe = {
332 c0adf7e9 Ondrej Zajicek
  name:                        "Pipe",
333
  template:                "pipe%d",
334
  multitable:                1,
335
  preference:                DEF_PREF_PIPE,
336
  postconfig:                pipe_postconfig,
337
  init:                        pipe_init,
338
  start:                pipe_start,
339
  cleanup:                pipe_cleanup,
340
  reconfigure:                pipe_reconfigure,
341
  copy_config:          pipe_copy_config,
342
  get_status:                pipe_get_status,
343
  show_proto_info:        pipe_show_proto_info
344 55e06729 Martin Mares
};