Annotation of embedaddon/bird/proto/pipe/pipe.c, revision 1.1.1.1
1.1 misho 1: /*
2: * BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
3: *
4: * (c) 1999--2000 Martin Mares <mj@ucw.cz>
5: *
6: * Can be freely distributed and used under the terms of the GNU GPL.
7: */
8:
9: /**
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: *
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: * 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: */
32:
33: #undef LOCAL_DEBUG
34:
35: #include "nest/bird.h"
36: #include "nest/iface.h"
37: #include "nest/protocol.h"
38: #include "nest/route.h"
39: #include "nest/cli.h"
40: #include "conf/conf.h"
41: #include "filter/filter.h"
42: #include "lib/string.h"
43:
44: #include "pipe.h"
45:
46: static void
47: pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
48: {
49: struct pipe_proto *p = (struct pipe_proto *) P;
50: struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
51: rtable *dst_table = ah->table;
52: struct rte_src *src;
53:
54: net *nn;
55: rte *e;
56: rta a;
57:
58: if (!new && !old)
59: return;
60:
61: if (dst_table->pipe_busy)
62: {
63: log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
64: n->n.prefix, n->n.pxlen, dst_table->name);
65: return;
66: }
67:
68: nn = net_get(dst_table, n->n.prefix, n->n.pxlen);
69: if (new)
70: {
71: memcpy(&a, new->attrs, sizeof(rta));
72:
73: if (p->mode == PIPE_OPAQUE)
74: {
75: a.src = P->main_source;
76: a.source = RTS_PIPE;
77: }
78:
79: a.aflags = 0;
80: a.eattrs = attrs;
81: a.hostentry = NULL;
82: e = rte_get_temp(&a);
83: e->net = nn;
84: e->pflags = 0;
85:
86: if (p->mode == PIPE_TRANSPARENT)
87: {
88: /* Copy protocol specific embedded attributes. */
89: memcpy(&(e->u), &(new->u), sizeof(e->u));
90: e->pref = new->pref;
91: e->pflags = new->pflags;
92: }
93:
94: src = a.src;
95: }
96: else
97: {
98: e = NULL;
99: src = old->attrs->src;
100: }
101:
102: src_table->pipe_busy = 1;
103: rte_update2(ah, nn, e, src);
104: src_table->pipe_busy = 0;
105: }
106:
107: static int
108: pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED)
109: {
110: struct proto *pp = (*ee)->sender->proto;
111:
112: if (pp == P)
113: return -1; /* Avoid local loops automatically */
114: return 0;
115: }
116:
117: static int
118: pipe_reload_routes(struct proto *P)
119: {
120: struct pipe_proto *p = (struct pipe_proto *) P;
121:
122: /*
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:
130: proto_reset_limit(P->main_ahook->in_limit);
131: proto_reset_limit(p->peer_ahook->in_limit);
132:
133: return 1;
134: }
135:
136: 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: static int
154: pipe_start(struct proto *P)
155: {
156: struct pipe_config *cf = (struct pipe_config *) P->cf;
157: struct pipe_proto *p = (struct pipe_proto *) P;
158:
159: /* Lock both tables, unlock is handled in pipe_cleanup() */
160: rt_lock_table(P->table);
161: rt_lock_table(p->peer_table);
162:
163: /* Going directly to PS_UP - prepare for feeding,
164: connect the protocol to both routing tables */
165:
166: 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: proto_reset_limit(P->main_ahook->in_limit);
170:
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: p->peer_ahook->in_limit = cf->c.out_limit;
174: proto_reset_limit(p->peer_ahook->in_limit);
175:
176: 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: return PS_UP;
183: }
184:
185: static void
186: pipe_cleanup(struct proto *P)
187: {
188: struct pipe_proto *p = (struct pipe_proto *) P;
189:
190: bzero(&P->stats, sizeof(struct proto_stats));
191: bzero(&p->peer_stats, sizeof(struct proto_stats));
192:
193: P->main_ahook = NULL;
194: p->peer_ahook = NULL;
195:
196: if (p->mode == PIPE_OPAQUE)
197: rt_unlock_source(P->main_source);
198: P->main_source = NULL;
199:
200: rt_unlock_table(P->table);
201: rt_unlock_table(p->peer_table);
202: }
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: if (C->in_keep_filtered)
215: cf_error("Pipe protocol prohibits keeping filtered routes");
216: if (C->rx_limit)
217: cf_error("Pipe protocol does not support receive limits");
218: }
219:
220: extern int proto_reconfig_type;
221:
222: static int
223: pipe_reconfigure(struct proto *P, struct proto_config *new)
224: {
225: struct pipe_proto *p = (struct pipe_proto *)P;
226: struct proto_config *old = P->cf;
227: struct pipe_config *oc = (struct pipe_config *) old;
228: struct pipe_config *nc = (struct pipe_config *) new;
229:
230: if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
231: return 0;
232:
233: /* Update output filters in ahooks */
234: if (P->main_ahook)
235: {
236: P->main_ahook->out_filter = new->out_filter;
237: P->main_ahook->in_limit = new->in_limit;
238: proto_verify_limits(P->main_ahook);
239: }
240:
241: if (p->peer_ahook)
242: {
243: p->peer_ahook->out_filter = new->in_filter;
244: p->peer_ahook->in_limit = new->out_limit;
245: proto_verify_limits(p->peer_ahook);
246: }
247:
248: if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
249: return 1;
250:
251: if ((new->preference != old->preference)
252: || ! filter_same(new->in_filter, old->in_filter)
253: || ! filter_same(new->out_filter, old->out_filter))
254: proto_request_feeding(P);
255:
256: return 1;
257: }
258:
259: static void
260: pipe_copy_config(struct proto_config *dest, struct proto_config *src)
261: {
262: /* Just a shallow copy, not many items here */
263: proto_copy_rest(dest, src, sizeof(struct pipe_config));
264: }
265:
266: static void
267: pipe_get_status(struct proto *P, byte *buf)
268: {
269: struct pipe_proto *p = (struct pipe_proto *) P;
270:
271: bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name);
272: }
273:
274: static void
275: pipe_show_stats(struct pipe_proto *p)
276: {
277: struct proto_stats *s1 = &p->p.stats;
278: struct proto_stats *s2 = &p->peer_stats;
279:
280: /*
281: * Pipe stats (as anything related to pipes) are a bit tricky. There
282: * are two sets of stats - s1 for ahook to the primary routing and
283: * s2 for the ahook to the secondary routing table. The user point
284: * of view is that routes going from the primary routing table to
285: * the secondary routing table are 'exported', while routes going in
286: * the other direction are 'imported'.
287: *
288: * Each route going through a pipe is, technically, first exported
289: * to the pipe and then imported from that pipe and such operations
290: * are counted in one set of stats according to the direction of the
291: * route propagation. Filtering is done just in the first part
292: * (export). Therefore, we compose stats for one directon for one
293: * user direction from both import and export stats, skipping
294: * immediate and irrelevant steps (exp_updates_accepted,
295: * imp_updates_received, imp_updates_filtered, ...).
296: *
297: * Rule of thumb is that stats s1 have the correct 'polarity'
298: * (imp/exp), while stats s2 have switched 'polarity'.
299: */
300:
301: cli_msg(-1006, " Routes: %u imported, %u exported",
302: s1->imp_routes, s2->imp_routes);
303: cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
304: cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
305: s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid,
306: s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
307: cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
308: s2->exp_withdraws_received, s1->imp_withdraws_invalid,
309: s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
310: cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
311: s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
312: s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
313: cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
314: s1->exp_withdraws_received, s2->imp_withdraws_invalid,
315: s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
316: }
317:
318: static void
319: pipe_show_proto_info(struct proto *P)
320: {
321: struct pipe_proto *p = (struct pipe_proto *) P;
322: struct pipe_config *cf = (struct pipe_config *) P->cf;
323:
324: // cli_msg(-1006, " Table: %s", P->table->name);
325: // cli_msg(-1006, " Peer table: %s", p->peer_table->name);
326: cli_msg(-1006, " Preference: %d", P->preference);
327: cli_msg(-1006, " Input filter: %s", filter_name(cf->c.in_filter));
328: cli_msg(-1006, " Output filter: %s", filter_name(cf->c.out_filter));
329:
330: proto_show_limit(cf->c.in_limit, "Import limit:");
331: proto_show_limit(cf->c.out_limit, "Export limit:");
332:
333: if (P->proto_state != PS_DOWN)
334: pipe_show_stats(p);
335: }
336:
337:
338: struct protocol proto_pipe = {
339: .name = "Pipe",
340: .template = "pipe%d",
341: .multitable = 1,
342: .preference = DEF_PREF_PIPE,
343: .config_size = sizeof(struct pipe_config),
344: .postconfig = pipe_postconfig,
345: .init = pipe_init,
346: .start = pipe_start,
347: .cleanup = pipe_cleanup,
348: .reconfigure = pipe_reconfigure,
349: .copy_config = pipe_copy_config,
350: .get_status = pipe_get_status,
351: .show_proto_info = pipe_show_proto_info
352: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>