]>
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" | |
ae97b946 | 21 | #include "nest/cli.h" |
529c4149 | 22 | #include "filter/filter.h" |
2326b001 | 23 | |
acb60628 | 24 | pool *proto_pool; |
f4a60a9b | 25 | list proto_list; |
67bd949a | 26 | |
3991d84e | 27 | static list protocol_list; |
ee7e2ffd | 28 | struct protocol *class_to_protocol[PROTOCOL__MAX]; |
67bd949a | 29 | |
839380d7 MM |
30 | #define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0) |
31 | ||
ebecb6f6 | 32 | static timer *proto_shutdown_timer; |
0c791f87 OZ |
33 | static timer *gr_wait_timer; |
34 | ||
35 | #define GRS_NONE 0 | |
36 | #define GRS_INIT 1 | |
37 | #define GRS_ACTIVE 2 | |
38 | #define GRS_DONE 3 | |
39 | ||
40 | static int graceful_restart_state; | |
41 | static u32 graceful_restart_locks; | |
67bd949a MM |
42 | |
43 | static char *p_states[] = { "DOWN", "START", "UP", "STOP" }; | |
d15b0b0a | 44 | static char *c_states[] = { "DOWN", "START", "UP", "FLUSHING" }; |
67bd949a | 45 | |
f4a60a9b OZ |
46 | extern struct protocol proto_unix_iface; |
47 | ||
02552526 | 48 | static void proto_shutdown_loop(timer *); |
50fe90ed | 49 | static void proto_rethink_goal(struct proto *p); |
839380d7 | 50 | static char *proto_state_name(struct proto *p); |
f4a60a9b | 51 | static void channel_verify_limits(struct channel *c); |
734e9fb8 | 52 | static inline void channel_reset_limit(struct channel_limit *l); |
1a54b1c6 | 53 | |
1a54b1c6 | 54 | |
f4a60a9b OZ |
55 | static inline int proto_is_done(struct proto *p) |
56 | { return (p->proto_state == PS_DOWN) && (p->active_channels == 0); } | |
227af309 | 57 | |
f4a60a9b OZ |
58 | static inline int channel_is_active(struct channel *c) |
59 | { return (c->channel_state == CS_START) || (c->channel_state == CS_UP); } | |
227af309 OZ |
60 | |
61 | static void | |
62 | proto_log_state_change(struct proto *p) | |
63 | { | |
64 | if (p->debug & D_STATES) | |
f4a60a9b OZ |
65 | { |
66 | char *name = proto_state_name(p); | |
67 | if (name != p->last_state_name_announced) | |
227af309 | 68 | { |
f4a60a9b OZ |
69 | p->last_state_name_announced = name; |
70 | PD(p, "State changed to %s", proto_state_name(p)); | |
227af309 | 71 | } |
f4a60a9b | 72 | } |
227af309 OZ |
73 | else |
74 | p->last_state_name_announced = NULL; | |
67bd949a | 75 | } |
2326b001 | 76 | |
227af309 | 77 | |
f4a60a9b OZ |
78 | struct channel_config * |
79 | proto_cf_find_channel(struct proto_config *pc, uint net_type) | |
7f4a3988 | 80 | { |
f4a60a9b OZ |
81 | struct channel_config *cc; |
82 | ||
83 | WALK_LIST(cc, pc->channels) | |
84 | if (cc->net_type == net_type) | |
85 | return cc; | |
86 | ||
87 | return NULL; | |
7f4a3988 MM |
88 | } |
89 | ||
f4a60a9b OZ |
90 | /** |
91 | * proto_find_channel_by_table - find channel connected to a routing table | |
92 | * @p: protocol instance | |
93 | * @t: routing table | |
94 | * | |
95 | * Returns pointer to channel or NULL | |
96 | */ | |
97 | struct channel * | |
98 | proto_find_channel_by_table(struct proto *p, struct rtable *t) | |
1a54b1c6 | 99 | { |
f4a60a9b | 100 | struct channel *c; |
c0adf7e9 | 101 | |
f4a60a9b OZ |
102 | WALK_LIST(c, p->channels) |
103 | if (c->table == t) | |
104 | return c; | |
0c791f87 | 105 | |
f4a60a9b | 106 | return NULL; |
1a54b1c6 MM |
107 | } |
108 | ||
b2949999 OZ |
109 | /** |
110 | * proto_find_channel_by_name - find channel by its name | |
111 | * @p: protocol instance | |
112 | * @n: channel name | |
113 | * | |
114 | * Returns pointer to channel or NULL | |
115 | */ | |
116 | struct channel * | |
117 | proto_find_channel_by_name(struct proto *p, const char *n) | |
118 | { | |
119 | struct channel *c; | |
120 | ||
121 | WALK_LIST(c, p->channels) | |
122 | if (!strcmp(c->name, n)) | |
123 | return c; | |
124 | ||
125 | return NULL; | |
126 | } | |
127 | ||
3c6269b8 | 128 | /** |
f4a60a9b | 129 | * proto_add_channel - connect protocol to a routing table |
3c6269b8 | 130 | * @p: protocol instance |
f4a60a9b | 131 | * @cf: channel configuration |
3c6269b8 | 132 | * |
f4a60a9b OZ |
133 | * This function creates a channel between the protocol instance @p and the |
134 | * routing table specified in the configuration @cf, making the protocol hear | |
135 | * all changes in the table and allowing the protocol to update routes in the | |
136 | * table. | |
c0adf7e9 | 137 | * |
f4a60a9b OZ |
138 | * The channel is linked in the protocol channel list and when active also in |
139 | * the table channel list. Channels are allocated from the global resource pool | |
140 | * (@proto_pool) and they are automatically freed when the protocol is removed. | |
3c6269b8 | 141 | */ |
f4a60a9b OZ |
142 | |
143 | struct channel * | |
144 | proto_add_channel(struct proto *p, struct channel_config *cf) | |
145 | { | |
146 | struct channel *c = mb_allocz(proto_pool, cf->channel->channel_size); | |
147 | ||
148 | c->name = cf->name; | |
149 | c->channel = cf->channel; | |
150 | c->proto = p; | |
151 | c->table = cf->table->table; | |
152 | ||
153 | c->in_filter = cf->in_filter; | |
154 | c->out_filter = cf->out_filter; | |
155 | c->rx_limit = cf->rx_limit; | |
156 | c->in_limit = cf->in_limit; | |
157 | c->out_limit = cf->out_limit; | |
158 | ||
159 | c->net_type = cf->net_type; | |
160 | c->ra_mode = cf->ra_mode; | |
161 | c->preference = cf->preference; | |
162 | c->merge_limit = cf->merge_limit; | |
163 | c->in_keep_filtered = cf->in_keep_filtered; | |
164 | ||
165 | c->channel_state = CS_DOWN; | |
166 | c->export_state = ES_DOWN; | |
f047271c | 167 | c->last_state_change = current_time(); |
a81e18da | 168 | c->last_tx_filter_change = current_time(); |
f4a60a9b OZ |
169 | c->reloadable = 1; |
170 | ||
171 | CALL(c->channel->init, c, cf); | |
172 | ||
173 | add_tail(&p->channels, &c->n); | |
174 | ||
175 | PD(p, "Channel %s connected to table %s", c->name, c->table->name); | |
176 | ||
177 | return c; | |
178 | } | |
179 | ||
180 | void | |
181 | proto_remove_channel(struct proto *p, struct channel *c) | |
182 | { | |
183 | ASSERT(c->channel_state == CS_DOWN); | |
184 | ||
185 | PD(p, "Channel %s removed", c->name); | |
186 | ||
187 | rem_node(&c->n); | |
188 | mb_free(c); | |
189 | } | |
190 | ||
191 | ||
192 | static void | |
193 | proto_start_channels(struct proto *p) | |
194 | { | |
195 | struct channel *c; | |
196 | WALK_LIST(c, p->channels) | |
197 | if (!c->disabled) | |
198 | channel_set_state(c, CS_UP); | |
199 | } | |
200 | ||
201 | static void | |
202 | proto_pause_channels(struct proto *p) | |
0e02abfd | 203 | { |
f4a60a9b OZ |
204 | struct channel *c; |
205 | WALK_LIST(c, p->channels) | |
206 | if (!c->disabled && channel_is_active(c)) | |
207 | channel_set_state(c, CS_START); | |
208 | } | |
0e02abfd | 209 | |
f4a60a9b OZ |
210 | static void |
211 | proto_stop_channels(struct proto *p) | |
212 | { | |
213 | struct channel *c; | |
214 | WALK_LIST(c, p->channels) | |
215 | if (!c->disabled && channel_is_active(c)) | |
216 | channel_set_state(c, CS_FLUSHING); | |
217 | } | |
c0adf7e9 | 218 | |
f4a60a9b OZ |
219 | static void |
220 | proto_remove_channels(struct proto *p) | |
221 | { | |
222 | struct channel *c; | |
223 | WALK_LIST_FIRST(c, p->channels) | |
224 | proto_remove_channel(p, c); | |
225 | } | |
226 | ||
227 | static void | |
228 | channel_schedule_feed(struct channel *c, int initial) | |
229 | { | |
230 | // DBG("%s: Scheduling meal\n", p->name); | |
231 | ASSERT(c->channel_state == CS_UP); | |
c0adf7e9 | 232 | |
f4a60a9b OZ |
233 | c->export_state = ES_FEEDING; |
234 | c->refeeding = !initial; | |
c0adf7e9 | 235 | |
f4a60a9b OZ |
236 | ev_schedule(c->feed_event); |
237 | } | |
238 | ||
239 | static void | |
240 | channel_feed_loop(void *ptr) | |
241 | { | |
242 | struct channel *c = ptr; | |
243 | ||
244 | if (c->export_state != ES_FEEDING) | |
245 | return; | |
246 | ||
247 | if (!c->feed_active) | |
248 | if (c->proto->feed_begin) | |
249 | c->proto->feed_begin(c, !c->refeeding); | |
250 | ||
251 | // DBG("Feeding protocol %s continued\n", p->name); | |
252 | if (!rt_feed_channel(c)) | |
253 | { | |
254 | ev_schedule(c->feed_event); | |
255 | return; | |
256 | } | |
257 | ||
258 | // DBG("Feeding protocol %s finished\n", p->name); | |
259 | c->export_state = ES_READY; | |
260 | // proto_log_state_change(p); | |
261 | ||
262 | if (c->proto->feed_end) | |
263 | c->proto->feed_end(c); | |
264 | } | |
265 | ||
266 | ||
267 | static void | |
268 | channel_start_export(struct channel *c) | |
269 | { | |
270 | ASSERT(c->channel_state == CS_UP); | |
271 | ASSERT(c->export_state == ES_DOWN); | |
272 | ||
273 | channel_schedule_feed(c, 1); /* Sets ES_FEEDING */ | |
274 | } | |
275 | ||
276 | static void | |
277 | channel_stop_export(struct channel *c) | |
278 | { | |
279 | /* Need to abort feeding */ | |
280 | if (c->export_state == ES_FEEDING) | |
281 | rt_feed_channel_abort(c); | |
282 | ||
283 | c->export_state = ES_DOWN; | |
7a7ac656 | 284 | c->stats.exp_routes = 0; |
f4a60a9b OZ |
285 | } |
286 | ||
287 | static void | |
288 | channel_do_start(struct channel *c) | |
289 | { | |
290 | rt_lock_table(c->table); | |
291 | add_tail(&c->table->channels, &c->table_node); | |
292 | c->proto->active_channels++; | |
293 | ||
961671c0 | 294 | c->feed_event = ev_new_init(c->proto->pool, channel_feed_loop, c); |
f4a60a9b OZ |
295 | |
296 | channel_reset_limit(&c->rx_limit); | |
297 | channel_reset_limit(&c->in_limit); | |
298 | channel_reset_limit(&c->out_limit); | |
299 | ||
300 | CALL(c->channel->start, c); | |
301 | } | |
302 | ||
303 | static void | |
304 | channel_do_flush(struct channel *c) | |
305 | { | |
306 | rt_schedule_prune(c->table); | |
307 | ||
308 | c->gr_wait = 0; | |
309 | if (c->gr_lock) | |
310 | channel_graceful_restart_unlock(c); | |
311 | ||
312 | CALL(c->channel->shutdown, c); | |
313 | } | |
314 | ||
315 | static void | |
316 | channel_do_down(struct channel *c) | |
317 | { | |
7a7ac656 | 318 | rem_node(&c->table_node); |
f4a60a9b OZ |
319 | rt_unlock_table(c->table); |
320 | c->proto->active_channels--; | |
321 | ||
322 | if ((c->stats.imp_routes + c->stats.filt_routes) != 0) | |
323 | log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name); | |
324 | ||
325 | memset(&c->stats, 0, sizeof(struct proto_stats)); | |
326 | ||
d15b0b0a OZ |
327 | CALL(c->channel->cleanup, c); |
328 | ||
f4a60a9b OZ |
329 | /* Schedule protocol shutddown */ |
330 | if (proto_is_done(c->proto)) | |
331 | ev_schedule(c->proto->event); | |
332 | } | |
333 | ||
334 | void | |
335 | channel_set_state(struct channel *c, uint state) | |
336 | { | |
337 | uint cs = c->channel_state; | |
338 | uint es = c->export_state; | |
339 | ||
286e2011 | 340 | DBG("%s reporting channel %s state transition %s -> %s\n", c->proto->name, c->name, c_states[cs], c_states[state]); |
f4a60a9b OZ |
341 | if (state == cs) |
342 | return; | |
343 | ||
344 | c->channel_state = state; | |
f047271c | 345 | c->last_state_change = current_time(); |
f4a60a9b OZ |
346 | |
347 | switch (state) | |
348 | { | |
349 | case CS_START: | |
350 | ASSERT(cs == CS_DOWN || cs == CS_UP); | |
351 | ||
352 | if (cs == CS_DOWN) | |
353 | channel_do_start(c); | |
354 | ||
355 | if (es != ES_DOWN) | |
356 | channel_stop_export(c); | |
357 | ||
358 | break; | |
359 | ||
360 | case CS_UP: | |
361 | ASSERT(cs == CS_DOWN || cs == CS_START); | |
362 | ||
363 | if (cs == CS_DOWN) | |
364 | channel_do_start(c); | |
365 | ||
2a013bb3 | 366 | if (!c->gr_wait && c->proto->rt_notify) |
f4a60a9b OZ |
367 | channel_start_export(c); |
368 | ||
369 | break; | |
370 | ||
371 | case CS_FLUSHING: | |
372 | ASSERT(cs == CS_START || cs == CS_UP); | |
373 | ||
374 | if (es != ES_DOWN) | |
375 | channel_stop_export(c); | |
376 | ||
377 | channel_do_flush(c); | |
378 | break; | |
379 | ||
380 | case CS_DOWN: | |
381 | ASSERT(cs == CS_FLUSHING); | |
382 | ||
383 | channel_do_down(c); | |
384 | break; | |
385 | ||
386 | default: | |
387 | ASSERT(0); | |
388 | } | |
389 | // XXXX proto_log_state_change(c); | |
0e02abfd MM |
390 | } |
391 | ||
c0adf7e9 | 392 | /** |
f4a60a9b OZ |
393 | * channel_request_feeding - request feeding routes to the channel |
394 | * @c: given channel | |
c0adf7e9 | 395 | * |
f4a60a9b OZ |
396 | * Sometimes it is needed to send again all routes to the channel. This is |
397 | * called feeding and can be requested by this function. This would cause | |
398 | * channel export state transition to ES_FEEDING (during feeding) and when | |
399 | * completed, it will switch back to ES_READY. This function can be called | |
400 | * even when feeding is already running, in that case it is restarted. | |
c0adf7e9 | 401 | */ |
f4a60a9b OZ |
402 | void |
403 | channel_request_feeding(struct channel *c) | |
c0adf7e9 | 404 | { |
f4a60a9b | 405 | ASSERT(c->channel_state == CS_UP); |
c0adf7e9 | 406 | |
f4a60a9b OZ |
407 | /* Do nothing if we are still waiting for feeding */ |
408 | if (c->export_state == ES_DOWN) | |
409 | return; | |
c0adf7e9 | 410 | |
f4a60a9b OZ |
411 | /* If we are already feeding, we want to restart it */ |
412 | if (c->export_state == ES_FEEDING) | |
413 | { | |
414 | /* Unless feeding is in initial state */ | |
415 | if (!c->feed_active) | |
416 | return; | |
417 | ||
418 | rt_feed_channel_abort(c); | |
419 | } | |
420 | ||
421 | channel_reset_limit(&c->out_limit); | |
422 | ||
423 | /* Hack: reset exp_routes during refeed, and do not decrease it later */ | |
424 | c->stats.exp_routes = 0; | |
425 | ||
426 | channel_schedule_feed(c, 0); /* Sets ES_FEEDING */ | |
427 | // proto_log_state_change(c); | |
428 | } | |
429 | ||
430 | static inline int | |
431 | channel_reloadable(struct channel *c) | |
432 | { | |
433 | return c->proto->reload_routes && c->reloadable; | |
c0adf7e9 OZ |
434 | } |
435 | ||
0c791f87 | 436 | static void |
f4a60a9b | 437 | channel_request_reload(struct channel *c) |
0c791f87 | 438 | { |
f4a60a9b | 439 | ASSERT(c->channel_state == CS_UP); |
2e507a74 | 440 | ASSERT(channel_reloadable(c)); |
f4a60a9b OZ |
441 | |
442 | c->proto->reload_routes(c); | |
0c791f87 | 443 | |
f4a60a9b OZ |
444 | /* |
445 | * Should this be done before reload_routes() hook? | |
446 | * Perhaps, but routes are updated asynchronously. | |
447 | */ | |
448 | channel_reset_limit(&c->rx_limit); | |
449 | channel_reset_limit(&c->in_limit); | |
0c791f87 OZ |
450 | } |
451 | ||
f4a60a9b OZ |
452 | const struct channel_class channel_basic = { |
453 | .channel_size = sizeof(struct channel), | |
454 | .config_size = sizeof(struct channel_config) | |
455 | }; | |
456 | ||
457 | void * | |
72163bd5 | 458 | channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto) |
f4a60a9b OZ |
459 | { |
460 | struct channel_config *cf = NULL; | |
461 | struct rtable_config *tab = NULL; | |
f4a60a9b OZ |
462 | |
463 | if (net_type) | |
464 | { | |
465 | if (!net_val_match(net_type, proto->protocol->channel_mask)) | |
466 | cf_error("Unsupported channel type"); | |
467 | ||
468 | if (proto->net_type && (net_type != proto->net_type)) | |
469 | cf_error("Different channel type"); | |
470 | ||
471 | tab = new_config->def_tables[net_type]; | |
f4a60a9b OZ |
472 | } |
473 | ||
474 | if (!cc) | |
475 | cc = &channel_basic; | |
476 | ||
477 | cf = cfg_allocz(cc->config_size); | |
478 | cf->name = name; | |
479 | cf->channel = cc; | |
72163bd5 | 480 | cf->parent = proto; |
f4a60a9b OZ |
481 | cf->table = tab; |
482 | cf->out_filter = FILTER_REJECT; | |
483 | ||
484 | cf->net_type = net_type; | |
485 | cf->ra_mode = RA_OPTIMAL; | |
486 | cf->preference = proto->protocol->preference; | |
487 | ||
488 | add_tail(&proto->channels, &cf->n); | |
489 | ||
490 | return cf; | |
491 | } | |
492 | ||
72163bd5 OZ |
493 | void * |
494 | channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto) | |
495 | { | |
496 | struct channel_config *cf; | |
497 | ||
498 | /* We are using name as token, so no strcmp() */ | |
499 | WALK_LIST(cf, proto->channels) | |
500 | if (cf->name == name) | |
501 | { | |
502 | /* Allow to redefine channel only if inherited from template */ | |
503 | if (cf->parent == proto) | |
504 | cf_error("Multiple %s channels", name); | |
505 | ||
506 | cf->parent = proto; | |
507 | return cf; | |
508 | } | |
509 | ||
510 | return channel_config_new(cc, name, net_type, proto); | |
511 | } | |
512 | ||
f4a60a9b OZ |
513 | struct channel_config * |
514 | channel_copy_config(struct channel_config *src, struct proto_config *proto) | |
515 | { | |
516 | struct channel_config *dst = cfg_alloc(src->channel->config_size); | |
517 | ||
518 | memcpy(dst, src, src->channel->config_size); | |
519 | add_tail(&proto->channels, &dst->n); | |
520 | CALL(src->channel->copy_config, dst, src); | |
521 | ||
522 | return dst; | |
523 | } | |
524 | ||
525 | ||
526 | static int reconfigure_type; /* Hack to propagate type info to channel_reconfigure() */ | |
527 | ||
528 | int | |
529 | channel_reconfigure(struct channel *c, struct channel_config *cf) | |
530 | { | |
531 | /* FIXME: better handle these changes, also handle in_keep_filtered */ | |
f8aad5d5 | 532 | if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode))) |
f4a60a9b OZ |
533 | return 0; |
534 | ||
94f9be80 OZ |
535 | /* Note that filter_same() requires arguments in (new, old) order */ |
536 | int import_changed = !filter_same(cf->in_filter, c->in_filter); | |
537 | int export_changed = !filter_same(cf->out_filter, c->out_filter); | |
f4a60a9b OZ |
538 | |
539 | if (c->preference != cf->preference) | |
540 | import_changed = 1; | |
541 | ||
542 | if (c->merge_limit != cf->merge_limit) | |
543 | export_changed = 1; | |
544 | ||
545 | /* Reconfigure channel fields */ | |
546 | c->in_filter = cf->in_filter; | |
547 | c->out_filter = cf->out_filter; | |
548 | c->rx_limit = cf->rx_limit; | |
549 | c->in_limit = cf->in_limit; | |
550 | c->out_limit = cf->out_limit; | |
551 | ||
552 | // c->ra_mode = cf->ra_mode; | |
553 | c->merge_limit = cf->merge_limit; | |
554 | c->preference = cf->preference; | |
555 | c->in_keep_filtered = cf->in_keep_filtered; | |
556 | ||
557 | channel_verify_limits(c); | |
558 | ||
a81e18da OZ |
559 | if (export_changed) |
560 | c->last_tx_filter_change = current_time(); | |
561 | ||
d15b0b0a OZ |
562 | /* Execute channel-specific reconfigure hook */ |
563 | if (c->channel->reconfigure && !c->channel->reconfigure(c, cf)) | |
564 | return 0; | |
f4a60a9b OZ |
565 | |
566 | /* If the channel is not open, it has no routes and we cannot reload it anyways */ | |
567 | if (c->channel_state != CS_UP) | |
568 | return 1; | |
569 | ||
570 | if (reconfigure_type == RECONFIG_SOFT) | |
571 | { | |
572 | if (import_changed) | |
573 | log(L_INFO "Channel %s.%s changed import", c->proto->name, c->name); | |
574 | ||
575 | if (export_changed) | |
576 | log(L_INFO "Channel %s.%s changed export", c->proto->name, c->name); | |
577 | ||
578 | return 1; | |
579 | } | |
580 | ||
581 | /* Route reload may be not supported */ | |
582 | if (import_changed && !channel_reloadable(c)) | |
583 | return 0; | |
584 | ||
585 | if (import_changed || export_changed) | |
586 | log(L_INFO "Reloading channel %s.%s", c->proto->name, c->name); | |
587 | ||
588 | if (import_changed) | |
589 | channel_request_reload(c); | |
590 | ||
591 | if (export_changed) | |
592 | channel_request_feeding(c); | |
593 | ||
594 | return 1; | |
595 | } | |
596 | ||
597 | ||
598 | int | |
599 | proto_configure_channel(struct proto *p, struct channel **pc, struct channel_config *cf) | |
0e02abfd | 600 | { |
f4a60a9b | 601 | struct channel *c = *pc; |
0e02abfd | 602 | |
f4a60a9b OZ |
603 | if (!c && cf) |
604 | { | |
605 | *pc = proto_add_channel(p, cf); | |
606 | } | |
607 | else if (c && !cf) | |
608 | { | |
609 | if (c->channel_state != CS_DOWN) | |
610 | { | |
611 | log(L_INFO "Cannot remove channel %s.%s", c->proto->name, c->name); | |
612 | return 0; | |
613 | } | |
614 | ||
615 | proto_remove_channel(p, c); | |
616 | *pc = NULL; | |
617 | } | |
618 | else if (c && cf) | |
619 | { | |
620 | if (!channel_reconfigure(c, cf)) | |
621 | { | |
622 | log(L_INFO "Cannot reconfigure channel %s.%s", c->proto->name, c->name); | |
623 | return 0; | |
624 | } | |
625 | } | |
626 | ||
627 | return 1; | |
c0adf7e9 OZ |
628 | } |
629 | ||
f4a60a9b | 630 | |
c0adf7e9 | 631 | static void |
f4a60a9b | 632 | proto_event(void *ptr) |
c0adf7e9 | 633 | { |
f4a60a9b OZ |
634 | struct proto *p = ptr; |
635 | ||
636 | if (p->do_start) | |
637 | { | |
638 | if_feed_baby(p); | |
639 | p->do_start = 0; | |
640 | } | |
c0adf7e9 | 641 | |
f4a60a9b | 642 | if (p->do_stop) |
c0adf7e9 | 643 | { |
f4a60a9b OZ |
644 | if (p->proto == &proto_unix_iface) |
645 | if_flush_ifaces(p); | |
646 | p->do_stop = 0; | |
c0adf7e9 OZ |
647 | } |
648 | ||
f4a60a9b OZ |
649 | if (proto_is_done(p)) |
650 | { | |
651 | if (p->proto->cleanup) | |
652 | p->proto->cleanup(p); | |
653 | ||
654 | p->active = 0; | |
655 | proto_log_state_change(p); | |
656 | proto_rethink_goal(p); | |
657 | } | |
658 | } | |
659 | ||
660 | ||
661 | /** | |
662 | * proto_new - create a new protocol instance | |
663 | * @c: protocol configuration | |
664 | * | |
665 | * When a new configuration has been read in, the core code starts | |
666 | * initializing all the protocol instances configured by calling their | |
667 | * init() hooks with the corresponding instance configuration. The initialization | |
668 | * code of the protocol is expected to create a new instance according to the | |
669 | * configuration by calling this function and then modifying the default settings | |
670 | * to values wanted by the protocol. | |
671 | */ | |
672 | void * | |
673 | proto_new(struct proto_config *cf) | |
674 | { | |
675 | struct proto *p = mb_allocz(proto_pool, cf->protocol->proto_size); | |
676 | ||
677 | p->cf = cf; | |
678 | p->debug = cf->debug; | |
679 | p->mrtdump = cf->mrtdump; | |
680 | p->name = cf->name; | |
681 | p->proto = cf->protocol; | |
682 | p->net_type = cf->net_type; | |
683 | p->disabled = cf->disabled; | |
684 | p->hash_key = random_u32(); | |
685 | cf->proto = p; | |
686 | ||
687 | init_list(&p->channels); | |
688 | ||
689 | return p; | |
690 | } | |
691 | ||
692 | static struct proto * | |
693 | proto_init(struct proto_config *c, node *n) | |
694 | { | |
695 | struct protocol *pr = c->protocol; | |
696 | struct proto *p = pr->init(c); | |
697 | ||
698 | p->proto_state = PS_DOWN; | |
f047271c | 699 | p->last_state_change = current_time(); |
46434a3c | 700 | p->vrf = c->vrf; |
f4a60a9b OZ |
701 | insert_node(&p->n, n); |
702 | ||
961671c0 | 703 | p->event = ev_new_init(proto_pool, proto_event, p); |
f4a60a9b OZ |
704 | |
705 | PD(p, "Initializing%s", p->disabled ? " [disabled]" : ""); | |
706 | ||
707 | return p; | |
708 | } | |
709 | ||
710 | static void | |
711 | proto_start(struct proto *p) | |
712 | { | |
713 | /* Here we cannot use p->cf->name since it won't survive reconfiguration */ | |
714 | p->pool = rp_new(proto_pool, p->proto->name); | |
715 | ||
716 | if (graceful_restart_state == GRS_INIT) | |
717 | p->gr_recovery = 1; | |
0e02abfd MM |
718 | } |
719 | ||
094d2bdb | 720 | |
3c6269b8 MM |
721 | /** |
722 | * proto_config_new - create a new protocol configuration | |
723 | * @pr: protocol the configuration will belong to | |
a7f23f58 | 724 | * @class: SYM_PROTO or SYM_TEMPLATE |
3c6269b8 MM |
725 | * |
726 | * Whenever the configuration file says that a new instance | |
727 | * of a routing protocol should be created, the parser calls | |
728 | * proto_config_new() to create a configuration entry for this | |
729 | * instance (a structure staring with the &proto_config header | |
730 | * containing all the generic items followed by protocol-specific | |
731 | * ones). Also, the configuration entry gets added to the list | |
732 | * of protocol instances kept in the configuration. | |
a7f23f58 OZ |
733 | * |
734 | * The function is also used to create protocol templates (when class | |
735 | * SYM_TEMPLATE is specified), the only difference is that templates | |
736 | * are not added to the list of protocol instances and therefore not | |
737 | * initialized during protos_commit()). | |
3c6269b8 | 738 | */ |
31b3e1bb | 739 | void * |
2bbc3083 | 740 | proto_config_new(struct protocol *pr, int class) |
31b3e1bb | 741 | { |
f4a60a9b | 742 | struct proto_config *cf = cfg_allocz(pr->config_size); |
31b3e1bb | 743 | |
a7f23f58 | 744 | if (class == SYM_PROTO) |
f4a60a9b OZ |
745 | add_tail(&new_config->protos, &cf->n); |
746 | ||
747 | cf->global = new_config; | |
748 | cf->protocol = pr; | |
749 | cf->name = pr->name; | |
750 | cf->class = class; | |
751 | cf->debug = new_config->proto_default_debug; | |
752 | cf->mrtdump = new_config->proto_default_mrtdump; | |
753 | ||
754 | init_list(&cf->channels); | |
755 | ||
756 | return cf; | |
31b3e1bb MM |
757 | } |
758 | ||
f4a60a9b | 759 | |
a7f23f58 OZ |
760 | /** |
761 | * proto_copy_config - copy a protocol configuration | |
762 | * @dest: destination protocol configuration | |
763 | * @src: source protocol configuration | |
764 | * | |
765 | * Whenever a new instance of a routing protocol is created from the | |
766 | * template, proto_copy_config() is called to copy a content of | |
767 | * the source protocol configuration to the new protocol configuration. | |
768 | * Name, class and a node in protos list of @dest are kept intact. | |
769 | * copy_config() protocol hook is used to copy protocol-specific data. | |
770 | */ | |
771 | void | |
772 | proto_copy_config(struct proto_config *dest, struct proto_config *src) | |
773 | { | |
f4a60a9b | 774 | struct channel_config *cc; |
a7f23f58 OZ |
775 | node old_node; |
776 | int old_class; | |
777 | char *old_name; | |
778 | ||
779 | if (dest->protocol != src->protocol) | |
780 | cf_error("Can't copy configuration from a different protocol type"); | |
781 | ||
782 | if (dest->protocol->copy_config == NULL) | |
783 | cf_error("Inheriting configuration for %s is not supported", src->protocol->name); | |
784 | ||
785 | DBG("Copying configuration from %s to %s\n", src->name, dest->name); | |
786 | ||
f4a60a9b | 787 | /* |
a7f23f58 OZ |
788 | * Copy struct proto_config here. Keep original node, class and name. |
789 | * protocol-specific config copy is handled by protocol copy_config() hook | |
790 | */ | |
791 | ||
792 | old_node = dest->n; | |
793 | old_class = dest->class; | |
794 | old_name = dest->name; | |
795 | ||
f4a60a9b | 796 | memcpy(dest, src, src->protocol->config_size); |
a7f23f58 OZ |
797 | |
798 | dest->n = old_node; | |
799 | dest->class = old_class; | |
800 | dest->name = old_name; | |
f4a60a9b | 801 | init_list(&dest->channels); |
a7f23f58 | 802 | |
f4a60a9b OZ |
803 | WALK_LIST(cc, src->channels) |
804 | channel_copy_config(cc, dest); | |
805 | ||
806 | /* FIXME: allow for undefined copy_config */ | |
a7f23f58 OZ |
807 | dest->protocol->copy_config(dest, src); |
808 | } | |
809 | ||
3c6269b8 MM |
810 | /** |
811 | * protos_preconfig - pre-configuration processing | |
812 | * @c: new configuration | |
813 | * | |
814 | * This function calls the preconfig() hooks of all routing | |
815 | * protocols available to prepare them for reading of the new | |
816 | * configuration. | |
817 | */ | |
2326b001 | 818 | void |
31b3e1bb | 819 | protos_preconfig(struct config *c) |
2326b001 | 820 | { |
7f4a3988 MM |
821 | struct protocol *p; |
822 | ||
7c0cc76e | 823 | init_list(&c->protos); |
6b9fa320 | 824 | DBG("Protocol preconfig:"); |
7f4a3988 | 825 | WALK_LIST(p, protocol_list) |
f4a60a9b OZ |
826 | { |
827 | DBG(" %s", p->name); | |
828 | p->name_counter = 0; | |
829 | if (p->preconfig) | |
830 | p->preconfig(p, c); | |
831 | } | |
6b9fa320 | 832 | DBG("\n"); |
31b3e1bb MM |
833 | } |
834 | ||
ebae4770 OZ |
835 | static int |
836 | proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type) | |
837 | { | |
838 | /* If the protocol is DOWN, we just restart it */ | |
839 | if (p->proto_state == PS_DOWN) | |
840 | return 0; | |
841 | ||
842 | /* If there is a too big change in core attributes, ... */ | |
843 | if ((nc->protocol != oc->protocol) || | |
f4a60a9b | 844 | (nc->net_type != oc->net_type) || |
23fd4644 | 845 | (nc->disabled != p->disabled) || |
46434a3c | 846 | (nc->vrf != oc->vrf)) |
ebae4770 OZ |
847 | return 0; |
848 | ||
f4a60a9b | 849 | p->name = nc->name; |
ebae4770 OZ |
850 | p->debug = nc->debug; |
851 | p->mrtdump = nc->mrtdump; | |
f4a60a9b | 852 | reconfigure_type = type; |
ebae4770 OZ |
853 | |
854 | /* Execute protocol specific reconfigure hook */ | |
f4a60a9b | 855 | if (!p->proto->reconfigure || !p->proto->reconfigure(p, nc)) |
ebae4770 OZ |
856 | return 0; |
857 | ||
858 | DBG("\t%s: same\n", oc->name); | |
859 | PD(p, "Reconfigured"); | |
860 | p->cf = nc; | |
ebae4770 OZ |
861 | |
862 | return 1; | |
863 | } | |
864 | ||
3c6269b8 MM |
865 | /** |
866 | * protos_commit - commit new protocol configuration | |
867 | * @new: new configuration | |
868 | * @old: old configuration or %NULL if it's boot time config | |
869 | * @force_reconfig: force restart of all protocols (used for example | |
870 | * when the router ID changes) | |
bf1aec97 | 871 | * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD) |
3c6269b8 MM |
872 | * |
873 | * Scan differences between @old and @new configuration and adjust all | |
874 | * protocol instances to conform to the new configuration. | |
875 | * | |
876 | * When a protocol exists in the new configuration, but it doesn't in the | |
877 | * original one, it's immediately started. When a collision with the other | |
878 | * running protocol would arise, the new protocol will be temporarily stopped | |
879 | * by the locking mechanism. | |
880 | * | |
881 | * When a protocol exists in the old configuration, but it doesn't in the | |
882 | * new one, it's shut down and deleted after the shutdown completes. | |
883 | * | |
bf1aec97 OZ |
884 | * When a protocol exists in both configurations, the core decides |
885 | * whether it's possible to reconfigure it dynamically - it checks all | |
886 | * the core properties of the protocol (changes in filters are ignored | |
887 | * if type is RECONFIG_SOFT) and if they match, it asks the | |
888 | * reconfigure() hook of the protocol to see if the protocol is able | |
889 | * to switch to the new configuration. If it isn't possible, the | |
890 | * protocol is shut down and a new instance is started with the new | |
891 | * configuration after the shutdown is completed. | |
3c6269b8 | 892 | */ |
31b3e1bb | 893 | void |
bf1aec97 | 894 | protos_commit(struct config *new, struct config *old, int force_reconfig, int type) |
31b3e1bb | 895 | { |
50fe90ed | 896 | struct proto_config *oc, *nc; |
a7f23f58 | 897 | struct symbol *sym; |
f4a60a9b OZ |
898 | struct proto *p; |
899 | node *n; | |
900 | ||
31b3e1bb | 901 | |
50fe90ed MM |
902 | DBG("protos_commit:\n"); |
903 | if (old) | |
f4a60a9b OZ |
904 | { |
905 | WALK_LIST(oc, old->protos) | |
31b3e1bb | 906 | { |
f4a60a9b OZ |
907 | p = oc->proto; |
908 | sym = cf_find_symbol(new, oc->name); | |
909 | if (sym && sym->class == SYM_PROTO && !new->shutdown) | |
910 | { | |
911 | /* Found match, let's check if we can smoothly switch to new configuration */ | |
912 | /* No need to check description */ | |
913 | nc = sym->def; | |
914 | nc->proto = p; | |
915 | ||
916 | /* We will try to reconfigure protocol p */ | |
917 | if (! force_reconfig && proto_reconfigure(p, oc, nc, type)) | |
918 | continue; | |
919 | ||
920 | /* Unsuccessful, we will restart it */ | |
921 | if (!p->disabled && !nc->disabled) | |
922 | log(L_INFO "Restarting protocol %s", p->name); | |
923 | else if (p->disabled && !nc->disabled) | |
924 | log(L_INFO "Enabling protocol %s", p->name); | |
925 | else if (!p->disabled && nc->disabled) | |
926 | log(L_INFO "Disabling protocol %s", p->name); | |
927 | ||
928 | p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART; | |
929 | p->cf_new = nc; | |
930 | } | |
931 | else if (!new->shutdown) | |
932 | { | |
933 | log(L_INFO "Removing protocol %s", p->name); | |
934 | p->down_code = PDC_CF_REMOVE; | |
935 | p->cf_new = NULL; | |
936 | } | |
937 | else /* global shutdown */ | |
938 | { | |
939 | p->down_code = PDC_CMD_SHUTDOWN; | |
940 | p->cf_new = NULL; | |
941 | } | |
942 | ||
943 | p->reconfiguring = 1; | |
944 | config_add_obstacle(old); | |
945 | proto_rethink_goal(p); | |
7f4a3988 | 946 | } |
f4a60a9b OZ |
947 | } |
948 | ||
949 | struct proto *first_dev_proto = NULL; | |
50fe90ed | 950 | |
f4a60a9b | 951 | n = NODE &(proto_list.head); |
50fe90ed MM |
952 | WALK_LIST(nc, new->protos) |
953 | if (!nc->proto) | |
f4a60a9b OZ |
954 | { |
955 | /* Not a first-time configuration */ | |
956 | if (old) | |
957 | log(L_INFO "Adding protocol %s", nc->name); | |
958 | ||
959 | p = proto_init(nc, n); | |
960 | n = NODE p; | |
961 | ||
962 | if (p->proto == &proto_unix_iface) | |
963 | first_dev_proto = p; | |
964 | } | |
965 | else | |
966 | n = NODE nc->proto; | |
50fe90ed MM |
967 | |
968 | DBG("Protocol start\n"); | |
4ef09506 OZ |
969 | |
970 | /* Start device protocol first */ | |
f4a60a9b OZ |
971 | if (first_dev_proto) |
972 | proto_rethink_goal(first_dev_proto); | |
4ef09506 | 973 | |
79b4e12e OZ |
974 | /* Determine router ID for the first time - it has to be here and not in |
975 | global_commit() because it is postponed after start of device protocol */ | |
976 | if (!config->router_id) | |
f4a60a9b OZ |
977 | { |
978 | config->router_id = if_choose_router_id(config->router_id_from, 0); | |
979 | if (!config->router_id) | |
980 | die("Cannot determine router ID, please configure it manually"); | |
981 | } | |
79b4e12e | 982 | |
f4a60a9b OZ |
983 | /* Start all new protocols */ |
984 | WALK_LIST_DELSAFE(p, n, proto_list) | |
50fe90ed | 985 | proto_rethink_goal(p); |
7f4a3988 MM |
986 | } |
987 | ||
47b79306 | 988 | static void |
67bd949a | 989 | proto_rethink_goal(struct proto *p) |
47b79306 | 990 | { |
50fe90ed | 991 | struct protocol *q; |
0c791f87 | 992 | byte goal; |
50fe90ed | 993 | |
f4a60a9b OZ |
994 | if (p->reconfiguring && !p->active) |
995 | { | |
996 | struct proto_config *nc = p->cf_new; | |
997 | node *n = p->n.prev; | |
998 | DBG("%s has shut down for reconfiguration\n", p->name); | |
999 | p->cf->proto = NULL; | |
1000 | config_del_obstacle(p->cf->global); | |
1001 | proto_remove_channels(p); | |
1002 | rem_node(&p->n); | |
1003 | rfree(p->event); | |
830ba75e | 1004 | mb_free(p->message); |
f4a60a9b OZ |
1005 | mb_free(p); |
1006 | if (!nc) | |
1007 | return; | |
1008 | p = proto_init(nc, n); | |
1009 | } | |
50fe90ed MM |
1010 | |
1011 | /* Determine what state we want to reach */ | |
bf8558bc | 1012 | if (p->disabled || p->reconfiguring) |
0c791f87 | 1013 | goal = PS_DOWN; |
50fe90ed | 1014 | else |
0c791f87 | 1015 | goal = PS_UP; |
50fe90ed MM |
1016 | |
1017 | q = p->proto; | |
f4a60a9b OZ |
1018 | if (goal == PS_UP) |
1019 | { | |
1020 | if (!p->active) | |
67bd949a | 1021 | { |
f4a60a9b OZ |
1022 | /* Going up */ |
1023 | DBG("Kicking %s up\n", p->name); | |
1024 | PD(p, "Starting"); | |
1025 | proto_start(p); | |
1026 | proto_notify_state(p, (q->start ? q->start(p) : PS_UP)); | |
67bd949a | 1027 | } |
f4a60a9b OZ |
1028 | } |
1029 | else | |
1030 | { | |
1031 | if (p->proto_state == PS_START || p->proto_state == PS_UP) | |
67bd949a | 1032 | { |
f4a60a9b OZ |
1033 | /* Going down */ |
1034 | DBG("Kicking %s down\n", p->name); | |
1035 | PD(p, "Shutting down"); | |
1036 | proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN)); | |
67bd949a | 1037 | } |
f4a60a9b | 1038 | } |
67bd949a MM |
1039 | } |
1040 | ||
0c791f87 | 1041 | |
6eda3f13 OZ |
1042 | /** |
1043 | * DOC: Graceful restart recovery | |
1044 | * | |
1045 | * Graceful restart of a router is a process when the routing plane (e.g. BIRD) | |
1046 | * restarts but both the forwarding plane (e.g kernel routing table) and routing | |
1047 | * neighbors keep proper routes, and therefore uninterrupted packet forwarding | |
1048 | * is maintained. | |
1049 | * | |
1050 | * BIRD implements graceful restart recovery by deferring export of routes to | |
1051 | * protocols until routing tables are refilled with the expected content. After | |
1052 | * start, protocols generate routes as usual, but routes are not propagated to | |
1053 | * them, until protocols report that they generated all routes. After that, | |
1054 | * graceful restart recovery is finished and the export (and the initial feed) | |
1055 | * to protocols is enabled. | |
1056 | * | |
1057 | * When graceful restart recovery need is detected during initialization, then | |
1058 | * enabled protocols are marked with @gr_recovery flag before start. Such | |
1059 | * protocols then decide how to proceed with graceful restart, participation is | |
f4a60a9b | 1060 | * voluntary. Protocols could lock the recovery for each channel by function |
286e2011 | 1061 | * channel_graceful_restart_lock() (state stored in @gr_lock flag), which means |
f4a60a9b OZ |
1062 | * that they want to postpone the end of the recovery until they converge and |
1063 | * then unlock it. They also could set @gr_wait before advancing to %PS_UP, | |
1064 | * which means that the core should defer route export to that channel until | |
1065 | * the end of the recovery. This should be done by protocols that expect their | |
1066 | * neigbors to keep the proper routes (kernel table, BGP sessions with BGP | |
1067 | * graceful restart capability). | |
6eda3f13 OZ |
1068 | * |
1069 | * The graceful restart recovery is finished when either all graceful restart | |
1070 | * locks are unlocked or when graceful restart wait timer fires. | |
1071 | * | |
1072 | */ | |
0c791f87 | 1073 | |
02552526 | 1074 | static void graceful_restart_done(timer *t); |
0c791f87 | 1075 | |
6eda3f13 OZ |
1076 | /** |
1077 | * graceful_restart_recovery - request initial graceful restart recovery | |
1078 | * | |
1079 | * Called by the platform initialization code if the need for recovery | |
1080 | * after graceful restart is detected during boot. Have to be called | |
1081 | * before protos_commit(). | |
1082 | */ | |
0c791f87 OZ |
1083 | void |
1084 | graceful_restart_recovery(void) | |
1085 | { | |
1086 | graceful_restart_state = GRS_INIT; | |
1087 | } | |
1088 | ||
6eda3f13 OZ |
1089 | /** |
1090 | * graceful_restart_init - initialize graceful restart | |
1091 | * | |
1092 | * When graceful restart recovery was requested, the function starts an active | |
1093 | * phase of the recovery and initializes graceful restart wait timer. The | |
1094 | * function have to be called after protos_commit(). | |
1095 | */ | |
0c791f87 OZ |
1096 | void |
1097 | graceful_restart_init(void) | |
1098 | { | |
1099 | if (!graceful_restart_state) | |
1100 | return; | |
1101 | ||
1102 | log(L_INFO "Graceful restart started"); | |
1103 | ||
1104 | if (!graceful_restart_locks) | |
f4a60a9b OZ |
1105 | { |
1106 | graceful_restart_done(NULL); | |
1107 | return; | |
1108 | } | |
0c791f87 OZ |
1109 | |
1110 | graceful_restart_state = GRS_ACTIVE; | |
a6f79ca5 OZ |
1111 | gr_wait_timer = tm_new_init(proto_pool, graceful_restart_done, NULL, 0, 0); |
1112 | tm_start(gr_wait_timer, config->gr_wait S); | |
0c791f87 OZ |
1113 | } |
1114 | ||
6eda3f13 OZ |
1115 | /** |
1116 | * graceful_restart_done - finalize graceful restart | |
8e433d6a | 1117 | * @t: unused |
6eda3f13 OZ |
1118 | * |
1119 | * When there are no locks on graceful restart, the functions finalizes the | |
1120 | * graceful restart recovery. Protocols postponing route export until the end of | |
1121 | * the recovery are awakened and the export to them is enabled. All other | |
1122 | * related state is cleared. The function is also called when the graceful | |
1123 | * restart wait timer fires (but there are still some locks). | |
1124 | */ | |
0c791f87 | 1125 | static void |
02552526 | 1126 | graceful_restart_done(timer *t UNUSED) |
0c791f87 | 1127 | { |
0c791f87 OZ |
1128 | log(L_INFO "Graceful restart done"); |
1129 | graceful_restart_state = GRS_DONE; | |
1130 | ||
f4a60a9b OZ |
1131 | struct proto *p; |
1132 | WALK_LIST(p, proto_list) | |
1133 | { | |
1134 | if (!p->gr_recovery) | |
1135 | continue; | |
0c791f87 | 1136 | |
f4a60a9b OZ |
1137 | struct channel *c; |
1138 | WALK_LIST(c, p->channels) | |
1139 | { | |
0c791f87 | 1140 | /* Resume postponed export of routes */ |
2a013bb3 | 1141 | if ((c->channel_state == CS_UP) && c->gr_wait && c->proto->rt_notify) |
f4a60a9b | 1142 | channel_start_export(c); |
0c791f87 OZ |
1143 | |
1144 | /* Cleanup */ | |
f4a60a9b OZ |
1145 | c->gr_wait = 0; |
1146 | c->gr_lock = 0; | |
0c791f87 OZ |
1147 | } |
1148 | ||
f4a60a9b OZ |
1149 | p->gr_recovery = 0; |
1150 | } | |
1151 | ||
0c791f87 OZ |
1152 | graceful_restart_locks = 0; |
1153 | } | |
1154 | ||
1155 | void | |
1156 | graceful_restart_show_status(void) | |
1157 | { | |
1158 | if (graceful_restart_state != GRS_ACTIVE) | |
1159 | return; | |
1160 | ||
1161 | cli_msg(-24, "Graceful restart recovery in progress"); | |
f4a60a9b | 1162 | cli_msg(-24, " Waiting for %d channels to recover", graceful_restart_locks); |
a6f79ca5 | 1163 | cli_msg(-24, " Wait timer is %t/%u", tm_remains(gr_wait_timer), config->gr_wait); |
0c791f87 OZ |
1164 | } |
1165 | ||
6eda3f13 | 1166 | /** |
f4a60a9b OZ |
1167 | * channel_graceful_restart_lock - lock graceful restart by channel |
1168 | * @p: channel instance | |
6eda3f13 OZ |
1169 | * |
1170 | * This function allows a protocol to postpone the end of graceful restart | |
1171 | * recovery until it converges. The lock is removed when the protocol calls | |
f4a60a9b | 1172 | * channel_graceful_restart_unlock() or when the channel is closed. |
6eda3f13 OZ |
1173 | * |
1174 | * The function have to be called during the initial phase of graceful restart | |
1175 | * recovery and only for protocols that are part of graceful restart (i.e. their | |
1176 | * @gr_recovery is set), which means it should be called from protocol start | |
1177 | * hooks. | |
1178 | */ | |
0c791f87 | 1179 | void |
f4a60a9b | 1180 | channel_graceful_restart_lock(struct channel *c) |
0c791f87 OZ |
1181 | { |
1182 | ASSERT(graceful_restart_state == GRS_INIT); | |
f4a60a9b | 1183 | ASSERT(c->proto->gr_recovery); |
0c791f87 | 1184 | |
f4a60a9b | 1185 | if (c->gr_lock) |
0c791f87 OZ |
1186 | return; |
1187 | ||
f4a60a9b | 1188 | c->gr_lock = 1; |
0c791f87 OZ |
1189 | graceful_restart_locks++; |
1190 | } | |
1191 | ||
6eda3f13 | 1192 | /** |
f4a60a9b OZ |
1193 | * channel_graceful_restart_unlock - unlock graceful restart by channel |
1194 | * @p: channel instance | |
6eda3f13 | 1195 | * |
f4a60a9b | 1196 | * This function unlocks a lock from channel_graceful_restart_lock(). It is also |
6eda3f13 OZ |
1197 | * automatically called when the lock holding protocol went down. |
1198 | */ | |
0c791f87 | 1199 | void |
f4a60a9b | 1200 | channel_graceful_restart_unlock(struct channel *c) |
0c791f87 | 1201 | { |
f4a60a9b | 1202 | if (!c->gr_lock) |
0c791f87 OZ |
1203 | return; |
1204 | ||
f4a60a9b | 1205 | c->gr_lock = 0; |
0c791f87 OZ |
1206 | graceful_restart_locks--; |
1207 | ||
1208 | if ((graceful_restart_state == GRS_ACTIVE) && !graceful_restart_locks) | |
a6f79ca5 | 1209 | tm_start(gr_wait_timer, 0); |
0c791f87 OZ |
1210 | } |
1211 | ||
1212 | ||
1213 | ||
3c6269b8 MM |
1214 | /** |
1215 | * protos_dump_all - dump status of all protocols | |
1216 | * | |
1217 | * This function dumps status of all existing protocol instances to the | |
1218 | * debug output. It involves printing of general status information | |
1219 | * such as protocol states, its position on the protocol lists | |
1220 | * and also calling of a dump() hook of the protocol to print | |
1221 | * the internals. | |
1222 | */ | |
87d2be86 PM |
1223 | void |
1224 | protos_dump_all(void) | |
1225 | { | |
87d2be86 PM |
1226 | debug("Protocols:\n"); |
1227 | ||
f4a60a9b OZ |
1228 | struct proto *p; |
1229 | WALK_LIST(p, proto_list) | |
1230 | { | |
1231 | debug(" protocol %s state %s\n", p->name, p_states[p->proto_state]); | |
1232 | ||
1233 | struct channel *c; | |
1234 | WALK_LIST(c, p->channels) | |
87d2be86 | 1235 | { |
f4a60a9b OZ |
1236 | debug("\tTABLE %s\n", c->table->name); |
1237 | if (c->in_filter) | |
1238 | debug("\tInput filter: %s\n", filter_name(c->in_filter)); | |
1239 | if (c->out_filter) | |
1240 | debug("\tOutput filter: %s\n", filter_name(c->out_filter)); | |
87d2be86 | 1241 | } |
f4a60a9b OZ |
1242 | |
1243 | if (p->proto->dump && (p->proto_state != PS_DOWN)) | |
1244 | p->proto->dump(p); | |
1245 | } | |
87d2be86 PM |
1246 | } |
1247 | ||
3c6269b8 MM |
1248 | /** |
1249 | * proto_build - make a single protocol available | |
1250 | * @p: the protocol | |
1251 | * | |
1252 | * After the platform specific initialization code uses protos_build() | |
1253 | * to add all the standard protocols, it should call proto_build() for | |
2e9b2421 | 1254 | * all platform specific protocols to inform the core that they exist. |
3c6269b8 | 1255 | */ |
3991d84e MM |
1256 | void |
1257 | proto_build(struct protocol *p) | |
1258 | { | |
1259 | add_tail(&protocol_list, &p->n); | |
ee7e2ffd JMM |
1260 | ASSERT(p->class); |
1261 | ASSERT(!class_to_protocol[p->class]); | |
1262 | class_to_protocol[p->class] = p; | |
3991d84e MM |
1263 | } |
1264 | ||
1ec52253 OZ |
1265 | /* FIXME: convert this call to some protocol hook */ |
1266 | extern void bfd_init_all(void); | |
1267 | ||
3c6269b8 MM |
1268 | /** |
1269 | * protos_build - build a protocol list | |
1270 | * | |
1271 | * This function is called during BIRD startup to insert | |
1272 | * all standard protocols to the global protocol list. Insertion | |
1273 | * of platform specific protocols (such as the kernel syncer) | |
1274 | * is in the domain of competence of the platform dependent | |
1275 | * startup code. | |
1276 | */ | |
0432c017 MM |
1277 | void |
1278 | protos_build(void) | |
1279 | { | |
471cc0be | 1280 | init_list(&proto_list); |
f4a60a9b OZ |
1281 | init_list(&protocol_list); |
1282 | ||
3991d84e | 1283 | proto_build(&proto_device); |
93e868c7 OZ |
1284 | #ifdef CONFIG_RADV |
1285 | proto_build(&proto_radv); | |
1286 | #endif | |
18fff6a1 | 1287 | #ifdef CONFIG_RIP |
3991d84e | 1288 | proto_build(&proto_rip); |
18fff6a1 MM |
1289 | #endif |
1290 | #ifdef CONFIG_STATIC | |
3991d84e | 1291 | proto_build(&proto_static); |
c1f8dc91 | 1292 | #endif |
863ecfc7 OZ |
1293 | #ifdef CONFIG_MRT |
1294 | proto_build(&proto_mrt); | |
1295 | #endif | |
c1f8dc91 | 1296 | #ifdef CONFIG_OSPF |
3991d84e | 1297 | proto_build(&proto_ospf); |
26368f65 MM |
1298 | #endif |
1299 | #ifdef CONFIG_PIPE | |
3991d84e | 1300 | proto_build(&proto_pipe); |
2638249d MM |
1301 | #endif |
1302 | #ifdef CONFIG_BGP | |
3991d84e | 1303 | proto_build(&proto_bgp); |
18fff6a1 | 1304 | #endif |
1ec52253 | 1305 | #ifdef CONFIG_BFD |
6a8d3f1c | 1306 | proto_build(&proto_bfd); |
1ec52253 OZ |
1307 | bfd_init_all(); |
1308 | #endif | |
937e75d8 OZ |
1309 | #ifdef CONFIG_BABEL |
1310 | proto_build(&proto_babel); | |
1311 | #endif | |
65d2a88d PT |
1312 | #ifdef CONFIG_RPKI |
1313 | proto_build(&proto_rpki); | |
1314 | #endif | |
6a8d3f1c | 1315 | |
67bd949a | 1316 | proto_pool = rp_new(&root_pool, "Protocols"); |
a6f79ca5 | 1317 | proto_shutdown_timer = tm_new(proto_pool); |
ebecb6f6 | 1318 | proto_shutdown_timer->hook = proto_shutdown_loop; |
67bd949a MM |
1319 | } |
1320 | ||
fb829de6 | 1321 | |
d9b77cc2 OZ |
1322 | /* Temporary hack to propagate restart to BGP */ |
1323 | int proto_restart; | |
fb829de6 | 1324 | |
ebecb6f6 | 1325 | static void |
02552526 | 1326 | proto_shutdown_loop(timer *t UNUSED) |
ebecb6f6 OZ |
1327 | { |
1328 | struct proto *p, *p_next; | |
1329 | ||
f4a60a9b | 1330 | WALK_LIST_DELSAFE(p, p_next, proto_list) |
ebecb6f6 | 1331 | if (p->down_sched) |
f4a60a9b OZ |
1332 | { |
1333 | proto_restart = (p->down_sched == PDS_RESTART); | |
ebecb6f6 | 1334 | |
f4a60a9b OZ |
1335 | p->disabled = 1; |
1336 | proto_rethink_goal(p); | |
1337 | if (proto_restart) | |
1338 | { | |
1339 | p->disabled = 0; | |
ebecb6f6 | 1340 | proto_rethink_goal(p); |
ebecb6f6 | 1341 | } |
f4a60a9b | 1342 | } |
ebecb6f6 OZ |
1343 | } |
1344 | ||
1345 | static inline void | |
1346 | proto_schedule_down(struct proto *p, byte restart, byte code) | |
1347 | { | |
1348 | /* Does not work for other states (even PS_START) */ | |
1349 | ASSERT(p->proto_state == PS_UP); | |
1350 | ||
1351 | /* Scheduled restart may change to shutdown, but not otherwise */ | |
1352 | if (p->down_sched == PDS_DISABLE) | |
1353 | return; | |
1354 | ||
1355 | p->down_sched = restart ? PDS_RESTART : PDS_DISABLE; | |
1356 | p->down_code = code; | |
a6f79ca5 | 1357 | tm_start_max(proto_shutdown_timer, restart ? 250 MS : 0); |
ebecb6f6 OZ |
1358 | } |
1359 | ||
cd1d9961 OZ |
1360 | /** |
1361 | * proto_set_message - set administrative message to protocol | |
1362 | * @p: protocol | |
1363 | * @msg: message | |
1364 | * @len: message length (-1 for NULL-terminated string) | |
1365 | * | |
1366 | * The function sets administrative message (string) related to protocol state | |
1367 | * change. It is called by the nest code for manual enable/disable/restart | |
1368 | * commands all routes to the protocol, and by protocol-specific code when the | |
1369 | * protocol state change is initiated by the protocol. Using NULL message clears | |
1370 | * the last message. The message string may be either NULL-terminated or with an | |
1371 | * explicit length. | |
1372 | */ | |
1373 | void | |
1374 | proto_set_message(struct proto *p, char *msg, int len) | |
1375 | { | |
1376 | mb_free(p->message); | |
1377 | p->message = NULL; | |
1378 | ||
1379 | if (!msg || !len) | |
1380 | return; | |
1381 | ||
1382 | if (len < 0) | |
1383 | len = strlen(msg); | |
1384 | ||
1385 | if (!len) | |
1386 | return; | |
1387 | ||
1388 | p->message = mb_alloc(proto_pool, len + 1); | |
1389 | memcpy(p->message, msg, len); | |
1390 | p->message[len] = 0; | |
1391 | } | |
1392 | ||
ebecb6f6 | 1393 | |
ebecb6f6 | 1394 | static const char * |
f4a60a9b | 1395 | channel_limit_name(struct channel_limit *l) |
ebecb6f6 OZ |
1396 | { |
1397 | const char *actions[] = { | |
1398 | [PLA_WARN] = "warn", | |
1399 | [PLA_BLOCK] = "block", | |
1400 | [PLA_RESTART] = "restart", | |
1401 | [PLA_DISABLE] = "disable", | |
1402 | }; | |
1403 | ||
1404 | return actions[l->action]; | |
1405 | } | |
1406 | ||
1407 | /** | |
f4a60a9b OZ |
1408 | * channel_notify_limit: notify about limit hit and take appropriate action |
1409 | * @c: channel | |
ebecb6f6 | 1410 | * @l: limit being hit |
b662290f | 1411 | * @dir: limit direction (PLD_*) |
f4a60a9b | 1412 | * @rt_count: the number of routes |
ebecb6f6 OZ |
1413 | * |
1414 | * The function is called by the route processing core when limit @l | |
1415 | * is breached. It activates the limit and tooks appropriate action | |
7d0a31de | 1416 | * according to @l->action. |
ebecb6f6 | 1417 | */ |
7d0a31de | 1418 | void |
f4a60a9b | 1419 | channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count) |
ebecb6f6 | 1420 | { |
b662290f OZ |
1421 | const char *dir_name[PLD_MAX] = { "receive", "import" , "export" }; |
1422 | const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT }; | |
f4a60a9b | 1423 | struct proto *p = c->proto; |
ebecb6f6 | 1424 | |
7d0a31de OZ |
1425 | if (l->state == PLS_BLOCKED) |
1426 | return; | |
ebecb6f6 | 1427 | |
d9b77cc2 OZ |
1428 | /* For warning action, we want the log message every time we hit the limit */ |
1429 | if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit))) | |
7d0a31de | 1430 | log(L_WARN "Protocol %s hits route %s limit (%d), action: %s", |
f4a60a9b | 1431 | p->name, dir_name[dir], l->limit, channel_limit_name(l)); |
ebecb6f6 OZ |
1432 | |
1433 | switch (l->action) | |
f4a60a9b OZ |
1434 | { |
1435 | case PLA_WARN: | |
1436 | l->state = PLS_ACTIVE; | |
1437 | break; | |
1438 | ||
1439 | case PLA_BLOCK: | |
1440 | l->state = PLS_BLOCKED; | |
1441 | break; | |
1442 | ||
1443 | case PLA_RESTART: | |
1444 | case PLA_DISABLE: | |
1445 | l->state = PLS_BLOCKED; | |
1446 | if (p->proto_state == PS_UP) | |
1447 | proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]); | |
1448 | break; | |
1449 | } | |
ebecb6f6 OZ |
1450 | } |
1451 | ||
f4a60a9b OZ |
1452 | static void |
1453 | channel_verify_limits(struct channel *c) | |
984d7349 | 1454 | { |
f4a60a9b OZ |
1455 | struct channel_limit *l; |
1456 | u32 all_routes = c->stats.imp_routes + c->stats.filt_routes; | |
984d7349 | 1457 | |
f4a60a9b OZ |
1458 | l = &c->rx_limit; |
1459 | if (l->action && (all_routes > l->limit)) | |
1460 | channel_notify_limit(c, l, PLD_RX, all_routes); | |
984d7349 | 1461 | |
f4a60a9b OZ |
1462 | l = &c->in_limit; |
1463 | if (l->action && (c->stats.imp_routes > l->limit)) | |
1464 | channel_notify_limit(c, l, PLD_IN, c->stats.imp_routes); | |
984d7349 | 1465 | |
f4a60a9b OZ |
1466 | l = &c->out_limit; |
1467 | if (l->action && (c->stats.exp_routes > l->limit)) | |
1468 | channel_notify_limit(c, l, PLD_OUT, c->stats.exp_routes); | |
984d7349 OZ |
1469 | } |
1470 | ||
f4a60a9b OZ |
1471 | static inline void |
1472 | channel_reset_limit(struct channel_limit *l) | |
0c791f87 | 1473 | { |
f4a60a9b OZ |
1474 | if (l->action) |
1475 | l->state = PLS_INITIAL; | |
0c791f87 OZ |
1476 | } |
1477 | ||
f4a60a9b OZ |
1478 | static inline void |
1479 | proto_do_start(struct proto *p) | |
0c791f87 | 1480 | { |
f4a60a9b OZ |
1481 | p->active = 1; |
1482 | p->do_start = 1; | |
1483 | ev_schedule(p->event); | |
0c791f87 OZ |
1484 | } |
1485 | ||
1486 | static void | |
f4a60a9b | 1487 | proto_do_up(struct proto *p) |
0c791f87 | 1488 | { |
f4a60a9b OZ |
1489 | if (!p->main_source) |
1490 | { | |
1491 | p->main_source = rt_get_source(p, 0); | |
1492 | rt_lock_source(p->main_source); | |
1493 | } | |
0c791f87 | 1494 | |
f4a60a9b | 1495 | proto_start_channels(p); |
0c791f87 OZ |
1496 | } |
1497 | ||
f4a60a9b OZ |
1498 | static inline void |
1499 | proto_do_pause(struct proto *p) | |
0c791f87 | 1500 | { |
f4a60a9b | 1501 | proto_pause_channels(p); |
0c791f87 OZ |
1502 | } |
1503 | ||
1504 | static void | |
f4a60a9b | 1505 | proto_do_stop(struct proto *p) |
0c791f87 | 1506 | { |
f4a60a9b | 1507 | p->down_sched = 0; |
0c791f87 | 1508 | p->gr_recovery = 0; |
6eda3f13 | 1509 | |
f4a60a9b OZ |
1510 | p->do_stop = 1; |
1511 | ev_schedule(p->event); | |
6eda3f13 | 1512 | |
f4a60a9b OZ |
1513 | if (p->main_source) |
1514 | { | |
1515 | rt_unlock_source(p->main_source); | |
1516 | p->main_source = NULL; | |
1517 | } | |
6eda3f13 | 1518 | |
f4a60a9b OZ |
1519 | proto_stop_channels(p); |
1520 | } | |
6eda3f13 | 1521 | |
f4a60a9b OZ |
1522 | static void |
1523 | proto_do_down(struct proto *p) | |
1524 | { | |
1525 | p->down_code = 0; | |
1526 | neigh_prune(); | |
1527 | rfree(p->pool); | |
1528 | p->pool = NULL; | |
1529 | ||
1530 | /* Shutdown is finished in the protocol event */ | |
1531 | if (proto_is_done(p)) | |
1532 | ev_schedule(p->event); | |
6eda3f13 OZ |
1533 | } |
1534 | ||
0c791f87 | 1535 | |
f4a60a9b | 1536 | |
3c6269b8 MM |
1537 | /** |
1538 | * proto_notify_state - notify core about protocol state change | |
1539 | * @p: protocol the state of which has changed | |
1540 | * @ps: the new status | |
1541 | * | |
1542 | * Whenever a state of a protocol changes due to some event internal | |
1543 | * to the protocol (i.e., not inside a start() or shutdown() hook), | |
1544 | * it should immediately notify the core about the change by calling | |
1545 | * proto_notify_state() which will write the new state to the &proto | |
1546 | * structure and take all the actions necessary to adapt to the new | |
d6a836f8 OZ |
1547 | * state. State change to PS_DOWN immediately frees resources of protocol |
1548 | * and might execute start callback of protocol; therefore, | |
1549 | * it should be used at tail positions of protocol callbacks. | |
3c6269b8 | 1550 | */ |
67bd949a | 1551 | void |
f4a60a9b | 1552 | proto_notify_state(struct proto *p, uint state) |
67bd949a | 1553 | { |
f4a60a9b | 1554 | uint ps = p->proto_state; |
67bd949a | 1555 | |
f4a60a9b OZ |
1556 | DBG("%s reporting state transition %s -> %s\n", p->name, p_states[ps], p_states[state]); |
1557 | if (state == ps) | |
67bd949a MM |
1558 | return; |
1559 | ||
f4a60a9b | 1560 | p->proto_state = state; |
f047271c | 1561 | p->last_state_change = current_time(); |
d6a836f8 | 1562 | |
f4a60a9b OZ |
1563 | switch (state) |
1564 | { | |
1565 | case PS_START: | |
1566 | ASSERT(ps == PS_DOWN || ps == PS_UP); | |
1567 | ||
1568 | if (ps == PS_DOWN) | |
1569 | proto_do_start(p); | |
1570 | else | |
1571 | proto_do_pause(p); | |
1572 | break; | |
1573 | ||
1574 | case PS_UP: | |
1575 | ASSERT(ps == PS_DOWN || ps == PS_START); | |
1576 | ||
1577 | if (ps == PS_DOWN) | |
1578 | proto_do_start(p); | |
1579 | ||
1580 | proto_do_up(p); | |
1581 | break; | |
1582 | ||
1583 | case PS_STOP: | |
1584 | ASSERT(ps == PS_START || ps == PS_UP); | |
1585 | ||
1586 | proto_do_stop(p); | |
1587 | break; | |
1588 | ||
1589 | case PS_DOWN: | |
1590 | if (ps != PS_STOP) | |
1591 | proto_do_stop(p); | |
1592 | ||
1593 | proto_do_down(p); | |
1594 | break; | |
1595 | ||
1596 | default: | |
1597 | bug("%s: Invalid state %d", p->name, ps); | |
1598 | } | |
227af309 OZ |
1599 | |
1600 | proto_log_state_change(p); | |
0432c017 | 1601 | } |
1a54b1c6 | 1602 | |
0d3e6bce MM |
1603 | /* |
1604 | * CLI Commands | |
1605 | */ | |
1606 | ||
1607 | static char * | |
1608 | proto_state_name(struct proto *p) | |
1609 | { | |
f4a60a9b OZ |
1610 | switch (p->proto_state) |
1611 | { | |
1612 | case PS_DOWN: return p->active ? "flush" : "down"; | |
1613 | case PS_START: return "start"; | |
1614 | case PS_UP: return "up"; | |
1615 | case PS_STOP: return "stop"; | |
1616 | default: return "???"; | |
1617 | } | |
0d3e6bce MM |
1618 | } |
1619 | ||
9db74169 | 1620 | static void |
f4a60a9b | 1621 | channel_show_stats(struct channel *c) |
9db74169 | 1622 | { |
f4a60a9b OZ |
1623 | struct proto_stats *s = &c->stats; |
1624 | ||
1625 | if (c->in_keep_filtered) | |
1626 | cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported", | |
1627 | s->imp_routes, s->filt_routes, s->exp_routes); | |
cf98be7b | 1628 | else |
f4a60a9b OZ |
1629 | cli_msg(-1006, " Routes: %u imported, %u exported", |
1630 | s->imp_routes, s->exp_routes); | |
cf98be7b | 1631 | |
f4a60a9b OZ |
1632 | cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); |
1633 | cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", | |
9db74169 OZ |
1634 | s->imp_updates_received, s->imp_updates_invalid, |
1635 | s->imp_updates_filtered, s->imp_updates_ignored, | |
1636 | s->imp_updates_accepted); | |
f4a60a9b | 1637 | cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u", |
9db74169 OZ |
1638 | s->imp_withdraws_received, s->imp_withdraws_invalid, |
1639 | s->imp_withdraws_ignored, s->imp_withdraws_accepted); | |
f4a60a9b | 1640 | cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u", |
9db74169 OZ |
1641 | s->exp_updates_received, s->exp_updates_rejected, |
1642 | s->exp_updates_filtered, s->exp_updates_accepted); | |
f4a60a9b | 1643 | cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u", |
9db74169 OZ |
1644 | s->exp_withdraws_received, s->exp_withdraws_accepted); |
1645 | } | |
1646 | ||
ebecb6f6 | 1647 | void |
f4a60a9b | 1648 | channel_show_limit(struct channel_limit *l, const char *dsc) |
ebecb6f6 | 1649 | { |
f4a60a9b | 1650 | if (!l->action) |
7d0a31de OZ |
1651 | return; |
1652 | ||
f4a60a9b OZ |
1653 | cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : ""); |
1654 | cli_msg(-1006, " Action: %s", channel_limit_name(l)); | |
ebecb6f6 OZ |
1655 | } |
1656 | ||
c0adf7e9 | 1657 | void |
f4a60a9b | 1658 | channel_show_info(struct channel *c) |
9db74169 | 1659 | { |
f4a60a9b | 1660 | cli_msg(-1006, " Channel %s", c->name); |
d15b0b0a | 1661 | cli_msg(-1006, " State: %s", c_states[c->channel_state]); |
f4a60a9b OZ |
1662 | cli_msg(-1006, " Table: %s", c->table->name); |
1663 | cli_msg(-1006, " Preference: %d", c->preference); | |
1664 | cli_msg(-1006, " Input filter: %s", filter_name(c->in_filter)); | |
1665 | cli_msg(-1006, " Output filter: %s", filter_name(c->out_filter)); | |
9db74169 | 1666 | |
0c791f87 | 1667 | if (graceful_restart_state == GRS_ACTIVE) |
f4a60a9b OZ |
1668 | cli_msg(-1006, " GR recovery: %s%s", |
1669 | c->gr_lock ? " pending" : "", | |
1670 | c->gr_wait ? " waiting" : ""); | |
0c791f87 | 1671 | |
f4a60a9b OZ |
1672 | channel_show_limit(&c->rx_limit, "Receive limit:"); |
1673 | channel_show_limit(&c->in_limit, "Import limit:"); | |
1674 | channel_show_limit(&c->out_limit, "Export limit:"); | |
ebecb6f6 | 1675 | |
f4a60a9b OZ |
1676 | if (c->channel_state != CS_DOWN) |
1677 | channel_show_stats(c); | |
9db74169 OZ |
1678 | } |
1679 | ||
e304fd4b | 1680 | void |
cd1d9961 | 1681 | proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt) |
1d2664a4 | 1682 | { |
c37e7851 | 1683 | byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE]; |
9685deb9 | 1684 | |
e304fd4b OZ |
1685 | /* First protocol - show header */ |
1686 | if (!cnt) | |
eb95b5ec OZ |
1687 | cli_msg(-2002, "%-10s %-10s %-10s %-6s %-12s %s", |
1688 | "Name", "Proto", "Table", "State", "Since", "Info"); | |
e304fd4b | 1689 | |
9685deb9 MM |
1690 | buf[0] = 0; |
1691 | if (p->proto->get_status) | |
1692 | p->proto->get_status(p, buf); | |
f047271c | 1693 | tm_format_time(tbuf, &config->tf_proto, p->last_state_change); |
eb95b5ec | 1694 | cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s", |
1d2664a4 MM |
1695 | p->name, |
1696 | p->proto->name, | |
f4a60a9b | 1697 | p->main_channel ? p->main_channel->table->name : "---", |
1d2664a4 | 1698 | proto_state_name(p), |
c37e7851 | 1699 | tbuf, |
9685deb9 | 1700 | buf); |
f4a60a9b | 1701 | |
1d2664a4 | 1702 | if (verbose) |
f4a60a9b OZ |
1703 | { |
1704 | if (p->cf->dsc) | |
1705 | cli_msg(-1006, " Description: %s", p->cf->dsc); | |
830ba75e OZ |
1706 | if (p->message) |
1707 | cli_msg(-1006, " Message: %s", p->message); | |
f4a60a9b OZ |
1708 | if (p->cf->router_id) |
1709 | cli_msg(-1006, " Router ID: %R", p->cf->router_id); | |
46434a3c OZ |
1710 | if (p->vrf) |
1711 | cli_msg(-1006, " VRF: %s", p->vrf->name); | |
f4a60a9b OZ |
1712 | |
1713 | if (p->proto->show_proto_info) | |
1714 | p->proto->show_proto_info(p); | |
1715 | else | |
1d2664a4 | 1716 | { |
f4a60a9b OZ |
1717 | struct channel *c; |
1718 | WALK_LIST(c, p->channels) | |
1719 | channel_show_info(c); | |
1d2664a4 | 1720 | } |
f4a60a9b OZ |
1721 | |
1722 | cli_msg(-1006, ""); | |
1723 | } | |
1d2664a4 MM |
1724 | } |
1725 | ||
ae97b946 | 1726 | void |
cd1d9961 | 1727 | proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED) |
ae97b946 | 1728 | { |
e304fd4b | 1729 | if (p->disabled) |
f4a60a9b OZ |
1730 | { |
1731 | cli_msg(-8, "%s: already disabled", p->name); | |
1732 | return; | |
1733 | } | |
e304fd4b OZ |
1734 | |
1735 | log(L_INFO "Disabling protocol %s", p->name); | |
1736 | p->disabled = 1; | |
ebecb6f6 | 1737 | p->down_code = PDC_CMD_DISABLE; |
cd1d9961 | 1738 | proto_set_message(p, (char *) arg, -1); |
e304fd4b OZ |
1739 | proto_rethink_goal(p); |
1740 | cli_msg(-9, "%s: disabled", p->name); | |
1741 | } | |
1742 | ||
1743 | void | |
cd1d9961 | 1744 | proto_cmd_enable(struct proto *p, uintptr_t arg, int cnt UNUSED) |
e304fd4b OZ |
1745 | { |
1746 | if (!p->disabled) | |
f4a60a9b OZ |
1747 | { |
1748 | cli_msg(-10, "%s: already enabled", p->name); | |
1749 | return; | |
1750 | } | |
e304fd4b OZ |
1751 | |
1752 | log(L_INFO "Enabling protocol %s", p->name); | |
1753 | p->disabled = 0; | |
cd1d9961 | 1754 | proto_set_message(p, (char *) arg, -1); |
e304fd4b OZ |
1755 | proto_rethink_goal(p); |
1756 | cli_msg(-11, "%s: enabled", p->name); | |
1757 | } | |
1758 | ||
1759 | void | |
cd1d9961 | 1760 | proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED) |
e304fd4b OZ |
1761 | { |
1762 | if (p->disabled) | |
f4a60a9b OZ |
1763 | { |
1764 | cli_msg(-8, "%s: already disabled", p->name); | |
1765 | return; | |
1766 | } | |
e304fd4b OZ |
1767 | |
1768 | log(L_INFO "Restarting protocol %s", p->name); | |
1769 | p->disabled = 1; | |
ebecb6f6 | 1770 | p->down_code = PDC_CMD_RESTART; |
cd1d9961 | 1771 | proto_set_message(p, (char *) arg, -1); |
e304fd4b OZ |
1772 | proto_rethink_goal(p); |
1773 | p->disabled = 0; | |
1774 | proto_rethink_goal(p); | |
1775 | cli_msg(-12, "%s: restarted", p->name); | |
1776 | } | |
1777 | ||
1778 | void | |
cd1d9961 | 1779 | proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED) |
e304fd4b | 1780 | { |
f4a60a9b OZ |
1781 | struct channel *c; |
1782 | ||
e304fd4b | 1783 | if (p->disabled) |
f4a60a9b OZ |
1784 | { |
1785 | cli_msg(-8, "%s: already disabled", p->name); | |
1786 | return; | |
1787 | } | |
e304fd4b OZ |
1788 | |
1789 | /* If the protocol in not UP, it has no routes */ | |
1790 | if (p->proto_state != PS_UP) | |
1791 | return; | |
1792 | ||
f4a60a9b OZ |
1793 | /* All channels must support reload */ |
1794 | if (dir != CMD_RELOAD_OUT) | |
1795 | WALK_LIST(c, p->channels) | |
1796 | if (!channel_reloadable(c)) | |
1797 | { | |
1798 | cli_msg(-8006, "%s: reload failed", p->name); | |
1799 | return; | |
1800 | } | |
1801 | ||
e304fd4b OZ |
1802 | log(L_INFO "Reloading protocol %s", p->name); |
1803 | ||
1804 | /* re-importing routes */ | |
1805 | if (dir != CMD_RELOAD_OUT) | |
f4a60a9b OZ |
1806 | WALK_LIST(c, p->channels) |
1807 | channel_request_reload(c); | |
ebecb6f6 | 1808 | |
e304fd4b OZ |
1809 | /* re-exporting routes */ |
1810 | if (dir != CMD_RELOAD_IN) | |
f4a60a9b OZ |
1811 | WALK_LIST(c, p->channels) |
1812 | channel_request_feeding(c); | |
e304fd4b OZ |
1813 | |
1814 | cli_msg(-15, "%s: reloading", p->name); | |
1815 | } | |
1816 | ||
1817 | void | |
cd1d9961 | 1818 | proto_cmd_debug(struct proto *p, uintptr_t mask, int cnt UNUSED) |
e304fd4b OZ |
1819 | { |
1820 | p->debug = mask; | |
1821 | } | |
1822 | ||
1823 | void | |
cd1d9961 | 1824 | proto_cmd_mrtdump(struct proto *p, uintptr_t mask, int cnt UNUSED) |
e304fd4b OZ |
1825 | { |
1826 | p->mrtdump = mask; | |
1827 | } | |
1828 | ||
1829 | static void | |
cd1d9961 | 1830 | proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg) |
e304fd4b OZ |
1831 | { |
1832 | if (s->class != SYM_PROTO) | |
f4a60a9b OZ |
1833 | { |
1834 | cli_msg(9002, "%s is not a protocol", s->name); | |
1835 | return; | |
1836 | } | |
e304fd4b OZ |
1837 | |
1838 | cmd(((struct proto_config *)s->def)->proto, arg, 0); | |
ae97b946 MM |
1839 | cli_msg(0, ""); |
1840 | } | |
02c1fbdd | 1841 | |
e304fd4b | 1842 | static void |
cd1d9961 | 1843 | proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg) |
e304fd4b | 1844 | { |
f4a60a9b | 1845 | struct proto *p; |
e304fd4b OZ |
1846 | int cnt = 0; |
1847 | ||
f4a60a9b OZ |
1848 | WALK_LIST(p, proto_list) |
1849 | if (!patt || patmatch(patt, p->name)) | |
1850 | cmd(p, arg, cnt++); | |
e304fd4b OZ |
1851 | |
1852 | if (!cnt) | |
1853 | cli_msg(8003, "No protocols match"); | |
1854 | else | |
1855 | cli_msg(0, ""); | |
1856 | } | |
1857 | ||
1858 | void | |
cd1d9961 OZ |
1859 | proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), |
1860 | int restricted, uintptr_t arg) | |
e304fd4b | 1861 | { |
e0a45fb4 OZ |
1862 | if (restricted && cli_access_restricted()) |
1863 | return; | |
1864 | ||
e304fd4b OZ |
1865 | if (ps.patt) |
1866 | proto_apply_cmd_patt(ps.ptr, cmd, arg); | |
1867 | else | |
1868 | proto_apply_cmd_symbol(ps.ptr, cmd, arg); | |
1869 | } | |
1870 | ||
02c1fbdd MM |
1871 | struct proto * |
1872 | proto_get_named(struct symbol *sym, struct protocol *pr) | |
1873 | { | |
1874 | struct proto *p, *q; | |
1875 | ||
1876 | if (sym) | |
f4a60a9b OZ |
1877 | { |
1878 | if (sym->class != SYM_PROTO) | |
1879 | cf_error("%s: Not a protocol", sym->name); | |
1880 | ||
1881 | p = ((struct proto_config *) sym->def)->proto; | |
1882 | if (!p || p->proto != pr) | |
1883 | cf_error("%s: Not a %s protocol", sym->name, pr->name); | |
1884 | } | |
02c1fbdd | 1885 | else |
f4a60a9b OZ |
1886 | { |
1887 | p = NULL; | |
1888 | WALK_LIST(q, proto_list) | |
1889 | if ((q->proto == pr) && (q->proto_state != PS_DOWN)) | |
1890 | { | |
1891 | if (p) | |
1892 | cf_error("There are multiple %s protocols running", pr->name); | |
1893 | p = q; | |
1894 | } | |
1895 | if (!p) | |
1896 | cf_error("There is no %s protocol running", pr->name); | |
1897 | } | |
1898 | ||
02c1fbdd MM |
1899 | return p; |
1900 | } |