]> git.ipfire.org Git - thirdparty/bird.git/blob - proto/pipe/pipe.c
Nest: Fix race condition during reconfiguration
[thirdparty/bird.git] / proto / pipe / pipe.c
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 int import_changed = ! filter_same(new->in_filter, old->in_filter);
234 int export_changed = ! filter_same(new->out_filter, old->out_filter);
235
236 /* Update output filters in ahooks */
237 if (P->main_ahook)
238 {
239 P->main_ahook->out_filter = new->out_filter;
240 P->main_ahook->in_limit = new->in_limit;
241 proto_verify_limits(P->main_ahook);
242
243 if (export_changed)
244 P->main_ahook->last_out_filter_change = now;
245 }
246
247 if (p->peer_ahook)
248 {
249 p->peer_ahook->out_filter = new->in_filter;
250 p->peer_ahook->in_limit = new->out_limit;
251 proto_verify_limits(p->peer_ahook);
252
253 if (import_changed)
254 p->peer_ahook->last_out_filter_change = now;
255 }
256
257 if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
258 return 1;
259
260 if (import_changed || export_changed || (new->preference != old->preference))
261 proto_request_feeding(P);
262
263 return 1;
264 }
265
266 static void
267 pipe_copy_config(struct proto_config *dest, struct proto_config *src)
268 {
269 /* Just a shallow copy, not many items here */
270 proto_copy_rest(dest, src, sizeof(struct pipe_config));
271 }
272
273 static void
274 pipe_get_status(struct proto *P, byte *buf)
275 {
276 struct pipe_proto *p = (struct pipe_proto *) P;
277
278 bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name);
279 }
280
281 static void
282 pipe_show_stats(struct pipe_proto *p)
283 {
284 struct proto_stats *s1 = &p->p.stats;
285 struct proto_stats *s2 = &p->peer_stats;
286
287 /*
288 * Pipe stats (as anything related to pipes) are a bit tricky. There
289 * are two sets of stats - s1 for ahook to the primary routing and
290 * s2 for the ahook to the secondary routing table. The user point
291 * of view is that routes going from the primary routing table to
292 * the secondary routing table are 'exported', while routes going in
293 * the other direction are 'imported'.
294 *
295 * Each route going through a pipe is, technically, first exported
296 * to the pipe and then imported from that pipe and such operations
297 * are counted in one set of stats according to the direction of the
298 * route propagation. Filtering is done just in the first part
299 * (export). Therefore, we compose stats for one directon for one
300 * user direction from both import and export stats, skipping
301 * immediate and irrelevant steps (exp_updates_accepted,
302 * imp_updates_received, imp_updates_filtered, ...).
303 *
304 * Rule of thumb is that stats s1 have the correct 'polarity'
305 * (imp/exp), while stats s2 have switched 'polarity'.
306 */
307
308 cli_msg(-1006, " Routes: %u imported, %u exported",
309 s1->imp_routes, s2->imp_routes);
310 cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
311 cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
312 s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid,
313 s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
314 cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
315 s2->exp_withdraws_received, s1->imp_withdraws_invalid,
316 s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
317 cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
318 s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
319 s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
320 cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
321 s1->exp_withdraws_received, s2->imp_withdraws_invalid,
322 s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
323 }
324
325 static void
326 pipe_show_proto_info(struct proto *P)
327 {
328 struct pipe_proto *p = (struct pipe_proto *) P;
329 struct pipe_config *cf = (struct pipe_config *) P->cf;
330
331 // cli_msg(-1006, " Table: %s", P->table->name);
332 // cli_msg(-1006, " Peer table: %s", p->peer_table->name);
333 cli_msg(-1006, " Preference: %d", P->preference);
334 cli_msg(-1006, " Input filter: %s", filter_name(cf->c.in_filter));
335 cli_msg(-1006, " Output filter: %s", filter_name(cf->c.out_filter));
336
337 proto_show_limit(cf->c.in_limit, "Import limit:");
338 proto_show_limit(cf->c.out_limit, "Export limit:");
339
340 if (P->proto_state != PS_DOWN)
341 pipe_show_stats(p);
342 }
343
344
345 struct protocol proto_pipe = {
346 .name = "Pipe",
347 .template = "pipe%d",
348 .multitable = 1,
349 .preference = DEF_PREF_PIPE,
350 .config_size = sizeof(struct pipe_config),
351 .postconfig = pipe_postconfig,
352 .init = pipe_init,
353 .start = pipe_start,
354 .cleanup = pipe_cleanup,
355 .reconfigure = pipe_reconfigure,
356 .copy_config = pipe_copy_config,
357 .get_status = pipe_get_status,
358 .show_proto_info = pipe_show_proto_info
359 };