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