]>
Commit | Line | Data |
---|---|---|
2326b001 MM |
1 | /* |
2 | * BIRD -- Protocols | |
3 | * | |
50fe90ed | 4 | * (c) 1998--2000 Martin Mares <mj@ucw.cz> |
2326b001 MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
6b9fa320 | 9 | #undef LOCAL_DEBUG |
7f4a3988 | 10 | |
2326b001 MM |
11 | #include "nest/bird.h" |
12 | #include "nest/protocol.h" | |
13 | #include "lib/resource.h" | |
14 | #include "lib/lists.h" | |
67bd949a | 15 | #include "lib/event.h" |
46434a3c | 16 | #include "lib/timer.h" |
f14a4bec | 17 | #include "lib/string.h" |
fe7cec12 | 18 | #include "conf/conf.h" |
47b79306 MM |
19 | #include "nest/route.h" |
20 | #include "nest/iface.h" | |
333ddd4f | 21 | #include "nest/mpls.h" |
ae97b946 | 22 | #include "nest/cli.h" |
529c4149 | 23 | #include "filter/filter.h" |
d06a875b | 24 | #include "filter/f-inst.h" |
2326b001 | 25 | |
acb60628 | 26 | pool *proto_pool; |
4a23ede2 | 27 | list STATIC_LIST_INIT(proto_list); |
67bd949a | 28 | |
4a23ede2 | 29 | static list STATIC_LIST_INIT(protocol_list); |
ee7e2ffd | 30 | struct protocol *class_to_protocol[PROTOCOL__MAX]; |
67bd949a | 31 | |
61dae32b OZ |
32 | #define CD(c, msg, args...) ({ if (c->debug & D_STATES) log(L_TRACE "%s.%s: " msg, c->proto->name, c->name ?: "?", ## args); }) |
33 | #define PD(p, msg, args...) ({ if (p->debug & D_STATES) log(L_TRACE "%s: " msg, p->name, ## args); }) | |
839380d7 | 34 | |
ebecb6f6 | 35 | static timer *proto_shutdown_timer; |
0c791f87 OZ |
36 | static timer *gr_wait_timer; |
37 | ||
38 | #define GRS_NONE 0 | |
39 | #define GRS_INIT 1 | |
40 | #define GRS_ACTIVE 2 | |
41 | #define GRS_DONE 3 | |
42 | ||
43 | static int graceful_restart_state; | |
44 | static u32 graceful_restart_locks; | |
67bd949a MM |
45 | |
46 | static char *p_states[] = { "DOWN", "START", "UP", "STOP" }; | |
d15b0b0a | 47 | static char *c_states[] = { "DOWN", "START", "UP", "FLUSHING" }; |
61dae32b | 48 | static char *e_states[] = { "DOWN", "FEEDING", "READY" }; |
67bd949a | 49 | |
f4a60a9b OZ |
50 | extern struct protocol proto_unix_iface; |
51 | ||
00b85905 | 52 | static void channel_request_reload(struct channel *c); |
02552526 | 53 | static void proto_shutdown_loop(timer *); |
50fe90ed | 54 | static void proto_rethink_goal(struct proto *p); |
839380d7 | 55 | static char *proto_state_name(struct proto *p); |
f4a60a9b | 56 | static void channel_verify_limits(struct channel *c); |
734e9fb8 | 57 | static inline void channel_reset_limit(struct channel_limit *l); |
1a54b1c6 | 58 | |
1a54b1c6 | 59 | |
f4a60a9b OZ |
60 | static inline int proto_is_done(struct proto *p) |
61 | { return (p->proto_state == PS_DOWN) && (p->active_channels == 0); } | |
227af309 | 62 | |
f4a60a9b OZ |
63 | static inline int channel_is_active(struct channel *c) |
64 | { return (c->channel_state == CS_START) || (c->channel_state == CS_UP); } | |
227af309 | 65 | |
211fe69c OZ |
66 | static inline int channel_reloadable(struct channel *c) |
67 | { return c->proto->reload_routes && c->reloadable; } | |
68 | ||
61dae32b OZ |
69 | static inline void |
70 | channel_log_state_change(struct channel *c) | |
71 | { | |
72 | if (c->export_state) | |
73 | CD(c, "State changed to %s/%s", c_states[c->channel_state], e_states[c->export_state]); | |
74 | else | |
75 | CD(c, "State changed to %s", c_states[c->channel_state]); | |
76 | } | |
77 | ||
227af309 OZ |
78 | static void |
79 | proto_log_state_change(struct proto *p) | |
80 | { | |
81 | if (p->debug & D_STATES) | |
f4a60a9b OZ |
82 | { |
83 | char *name = proto_state_name(p); | |
84 | if (name != p->last_state_name_announced) | |
227af309 | 85 | { |
f4a60a9b OZ |
86 | p->last_state_name_announced = name; |
87 | PD(p, "State changed to %s", proto_state_name(p)); | |
227af309 | 88 | } |
f4a60a9b | 89 | } |
227af309 OZ |
90 | else |
91 | p->last_state_name_announced = NULL; | |
67bd949a | 92 | } |
2326b001 | 93 | |
f4a60a9b OZ |
94 | struct channel_config * |
95 | proto_cf_find_channel(struct proto_config *pc, uint net_type) | |
7f4a3988 | 96 | { |
f4a60a9b OZ |
97 | struct channel_config *cc; |
98 | ||
99 | WALK_LIST(cc, pc->channels) | |
100 | if (cc->net_type == net_type) | |
101 | return cc; | |
102 | ||
103 | return NULL; | |
7f4a3988 MM |
104 | } |
105 | ||
f4a60a9b OZ |
106 | /** |
107 | * proto_find_channel_by_table - find channel connected to a routing table | |
108 | * @p: protocol instance | |
109 | * @t: routing table | |
110 | * | |
111 | * Returns pointer to channel or NULL | |
112 | */ | |
113 | struct channel * | |
114 | proto_find_channel_by_table(struct proto *p, struct rtable *t) | |
1a54b1c6 | 115 | { |
f4a60a9b | 116 | struct channel *c; |
c0adf7e9 | 117 | |
f4a60a9b OZ |
118 | WALK_LIST(c, p->channels) |
119 | if (c->table == t) | |
120 | return c; | |
0c791f87 | 121 | |
f4a60a9b | 122 | return NULL; |
1a54b1c6 MM |
123 | } |
124 | ||
b2949999 OZ |
125 | /** |
126 | * proto_find_channel_by_name - find channel by its name | |
127 | * @p: protocol instance | |
128 | * @n: channel name | |
129 | * | |
130 | * Returns pointer to channel or NULL | |
131 | */ | |
132 | struct channel * | |
133 | proto_find_channel_by_name(struct proto *p, const char *n) | |
134 | { | |
135 | struct channel *c; | |
136 | ||
137 | WALK_LIST(c, p->channels) | |
138 | if (!strcmp(c->name, n)) | |
139 | return c; | |
140 | ||
141 | return NULL; | |
142 | } | |
143 | ||
3c6269b8 | 144 | /** |
f4a60a9b | 145 | * proto_add_channel - connect protocol to a routing table |
3c6269b8 | 146 | * @p: protocol instance |
f4a60a9b | 147 | * @cf: channel configuration |
3c6269b8 | 148 | * |
f4a60a9b OZ |
149 | * This function creates a channel between the protocol instance @p and the |
150 | * routing table specified in the configuration @cf, making the protocol hear | |
151 | * all changes in the table and allowing the protocol to update routes in the | |
152 | * table. | |
c0adf7e9 | 153 | * |
f4a60a9b OZ |
154 | * The channel is linked in the protocol channel list and when active also in |
155 | * the table channel list. Channels are allocated from the global resource pool | |
156 | * (@proto_pool) and they are automatically freed when the protocol is removed. | |
3c6269b8 | 157 | */ |
f4a60a9b OZ |
158 | |
159 | struct channel * | |
160 | proto_add_channel(struct proto *p, struct channel_config *cf) | |
161 | { | |
162 | struct channel *c = mb_allocz(proto_pool, cf->channel->channel_size); | |
163 | ||
164 | c->name = cf->name; | |
165 | c->channel = cf->channel; | |
166 | c->proto = p; | |
167 | c->table = cf->table->table; | |
168 | ||
169 | c->in_filter = cf->in_filter; | |
170 | c->out_filter = cf->out_filter; | |
171 | c->rx_limit = cf->rx_limit; | |
172 | c->in_limit = cf->in_limit; | |
173 | c->out_limit = cf->out_limit; | |
174 | ||
175 | c->net_type = cf->net_type; | |
176 | c->ra_mode = cf->ra_mode; | |
177 | c->preference = cf->preference; | |
61dae32b | 178 | c->debug = cf->debug; |
f4a60a9b OZ |
179 | c->merge_limit = cf->merge_limit; |
180 | c->in_keep_filtered = cf->in_keep_filtered; | |
d3782c72 | 181 | c->rpki_reload = cf->rpki_reload; |
f4deef89 | 182 | c->bmp_hack = cf->bmp_hack; |
f4a60a9b OZ |
183 | |
184 | c->channel_state = CS_DOWN; | |
185 | c->export_state = ES_DOWN; | |
f047271c | 186 | c->last_state_change = current_time(); |
f4a60a9b OZ |
187 | c->reloadable = 1; |
188 | ||
00b85905 OZ |
189 | init_list(&c->roa_subscriptions); |
190 | ||
f4a60a9b OZ |
191 | CALL(c->channel->init, c, cf); |
192 | ||
193 | add_tail(&p->channels, &c->n); | |
194 | ||
61dae32b | 195 | CD(c, "Connected to table %s", c->table->name); |
f4a60a9b OZ |
196 | |
197 | return c; | |
198 | } | |
199 | ||
200 | void | |
61dae32b | 201 | proto_remove_channel(struct proto *p UNUSED, struct channel *c) |
f4a60a9b OZ |
202 | { |
203 | ASSERT(c->channel_state == CS_DOWN); | |
204 | ||
61dae32b | 205 | CD(c, "Removed", c->name); |
f4a60a9b OZ |
206 | |
207 | rem_node(&c->n); | |
208 | mb_free(c); | |
209 | } | |
210 | ||
211 | ||
212 | static void | |
213 | proto_start_channels(struct proto *p) | |
214 | { | |
215 | struct channel *c; | |
216 | WALK_LIST(c, p->channels) | |
217 | if (!c->disabled) | |
218 | channel_set_state(c, CS_UP); | |
219 | } | |
220 | ||
221 | static void | |
222 | proto_pause_channels(struct proto *p) | |
0e02abfd | 223 | { |
f4a60a9b OZ |
224 | struct channel *c; |
225 | WALK_LIST(c, p->channels) | |
226 | if (!c->disabled && channel_is_active(c)) | |
227 | channel_set_state(c, CS_START); | |
228 | } | |
0e02abfd | 229 | |
f4a60a9b OZ |
230 | static void |
231 | proto_stop_channels(struct proto *p) | |
232 | { | |
233 | struct channel *c; | |
234 | WALK_LIST(c, p->channels) | |
235 | if (!c->disabled && channel_is_active(c)) | |
236 | channel_set_state(c, CS_FLUSHING); | |
237 | } | |
c0adf7e9 | 238 | |
f4a60a9b OZ |
239 | static void |
240 | proto_remove_channels(struct proto *p) | |
241 | { | |
242 | struct channel *c; | |
243 | WALK_LIST_FIRST(c, p->channels) | |
244 | proto_remove_channel(p, c); | |
245 | } | |
246 | ||
247 | static void | |
248 | channel_schedule_feed(struct channel *c, int initial) | |
249 | { | |
250 | // DBG("%s: Scheduling meal\n", p->name); | |
251 | ASSERT(c->channel_state == CS_UP); | |
c0adf7e9 | 252 | |
f4a60a9b OZ |
253 | c->export_state = ES_FEEDING; |
254 | c->refeeding = !initial; | |
c0adf7e9 | 255 | |
7be3af7f | 256 | ev_schedule_work(c->feed_event); |
f4a60a9b OZ |
257 | } |
258 | ||
259 | static void | |
260 | channel_feed_loop(void *ptr) | |
261 | { | |
262 | struct channel *c = ptr; | |
263 | ||
264 | if (c->export_state != ES_FEEDING) | |
265 | return; | |
266 | ||
00b85905 | 267 | /* Start feeding */ |
f4a60a9b | 268 | if (!c->feed_active) |
00b85905 | 269 | { |
f4a60a9b OZ |
270 | if (c->proto->feed_begin) |
271 | c->proto->feed_begin(c, !c->refeeding); | |
272 | ||
00b85905 OZ |
273 | c->refeed_pending = 0; |
274 | } | |
275 | ||
f4a60a9b OZ |
276 | // DBG("Feeding protocol %s continued\n", p->name); |
277 | if (!rt_feed_channel(c)) | |
278 | { | |
7be3af7f | 279 | ev_schedule_work(c->feed_event); |
f4a60a9b OZ |
280 | return; |
281 | } | |
282 | ||
5ea39eaa OZ |
283 | /* Reset export limit if the feed ended with acceptable number of exported routes */ |
284 | struct channel_limit *l = &c->out_limit; | |
285 | if (c->refeeding && | |
286 | (l->state == PLS_BLOCKED) && | |
287 | (c->refeed_count <= l->limit) && | |
288 | (c->stats.exp_routes <= l->limit)) | |
289 | { | |
290 | log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->limit); | |
291 | channel_reset_limit(&c->out_limit); | |
292 | ||
293 | /* Continue in feed - it will process routing table again from beginning */ | |
294 | c->refeed_count = 0; | |
7be3af7f | 295 | ev_schedule_work(c->feed_event); |
5ea39eaa OZ |
296 | return; |
297 | } | |
298 | ||
f4a60a9b OZ |
299 | // DBG("Feeding protocol %s finished\n", p->name); |
300 | c->export_state = ES_READY; | |
61dae32b | 301 | channel_log_state_change(c); |
f4a60a9b OZ |
302 | |
303 | if (c->proto->feed_end) | |
304 | c->proto->feed_end(c); | |
00b85905 OZ |
305 | |
306 | /* Restart feeding */ | |
307 | if (c->refeed_pending) | |
308 | channel_request_feeding(c); | |
f4a60a9b OZ |
309 | } |
310 | ||
311 | ||
00b85905 OZ |
312 | static void |
313 | channel_roa_in_changed(struct rt_subscription *s) | |
314 | { | |
315 | struct channel *c = s->data; | |
316 | int active = c->reload_event && ev_active(c->reload_event); | |
317 | ||
318 | CD(c, "Reload triggered by RPKI change%s", active ? " - already active" : ""); | |
319 | ||
320 | if (!active) | |
321 | channel_request_reload(c); | |
322 | else | |
323 | c->reload_pending = 1; | |
324 | } | |
325 | ||
326 | static void | |
327 | channel_roa_out_changed(struct rt_subscription *s) | |
328 | { | |
329 | struct channel *c = s->data; | |
330 | int active = (c->export_state == ES_FEEDING); | |
331 | ||
332 | CD(c, "Feeding triggered by RPKI change%s", active ? " - already active" : ""); | |
333 | ||
334 | if (!active) | |
335 | channel_request_feeding(c); | |
336 | else | |
337 | c->refeed_pending = 1; | |
338 | } | |
339 | ||
340 | /* Temporary code, subscriptions should be changed to resources */ | |
341 | struct roa_subscription { | |
342 | struct rt_subscription s; | |
343 | node roa_node; | |
344 | }; | |
345 | ||
346 | static int | |
347 | channel_roa_is_subscribed(struct channel *c, rtable *tab, int dir) | |
348 | { | |
349 | void (*hook)(struct rt_subscription *) = | |
350 | dir ? channel_roa_in_changed : channel_roa_out_changed; | |
351 | ||
352 | struct roa_subscription *s; | |
353 | node *n; | |
354 | ||
355 | WALK_LIST2(s, n, c->roa_subscriptions, roa_node) | |
356 | if ((s->s.tab == tab) && (s->s.hook == hook)) | |
357 | return 1; | |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
362 | ||
363 | static void | |
364 | channel_roa_subscribe(struct channel *c, rtable *tab, int dir) | |
365 | { | |
366 | if (channel_roa_is_subscribed(c, tab, dir)) | |
367 | return; | |
368 | ||
369 | struct roa_subscription *s = mb_allocz(c->proto->pool, sizeof(struct roa_subscription)); | |
370 | ||
371 | s->s.hook = dir ? channel_roa_in_changed : channel_roa_out_changed; | |
372 | s->s.data = c; | |
373 | rt_subscribe(tab, &s->s); | |
374 | ||
375 | add_tail(&c->roa_subscriptions, &s->roa_node); | |
376 | } | |
377 | ||
378 | static void | |
379 | channel_roa_unsubscribe(struct roa_subscription *s) | |
380 | { | |
381 | rt_unsubscribe(&s->s); | |
382 | rem_node(&s->roa_node); | |
383 | mb_free(s); | |
384 | } | |
385 | ||
386 | static void | |
387 | channel_roa_subscribe_filter(struct channel *c, int dir) | |
388 | { | |
389 | const struct filter *f = dir ? c->in_filter : c->out_filter; | |
390 | struct rtable *tab; | |
d3782c72 | 391 | int valid = 1, found = 0; |
00b85905 OZ |
392 | |
393 | if ((f == FILTER_ACCEPT) || (f == FILTER_REJECT)) | |
394 | return; | |
395 | ||
211fe69c OZ |
396 | /* No automatic reload for non-reloadable channels */ |
397 | if (dir && !channel_reloadable(c)) | |
398 | valid = 0; | |
399 | ||
0a3db4c6 | 400 | #ifdef CONFIG_BGP |
d3782c72 OZ |
401 | /* No automatic reload for BGP channels without in_table / out_table */ |
402 | if (c->channel == &channel_bgp) | |
403 | valid = dir ? !!c->in_table : !!c->out_table; | |
0a3db4c6 | 404 | #endif |
d3782c72 | 405 | |
00b85905 OZ |
406 | struct filter_iterator fit; |
407 | FILTER_ITERATE_INIT(&fit, f, c->proto->pool); | |
408 | ||
409 | FILTER_ITERATE(&fit, fi) | |
410 | { | |
411 | switch (fi->fi_code) | |
412 | { | |
413 | case FI_ROA_CHECK_IMPLICIT: | |
414 | tab = fi->i_FI_ROA_CHECK_IMPLICIT.rtc->table; | |
d3782c72 OZ |
415 | if (valid) channel_roa_subscribe(c, tab, dir); |
416 | found = 1; | |
00b85905 OZ |
417 | break; |
418 | ||
419 | case FI_ROA_CHECK_EXPLICIT: | |
420 | tab = fi->i_FI_ROA_CHECK_EXPLICIT.rtc->table; | |
d3782c72 OZ |
421 | if (valid) channel_roa_subscribe(c, tab, dir); |
422 | found = 1; | |
00b85905 OZ |
423 | break; |
424 | ||
425 | default: | |
426 | break; | |
427 | } | |
428 | } | |
429 | FILTER_ITERATE_END; | |
430 | ||
431 | FILTER_ITERATE_CLEANUP(&fit); | |
d3782c72 OZ |
432 | |
433 | if (!valid && found) | |
434 | log(L_WARN "%s.%s: Automatic RPKI reload not active for %s", | |
435 | c->proto->name, c->name ?: "?", dir ? "import" : "export"); | |
00b85905 OZ |
436 | } |
437 | ||
438 | static void | |
439 | channel_roa_unsubscribe_all(struct channel *c) | |
440 | { | |
441 | struct roa_subscription *s; | |
442 | node *n, *x; | |
443 | ||
444 | WALK_LIST2_DELSAFE(s, n, x, c->roa_subscriptions, roa_node) | |
445 | channel_roa_unsubscribe(s); | |
446 | } | |
447 | ||
f4a60a9b OZ |
448 | static void |
449 | channel_start_export(struct channel *c) | |
450 | { | |
451 | ASSERT(c->channel_state == CS_UP); | |
452 | ASSERT(c->export_state == ES_DOWN); | |
453 | ||
52641e08 | 454 | channel_schedule_feed(c, 1); /* Sets ES_FEEDING */ |
f4a60a9b OZ |
455 | } |
456 | ||
457 | static void | |
458 | channel_stop_export(struct channel *c) | |
459 | { | |
460 | /* Need to abort feeding */ | |
461 | if (c->export_state == ES_FEEDING) | |
462 | rt_feed_channel_abort(c); | |
463 | ||
464 | c->export_state = ES_DOWN; | |
7a7ac656 | 465 | c->stats.exp_routes = 0; |
5ea39eaa | 466 | bmap_reset(&c->export_map, 1024); |
f4a60a9b OZ |
467 | } |
468 | ||
682d3f7d OZ |
469 | |
470 | /* Called by protocol for reload from in_table */ | |
471 | void | |
472 | channel_schedule_reload(struct channel *c) | |
473 | { | |
474 | ASSERT(c->channel_state == CS_UP); | |
475 | ||
476 | rt_reload_channel_abort(c); | |
7be3af7f | 477 | ev_schedule_work(c->reload_event); |
682d3f7d OZ |
478 | } |
479 | ||
480 | static void | |
481 | channel_reload_loop(void *ptr) | |
482 | { | |
483 | struct channel *c = ptr; | |
484 | ||
00b85905 OZ |
485 | /* Start reload */ |
486 | if (!c->reload_active) | |
487 | c->reload_pending = 0; | |
488 | ||
682d3f7d OZ |
489 | if (!rt_reload_channel(c)) |
490 | { | |
7be3af7f | 491 | ev_schedule_work(c->reload_event); |
682d3f7d OZ |
492 | return; |
493 | } | |
00b85905 OZ |
494 | |
495 | /* Restart reload */ | |
496 | if (c->reload_pending) | |
497 | channel_request_reload(c); | |
682d3f7d OZ |
498 | } |
499 | ||
500 | static void | |
501 | channel_reset_import(struct channel *c) | |
502 | { | |
503 | /* Need to abort feeding */ | |
504 | ev_postpone(c->reload_event); | |
505 | rt_reload_channel_abort(c); | |
506 | ||
507 | rt_prune_sync(c->in_table, 1); | |
508 | } | |
509 | ||
b7d7599c OZ |
510 | static void |
511 | channel_reset_export(struct channel *c) | |
512 | { | |
513 | /* Just free the routes */ | |
514 | rt_prune_sync(c->out_table, 1); | |
515 | } | |
516 | ||
682d3f7d OZ |
517 | /* Called by protocol to activate in_table */ |
518 | void | |
519 | channel_setup_in_table(struct channel *c) | |
520 | { | |
521 | struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config)); | |
ff397df7 | 522 | |
682d3f7d OZ |
523 | cf->name = "import"; |
524 | cf->addr_type = c->net_type; | |
ff397df7 | 525 | cf->internal = 1; |
682d3f7d | 526 | |
f4deef89 | 527 | c->in_table = cf->table = rt_setup(c->proto->pool, cf); |
682d3f7d OZ |
528 | |
529 | c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c); | |
530 | } | |
531 | ||
b7d7599c OZ |
532 | /* Called by protocol to activate out_table */ |
533 | void | |
534 | channel_setup_out_table(struct channel *c) | |
535 | { | |
536 | struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config)); | |
537 | cf->name = "export"; | |
538 | cf->addr_type = c->net_type; | |
ff397df7 | 539 | cf->internal = 1; |
b7d7599c | 540 | |
ff397df7 | 541 | c->out_table = rt_setup(c->proto->pool, cf); |
b7d7599c OZ |
542 | } |
543 | ||
682d3f7d | 544 | |
f4a60a9b OZ |
545 | static void |
546 | channel_do_start(struct channel *c) | |
547 | { | |
548 | rt_lock_table(c->table); | |
549 | add_tail(&c->table->channels, &c->table_node); | |
550 | c->proto->active_channels++; | |
551 | ||
961671c0 | 552 | c->feed_event = ev_new_init(c->proto->pool, channel_feed_loop, c); |
f4a60a9b | 553 | |
5ea39eaa OZ |
554 | bmap_init(&c->export_map, c->proto->pool, 1024); |
555 | memset(&c->stats, 0, sizeof(struct proto_stats)); | |
556 | ||
f4a60a9b OZ |
557 | channel_reset_limit(&c->rx_limit); |
558 | channel_reset_limit(&c->in_limit); | |
559 | channel_reset_limit(&c->out_limit); | |
560 | ||
561 | CALL(c->channel->start, c); | |
562 | } | |
563 | ||
00b85905 OZ |
564 | static void |
565 | channel_do_up(struct channel *c) | |
566 | { | |
567 | /* Register RPKI/ROA subscriptions */ | |
d3782c72 OZ |
568 | if (c->rpki_reload) |
569 | { | |
570 | channel_roa_subscribe_filter(c, 1); | |
571 | channel_roa_subscribe_filter(c, 0); | |
572 | } | |
00b85905 OZ |
573 | } |
574 | ||
f4a60a9b OZ |
575 | static void |
576 | channel_do_flush(struct channel *c) | |
577 | { | |
f4deef89 OZ |
578 | if (!c->bmp_hack) |
579 | rt_schedule_prune(c->table); | |
f4a60a9b OZ |
580 | |
581 | c->gr_wait = 0; | |
582 | if (c->gr_lock) | |
583 | channel_graceful_restart_unlock(c); | |
584 | ||
585 | CALL(c->channel->shutdown, c); | |
4ab54f1a OZ |
586 | |
587 | /* This have to be done in here, as channel pool is freed before channel_do_down() */ | |
588 | bmap_free(&c->export_map); | |
589 | c->in_table = NULL; | |
590 | c->reload_event = NULL; | |
591 | c->out_table = NULL; | |
00b85905 OZ |
592 | |
593 | channel_roa_unsubscribe_all(c); | |
f4a60a9b OZ |
594 | } |
595 | ||
596 | static void | |
597 | channel_do_down(struct channel *c) | |
598 | { | |
682d3f7d OZ |
599 | ASSERT(!c->feed_active && !c->reload_active); |
600 | ||
7a7ac656 | 601 | rem_node(&c->table_node); |
f4a60a9b OZ |
602 | rt_unlock_table(c->table); |
603 | c->proto->active_channels--; | |
604 | ||
605 | if ((c->stats.imp_routes + c->stats.filt_routes) != 0) | |
606 | log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name); | |
607 | ||
4ab54f1a | 608 | // bmap_free(&c->export_map); |
f4a60a9b OZ |
609 | memset(&c->stats, 0, sizeof(struct proto_stats)); |
610 | ||
682d3f7d OZ |
611 | c->in_table = NULL; |
612 | c->reload_event = NULL; | |
b7d7599c | 613 | c->out_table = NULL; |
682d3f7d | 614 | |
ff397df7 MM |
615 | /* The in_table and out_table are going to be freed by freeing their resource pools. */ |
616 | ||
d15b0b0a OZ |
617 | CALL(c->channel->cleanup, c); |
618 | ||
f4a60a9b OZ |
619 | /* Schedule protocol shutddown */ |
620 | if (proto_is_done(c->proto)) | |
621 | ev_schedule(c->proto->event); | |
622 | } | |
623 | ||
624 | void | |
625 | channel_set_state(struct channel *c, uint state) | |
626 | { | |
627 | uint cs = c->channel_state; | |
628 | uint es = c->export_state; | |
629 | ||
286e2011 | 630 | DBG("%s reporting channel %s state transition %s -> %s\n", c->proto->name, c->name, c_states[cs], c_states[state]); |
f4a60a9b OZ |
631 | if (state == cs) |
632 | return; | |
633 | ||
634 | c->channel_state = state; | |
f047271c | 635 | c->last_state_change = current_time(); |
f4a60a9b OZ |
636 | |
637 | switch (state) | |
638 | { | |
639 | case CS_START: | |
640 | ASSERT(cs == CS_DOWN || cs == CS_UP); | |
641 | ||
642 | if (cs == CS_DOWN) | |
643 | channel_do_start(c); | |
644 | ||
645 | if (es != ES_DOWN) | |
646 | channel_stop_export(c); | |
647 | ||
682d3f7d OZ |
648 | if (c->in_table && (cs == CS_UP)) |
649 | channel_reset_import(c); | |
650 | ||
b7d7599c OZ |
651 | if (c->out_table && (cs == CS_UP)) |
652 | channel_reset_export(c); | |
653 | ||
f4a60a9b OZ |
654 | break; |
655 | ||
656 | case CS_UP: | |
657 | ASSERT(cs == CS_DOWN || cs == CS_START); | |
658 | ||
659 | if (cs == CS_DOWN) | |
660 | channel_do_start(c); | |
661 | ||
2a013bb3 | 662 | if (!c->gr_wait && c->proto->rt_notify) |
f4a60a9b OZ |
663 | channel_start_export(c); |
664 | ||
00b85905 | 665 | channel_do_up(c); |
f4a60a9b OZ |
666 | break; |
667 | ||
668 | case CS_FLUSHING: | |
669 | ASSERT(cs == CS_START || cs == CS_UP); | |
670 | ||
671 | if (es != ES_DOWN) | |
672 | channel_stop_export(c); | |
673 | ||
682d3f7d OZ |
674 | if (c->in_table && (cs == CS_UP)) |
675 | channel_reset_import(c); | |
676 | ||
b7d7599c OZ |
677 | if (c->out_table && (cs == CS_UP)) |
678 | channel_reset_export(c); | |
679 | ||
f4a60a9b OZ |
680 | channel_do_flush(c); |
681 | break; | |
682 | ||
683 | case CS_DOWN: | |
684 | ASSERT(cs == CS_FLUSHING); | |
685 | ||
686 | channel_do_down(c); | |
687 | break; | |
688 | ||
689 | default: | |
690 | ASSERT(0); | |
691 | } | |
61dae32b OZ |
692 | |
693 | channel_log_state_change(c); | |
0e02abfd MM |
694 | } |
695 | ||
c0adf7e9 | 696 | /** |
f4a60a9b OZ |
697 | * channel_request_feeding - request feeding routes to the channel |
698 | * @c: given channel | |
c0adf7e9 | 699 | * |
f4a60a9b OZ |
700 | * Sometimes it is needed to send again all routes to the channel. This is |
701 | * called feeding and can be requested by this function. This would cause | |
702 | * channel export state transition to ES_FEEDING (during feeding) and when | |
703 | * completed, it will switch back to ES_READY. This function can be called | |
704 | * even when feeding is already running, in that case it is restarted. | |
c0adf7e9 | 705 | */ |
f4a60a9b OZ |
706 | void |
707 | channel_request_feeding(struct channel *c) | |
c0adf7e9 | 708 | { |
f4a60a9b | 709 | ASSERT(c->channel_state == CS_UP); |
c0adf7e9 | 710 | |
61dae32b OZ |
711 | CD(c, "Feeding requested"); |
712 | ||
f4a60a9b OZ |
713 | /* Do nothing if we are still waiting for feeding */ |
714 | if (c->export_state == ES_DOWN) | |
715 | return; | |
c0adf7e9 | 716 | |
f4a60a9b OZ |
717 | /* If we are already feeding, we want to restart it */ |
718 | if (c->export_state == ES_FEEDING) | |
719 | { | |
720 | /* Unless feeding is in initial state */ | |
721 | if (!c->feed_active) | |
722 | return; | |
723 | ||
724 | rt_feed_channel_abort(c); | |
725 | } | |
726 | ||
5ea39eaa OZ |
727 | /* Track number of exported routes during refeed */ |
728 | c->refeed_count = 0; | |
f4a60a9b OZ |
729 | |
730 | channel_schedule_feed(c, 0); /* Sets ES_FEEDING */ | |
61dae32b | 731 | channel_log_state_change(c); |
f4a60a9b OZ |
732 | } |
733 | ||
0c791f87 | 734 | static void |
f4a60a9b | 735 | channel_request_reload(struct channel *c) |
0c791f87 | 736 | { |
f4a60a9b | 737 | ASSERT(c->channel_state == CS_UP); |
2e507a74 | 738 | ASSERT(channel_reloadable(c)); |
f4a60a9b | 739 | |
61dae32b OZ |
740 | CD(c, "Reload requested"); |
741 | ||
f4a60a9b | 742 | c->proto->reload_routes(c); |
0c791f87 | 743 | |
f4a60a9b OZ |
744 | /* |
745 | * Should this be done before reload_routes() hook? | |
746 | * Perhaps, but routes are updated asynchronously. | |
747 | */ | |
748 | channel_reset_limit(&c->rx_limit); | |
749 | channel_reset_limit(&c->in_limit); | |
0c791f87 OZ |
750 | } |
751 | ||
f4a60a9b OZ |
752 | const struct channel_class channel_basic = { |
753 | .channel_size = sizeof(struct channel), | |
754 | .config_size = sizeof(struct channel_config) | |
755 | }; | |
756 | ||
757 | void * | |
72163bd5 | 758 | channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto) |
f4a60a9b OZ |
759 | { |
760 | struct channel_config *cf = NULL; | |
761 | struct rtable_config *tab = NULL; | |
f4a60a9b OZ |
762 | |
763 | if (net_type) | |
764 | { | |
765 | if (!net_val_match(net_type, proto->protocol->channel_mask)) | |
766 | cf_error("Unsupported channel type"); | |
767 | ||
333ddd4f | 768 | if (proto->net_type && (net_type != proto->net_type) && (net_type != NET_MPLS)) |
f4a60a9b OZ |
769 | cf_error("Different channel type"); |
770 | ||
771 | tab = new_config->def_tables[net_type]; | |
f4a60a9b OZ |
772 | } |
773 | ||
774 | if (!cc) | |
775 | cc = &channel_basic; | |
776 | ||
777 | cf = cfg_allocz(cc->config_size); | |
778 | cf->name = name; | |
779 | cf->channel = cc; | |
72163bd5 | 780 | cf->parent = proto; |
f4a60a9b OZ |
781 | cf->table = tab; |
782 | cf->out_filter = FILTER_REJECT; | |
783 | ||
784 | cf->net_type = net_type; | |
785 | cf->ra_mode = RA_OPTIMAL; | |
786 | cf->preference = proto->protocol->preference; | |
61dae32b | 787 | cf->debug = new_config->channel_default_debug; |
d3782c72 | 788 | cf->rpki_reload = 1; |
f4a60a9b OZ |
789 | |
790 | add_tail(&proto->channels, &cf->n); | |
791 | ||
792 | return cf; | |
793 | } | |
794 | ||
72163bd5 OZ |
795 | void * |
796 | channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto) | |
797 | { | |
798 | struct channel_config *cf; | |
799 | ||
800 | /* We are using name as token, so no strcmp() */ | |
801 | WALK_LIST(cf, proto->channels) | |
802 | if (cf->name == name) | |
803 | { | |
804 | /* Allow to redefine channel only if inherited from template */ | |
805 | if (cf->parent == proto) | |
806 | cf_error("Multiple %s channels", name); | |
807 | ||
808 | cf->parent = proto; | |
8478de88 | 809 | cf->copy = 1; |
72163bd5 OZ |
810 | return cf; |
811 | } | |
812 | ||
813 | return channel_config_new(cc, name, net_type, proto); | |
814 | } | |
815 | ||
f4a60a9b OZ |
816 | struct channel_config * |
817 | channel_copy_config(struct channel_config *src, struct proto_config *proto) | |
818 | { | |
819 | struct channel_config *dst = cfg_alloc(src->channel->config_size); | |
820 | ||
821 | memcpy(dst, src, src->channel->config_size); | |
1678bc07 | 822 | memset(&dst->n, 0, sizeof(node)); |
f4a60a9b OZ |
823 | add_tail(&proto->channels, &dst->n); |
824 | CALL(src->channel->copy_config, dst, src); | |
825 | ||
826 | return dst; | |
827 | } | |
828 | ||
829 | ||
830 | static int reconfigure_type; /* Hack to propagate type info to channel_reconfigure() */ | |
831 | ||
832 | int | |
833 | channel_reconfigure(struct channel *c, struct channel_config *cf) | |
834 | { | |
54430df9 OZ |
835 | /* Touched by reconfiguration */ |
836 | c->stale = 0; | |
837 | ||
f4a60a9b | 838 | /* FIXME: better handle these changes, also handle in_keep_filtered */ |
f8aad5d5 | 839 | if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode))) |
f4a60a9b OZ |
840 | return 0; |
841 | ||
94f9be80 OZ |
842 | /* Note that filter_same() requires arguments in (new, old) order */ |
843 | int import_changed = !filter_same(cf->in_filter, c->in_filter); | |
844 | int export_changed = !filter_same(cf->out_filter, c->out_filter); | |
d3782c72 | 845 | int rpki_reload_changed = (cf->rpki_reload != c->rpki_reload); |
f4a60a9b OZ |
846 | |
847 | if (c->preference != cf->preference) | |
848 | import_changed = 1; | |
849 | ||
850 | if (c->merge_limit != cf->merge_limit) | |
851 | export_changed = 1; | |
852 | ||
853 | /* Reconfigure channel fields */ | |
854 | c->in_filter = cf->in_filter; | |
855 | c->out_filter = cf->out_filter; | |
856 | c->rx_limit = cf->rx_limit; | |
857 | c->in_limit = cf->in_limit; | |
858 | c->out_limit = cf->out_limit; | |
859 | ||
860 | // c->ra_mode = cf->ra_mode; | |
861 | c->merge_limit = cf->merge_limit; | |
862 | c->preference = cf->preference; | |
61dae32b | 863 | c->debug = cf->debug; |
f4a60a9b | 864 | c->in_keep_filtered = cf->in_keep_filtered; |
d3782c72 | 865 | c->rpki_reload = cf->rpki_reload; |
f4a60a9b OZ |
866 | |
867 | channel_verify_limits(c); | |
868 | ||
d15b0b0a | 869 | /* Execute channel-specific reconfigure hook */ |
e2b530aa | 870 | if (c->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed)) |
d15b0b0a | 871 | return 0; |
f4a60a9b OZ |
872 | |
873 | /* If the channel is not open, it has no routes and we cannot reload it anyways */ | |
874 | if (c->channel_state != CS_UP) | |
61dae32b | 875 | goto done; |
f4a60a9b | 876 | |
00b85905 | 877 | /* Update RPKI/ROA subscriptions */ |
d3782c72 | 878 | if (import_changed || export_changed || rpki_reload_changed) |
00b85905 OZ |
879 | { |
880 | channel_roa_unsubscribe_all(c); | |
d3782c72 OZ |
881 | |
882 | if (c->rpki_reload) | |
883 | { | |
884 | channel_roa_subscribe_filter(c, 1); | |
885 | channel_roa_subscribe_filter(c, 0); | |
886 | } | |
00b85905 OZ |
887 | } |
888 | ||
f4a60a9b OZ |
889 | if (reconfigure_type == RECONFIG_SOFT) |
890 | { | |
891 | if (import_changed) | |
892 | log(L_INFO "Channel %s.%s changed import", c->proto->name, c->name); | |
893 | ||
894 | if (export_changed) | |
895 | log(L_INFO "Channel %s.%s changed export", c->proto->name, c->name); | |
896 | ||
61dae32b | 897 | goto done; |
f4a60a9b OZ |
898 | } |
899 | ||
900 | /* Route reload may be not supported */ | |
901 | if (import_changed && !channel_reloadable(c)) | |
902 | return 0; | |
903 | ||
904 | if (import_changed || export_changed) | |
905 | log(L_INFO "Reloading channel %s.%s", c->proto->name, c->name); | |
906 | ||
907 | if (import_changed) | |
908 | channel_request_reload(c); | |
909 | ||
910 | if (export_changed) | |
911 | channel_request_feeding(c); | |
912 | ||
61dae32b OZ |
913 | done: |
914 | CD(c, "Reconfigured"); | |
f4a60a9b OZ |
915 | return 1; |
916 | } | |
917 | ||
918 | ||
919 | int | |
920 | proto_configure_channel(struct proto *p, struct channel **pc, struct channel_config *cf) | |
0e02abfd | 921 | { |
f4a60a9b | 922 | struct channel *c = *pc; |
0e02abfd | 923 | |
f4a60a9b OZ |
924 | if (!c && cf) |
925 | { | |
d506263d OZ |
926 | /* We could add the channel, but currently it would just stay in down state |
927 | until protocol is restarted, so it is better to force restart anyways. */ | |
cfa6ff95 OZ |
928 | if (p->proto_state != PS_DOWN) |
929 | { | |
930 | log(L_INFO "Cannot add channel %s.%s", p->name, cf->name); | |
931 | return 0; | |
932 | } | |
933 | ||
934 | *pc = proto_add_channel(p, cf); | |
f4a60a9b OZ |
935 | } |
936 | else if (c && !cf) | |
937 | { | |
938 | if (c->channel_state != CS_DOWN) | |
939 | { | |
940 | log(L_INFO "Cannot remove channel %s.%s", c->proto->name, c->name); | |
941 | return 0; | |
942 | } | |
943 | ||
944 | proto_remove_channel(p, c); | |
945 | *pc = NULL; | |
946 | } | |
947 | else if (c && cf) | |
948 | { | |
949 | if (!channel_reconfigure(c, cf)) | |
950 | { | |
951 | log(L_INFO "Cannot reconfigure channel %s.%s", c->proto->name, c->name); | |
952 | return 0; | |
953 | } | |
954 | } | |
955 | ||
956 | return 1; | |
c0adf7e9 OZ |
957 | } |
958 | ||
333ddd4f OZ |
959 | /** |
960 | * proto_setup_mpls_map - automatically setup FEC map for protocol | |
961 | * @p: affected protocol | |
962 | * @rts: RTS_* value for generated MPLS routes | |
963 | * @hooks: whether to update rte_insert / rte_remove hooks | |
964 | * | |
965 | * Add, remove or reconfigure MPLS FEC map of the protocol @p, depends on | |
966 | * whether MPLS channel exists, and setup rte_insert / rte_remove hooks with | |
967 | * default MPLS handlers. It is a convenience function supposed to be called | |
968 | * from the protocol start and configure hooks, after reconfiguration of | |
969 | * channels. For shutdown, use proto_shutdown_mpls_map(). If caller uses its own | |
970 | * rte_insert / rte_remove hooks, it is possible to disable updating hooks and | |
971 | * doing that manually. | |
972 | */ | |
973 | void | |
974 | proto_setup_mpls_map(struct proto *p, uint rts, int hooks) | |
975 | { | |
976 | struct mpls_fec_map *m = p->mpls_map; | |
977 | struct channel *c = p->mpls_channel; | |
978 | ||
979 | if (!m && c) | |
980 | { | |
981 | /* | |
982 | * Note that when called from a protocol start hook, it is called before | |
983 | * mpls_channel_start(). But FEC map locks MPLS domain internally so it does | |
984 | * not depend on lock from MPLS channel. | |
985 | */ | |
986 | p->mpls_map = mpls_fec_map_new(p->pool, c, rts); | |
987 | } | |
988 | else if (m && !c) | |
989 | { | |
990 | /* | |
991 | * Note that for reconfiguration, it is called after the MPLS channel has | |
992 | * been already removed. But removal of active MPLS channel would trigger | |
993 | * protocol restart anyways. | |
994 | */ | |
995 | mpls_fec_map_free(m); | |
996 | p->mpls_map = NULL; | |
997 | } | |
998 | else if (m && c) | |
999 | { | |
a7a9df86 | 1000 | mpls_fec_map_reconfigure(m, c); |
333ddd4f OZ |
1001 | } |
1002 | ||
1003 | if (hooks) | |
1004 | { | |
1005 | p->rte_insert = p->mpls_map ? mpls_rte_insert : NULL; | |
1006 | p->rte_remove = p->mpls_map ? mpls_rte_remove : NULL; | |
1007 | } | |
1008 | } | |
1009 | ||
1010 | /** | |
1011 | * proto_shutdown_mpls_map - automatically shutdown FEC map for protocol | |
1012 | * @p: affected protocol | |
1013 | * @hooks: whether to update rte_insert / rte_remove hooks | |
1014 | * | |
1015 | * Remove MPLS FEC map of the protocol @p during protocol shutdown. | |
1016 | */ | |
1017 | void | |
1018 | proto_shutdown_mpls_map(struct proto *p, int hooks) | |
1019 | { | |
1020 | struct mpls_fec_map *m = p->mpls_map; | |
1021 | ||
1022 | if (!m) | |
1023 | return; | |
1024 | ||
1025 | mpls_fec_map_free(m); | |
1026 | p->mpls_map = NULL; | |
1027 | ||
1028 | if (hooks) | |
1029 | { | |
1030 | p->rte_insert = NULL; | |
1031 | p->rte_remove = NULL; | |
1032 | } | |
1033 | } | |
f4a60a9b | 1034 | |
c0adf7e9 | 1035 | static void |
f4a60a9b | 1036 | proto_event(void *ptr) |
c0adf7e9 | 1037 | { |
f4a60a9b OZ |
1038 | struct proto *p = ptr; |
1039 | ||
1040 | if (p->do_start) | |
1041 | { | |
1042 | if_feed_baby(p); | |
1043 | p->do_start = 0; | |
1044 | } | |
c0adf7e9 | 1045 | |
f4a60a9b | 1046 | if (p->do_stop) |
c0adf7e9 | 1047 | { |
f4a60a9b OZ |
1048 | if (p->proto == &proto_unix_iface) |
1049 | if_flush_ifaces(p); | |
1050 | p->do_stop = 0; | |
c0adf7e9 OZ |
1051 | } |
1052 | ||
f4a60a9b OZ |
1053 | if (proto_is_done(p)) |
1054 | { | |
1055 | if (p->proto->cleanup) | |
1056 | p->proto->cleanup(p); | |
1057 | ||
1058 | p->active = 0; | |
1059 | proto_log_state_change(p); | |
1060 | proto_rethink_goal(p); | |
1061 | } | |
1062 | } | |
1063 | ||
1064 | ||
1065 | /** | |
1066 | * proto_new - create a new protocol instance | |
1067 | * @c: protocol configuration | |
1068 | * | |
1069 | * When a new configuration has been read in, the core code starts | |
1070 | * initializing all the protocol instances configured by calling their | |
1071 | * init() hooks with the corresponding instance configuration. The initialization | |
1072 | * code of the protocol is expected to create a new instance according to the | |
1073 | * configuration by calling this function and then modifying the default settings | |
1074 | * to values wanted by the protocol. | |
1075 | */ | |
1076 | void * | |
1077 | proto_new(struct proto_config *cf) | |
1078 | { | |
1079 | struct proto *p = mb_allocz(proto_pool, cf->protocol->proto_size); | |
1080 | ||
1081 | p->cf = cf; | |
1082 | p->debug = cf->debug; | |
1083 | p->mrtdump = cf->mrtdump; | |
1084 | p->name = cf->name; | |
1085 | p->proto = cf->protocol; | |
1086 | p->net_type = cf->net_type; | |
1087 | p->disabled = cf->disabled; | |
1088 | p->hash_key = random_u32(); | |
1089 | cf->proto = p; | |
1090 | ||
1091 | init_list(&p->channels); | |
1092 | ||
1093 | return p; | |
1094 | } | |
1095 | ||
1096 | static struct proto * | |
1097 | proto_init(struct proto_config *c, node *n) | |
1098 | { | |
1099 | struct protocol *pr = c->protocol; | |
1100 | struct proto *p = pr->init(c); | |
1101 | ||
1102 | p->proto_state = PS_DOWN; | |
f047271c | 1103 | p->last_state_change = current_time(); |
46434a3c | 1104 | p->vrf = c->vrf; |
18f70a62 | 1105 | p->vrf_set = c->vrf_set; |
f4a60a9b OZ |
1106 | insert_node(&p->n, n); |
1107 | ||
961671c0 | 1108 | p->event = ev_new_init(proto_pool, proto_event, p); |
f4a60a9b OZ |
1109 | |
1110 | PD(p, "Initializing%s", p->disabled ? " [disabled]" : ""); | |
1111 | ||
1112 | return p; | |
1113 | } | |
1114 | ||
1115 | static void | |
1116 | proto_start(struct proto *p) | |
1117 | { | |
1118 | /* Here we cannot use p->cf->name since it won't survive reconfiguration */ | |
1119 | p->pool = rp_new(proto_pool, p->proto->name); | |
1120 | ||
1121 | if (graceful_restart_state == GRS_INIT) | |
1122 | p->gr_recovery = 1; | |
0e02abfd MM |
1123 | } |
1124 | ||
094d2bdb | 1125 | |
3c6269b8 MM |
1126 | /** |
1127 | * proto_config_new - create a new protocol configuration | |
1128 | * @pr: protocol the configuration will belong to | |
a7f23f58 | 1129 | * @class: SYM_PROTO or SYM_TEMPLATE |
3c6269b8 MM |
1130 | * |
1131 | * Whenever the configuration file says that a new instance | |
1132 | * of a routing protocol should be created, the parser calls | |
1133 | * proto_config_new() to create a configuration entry for this | |
1134 | * instance (a structure staring with the &proto_config header | |
1135 | * containing all the generic items followed by protocol-specific | |
1136 | * ones). Also, the configuration entry gets added to the list | |
1137 | * of protocol instances kept in the configuration. | |
a7f23f58 OZ |
1138 | * |
1139 | * The function is also used to create protocol templates (when class | |
1140 | * SYM_TEMPLATE is specified), the only difference is that templates | |
1141 | * are not added to the list of protocol instances and therefore not | |
1142 | * initialized during protos_commit()). | |
3c6269b8 | 1143 | */ |
31b3e1bb | 1144 | void * |
2bbc3083 | 1145 | proto_config_new(struct protocol *pr, int class) |
31b3e1bb | 1146 | { |
f4a60a9b | 1147 | struct proto_config *cf = cfg_allocz(pr->config_size); |
31b3e1bb | 1148 | |
a7f23f58 | 1149 | if (class == SYM_PROTO) |
f4a60a9b OZ |
1150 | add_tail(&new_config->protos, &cf->n); |
1151 | ||
1152 | cf->global = new_config; | |
1153 | cf->protocol = pr; | |
1154 | cf->name = pr->name; | |
1155 | cf->class = class; | |
1156 | cf->debug = new_config->proto_default_debug; | |
1157 | cf->mrtdump = new_config->proto_default_mrtdump; | |
1158 | ||
1159 | init_list(&cf->channels); | |
1160 | ||
1161 | return cf; | |
31b3e1bb MM |
1162 | } |
1163 | ||
f4a60a9b | 1164 | |
a7f23f58 OZ |
1165 | /** |
1166 | * proto_copy_config - copy a protocol configuration | |
1167 | * @dest: destination protocol configuration | |
1168 | * @src: source protocol configuration | |
1169 | * | |
1170 | * Whenever a new instance of a routing protocol is created from the | |
1171 | * template, proto_copy_config() is called to copy a content of | |
1172 | * the source protocol configuration to the new protocol configuration. | |
1173 | * Name, class and a node in protos list of @dest are kept intact. | |
1174 | * copy_config() protocol hook is used to copy protocol-specific data. | |
1175 | */ | |
1176 | void | |
1177 | proto_copy_config(struct proto_config *dest, struct proto_config *src) | |
1178 | { | |
f4a60a9b | 1179 | struct channel_config *cc; |
a7f23f58 OZ |
1180 | node old_node; |
1181 | int old_class; | |
fd9f0c06 | 1182 | const char *old_name; |
a7f23f58 OZ |
1183 | |
1184 | if (dest->protocol != src->protocol) | |
1185 | cf_error("Can't copy configuration from a different protocol type"); | |
1186 | ||
1187 | if (dest->protocol->copy_config == NULL) | |
1188 | cf_error("Inheriting configuration for %s is not supported", src->protocol->name); | |
1189 | ||
1190 | DBG("Copying configuration from %s to %s\n", src->name, dest->name); | |
1191 | ||
f4a60a9b | 1192 | /* |
a7f23f58 OZ |
1193 | * Copy struct proto_config here. Keep original node, class and name. |
1194 | * protocol-specific config copy is handled by protocol copy_config() hook | |
1195 | */ | |
1196 | ||
1197 | old_node = dest->n; | |
1198 | old_class = dest->class; | |
1199 | old_name = dest->name; | |
1200 | ||
f4a60a9b | 1201 | memcpy(dest, src, src->protocol->config_size); |
a7f23f58 OZ |
1202 | |
1203 | dest->n = old_node; | |
1204 | dest->class = old_class; | |
1205 | dest->name = old_name; | |
f4a60a9b | 1206 | init_list(&dest->channels); |
a7f23f58 | 1207 | |
f4a60a9b OZ |
1208 | WALK_LIST(cc, src->channels) |
1209 | channel_copy_config(cc, dest); | |
1210 | ||
1211 | /* FIXME: allow for undefined copy_config */ | |
a7f23f58 OZ |
1212 | dest->protocol->copy_config(dest, src); |
1213 | } | |
1214 | ||
e0835db4 OZ |
1215 | void |
1216 | proto_clone_config(struct symbol *sym, struct proto_config *parent) | |
1217 | { | |
1218 | struct proto_config *cf = proto_config_new(parent->protocol, SYM_PROTO); | |
1219 | proto_copy_config(cf, parent); | |
1220 | cf->name = sym->name; | |
1221 | cf->proto = NULL; | |
1222 | cf->parent = parent; | |
1223 | ||
1224 | sym->class = cf->class; | |
eac9250f | 1225 | sym->proto = cf; |
e0835db4 OZ |
1226 | } |
1227 | ||
1228 | static void | |
1229 | proto_undef_clone(struct symbol *sym, struct proto_config *cf) | |
1230 | { | |
1231 | rem_node(&cf->n); | |
1232 | ||
1233 | sym->class = SYM_VOID; | |
eac9250f | 1234 | sym->proto = NULL; |
e0835db4 OZ |
1235 | } |
1236 | ||
3c6269b8 MM |
1237 | /** |
1238 | * protos_preconfig - pre-configuration processing | |
1239 | * @c: new configuration | |
1240 | * | |
1241 | * This function calls the preconfig() hooks of all routing | |
1242 | * protocols available to prepare them for reading of the new | |
1243 | * configuration. | |
1244 | */ | |
2326b001 | 1245 | void |
31b3e1bb | 1246 | protos_preconfig(struct config *c) |
2326b001 | 1247 | { |
7f4a3988 MM |
1248 | struct protocol *p; |
1249 | ||
7c0cc76e | 1250 | init_list(&c->protos); |
6b9fa320 | 1251 | DBG("Protocol preconfig:"); |
7f4a3988 | 1252 | WALK_LIST(p, protocol_list) |
f4a60a9b OZ |
1253 | { |
1254 | DBG(" %s", p->name); | |
1255 | p->name_counter = 0; | |
1256 | if (p->preconfig) | |
1257 | p->preconfig(p, c); | |
1258 | } | |
6b9fa320 | 1259 | DBG("\n"); |
31b3e1bb MM |
1260 | } |
1261 | ||
ebae4770 OZ |
1262 | static int |
1263 | proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type) | |
1264 | { | |
1265 | /* If the protocol is DOWN, we just restart it */ | |
1266 | if (p->proto_state == PS_DOWN) | |
1267 | return 0; | |
1268 | ||
1269 | /* If there is a too big change in core attributes, ... */ | |
1270 | if ((nc->protocol != oc->protocol) || | |
f4a60a9b | 1271 | (nc->net_type != oc->net_type) || |
23fd4644 | 1272 | (nc->disabled != p->disabled) || |
18f70a62 OZ |
1273 | (nc->vrf != oc->vrf) || |
1274 | (nc->vrf_set != oc->vrf_set)) | |
ebae4770 OZ |
1275 | return 0; |
1276 | ||
f4a60a9b | 1277 | p->name = nc->name; |
ebae4770 OZ |
1278 | p->debug = nc->debug; |
1279 | p->mrtdump = nc->mrtdump; | |
f4a60a9b | 1280 | reconfigure_type = type; |
ebae4770 OZ |
1281 | |
1282 | /* Execute protocol specific reconfigure hook */ | |
f4a60a9b | 1283 | if (!p->proto->reconfigure || !p->proto->reconfigure(p, nc)) |
ebae4770 OZ |
1284 | return 0; |
1285 | ||
1286 | DBG("\t%s: same\n", oc->name); | |
1287 | PD(p, "Reconfigured"); | |
1288 | p->cf = nc; | |
ebae4770 OZ |
1289 | |
1290 | return 1; | |
1291 | } | |
1292 | ||
3c6269b8 MM |
1293 | /** |
1294 | * protos_commit - commit new protocol configuration | |
1295 | * @new: new configuration | |
1296 | * @old: old configuration or %NULL if it's boot time config | |
1297 | * @force_reconfig: force restart of all protocols (used for example | |
1298 | * when the router ID changes) | |
bf1aec97 | 1299 | * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD) |
3c6269b8 MM |
1300 | * |
1301 | * Scan differences between @old and @new configuration and adjust all | |
1302 | * protocol instances to conform to the new configuration. | |
1303 | * | |
1304 | * When a protocol exists in the new configuration, but it doesn't in the | |
1305 | * original one, it's immediately started. When a collision with the other | |
1306 | * running protocol would arise, the new protocol will be temporarily stopped | |
1307 | * by the locking mechanism. | |
1308 | * | |
1309 | * When a protocol exists in the old configuration, but it doesn't in the | |
1310 | * new one, it's shut down and deleted after the shutdown completes. | |
1311 | * | |
bf1aec97 OZ |
1312 | * When a protocol exists in both configurations, the core decides |
1313 | * whether it's possible to reconfigure it dynamically - it checks all | |
1314 | * the core properties of the protocol (changes in filters are ignored | |
1315 | * if type is RECONFIG_SOFT) and if they match, it asks the | |
1316 | * reconfigure() hook of the protocol to see if the protocol is able | |
1317 | * to switch to the new configuration. If it isn't possible, the | |
1318 | * protocol is shut down and a new instance is started with the new | |
1319 | * configuration after the shutdown is completed. | |
3c6269b8 | 1320 | */ |
31b3e1bb | 1321 | void |
bf1aec97 | 1322 | protos_commit(struct config *new, struct config *old, int force_reconfig, int type) |
31b3e1bb | 1323 | { |
50fe90ed | 1324 | struct proto_config *oc, *nc; |
a7f23f58 | 1325 | struct symbol *sym; |
f4a60a9b OZ |
1326 | struct proto *p; |
1327 | node *n; | |
1328 | ||
31b3e1bb | 1329 | |
50fe90ed MM |
1330 | DBG("protos_commit:\n"); |
1331 | if (old) | |
f4a60a9b OZ |
1332 | { |
1333 | WALK_LIST(oc, old->protos) | |
31b3e1bb | 1334 | { |
f4a60a9b OZ |
1335 | p = oc->proto; |
1336 | sym = cf_find_symbol(new, oc->name); | |
e0835db4 OZ |
1337 | |
1338 | /* Handle dynamic protocols */ | |
1339 | if (!sym && oc->parent && !new->shutdown) | |
1340 | { | |
1341 | struct symbol *parsym = cf_find_symbol(new, oc->parent->name); | |
1342 | if (parsym && parsym->class == SYM_PROTO) | |
1343 | { | |
1344 | /* This is hack, we would like to share config, but we need to copy it now */ | |
1345 | new_config = new; | |
1346 | cfg_mem = new->mem; | |
51f2e7af MM |
1347 | new->current_scope = new->root_scope; |
1348 | sym = cf_get_symbol(new, oc->name); | |
eac9250f | 1349 | proto_clone_config(sym, parsym->proto); |
e0835db4 OZ |
1350 | new_config = NULL; |
1351 | cfg_mem = NULL; | |
1352 | } | |
1353 | } | |
1354 | ||
f4a60a9b OZ |
1355 | if (sym && sym->class == SYM_PROTO && !new->shutdown) |
1356 | { | |
1357 | /* Found match, let's check if we can smoothly switch to new configuration */ | |
1358 | /* No need to check description */ | |
0b39b1cb | 1359 | nc = sym->proto; |
f4a60a9b OZ |
1360 | nc->proto = p; |
1361 | ||
1362 | /* We will try to reconfigure protocol p */ | |
1363 | if (! force_reconfig && proto_reconfigure(p, oc, nc, type)) | |
1364 | continue; | |
1365 | ||
e0835db4 OZ |
1366 | if (nc->parent) |
1367 | { | |
1368 | proto_undef_clone(sym, nc); | |
1369 | goto remove; | |
1370 | } | |
1371 | ||
f4a60a9b OZ |
1372 | /* Unsuccessful, we will restart it */ |
1373 | if (!p->disabled && !nc->disabled) | |
1374 | log(L_INFO "Restarting protocol %s", p->name); | |
1375 | else if (p->disabled && !nc->disabled) | |
1376 | log(L_INFO "Enabling protocol %s", p->name); | |
1377 | else if (!p->disabled && nc->disabled) | |
1378 | log(L_INFO "Disabling protocol %s", p->name); | |
1379 | ||
1380 | p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART; | |
1381 | p->cf_new = nc; | |
1382 | } | |
1383 | else if (!new->shutdown) | |
1384 | { | |
e0835db4 | 1385 | remove: |
f4a60a9b OZ |
1386 | log(L_INFO "Removing protocol %s", p->name); |
1387 | p->down_code = PDC_CF_REMOVE; | |
1388 | p->cf_new = NULL; | |
1389 | } | |
8a68316e OZ |
1390 | else if (new->gr_down) |
1391 | { | |
1392 | p->down_code = PDC_CMD_GR_DOWN; | |
1393 | p->cf_new = NULL; | |
1394 | } | |
f4a60a9b OZ |
1395 | else /* global shutdown */ |
1396 | { | |
1397 | p->down_code = PDC_CMD_SHUTDOWN; | |
1398 | p->cf_new = NULL; | |
1399 | } | |
1400 | ||
1401 | p->reconfiguring = 1; | |
1402 | config_add_obstacle(old); | |
1403 | proto_rethink_goal(p); | |
7f4a3988 | 1404 | } |
f4a60a9b OZ |
1405 | } |
1406 | ||
1407 | struct proto *first_dev_proto = NULL; | |
50fe90ed | 1408 | |
f4a60a9b | 1409 | n = NODE &(proto_list.head); |
50fe90ed MM |
1410 | WALK_LIST(nc, new->protos) |
1411 | if (!nc->proto) | |
f4a60a9b OZ |
1412 | { |
1413 | /* Not a first-time configuration */ | |
1414 | if (old) | |
1415 | log(L_INFO "Adding protocol %s", nc->name); | |
1416 | ||
1417 | p = proto_init(nc, n); | |
1418 | n = NODE p; | |
1419 | ||
1420 | if (p->proto == &proto_unix_iface) | |
1421 | first_dev_proto = p; | |
1422 | } | |
1423 | else | |
1424 | n = NODE nc->proto; | |
50fe90ed MM |
1425 | |
1426 | DBG("Protocol start\n"); | |
4ef09506 OZ |
1427 | |
1428 | /* Start device protocol first */ | |
f4a60a9b OZ |
1429 | if (first_dev_proto) |
1430 | proto_rethink_goal(first_dev_proto); | |
4ef09506 | 1431 | |
79b4e12e OZ |
1432 | /* Determine router ID for the first time - it has to be here and not in |
1433 | global_commit() because it is postponed after start of device protocol */ | |
1434 | if (!config->router_id) | |
f4a60a9b OZ |
1435 | { |
1436 | config->router_id = if_choose_router_id(config->router_id_from, 0); | |
1437 | if (!config->router_id) | |
1438 | die("Cannot determine router ID, please configure it manually"); | |
1439 | } | |
79b4e12e | 1440 | |
f4a60a9b OZ |
1441 | /* Start all new protocols */ |
1442 | WALK_LIST_DELSAFE(p, n, proto_list) | |
50fe90ed | 1443 | proto_rethink_goal(p); |
7f4a3988 MM |
1444 | } |
1445 | ||
47b79306 | 1446 | static void |
67bd949a | 1447 | proto_rethink_goal(struct proto *p) |
47b79306 | 1448 | { |
50fe90ed | 1449 | struct protocol *q; |
0c791f87 | 1450 | byte goal; |
50fe90ed | 1451 | |
f4a60a9b OZ |
1452 | if (p->reconfiguring && !p->active) |
1453 | { | |
1454 | struct proto_config *nc = p->cf_new; | |
1455 | node *n = p->n.prev; | |
1456 | DBG("%s has shut down for reconfiguration\n", p->name); | |
1457 | p->cf->proto = NULL; | |
1458 | config_del_obstacle(p->cf->global); | |
1459 | proto_remove_channels(p); | |
1460 | rem_node(&p->n); | |
1461 | rfree(p->event); | |
830ba75e | 1462 | mb_free(p->message); |
f4a60a9b OZ |
1463 | mb_free(p); |
1464 | if (!nc) | |
1465 | return; | |
1466 | p = proto_init(nc, n); | |
1467 | } | |
50fe90ed MM |
1468 | |
1469 | /* Determine what state we want to reach */ | |
bf8558bc | 1470 | if (p->disabled || p->reconfiguring) |
0c791f87 | 1471 | goal = PS_DOWN; |
50fe90ed | 1472 | else |
0c791f87 | 1473 | goal = PS_UP; |
50fe90ed MM |
1474 | |
1475 | q = p->proto; | |
f4a60a9b OZ |
1476 | if (goal == PS_UP) |
1477 | { | |
1478 | if (!p->active) | |
67bd949a | 1479 | { |
f4a60a9b OZ |
1480 | /* Going up */ |
1481 | DBG("Kicking %s up\n", p->name); | |
1482 | PD(p, "Starting"); | |
1483 | proto_start(p); | |
1484 | proto_notify_state(p, (q->start ? q->start(p) : PS_UP)); | |
67bd949a | 1485 | } |
f4a60a9b OZ |
1486 | } |
1487 | else | |
1488 | { | |
1489 | if (p->proto_state == PS_START || p->proto_state == PS_UP) | |
67bd949a | 1490 | { |
f4a60a9b OZ |
1491 | /* Going down */ |
1492 | DBG("Kicking %s down\n", p->name); | |
1493 | PD(p, "Shutting down"); | |
1494 | proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN)); | |
67bd949a | 1495 | } |
f4a60a9b | 1496 | } |
67bd949a MM |
1497 | } |
1498 | ||
e0835db4 OZ |
1499 | struct proto * |
1500 | proto_spawn(struct proto_config *cf, uint disabled) | |
1501 | { | |
1502 | struct proto *p = proto_init(cf, TAIL(proto_list)); | |
1503 | p->disabled = disabled; | |
1504 | proto_rethink_goal(p); | |
1505 | return p; | |
1506 | } | |
1507 | ||
0c791f87 | 1508 | |
6eda3f13 OZ |
1509 | /** |
1510 | * DOC: Graceful restart recovery | |
1511 | * | |
1512 | * Graceful restart of a router is a process when the routing plane (e.g. BIRD) | |
1513 | * restarts but both the forwarding plane (e.g kernel routing table) and routing | |
1514 | * neighbors keep proper routes, and therefore uninterrupted packet forwarding | |
1515 | * is maintained. | |
1516 | * | |
1517 | * BIRD implements graceful restart recovery by deferring export of routes to | |
1518 | * protocols until routing tables are refilled with the expected content. After | |
1519 | * start, protocols generate routes as usual, but routes are not propagated to | |
1520 | * them, until protocols report that they generated all routes. After that, | |
1521 | * graceful restart recovery is finished and the export (and the initial feed) | |
1522 | * to protocols is enabled. | |
1523 | * | |
1524 | * When graceful restart recovery need is detected during initialization, then | |
1525 | * enabled protocols are marked with @gr_recovery flag before start. Such | |
1526 | * protocols then decide how to proceed with graceful restart, participation is | |
f4a60a9b | 1527 | * voluntary. Protocols could lock the recovery for each channel by function |
286e2011 | 1528 | * channel_graceful_restart_lock() (state stored in @gr_lock flag), which means |
f4a60a9b OZ |
1529 | * that they want to postpone the end of the recovery until they converge and |
1530 | * then unlock it. They also could set @gr_wait before advancing to %PS_UP, | |
1531 | * which means that the core should defer route export to that channel until | |
1532 | * the end of the recovery. This should be done by protocols that expect their | |
1533 | * neigbors to keep the proper routes (kernel table, BGP sessions with BGP | |
1534 | * graceful restart capability). | |
6eda3f13 OZ |
1535 | * |
1536 | * The graceful restart recovery is finished when either all graceful restart | |
1537 | * locks are unlocked or when graceful restart wait timer fires. | |
1538 | * | |
1539 | */ | |
0c791f87 | 1540 | |
02552526 | 1541 | static void graceful_restart_done(timer *t); |
0c791f87 | 1542 | |
6eda3f13 OZ |
1543 | /** |
1544 | * graceful_restart_recovery - request initial graceful restart recovery | |
1545 | * | |
1546 | * Called by the platform initialization code if the need for recovery | |
1547 | * after graceful restart is detected during boot. Have to be called | |
1548 | * before protos_commit(). | |
1549 | */ | |
0c791f87 OZ |
1550 | void |
1551 | graceful_restart_recovery(void) | |
1552 | { | |
1553 | graceful_restart_state = GRS_INIT; | |
1554 | } | |
1555 | ||
6eda3f13 OZ |
1556 | /** |
1557 | * graceful_restart_init - initialize graceful restart | |
1558 | * | |
1559 | * When graceful restart recovery was requested, the function starts an active | |
1560 | * phase of the recovery and initializes graceful restart wait timer. The | |
1561 | * function have to be called after protos_commit(). | |
1562 | */ | |
0c791f87 OZ |
1563 | void |
1564 | graceful_restart_init(void) | |
1565 | { | |
1566 | if (!graceful_restart_state) | |
1567 | return; | |
1568 | ||
1569 | log(L_INFO "Graceful restart started"); | |
1570 | ||
1571 | if (!graceful_restart_locks) | |
f4a60a9b OZ |
1572 | { |
1573 | graceful_restart_done(NULL); | |
1574 | return; | |
1575 | } | |
0c791f87 OZ |
1576 | |
1577 | graceful_restart_state = GRS_ACTIVE; | |
a6f79ca5 OZ |
1578 | gr_wait_timer = tm_new_init(proto_pool, graceful_restart_done, NULL, 0, 0); |
1579 | tm_start(gr_wait_timer, config->gr_wait S); | |
0c791f87 OZ |
1580 | } |
1581 | ||
6eda3f13 OZ |
1582 | /** |
1583 | * graceful_restart_done - finalize graceful restart | |
8e433d6a | 1584 | * @t: unused |
6eda3f13 OZ |
1585 | * |
1586 | * When there are no locks on graceful restart, the functions finalizes the | |
1587 | * graceful restart recovery. Protocols postponing route export until the end of | |
1588 | * the recovery are awakened and the export to them is enabled. All other | |
1589 | * related state is cleared. The function is also called when the graceful | |
1590 | * restart wait timer fires (but there are still some locks). | |
1591 | */ | |
0c791f87 | 1592 | static void |
02552526 | 1593 | graceful_restart_done(timer *t UNUSED) |
0c791f87 | 1594 | { |
0c791f87 OZ |
1595 | log(L_INFO "Graceful restart done"); |
1596 | graceful_restart_state = GRS_DONE; | |
1597 | ||
f4a60a9b OZ |
1598 | struct proto *p; |
1599 | WALK_LIST(p, proto_list) | |
1600 | { | |
1601 | if (!p->gr_recovery) | |
1602 | continue; | |
0c791f87 | 1603 | |
f4a60a9b OZ |
1604 | struct channel *c; |
1605 | WALK_LIST(c, p->channels) | |
1606 | { | |
0c791f87 | 1607 | /* Resume postponed export of routes */ |
2a013bb3 | 1608 | if ((c->channel_state == CS_UP) && c->gr_wait && c->proto->rt_notify) |
f4a60a9b | 1609 | channel_start_export(c); |
0c791f87 OZ |
1610 | |
1611 | /* Cleanup */ | |
f4a60a9b OZ |
1612 | c->gr_wait = 0; |
1613 | c->gr_lock = 0; | |
0c791f87 OZ |
1614 | } |
1615 | ||
f4a60a9b OZ |
1616 | p->gr_recovery = 0; |
1617 | } | |
1618 | ||
0c791f87 OZ |
1619 | graceful_restart_locks = 0; |
1620 | } | |
1621 | ||
1622 | void | |
1623 | graceful_restart_show_status(void) | |
1624 | { | |
1625 | if (graceful_restart_state != GRS_ACTIVE) | |
1626 | return; | |
1627 | ||
1628 | cli_msg(-24, "Graceful restart recovery in progress"); | |
f4a60a9b | 1629 | cli_msg(-24, " Waiting for %d channels to recover", graceful_restart_locks); |
a6f79ca5 | 1630 | cli_msg(-24, " Wait timer is %t/%u", tm_remains(gr_wait_timer), config->gr_wait); |
0c791f87 OZ |
1631 | } |
1632 | ||
6eda3f13 | 1633 | /** |
f4a60a9b OZ |
1634 | * channel_graceful_restart_lock - lock graceful restart by channel |
1635 | * @p: channel instance | |
6eda3f13 OZ |
1636 | * |
1637 | * This function allows a protocol to postpone the end of graceful restart | |
1638 | * recovery until it converges. The lock is removed when the protocol calls | |
f4a60a9b | 1639 | * channel_graceful_restart_unlock() or when the channel is closed. |
6eda3f13 OZ |
1640 | * |
1641 | * The function have to be called during the initial phase of graceful restart | |
1642 | * recovery and only for protocols that are part of graceful restart (i.e. their | |
1643 | * @gr_recovery is set), which means it should be called from protocol start | |
1644 | * hooks. | |
1645 | */ | |
0c791f87 | 1646 | void |
f4a60a9b | 1647 | channel_graceful_restart_lock(struct channel *c) |
0c791f87 OZ |
1648 | { |
1649 | ASSERT(graceful_restart_state == GRS_INIT); | |
f4a60a9b | 1650 | ASSERT(c->proto->gr_recovery); |
0c791f87 | 1651 | |
f4a60a9b | 1652 | if (c->gr_lock) |
0c791f87 OZ |
1653 | return; |
1654 | ||
f4a60a9b | 1655 | c->gr_lock = 1; |
0c791f87 OZ |
1656 | graceful_restart_locks++; |
1657 | } | |
1658 | ||
6eda3f13 | 1659 | /** |
f4a60a9b OZ |
1660 | * channel_graceful_restart_unlock - unlock graceful restart by channel |
1661 | * @p: channel instance | |
6eda3f13 | 1662 | * |
f4a60a9b | 1663 | * This function unlocks a lock from channel_graceful_restart_lock(). It is also |
6eda3f13 OZ |
1664 | * automatically called when the lock holding protocol went down. |
1665 | */ | |
0c791f87 | 1666 | void |
f4a60a9b | 1667 | channel_graceful_restart_unlock(struct channel *c) |
0c791f87 | 1668 | { |
f4a60a9b | 1669 | if (!c->gr_lock) |
0c791f87 OZ |
1670 | return; |
1671 | ||
f4a60a9b | 1672 | c->gr_lock = 0; |
0c791f87 OZ |
1673 | graceful_restart_locks--; |
1674 | ||
1675 | if ((graceful_restart_state == GRS_ACTIVE) && !graceful_restart_locks) | |
a6f79ca5 | 1676 | tm_start(gr_wait_timer, 0); |
0c791f87 OZ |
1677 | } |
1678 | ||
1679 | ||
1680 | ||
3c6269b8 MM |
1681 | /** |
1682 | * protos_dump_all - dump status of all protocols | |
1683 | * | |
1684 | * This function dumps status of all existing protocol instances to the | |
1685 | * debug output. It involves printing of general status information | |
1686 | * such as protocol states, its position on the protocol lists | |
1687 | * and also calling of a dump() hook of the protocol to print | |
1688 | * the internals. | |
1689 | */ | |
87d2be86 PM |
1690 | void |
1691 | protos_dump_all(void) | |
1692 | { | |
87d2be86 PM |
1693 | debug("Protocols:\n"); |
1694 | ||
f4a60a9b OZ |
1695 | struct proto *p; |
1696 | WALK_LIST(p, proto_list) | |
1697 | { | |
1698 | debug(" protocol %s state %s\n", p->name, p_states[p->proto_state]); | |
1699 | ||
1700 | struct channel *c; | |
1701 | WALK_LIST(c, p->channels) | |
87d2be86 | 1702 | { |
f4a60a9b OZ |
1703 | debug("\tTABLE %s\n", c->table->name); |
1704 | if (c->in_filter) | |
1705 | debug("\tInput filter: %s\n", filter_name(c->in_filter)); | |
1706 | if (c->out_filter) | |
1707 | debug("\tOutput filter: %s\n", filter_name(c->out_filter)); | |
87d2be86 | 1708 | } |
f4a60a9b OZ |
1709 | |
1710 | if (p->proto->dump && (p->proto_state != PS_DOWN)) | |
1711 | p->proto->dump(p); | |
1712 | } | |
87d2be86 PM |
1713 | } |
1714 | ||
3c6269b8 MM |
1715 | /** |
1716 | * proto_build - make a single protocol available | |
1717 | * @p: the protocol | |
1718 | * | |
1719 | * After the platform specific initialization code uses protos_build() | |
1720 | * to add all the standard protocols, it should call proto_build() for | |
2e9b2421 | 1721 | * all platform specific protocols to inform the core that they exist. |
3c6269b8 | 1722 | */ |
3991d84e MM |
1723 | void |
1724 | proto_build(struct protocol *p) | |
1725 | { | |
1726 | add_tail(&protocol_list, &p->n); | |
ee7e2ffd JMM |
1727 | ASSERT(p->class); |
1728 | ASSERT(!class_to_protocol[p->class]); | |
1729 | class_to_protocol[p->class] = p; | |
3991d84e MM |
1730 | } |
1731 | ||
1ec52253 OZ |
1732 | /* FIXME: convert this call to some protocol hook */ |
1733 | extern void bfd_init_all(void); | |
1734 | ||
4a23ede2 MM |
1735 | void protos_build_gen(void); |
1736 | ||
3c6269b8 MM |
1737 | /** |
1738 | * protos_build - build a protocol list | |
1739 | * | |
1740 | * This function is called during BIRD startup to insert | |
1741 | * all standard protocols to the global protocol list. Insertion | |
1742 | * of platform specific protocols (such as the kernel syncer) | |
1743 | * is in the domain of competence of the platform dependent | |
1744 | * startup code. | |
1745 | */ | |
0432c017 MM |
1746 | void |
1747 | protos_build(void) | |
1748 | { | |
4a23ede2 | 1749 | protos_build_gen(); |
6a8d3f1c | 1750 | |
67bd949a | 1751 | proto_pool = rp_new(&root_pool, "Protocols"); |
a6f79ca5 | 1752 | proto_shutdown_timer = tm_new(proto_pool); |
ebecb6f6 | 1753 | proto_shutdown_timer->hook = proto_shutdown_loop; |
67bd949a MM |
1754 | } |
1755 | ||
fb829de6 | 1756 | |
d9b77cc2 OZ |
1757 | /* Temporary hack to propagate restart to BGP */ |
1758 | int proto_restart; | |
fb829de6 | 1759 | |
ebecb6f6 | 1760 | static void |
02552526 | 1761 | proto_shutdown_loop(timer *t UNUSED) |
ebecb6f6 OZ |
1762 | { |
1763 | struct proto *p, *p_next; | |
1764 | ||
f4a60a9b | 1765 | WALK_LIST_DELSAFE(p, p_next, proto_list) |
ebecb6f6 | 1766 | if (p->down_sched) |
f4a60a9b OZ |
1767 | { |
1768 | proto_restart = (p->down_sched == PDS_RESTART); | |
ebecb6f6 | 1769 | |
f4a60a9b OZ |
1770 | p->disabled = 1; |
1771 | proto_rethink_goal(p); | |
1772 | if (proto_restart) | |
1773 | { | |
1774 | p->disabled = 0; | |
ebecb6f6 | 1775 | proto_rethink_goal(p); |
ebecb6f6 | 1776 | } |
f4a60a9b | 1777 | } |
ebecb6f6 OZ |
1778 | } |
1779 | ||
1780 | static inline void | |
1781 | proto_schedule_down(struct proto *p, byte restart, byte code) | |
1782 | { | |
1783 | /* Does not work for other states (even PS_START) */ | |
1784 | ASSERT(p->proto_state == PS_UP); | |
1785 | ||
1786 | /* Scheduled restart may change to shutdown, but not otherwise */ | |
1787 | if (p->down_sched == PDS_DISABLE) | |
1788 | return; | |
1789 | ||
1790 | p->down_sched = restart ? PDS_RESTART : PDS_DISABLE; | |
1791 | p->down_code = code; | |
a6f79ca5 | 1792 | tm_start_max(proto_shutdown_timer, restart ? 250 MS : 0); |
ebecb6f6 OZ |
1793 | } |
1794 | ||
cd1d9961 OZ |
1795 | /** |
1796 | * proto_set_message - set administrative message to protocol | |
1797 | * @p: protocol | |
1798 | * @msg: message | |
1799 | * @len: message length (-1 for NULL-terminated string) | |
1800 | * | |
1801 | * The function sets administrative message (string) related to protocol state | |
1802 | * change. It is called by the nest code for manual enable/disable/restart | |
1803 | * commands all routes to the protocol, and by protocol-specific code when the | |
1804 | * protocol state change is initiated by the protocol. Using NULL message clears | |
1805 | * the last message. The message string may be either NULL-terminated or with an | |
1806 | * explicit length. | |
1807 | */ | |
1808 | void | |
1809 | proto_set_message(struct proto *p, char *msg, int len) | |
1810 | { | |
1811 | mb_free(p->message); | |
1812 | p->message = NULL; | |
1813 | ||
1814 | if (!msg || !len) | |
1815 | return; | |
1816 | ||
1817 | if (len < 0) | |
1818 | len = strlen(msg); | |
1819 | ||
1820 | if (!len) | |
1821 | return; | |
1822 | ||
1823 | p->message = mb_alloc(proto_pool, len + 1); | |
1824 | memcpy(p->message, msg, len); | |
1825 | p->message[len] = 0; | |
1826 | } | |
1827 | ||
ebecb6f6 | 1828 | |
ebecb6f6 | 1829 | static const char * |
f4a60a9b | 1830 | channel_limit_name(struct channel_limit *l) |
ebecb6f6 OZ |
1831 | { |
1832 | const char *actions[] = { | |
1833 | [PLA_WARN] = "warn", | |
1834 | [PLA_BLOCK] = "block", | |
1835 | [PLA_RESTART] = "restart", | |
1836 | [PLA_DISABLE] = "disable", | |
1837 | }; | |
1838 | ||
1839 | return actions[l->action]; | |
1840 | } | |
1841 | ||
1842 | /** | |
f4a60a9b OZ |
1843 | * channel_notify_limit: notify about limit hit and take appropriate action |
1844 | * @c: channel | |
ebecb6f6 | 1845 | * @l: limit being hit |
b662290f | 1846 | * @dir: limit direction (PLD_*) |
f4a60a9b | 1847 | * @rt_count: the number of routes |
ebecb6f6 OZ |
1848 | * |
1849 | * The function is called by the route processing core when limit @l | |
1850 | * is breached. It activates the limit and tooks appropriate action | |
7d0a31de | 1851 | * according to @l->action. |
ebecb6f6 | 1852 | */ |
7d0a31de | 1853 | void |
f4a60a9b | 1854 | channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count) |
ebecb6f6 | 1855 | { |
b662290f OZ |
1856 | const char *dir_name[PLD_MAX] = { "receive", "import" , "export" }; |
1857 | const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT }; | |
f4a60a9b | 1858 | struct proto *p = c->proto; |
ebecb6f6 | 1859 | |
7d0a31de OZ |
1860 | if (l->state == PLS_BLOCKED) |
1861 | return; | |
ebecb6f6 | 1862 | |
d9b77cc2 OZ |
1863 | /* For warning action, we want the log message every time we hit the limit */ |
1864 | if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit))) | |
7d0a31de | 1865 | log(L_WARN "Protocol %s hits route %s limit (%d), action: %s", |
f4a60a9b | 1866 | p->name, dir_name[dir], l->limit, channel_limit_name(l)); |
ebecb6f6 OZ |
1867 | |
1868 | switch (l->action) | |
f4a60a9b OZ |
1869 | { |
1870 | case PLA_WARN: | |
1871 | l->state = PLS_ACTIVE; | |
1872 | break; | |
1873 | ||
1874 | case PLA_BLOCK: | |
1875 | l->state = PLS_BLOCKED; | |
1876 | break; | |
1877 | ||
1878 | case PLA_RESTART: | |
1879 | case PLA_DISABLE: | |
1880 | l->state = PLS_BLOCKED; | |
1881 | if (p->proto_state == PS_UP) | |
1882 | proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]); | |
1883 | break; | |
1884 | } | |
ebecb6f6 OZ |
1885 | } |
1886 | ||
f4a60a9b OZ |
1887 | static void |
1888 | channel_verify_limits(struct channel *c) | |
984d7349 | 1889 | { |
f4a60a9b OZ |
1890 | struct channel_limit *l; |
1891 | u32 all_routes = c->stats.imp_routes + c->stats.filt_routes; | |
984d7349 | 1892 | |
f4a60a9b OZ |
1893 | l = &c->rx_limit; |
1894 | if (l->action && (all_routes > l->limit)) | |
1895 | channel_notify_limit(c, l, PLD_RX, all_routes); | |
984d7349 | 1896 | |
f4a60a9b OZ |
1897 | l = &c->in_limit; |
1898 | if (l->action && (c->stats.imp_routes > l->limit)) | |
1899 | channel_notify_limit(c, l, PLD_IN, c->stats.imp_routes); | |
984d7349 | 1900 | |
f4a60a9b OZ |
1901 | l = &c->out_limit; |
1902 | if (l->action && (c->stats.exp_routes > l->limit)) | |
1903 | channel_notify_limit(c, l, PLD_OUT, c->stats.exp_routes); | |
984d7349 OZ |
1904 | } |
1905 | ||
f4a60a9b OZ |
1906 | static inline void |
1907 | channel_reset_limit(struct channel_limit *l) | |
0c791f87 | 1908 | { |
f4a60a9b OZ |
1909 | if (l->action) |
1910 | l->state = PLS_INITIAL; | |
0c791f87 OZ |
1911 | } |
1912 | ||
f4a60a9b OZ |
1913 | static inline void |
1914 | proto_do_start(struct proto *p) | |
0c791f87 | 1915 | { |
f4a60a9b OZ |
1916 | p->active = 1; |
1917 | p->do_start = 1; | |
1918 | ev_schedule(p->event); | |
0c791f87 OZ |
1919 | } |
1920 | ||
1921 | static void | |
f4a60a9b | 1922 | proto_do_up(struct proto *p) |
0c791f87 | 1923 | { |
f4a60a9b OZ |
1924 | if (!p->main_source) |
1925 | { | |
1926 | p->main_source = rt_get_source(p, 0); | |
1927 | rt_lock_source(p->main_source); | |
1928 | } | |
0c791f87 | 1929 | |
f4a60a9b | 1930 | proto_start_channels(p); |
0c791f87 OZ |
1931 | } |
1932 | ||
f4a60a9b OZ |
1933 | static inline void |
1934 | proto_do_pause(struct proto *p) | |
0c791f87 | 1935 | { |
f4a60a9b | 1936 | proto_pause_channels(p); |
0c791f87 OZ |
1937 | } |
1938 | ||
1939 | static void | |
f4a60a9b | 1940 | proto_do_stop(struct proto *p) |
0c791f87 | 1941 | { |
f4a60a9b | 1942 | p->down_sched = 0; |
0c791f87 | 1943 | p->gr_recovery = 0; |
6eda3f13 | 1944 | |
f4a60a9b OZ |
1945 | p->do_stop = 1; |
1946 | ev_schedule(p->event); | |
6eda3f13 | 1947 | |
f4a60a9b OZ |
1948 | if (p->main_source) |
1949 | { | |
1950 | rt_unlock_source(p->main_source); | |
1951 | p->main_source = NULL; | |
1952 | } | |
6eda3f13 | 1953 | |
f4a60a9b OZ |
1954 | proto_stop_channels(p); |
1955 | } | |
6eda3f13 | 1956 | |
f4a60a9b OZ |
1957 | static void |
1958 | proto_do_down(struct proto *p) | |
1959 | { | |
1960 | p->down_code = 0; | |
1961 | neigh_prune(); | |
1962 | rfree(p->pool); | |
1963 | p->pool = NULL; | |
1964 | ||
1965 | /* Shutdown is finished in the protocol event */ | |
1966 | if (proto_is_done(p)) | |
1967 | ev_schedule(p->event); | |
6eda3f13 OZ |
1968 | } |
1969 | ||
0c791f87 | 1970 | |
f4a60a9b | 1971 | |
3c6269b8 MM |
1972 | /** |
1973 | * proto_notify_state - notify core about protocol state change | |
1974 | * @p: protocol the state of which has changed | |
1975 | * @ps: the new status | |
1976 | * | |
1977 | * Whenever a state of a protocol changes due to some event internal | |
1978 | * to the protocol (i.e., not inside a start() or shutdown() hook), | |
1979 | * it should immediately notify the core about the change by calling | |
1980 | * proto_notify_state() which will write the new state to the &proto | |
1981 | * structure and take all the actions necessary to adapt to the new | |
d6a836f8 OZ |
1982 | * state. State change to PS_DOWN immediately frees resources of protocol |
1983 | * and might execute start callback of protocol; therefore, | |
1984 | * it should be used at tail positions of protocol callbacks. | |
3c6269b8 | 1985 | */ |
67bd949a | 1986 | void |
f4a60a9b | 1987 | proto_notify_state(struct proto *p, uint state) |
67bd949a | 1988 | { |
f4a60a9b | 1989 | uint ps = p->proto_state; |
67bd949a | 1990 | |
f4a60a9b OZ |
1991 | DBG("%s reporting state transition %s -> %s\n", p->name, p_states[ps], p_states[state]); |
1992 | if (state == ps) | |
67bd949a MM |
1993 | return; |
1994 | ||
f4a60a9b | 1995 | p->proto_state = state; |
f047271c | 1996 | p->last_state_change = current_time(); |
d6a836f8 | 1997 | |
f4a60a9b OZ |
1998 | switch (state) |
1999 | { | |
2000 | case PS_START: | |
2001 | ASSERT(ps == PS_DOWN || ps == PS_UP); | |
2002 | ||
2003 | if (ps == PS_DOWN) | |
2004 | proto_do_start(p); | |
2005 | else | |
2006 | proto_do_pause(p); | |
2007 | break; | |
2008 | ||
2009 | case PS_UP: | |
2010 | ASSERT(ps == PS_DOWN || ps == PS_START); | |
2011 | ||
2012 | if (ps == PS_DOWN) | |
2013 | proto_do_start(p); | |
2014 | ||
2015 | proto_do_up(p); | |
2016 | break; | |
2017 | ||
2018 | case PS_STOP: | |
2019 | ASSERT(ps == PS_START || ps == PS_UP); | |
2020 | ||
2021 | proto_do_stop(p); | |
2022 | break; | |
2023 | ||
2024 | case PS_DOWN: | |
2025 | if (ps != PS_STOP) | |
2026 | proto_do_stop(p); | |
2027 | ||
2028 | proto_do_down(p); | |
2029 | break; | |
2030 | ||
2031 | default: | |
2032 | bug("%s: Invalid state %d", p->name, ps); | |
2033 | } | |
227af309 OZ |
2034 | |
2035 | proto_log_state_change(p); | |
0432c017 | 2036 | } |
1a54b1c6 | 2037 | |
0d3e6bce MM |
2038 | /* |
2039 | * CLI Commands | |
2040 | */ | |
2041 | ||
2042 | static char * | |
2043 | proto_state_name(struct proto *p) | |
2044 | { | |
f4a60a9b OZ |
2045 | switch (p->proto_state) |
2046 | { | |
2047 | case PS_DOWN: return p->active ? "flush" : "down"; | |
2048 | case PS_START: return "start"; | |
2049 | case PS_UP: return "up"; | |
2050 | case PS_STOP: return "stop"; | |
2051 | default: return "???"; | |
2052 | } | |
0d3e6bce MM |
2053 | } |
2054 | ||
9db74169 | 2055 | static void |
f4a60a9b | 2056 | channel_show_stats(struct channel *c) |
9db74169 | 2057 | { |
f4a60a9b OZ |
2058 | struct proto_stats *s = &c->stats; |
2059 | ||
2060 | if (c->in_keep_filtered) | |
52fdd1cb OZ |
2061 | cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred", |
2062 | s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes); | |
cf98be7b | 2063 | else |
e1c275d8 OZ |
2064 | cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", |
2065 | s->imp_routes, s->exp_routes, s->pref_routes); | |
cf98be7b | 2066 | |
f4a60a9b OZ |
2067 | cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); |
2068 | cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", | |
9db74169 OZ |
2069 | s->imp_updates_received, s->imp_updates_invalid, |
2070 | s->imp_updates_filtered, s->imp_updates_ignored, | |
2071 | s->imp_updates_accepted); | |
f4a60a9b | 2072 | cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u", |
9db74169 OZ |
2073 | s->imp_withdraws_received, s->imp_withdraws_invalid, |
2074 | s->imp_withdraws_ignored, s->imp_withdraws_accepted); | |
f4a60a9b | 2075 | cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u", |
9db74169 OZ |
2076 | s->exp_updates_received, s->exp_updates_rejected, |
2077 | s->exp_updates_filtered, s->exp_updates_accepted); | |
f4a60a9b | 2078 | cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u", |
9db74169 OZ |
2079 | s->exp_withdraws_received, s->exp_withdraws_accepted); |
2080 | } | |
2081 | ||
ebecb6f6 | 2082 | void |
f4a60a9b | 2083 | channel_show_limit(struct channel_limit *l, const char *dsc) |
ebecb6f6 | 2084 | { |
f4a60a9b | 2085 | if (!l->action) |
7d0a31de OZ |
2086 | return; |
2087 | ||
f4a60a9b OZ |
2088 | cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : ""); |
2089 | cli_msg(-1006, " Action: %s", channel_limit_name(l)); | |
ebecb6f6 OZ |
2090 | } |
2091 | ||
c0adf7e9 | 2092 | void |
f4a60a9b | 2093 | channel_show_info(struct channel *c) |
9db74169 | 2094 | { |
f4a60a9b | 2095 | cli_msg(-1006, " Channel %s", c->name); |
d15b0b0a | 2096 | cli_msg(-1006, " State: %s", c_states[c->channel_state]); |
f4a60a9b OZ |
2097 | cli_msg(-1006, " Table: %s", c->table->name); |
2098 | cli_msg(-1006, " Preference: %d", c->preference); | |
2099 | cli_msg(-1006, " Input filter: %s", filter_name(c->in_filter)); | |
2100 | cli_msg(-1006, " Output filter: %s", filter_name(c->out_filter)); | |
9db74169 | 2101 | |
0c791f87 | 2102 | if (graceful_restart_state == GRS_ACTIVE) |
f4a60a9b OZ |
2103 | cli_msg(-1006, " GR recovery: %s%s", |
2104 | c->gr_lock ? " pending" : "", | |
2105 | c->gr_wait ? " waiting" : ""); | |
0c791f87 | 2106 | |
f4a60a9b OZ |
2107 | channel_show_limit(&c->rx_limit, "Receive limit:"); |
2108 | channel_show_limit(&c->in_limit, "Import limit:"); | |
2109 | channel_show_limit(&c->out_limit, "Export limit:"); | |
ebecb6f6 | 2110 | |
f4a60a9b OZ |
2111 | if (c->channel_state != CS_DOWN) |
2112 | channel_show_stats(c); | |
9db74169 OZ |
2113 | } |
2114 | ||
61dae32b OZ |
2115 | void |
2116 | channel_cmd_debug(struct channel *c, uint mask) | |
2117 | { | |
2118 | if (cli_access_restricted()) | |
2119 | return; | |
2120 | ||
2121 | c->debug = mask; | |
2122 | cli_msg(0, ""); | |
2123 | } | |
2124 | ||
e304fd4b | 2125 | void |
cd1d9961 | 2126 | proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt) |
1d2664a4 | 2127 | { |
c37e7851 | 2128 | byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE]; |
9685deb9 | 2129 | |
e304fd4b OZ |
2130 | /* First protocol - show header */ |
2131 | if (!cnt) | |
eb95b5ec OZ |
2132 | cli_msg(-2002, "%-10s %-10s %-10s %-6s %-12s %s", |
2133 | "Name", "Proto", "Table", "State", "Since", "Info"); | |
e304fd4b | 2134 | |
9685deb9 MM |
2135 | buf[0] = 0; |
2136 | if (p->proto->get_status) | |
2137 | p->proto->get_status(p, buf); | |
f047271c | 2138 | tm_format_time(tbuf, &config->tf_proto, p->last_state_change); |
eb95b5ec | 2139 | cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s", |
1d2664a4 MM |
2140 | p->name, |
2141 | p->proto->name, | |
f4a60a9b | 2142 | p->main_channel ? p->main_channel->table->name : "---", |
1d2664a4 | 2143 | proto_state_name(p), |
c37e7851 | 2144 | tbuf, |
9685deb9 | 2145 | buf); |
f4a60a9b | 2146 | |
1d2664a4 | 2147 | if (verbose) |
f4a60a9b OZ |
2148 | { |
2149 | if (p->cf->dsc) | |
2150 | cli_msg(-1006, " Description: %s", p->cf->dsc); | |
830ba75e OZ |
2151 | if (p->message) |
2152 | cli_msg(-1006, " Message: %s", p->message); | |
f4a60a9b OZ |
2153 | if (p->cf->router_id) |
2154 | cli_msg(-1006, " Router ID: %R", p->cf->router_id); | |
18f70a62 OZ |
2155 | if (p->vrf_set) |
2156 | cli_msg(-1006, " VRF: %s", p->vrf ? p->vrf->name : "default"); | |
f4a60a9b OZ |
2157 | |
2158 | if (p->proto->show_proto_info) | |
2159 | p->proto->show_proto_info(p); | |
2160 | else | |
1d2664a4 | 2161 | { |
f4a60a9b OZ |
2162 | struct channel *c; |
2163 | WALK_LIST(c, p->channels) | |
2164 | channel_show_info(c); | |
1d2664a4 | 2165 | } |
f4a60a9b OZ |
2166 | |
2167 | cli_msg(-1006, ""); | |
2168 | } | |
1d2664a4 MM |
2169 | } |
2170 | ||
ae97b946 | 2171 | void |
cd1d9961 | 2172 | proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED) |
ae97b946 | 2173 | { |
e304fd4b | 2174 | if (p->disabled) |
f4a60a9b OZ |
2175 | { |
2176 | cli_msg(-8, "%s: already disabled", p->name); | |
2177 | return; | |
2178 | } | |
e304fd4b OZ |
2179 | |
2180 | log(L_INFO "Disabling protocol %s", p->name); | |
2181 | p->disabled = 1; | |
ebecb6f6 | 2182 | p->down_code = PDC_CMD_DISABLE; |
cd1d9961 | 2183 | proto_set_message(p, (char *) arg, -1); |
e304fd4b OZ |
2184 | proto_rethink_goal(p); |
2185 | cli_msg(-9, "%s: disabled", p->name); | |
2186 | } | |
2187 | ||
2188 | void | |
cd1d9961 | 2189 | proto_cmd_enable(struct proto *p, uintptr_t arg, int cnt UNUSED) |
e304fd4b OZ |
2190 | { |
2191 | if (!p->disabled) | |
f4a60a9b OZ |
2192 | { |
2193 | cli_msg(-10, "%s: already enabled", p->name); | |
2194 | return; | |
2195 | } | |
e304fd4b OZ |
2196 | |
2197 | log(L_INFO "Enabling protocol %s", p->name); | |
2198 | p->disabled = 0; | |
cd1d9961 | 2199 | proto_set_message(p, (char *) arg, -1); |
e304fd4b OZ |
2200 | proto_rethink_goal(p); |
2201 | cli_msg(-11, "%s: enabled", p->name); | |
2202 | } | |
2203 | ||
2204 | void | |
cd1d9961 | 2205 | proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED) |
e304fd4b OZ |
2206 | { |
2207 | if (p->disabled) | |
f4a60a9b OZ |
2208 | { |
2209 | cli_msg(-8, "%s: already disabled", p->name); | |
2210 | return; | |
2211 | } | |
e304fd4b OZ |
2212 | |
2213 | log(L_INFO "Restarting protocol %s", p->name); | |
2214 | p->disabled = 1; | |
ebecb6f6 | 2215 | p->down_code = PDC_CMD_RESTART; |
cd1d9961 | 2216 | proto_set_message(p, (char *) arg, -1); |
e304fd4b OZ |
2217 | proto_rethink_goal(p); |
2218 | p->disabled = 0; | |
2219 | proto_rethink_goal(p); | |
2220 | cli_msg(-12, "%s: restarted", p->name); | |
2221 | } | |
2222 | ||
2223 | void | |
cd1d9961 | 2224 | proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED) |
e304fd4b | 2225 | { |
f4a60a9b OZ |
2226 | struct channel *c; |
2227 | ||
e304fd4b | 2228 | if (p->disabled) |
f4a60a9b OZ |
2229 | { |
2230 | cli_msg(-8, "%s: already disabled", p->name); | |
2231 | return; | |
2232 | } | |
e304fd4b OZ |
2233 | |
2234 | /* If the protocol in not UP, it has no routes */ | |
2235 | if (p->proto_state != PS_UP) | |
2236 | return; | |
2237 | ||
f4a60a9b OZ |
2238 | /* All channels must support reload */ |
2239 | if (dir != CMD_RELOAD_OUT) | |
2240 | WALK_LIST(c, p->channels) | |
a297a4f0 | 2241 | if ((c->channel_state == CS_UP) && !channel_reloadable(c)) |
f4a60a9b OZ |
2242 | { |
2243 | cli_msg(-8006, "%s: reload failed", p->name); | |
2244 | return; | |
2245 | } | |
2246 | ||
e304fd4b OZ |
2247 | log(L_INFO "Reloading protocol %s", p->name); |
2248 | ||
2249 | /* re-importing routes */ | |
2250 | if (dir != CMD_RELOAD_OUT) | |
f4a60a9b | 2251 | WALK_LIST(c, p->channels) |
a297a4f0 OZ |
2252 | if (c->channel_state == CS_UP) |
2253 | channel_request_reload(c); | |
ebecb6f6 | 2254 | |
e304fd4b OZ |
2255 | /* re-exporting routes */ |
2256 | if (dir != CMD_RELOAD_IN) | |
f4a60a9b | 2257 | WALK_LIST(c, p->channels) |
a297a4f0 OZ |
2258 | if (c->channel_state == CS_UP) |
2259 | channel_request_feeding(c); | |
e304fd4b OZ |
2260 | |
2261 | cli_msg(-15, "%s: reloading", p->name); | |
2262 | } | |
2263 | ||
2f981534 OZ |
2264 | extern void pipe_update_debug(struct proto *P); |
2265 | ||
e304fd4b | 2266 | void |
cd1d9961 | 2267 | proto_cmd_debug(struct proto *p, uintptr_t mask, int cnt UNUSED) |
e304fd4b OZ |
2268 | { |
2269 | p->debug = mask; | |
2f981534 OZ |
2270 | |
2271 | #ifdef CONFIG_PIPE | |
2272 | if (p->proto == &proto_pipe) | |
2273 | pipe_update_debug(p); | |
2274 | #endif | |
e304fd4b OZ |
2275 | } |
2276 | ||
2277 | void | |
cd1d9961 | 2278 | proto_cmd_mrtdump(struct proto *p, uintptr_t mask, int cnt UNUSED) |
e304fd4b OZ |
2279 | { |
2280 | p->mrtdump = mask; | |
2281 | } | |
2282 | ||
2283 | static void | |
fd9f0c06 | 2284 | proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg) |
e304fd4b OZ |
2285 | { |
2286 | if (s->class != SYM_PROTO) | |
f4a60a9b OZ |
2287 | { |
2288 | cli_msg(9002, "%s is not a protocol", s->name); | |
2289 | return; | |
2290 | } | |
e304fd4b | 2291 | |
8216ec30 MM |
2292 | if (s->proto->proto) |
2293 | { | |
2294 | cmd(s->proto->proto, arg, 0); | |
2295 | cli_msg(0, ""); | |
2296 | } | |
2297 | else | |
2298 | cli_msg(9002, "%s does not exist", s->name); | |
ae97b946 | 2299 | } |
02c1fbdd | 2300 | |
e304fd4b | 2301 | static void |
fd9f0c06 | 2302 | proto_apply_cmd_patt(const char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg) |
e304fd4b | 2303 | { |
f4a60a9b | 2304 | struct proto *p; |
e304fd4b OZ |
2305 | int cnt = 0; |
2306 | ||
f4a60a9b OZ |
2307 | WALK_LIST(p, proto_list) |
2308 | if (!patt || patmatch(patt, p->name)) | |
2309 | cmd(p, arg, cnt++); | |
e304fd4b OZ |
2310 | |
2311 | if (!cnt) | |
2312 | cli_msg(8003, "No protocols match"); | |
2313 | else | |
2314 | cli_msg(0, ""); | |
2315 | } | |
2316 | ||
2317 | void | |
cd1d9961 OZ |
2318 | proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), |
2319 | int restricted, uintptr_t arg) | |
e304fd4b | 2320 | { |
e0a45fb4 OZ |
2321 | if (restricted && cli_access_restricted()) |
2322 | return; | |
2323 | ||
e304fd4b OZ |
2324 | if (ps.patt) |
2325 | proto_apply_cmd_patt(ps.ptr, cmd, arg); | |
2326 | else | |
2327 | proto_apply_cmd_symbol(ps.ptr, cmd, arg); | |
2328 | } | |
2329 | ||
02c1fbdd MM |
2330 | struct proto * |
2331 | proto_get_named(struct symbol *sym, struct protocol *pr) | |
2332 | { | |
2333 | struct proto *p, *q; | |
2334 | ||
2335 | if (sym) | |
f4a60a9b OZ |
2336 | { |
2337 | if (sym->class != SYM_PROTO) | |
2338 | cf_error("%s: Not a protocol", sym->name); | |
2339 | ||
0b39b1cb | 2340 | p = sym->proto->proto; |
f4a60a9b OZ |
2341 | if (!p || p->proto != pr) |
2342 | cf_error("%s: Not a %s protocol", sym->name, pr->name); | |
2343 | } | |
02c1fbdd | 2344 | else |
f4a60a9b OZ |
2345 | { |
2346 | p = NULL; | |
2347 | WALK_LIST(q, proto_list) | |
2348 | if ((q->proto == pr) && (q->proto_state != PS_DOWN)) | |
2349 | { | |
2350 | if (p) | |
2351 | cf_error("There are multiple %s protocols running", pr->name); | |
2352 | p = q; | |
2353 | } | |
2354 | if (!p) | |
2355 | cf_error("There is no %s protocol running", pr->name); | |
2356 | } | |
2357 | ||
02c1fbdd MM |
2358 | return p; |
2359 | } | |
c26c6bc2 OZ |
2360 | |
2361 | struct proto * | |
2362 | proto_iterate_named(struct symbol *sym, struct protocol *proto, struct proto *old) | |
2363 | { | |
2364 | if (sym) | |
2365 | { | |
2366 | /* Just the first pass */ | |
2367 | if (old) | |
2368 | { | |
2369 | cli_msg(0, ""); | |
2370 | return NULL; | |
2371 | } | |
2372 | ||
2373 | if (sym->class != SYM_PROTO) | |
2374 | cf_error("%s: Not a protocol", sym->name); | |
2375 | ||
2376 | struct proto *p = sym->proto->proto; | |
2377 | if (!p || (p->proto != proto)) | |
2378 | cf_error("%s: Not a %s protocol", sym->name, proto->name); | |
2379 | ||
2380 | return p; | |
2381 | } | |
2382 | else | |
2383 | { | |
2384 | for (struct proto *p = !old ? HEAD(proto_list) : NODE_NEXT(old); | |
2385 | NODE_VALID(p); | |
2386 | p = NODE_NEXT(p)) | |
2387 | { | |
2388 | if ((p->proto == proto) && (p->proto_state != PS_DOWN)) | |
2389 | { | |
2390 | cli_separator(this_cli); | |
2391 | return p; | |
2392 | } | |
2393 | } | |
2394 | ||
2395 | /* Not found anything during first pass */ | |
2396 | if (!old) | |
2397 | cf_error("There is no %s protocol running", proto->name); | |
2398 | ||
2399 | /* No more items */ | |
2400 | cli_msg(0, ""); | |
2401 | return NULL; | |
2402 | } | |
2403 | } |