]>
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" |
f14a4bec | 16 | #include "lib/string.h" |
fe7cec12 | 17 | #include "conf/conf.h" |
47b79306 MM |
18 | #include "nest/route.h" |
19 | #include "nest/iface.h" | |
ae97b946 | 20 | #include "nest/cli.h" |
529c4149 | 21 | #include "filter/filter.h" |
2326b001 | 22 | |
acb60628 | 23 | pool *proto_pool; |
67bd949a | 24 | |
3991d84e | 25 | static list protocol_list; |
f14a4bec | 26 | static list proto_list; |
67bd949a | 27 | |
839380d7 MM |
28 | #define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0) |
29 | ||
f14a4bec | 30 | list active_proto_list; |
67bd949a MM |
31 | static list inactive_proto_list; |
32 | static list initial_proto_list; | |
1a54b1c6 | 33 | static list flush_proto_list; |
4ef09506 | 34 | static struct proto *initial_device_proto; |
1a54b1c6 MM |
35 | |
36 | static event *proto_flush_event; | |
67bd949a MM |
37 | |
38 | static char *p_states[] = { "DOWN", "START", "UP", "STOP" }; | |
39 | static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" }; | |
40 | ||
8f6accb5 | 41 | static void proto_flush_all(void *); |
50fe90ed | 42 | static void proto_rethink_goal(struct proto *p); |
839380d7 | 43 | static char *proto_state_name(struct proto *p); |
1a54b1c6 | 44 | |
b2280748 MM |
45 | static void |
46 | proto_enqueue(list *l, struct proto *p) | |
47 | { | |
3ea1ba63 | 48 | add_tail(l, &p->n); |
9685deb9 | 49 | p->last_state_change = now; |
b2280748 MM |
50 | } |
51 | ||
67bd949a MM |
52 | static void |
53 | proto_relink(struct proto *p) | |
54 | { | |
e81b440f | 55 | list *l = NULL; |
1a54b1c6 | 56 | |
df9f0fb3 MM |
57 | if (p->debug & D_STATES) |
58 | { | |
59 | char *name = proto_state_name(p); | |
60 | if (name != p->last_state_name_announced) | |
61 | { | |
62 | p->last_state_name_announced = name; | |
63 | PD(p, "State changed to %s", proto_state_name(p)); | |
64 | } | |
65 | } | |
66 | else | |
67 | p->last_state_name_announced = NULL; | |
67bd949a | 68 | rem_node(&p->n); |
1a54b1c6 MM |
69 | switch (p->core_state) |
70 | { | |
bf47fe4b OZ |
71 | case FS_HUNGRY: |
72 | l = &inactive_proto_list; | |
73 | break; | |
74 | case FS_FEEDING: | |
1a54b1c6 | 75 | case FS_HAPPY: |
f14a4bec | 76 | l = &active_proto_list; |
1a54b1c6 MM |
77 | break; |
78 | case FS_FLUSHING: | |
79 | l = &flush_proto_list; | |
80 | break; | |
81 | default: | |
bf47fe4b | 82 | ASSERT(0); |
1a54b1c6 | 83 | } |
b2280748 | 84 | proto_enqueue(l, p); |
67bd949a | 85 | } |
2326b001 | 86 | |
3c6269b8 MM |
87 | /** |
88 | * proto_new - create a new protocol instance | |
89 | * @c: protocol configuration | |
90 | * @size: size of protocol data structure (each protocol instance is represented by | |
91 | * a structure starting with generic part [struct &proto] and continued | |
92 | * with data specific to the protocol) | |
93 | * | |
94 | * When a new configuration has been read in, the core code starts | |
2e9b2421 | 95 | * initializing all the protocol instances configured by calling their |
3c6269b8 MM |
96 | * init() hooks with the corresponding instance configuration. The initialization |
97 | * code of the protocol is expected to create a new instance according to the | |
98 | * configuration by calling this function and then modifying the default settings | |
99 | * to values wanted by the protocol. | |
100 | */ | |
7f4a3988 | 101 | void * |
31b3e1bb | 102 | proto_new(struct proto_config *c, unsigned size) |
7f4a3988 | 103 | { |
1d2664a4 | 104 | struct protocol *pr = c->protocol; |
8fe48f13 | 105 | struct proto *p = mb_allocz(proto_pool, size); |
7f4a3988 | 106 | |
31b3e1bb MM |
107 | p->cf = c; |
108 | p->debug = c->debug; | |
cf31112f | 109 | p->mrtdump = c->mrtdump; |
67bd949a | 110 | p->name = c->name; |
31b3e1bb MM |
111 | p->preference = c->preference; |
112 | p->disabled = c->disabled; | |
7f4a3988 | 113 | p->proto = pr; |
9d885689 | 114 | p->table = c->table->table; |
529c4149 MM |
115 | p->in_filter = c->in_filter; |
116 | p->out_filter = c->out_filter; | |
7293c5dd | 117 | p->hash_key = random_u32(); |
1d2664a4 | 118 | c->proto = p; |
7f4a3988 MM |
119 | return p; |
120 | } | |
121 | ||
1a54b1c6 MM |
122 | static void |
123 | proto_init_instance(struct proto *p) | |
124 | { | |
5bc512aa MM |
125 | /* Here we cannot use p->cf->name since it won't survive reconfiguration */ |
126 | p->pool = rp_new(proto_pool, p->proto->name); | |
1a54b1c6 MM |
127 | p->attn = ev_new(p->pool); |
128 | p->attn->data = p; | |
50fe90ed | 129 | rt_lock_table(p->table); |
1a54b1c6 MM |
130 | } |
131 | ||
3c6269b8 MM |
132 | /** |
133 | * proto_add_announce_hook - connect protocol to a routing table | |
134 | * @p: protocol instance | |
135 | * @t: routing table to connect to | |
136 | * | |
137 | * This function creates a connection between the protocol instance @p | |
138 | * and the routing table @t, making the protocol hear all changes in | |
139 | * the table. | |
140 | * | |
141 | * Unless you want to listen to multiple routing tables (as the Pipe | |
142 | * protocol does), you needn't to worry about this function since the | |
143 | * connection to the protocol's primary routing table is initialized | |
144 | * automatically by the core code. | |
145 | */ | |
0e02abfd MM |
146 | struct announce_hook * |
147 | proto_add_announce_hook(struct proto *p, struct rtable *t) | |
148 | { | |
149 | struct announce_hook *h; | |
150 | ||
151 | if (!p->rt_notify) | |
152 | return NULL; | |
153 | DBG("Connecting protocol %s to table %s\n", p->name, t->name); | |
839380d7 | 154 | PD(p, "Connected to table %s", t->name); |
0e02abfd MM |
155 | h = mb_alloc(p->pool, sizeof(struct announce_hook)); |
156 | h->table = t; | |
157 | h->proto = p; | |
158 | h->next = p->ahooks; | |
159 | p->ahooks = h; | |
160 | add_tail(&t->hooks, &h->n); | |
161 | return h; | |
162 | } | |
163 | ||
164 | static void | |
165 | proto_flush_hooks(struct proto *p) | |
166 | { | |
167 | struct announce_hook *h; | |
168 | ||
169 | for(h=p->ahooks; h; h=h->next) | |
170 | rem_node(&h->n); | |
171 | p->ahooks = NULL; | |
172 | } | |
173 | ||
3c6269b8 MM |
174 | /** |
175 | * proto_config_new - create a new protocol configuration | |
176 | * @pr: protocol the configuration will belong to | |
177 | * @size: size of the structure including generic data | |
a7f23f58 | 178 | * @class: SYM_PROTO or SYM_TEMPLATE |
3c6269b8 MM |
179 | * |
180 | * Whenever the configuration file says that a new instance | |
181 | * of a routing protocol should be created, the parser calls | |
182 | * proto_config_new() to create a configuration entry for this | |
183 | * instance (a structure staring with the &proto_config header | |
184 | * containing all the generic items followed by protocol-specific | |
185 | * ones). Also, the configuration entry gets added to the list | |
186 | * of protocol instances kept in the configuration. | |
a7f23f58 OZ |
187 | * |
188 | * The function is also used to create protocol templates (when class | |
189 | * SYM_TEMPLATE is specified), the only difference is that templates | |
190 | * are not added to the list of protocol instances and therefore not | |
191 | * initialized during protos_commit()). | |
3c6269b8 | 192 | */ |
31b3e1bb | 193 | void * |
a7f23f58 | 194 | proto_config_new(struct protocol *pr, unsigned size, int class) |
31b3e1bb MM |
195 | { |
196 | struct proto_config *c = cfg_allocz(size); | |
197 | ||
a7f23f58 OZ |
198 | if (class == SYM_PROTO) |
199 | add_tail(&new_config->protos, &c->n); | |
31b3e1bb | 200 | c->global = new_config; |
1d2664a4 | 201 | c->protocol = pr; |
31b3e1bb | 202 | c->name = pr->name; |
39c028e9 | 203 | c->preference = pr->preference; |
a7f23f58 | 204 | c->class = class; |
5056c559 | 205 | c->out_filter = FILTER_REJECT; |
9d885689 | 206 | c->table = c->global->master_rtc; |
839380d7 | 207 | c->debug = new_config->proto_default_debug; |
cf31112f | 208 | c->mrtdump = new_config->proto_default_mrtdump; |
31b3e1bb MM |
209 | return c; |
210 | } | |
211 | ||
a7f23f58 OZ |
212 | /** |
213 | * proto_copy_config - copy a protocol configuration | |
214 | * @dest: destination protocol configuration | |
215 | * @src: source protocol configuration | |
216 | * | |
217 | * Whenever a new instance of a routing protocol is created from the | |
218 | * template, proto_copy_config() is called to copy a content of | |
219 | * the source protocol configuration to the new protocol configuration. | |
220 | * Name, class and a node in protos list of @dest are kept intact. | |
221 | * copy_config() protocol hook is used to copy protocol-specific data. | |
222 | */ | |
223 | void | |
224 | proto_copy_config(struct proto_config *dest, struct proto_config *src) | |
225 | { | |
226 | node old_node; | |
227 | int old_class; | |
228 | char *old_name; | |
229 | ||
230 | if (dest->protocol != src->protocol) | |
231 | cf_error("Can't copy configuration from a different protocol type"); | |
232 | ||
233 | if (dest->protocol->copy_config == NULL) | |
234 | cf_error("Inheriting configuration for %s is not supported", src->protocol->name); | |
235 | ||
236 | DBG("Copying configuration from %s to %s\n", src->name, dest->name); | |
237 | ||
238 | /* | |
239 | * Copy struct proto_config here. Keep original node, class and name. | |
240 | * protocol-specific config copy is handled by protocol copy_config() hook | |
241 | */ | |
242 | ||
243 | old_node = dest->n; | |
244 | old_class = dest->class; | |
245 | old_name = dest->name; | |
246 | ||
247 | memcpy(dest, src, sizeof(struct proto_config)); | |
248 | ||
249 | dest->n = old_node; | |
250 | dest->class = old_class; | |
251 | dest->name = old_name; | |
252 | ||
253 | dest->protocol->copy_config(dest, src); | |
254 | } | |
255 | ||
3c6269b8 MM |
256 | /** |
257 | * protos_preconfig - pre-configuration processing | |
258 | * @c: new configuration | |
259 | * | |
260 | * This function calls the preconfig() hooks of all routing | |
261 | * protocols available to prepare them for reading of the new | |
262 | * configuration. | |
263 | */ | |
2326b001 | 264 | void |
31b3e1bb | 265 | protos_preconfig(struct config *c) |
2326b001 | 266 | { |
7f4a3988 MM |
267 | struct protocol *p; |
268 | ||
7c0cc76e | 269 | init_list(&c->protos); |
6b9fa320 | 270 | DBG("Protocol preconfig:"); |
7f4a3988 MM |
271 | WALK_LIST(p, protocol_list) |
272 | { | |
6b9fa320 | 273 | DBG(" %s", p->name); |
4ba84ebc | 274 | p->name_counter = 0; |
3629bcf0 | 275 | if (p->preconfig) |
31b3e1bb | 276 | p->preconfig(p, c); |
7f4a3988 | 277 | } |
6b9fa320 | 278 | DBG("\n"); |
7f4a3988 MM |
279 | } |
280 | ||
3c6269b8 MM |
281 | /** |
282 | * protos_postconfig - post-configuration processing | |
283 | * @c: new configuration | |
284 | * | |
285 | * This function calls the postconfig() hooks of all protocol | |
a7f23f58 OZ |
286 | * instances specified in configuration @c. The hooks are not |
287 | * called for protocol templates. | |
3c6269b8 | 288 | */ |
7f4a3988 | 289 | void |
31b3e1bb | 290 | protos_postconfig(struct config *c) |
7f4a3988 | 291 | { |
31b3e1bb | 292 | struct proto_config *x; |
7f4a3988 MM |
293 | struct protocol *p; |
294 | ||
6b9fa320 | 295 | DBG("Protocol postconfig:"); |
31b3e1bb | 296 | WALK_LIST(x, c->protos) |
7f4a3988 | 297 | { |
6b9fa320 | 298 | DBG(" %s", x->name); |
1d2664a4 | 299 | p = x->protocol; |
3629bcf0 | 300 | if (p->postconfig) |
31b3e1bb MM |
301 | p->postconfig(x); |
302 | } | |
6b9fa320 | 303 | DBG("\n"); |
31b3e1bb MM |
304 | } |
305 | ||
4ef09506 OZ |
306 | extern struct protocol proto_unix_iface; |
307 | ||
50fe90ed MM |
308 | static struct proto * |
309 | proto_init(struct proto_config *c) | |
310 | { | |
311 | struct protocol *p = c->protocol; | |
312 | struct proto *q = p->init(c); | |
313 | ||
314 | q->proto_state = PS_DOWN; | |
315 | q->core_state = FS_HUNGRY; | |
316 | proto_enqueue(&initial_proto_list, q); | |
4ef09506 OZ |
317 | if (p == &proto_unix_iface) |
318 | initial_device_proto = q; | |
319 | ||
f14a4bec | 320 | add_tail(&proto_list, &q->glob_node); |
498c3339 | 321 | PD(q, "Initializing%s", q->disabled ? " [disabled]" : ""); |
50fe90ed MM |
322 | return q; |
323 | } | |
324 | ||
ebae4770 OZ |
325 | static int |
326 | proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type) | |
327 | { | |
328 | /* If the protocol is DOWN, we just restart it */ | |
329 | if (p->proto_state == PS_DOWN) | |
330 | return 0; | |
331 | ||
332 | /* If there is a too big change in core attributes, ... */ | |
333 | if ((nc->protocol != oc->protocol) || | |
23fd4644 | 334 | (nc->disabled != p->disabled) || |
ebae4770 | 335 | (nc->table->table != oc->table->table) || |
76b53a4e | 336 | (proto_get_router_id(nc) != proto_get_router_id(oc))) |
ebae4770 OZ |
337 | return 0; |
338 | ||
339 | int import_changed = (type != RECONFIG_SOFT) && ! filter_same(nc->in_filter, oc->in_filter); | |
340 | int export_changed = (type != RECONFIG_SOFT) && ! filter_same(nc->out_filter, oc->out_filter); | |
341 | ||
342 | /* We treat a change in preferences by reimporting routes */ | |
343 | if (nc->preference != oc->preference) | |
344 | import_changed = 1; | |
345 | ||
346 | /* If the protocol in not UP, it has no routes and we can ignore such changes */ | |
347 | if (p->proto_state != PS_UP) | |
348 | import_changed = export_changed = 0; | |
349 | ||
350 | /* Without this hook we cannot reload routes and have to restart the protocol */ | |
351 | if (import_changed && ! p->reload_routes) | |
352 | return 0; | |
353 | ||
354 | p->debug = nc->debug; | |
355 | p->mrtdump = nc->mrtdump; | |
356 | ||
357 | /* Execute protocol specific reconfigure hook */ | |
358 | if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc))) | |
359 | return 0; | |
360 | ||
361 | DBG("\t%s: same\n", oc->name); | |
362 | PD(p, "Reconfigured"); | |
363 | p->cf = nc; | |
364 | p->name = nc->name; | |
365 | p->in_filter = nc->in_filter; | |
366 | p->out_filter = nc->out_filter; | |
5a56f27c | 367 | p->preference = nc->preference; |
ebae4770 | 368 | |
76b53a4e OZ |
369 | if (import_changed || export_changed) |
370 | log(L_INFO "Reloading protocol %s", p->name); | |
371 | ||
ebae4770 OZ |
372 | if (import_changed && ! p->reload_routes(p)) |
373 | { | |
374 | /* Now, the protocol is reconfigured. But route reload failed | |
375 | and we have to do regular protocol restart. */ | |
76b53a4e | 376 | log(L_INFO "Restarting protocol %s", p->name); |
ebae4770 OZ |
377 | p->disabled = 1; |
378 | proto_rethink_goal(p); | |
379 | p->disabled = 0; | |
380 | proto_rethink_goal(p); | |
381 | return 1; | |
382 | } | |
383 | ||
384 | if (export_changed) | |
385 | proto_request_feeding(p); | |
386 | ||
387 | return 1; | |
388 | } | |
389 | ||
3c6269b8 MM |
390 | /** |
391 | * protos_commit - commit new protocol configuration | |
392 | * @new: new configuration | |
393 | * @old: old configuration or %NULL if it's boot time config | |
394 | * @force_reconfig: force restart of all protocols (used for example | |
395 | * when the router ID changes) | |
bf1aec97 | 396 | * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD) |
3c6269b8 MM |
397 | * |
398 | * Scan differences between @old and @new configuration and adjust all | |
399 | * protocol instances to conform to the new configuration. | |
400 | * | |
401 | * When a protocol exists in the new configuration, but it doesn't in the | |
402 | * original one, it's immediately started. When a collision with the other | |
403 | * running protocol would arise, the new protocol will be temporarily stopped | |
404 | * by the locking mechanism. | |
405 | * | |
406 | * When a protocol exists in the old configuration, but it doesn't in the | |
407 | * new one, it's shut down and deleted after the shutdown completes. | |
408 | * | |
bf1aec97 OZ |
409 | * When a protocol exists in both configurations, the core decides |
410 | * whether it's possible to reconfigure it dynamically - it checks all | |
411 | * the core properties of the protocol (changes in filters are ignored | |
412 | * if type is RECONFIG_SOFT) and if they match, it asks the | |
413 | * reconfigure() hook of the protocol to see if the protocol is able | |
414 | * to switch to the new configuration. If it isn't possible, the | |
415 | * protocol is shut down and a new instance is started with the new | |
416 | * configuration after the shutdown is completed. | |
3c6269b8 | 417 | */ |
31b3e1bb | 418 | void |
bf1aec97 | 419 | protos_commit(struct config *new, struct config *old, int force_reconfig, int type) |
31b3e1bb | 420 | { |
50fe90ed MM |
421 | struct proto_config *oc, *nc; |
422 | struct proto *p, *n; | |
a7f23f58 | 423 | struct symbol *sym; |
31b3e1bb | 424 | |
50fe90ed MM |
425 | DBG("protos_commit:\n"); |
426 | if (old) | |
31b3e1bb | 427 | { |
50fe90ed MM |
428 | WALK_LIST(oc, old->protos) |
429 | { | |
a7f23f58 OZ |
430 | p = oc->proto; |
431 | sym = cf_find_symbol(oc->name); | |
bf8558bc | 432 | if (sym && sym->class == SYM_PROTO && !new->shutdown) |
50fe90ed MM |
433 | { |
434 | /* Found match, let's check if we can smoothly switch to new configuration */ | |
e04555c0 | 435 | /* No need to check description */ |
50fe90ed | 436 | nc = sym->def; |
ebae4770 OZ |
437 | nc->proto = p; |
438 | ||
439 | /* We will try to reconfigure protocol p */ | |
440 | if (! force_reconfig && proto_reconfigure(p, oc, nc, type)) | |
441 | continue; | |
442 | ||
443 | /* Unsuccessful, we will restart it */ | |
76b53a4e OZ |
444 | if (!p->disabled && !nc->disabled) |
445 | log(L_INFO "Restarting protocol %s", p->name); | |
446 | else if (p->disabled && !nc->disabled) | |
447 | log(L_INFO "Enabling protocol %s", p->name); | |
448 | else if (!p->disabled && nc->disabled) | |
449 | log(L_INFO "Disabling protocol %s", p->name); | |
450 | ||
ebae4770 | 451 | PD(p, "Restarting"); |
50fe90ed MM |
452 | p->cf_new = nc; |
453 | } | |
454 | else | |
455 | { | |
76b53a4e OZ |
456 | if (!shutting_down) |
457 | log(L_INFO "Removing protocol %s", p->name); | |
839380d7 | 458 | PD(p, "Unconfigured"); |
50fe90ed MM |
459 | p->cf_new = NULL; |
460 | } | |
461 | p->reconfiguring = 1; | |
462 | config_add_obstacle(old); | |
463 | proto_rethink_goal(p); | |
464 | } | |
7f4a3988 | 465 | } |
50fe90ed MM |
466 | |
467 | WALK_LIST(nc, new->protos) | |
468 | if (!nc->proto) | |
469 | { | |
76b53a4e OZ |
470 | if (old_config) /* Not a first-time configuration */ |
471 | log(L_INFO "Adding protocol %s", nc->name); | |
50fe90ed MM |
472 | proto_init(nc); |
473 | } | |
474 | DBG("\tdone\n"); | |
475 | ||
476 | DBG("Protocol start\n"); | |
4ef09506 OZ |
477 | |
478 | /* Start device protocol first */ | |
479 | if (initial_device_proto) | |
480 | { | |
481 | proto_rethink_goal(initial_device_proto); | |
482 | initial_device_proto = NULL; | |
483 | } | |
484 | ||
50fe90ed MM |
485 | WALK_LIST_DELSAFE(p, n, initial_proto_list) |
486 | proto_rethink_goal(p); | |
7f4a3988 MM |
487 | } |
488 | ||
47b79306 | 489 | static void |
67bd949a | 490 | proto_rethink_goal(struct proto *p) |
47b79306 | 491 | { |
50fe90ed MM |
492 | struct protocol *q; |
493 | ||
494 | if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) | |
495 | { | |
496 | struct proto_config *nc = p->cf_new; | |
497 | DBG("%s has shut down for reconfiguration\n", p->name); | |
498 | config_del_obstacle(p->cf->global); | |
499 | rem_node(&p->n); | |
f14a4bec | 500 | rem_node(&p->glob_node); |
50fe90ed MM |
501 | mb_free(p); |
502 | if (!nc) | |
503 | return; | |
f098e072 | 504 | p = proto_init(nc); |
50fe90ed MM |
505 | } |
506 | ||
507 | /* Determine what state we want to reach */ | |
bf8558bc | 508 | if (p->disabled || p->reconfiguring) |
ebd3720f MM |
509 | { |
510 | p->core_goal = FS_HUNGRY; | |
511 | if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) | |
512 | return; | |
513 | } | |
50fe90ed | 514 | else |
ebd3720f MM |
515 | { |
516 | p->core_goal = FS_HAPPY; | |
517 | if (p->core_state == FS_HAPPY && p->proto_state == PS_UP) | |
518 | return; | |
519 | } | |
50fe90ed MM |
520 | |
521 | q = p->proto; | |
67bd949a MM |
522 | if (p->core_goal == FS_HAPPY) /* Going up */ |
523 | { | |
524 | if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) | |
525 | { | |
526 | DBG("Kicking %s up\n", p->name); | |
839380d7 | 527 | PD(p, "Starting"); |
1a54b1c6 | 528 | proto_init_instance(p); |
67bd949a MM |
529 | proto_notify_state(p, (q->start ? q->start(p) : PS_UP)); |
530 | } | |
531 | } | |
532 | else /* Going down */ | |
533 | { | |
534 | if (p->proto_state == PS_START || p->proto_state == PS_UP) | |
535 | { | |
536 | DBG("Kicking %s down\n", p->name); | |
839380d7 | 537 | PD(p, "Shutting down"); |
67bd949a MM |
538 | proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN)); |
539 | } | |
540 | } | |
541 | } | |
542 | ||
3c6269b8 MM |
543 | /** |
544 | * protos_dump_all - dump status of all protocols | |
545 | * | |
546 | * This function dumps status of all existing protocol instances to the | |
547 | * debug output. It involves printing of general status information | |
548 | * such as protocol states, its position on the protocol lists | |
549 | * and also calling of a dump() hook of the protocol to print | |
550 | * the internals. | |
551 | */ | |
87d2be86 PM |
552 | void |
553 | protos_dump_all(void) | |
554 | { | |
555 | struct proto *p; | |
556 | ||
557 | debug("Protocols:\n"); | |
558 | ||
f14a4bec | 559 | WALK_LIST(p, active_proto_list) |
87d2be86 | 560 | { |
3ea1ba63 | 561 | debug(" protocol %s state %s/%s\n", p->name, |
b2280748 | 562 | p_states[p->proto_state], c_states[p->core_state]); |
529c4149 | 563 | if (p->in_filter) |
5056c559 MM |
564 | debug("\tInput filter: %s\n", filter_name(p->in_filter)); |
565 | if (p->out_filter != FILTER_REJECT) | |
566 | debug("\tOutput filter: %s\n", filter_name(p->out_filter)); | |
66efdf96 MM |
567 | if (p->disabled) |
568 | debug("\tDISABLED\n"); | |
31b3e1bb MM |
569 | else if (p->proto->dump) |
570 | p->proto->dump(p); | |
87d2be86 | 571 | } |
47b79306 | 572 | WALK_LIST(p, inactive_proto_list) |
67bd949a MM |
573 | debug(" inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]); |
574 | WALK_LIST(p, initial_proto_list) | |
575 | debug(" initial %s\n", p->name); | |
f14a4bec MM |
576 | WALK_LIST(p, flush_proto_list) |
577 | debug(" flushing %s\n", p->name); | |
87d2be86 PM |
578 | } |
579 | ||
3c6269b8 MM |
580 | /** |
581 | * proto_build - make a single protocol available | |
582 | * @p: the protocol | |
583 | * | |
584 | * After the platform specific initialization code uses protos_build() | |
585 | * to add all the standard protocols, it should call proto_build() for | |
2e9b2421 | 586 | * all platform specific protocols to inform the core that they exist. |
3c6269b8 | 587 | */ |
3991d84e MM |
588 | void |
589 | proto_build(struct protocol *p) | |
590 | { | |
591 | add_tail(&protocol_list, &p->n); | |
592 | if (p->attr_class) | |
593 | { | |
594 | ASSERT(!attr_class_to_protocol[p->attr_class]); | |
595 | attr_class_to_protocol[p->attr_class] = p; | |
596 | } | |
597 | } | |
598 | ||
3c6269b8 MM |
599 | /** |
600 | * protos_build - build a protocol list | |
601 | * | |
602 | * This function is called during BIRD startup to insert | |
603 | * all standard protocols to the global protocol list. Insertion | |
604 | * of platform specific protocols (such as the kernel syncer) | |
605 | * is in the domain of competence of the platform dependent | |
606 | * startup code. | |
607 | */ | |
0432c017 MM |
608 | void |
609 | protos_build(void) | |
610 | { | |
611 | init_list(&protocol_list); | |
471cc0be MM |
612 | init_list(&proto_list); |
613 | init_list(&active_proto_list); | |
614 | init_list(&inactive_proto_list); | |
615 | init_list(&initial_proto_list); | |
616 | init_list(&flush_proto_list); | |
3991d84e | 617 | proto_build(&proto_device); |
93e868c7 OZ |
618 | #ifdef CONFIG_RADV |
619 | proto_build(&proto_radv); | |
620 | #endif | |
18fff6a1 | 621 | #ifdef CONFIG_RIP |
3991d84e | 622 | proto_build(&proto_rip); |
18fff6a1 MM |
623 | #endif |
624 | #ifdef CONFIG_STATIC | |
3991d84e | 625 | proto_build(&proto_static); |
c1f8dc91 OF |
626 | #endif |
627 | #ifdef CONFIG_OSPF | |
3991d84e | 628 | proto_build(&proto_ospf); |
26368f65 MM |
629 | #endif |
630 | #ifdef CONFIG_PIPE | |
3991d84e | 631 | proto_build(&proto_pipe); |
2638249d MM |
632 | #endif |
633 | #ifdef CONFIG_BGP | |
3991d84e | 634 | proto_build(&proto_bgp); |
18fff6a1 | 635 | #endif |
67bd949a | 636 | proto_pool = rp_new(&root_pool, "Protocols"); |
1a54b1c6 MM |
637 | proto_flush_event = ev_new(proto_pool); |
638 | proto_flush_event->hook = proto_flush_all; | |
67bd949a MM |
639 | } |
640 | ||
641 | static void | |
642 | proto_fell_down(struct proto *p) | |
643 | { | |
644 | DBG("Protocol %s down\n", p->name); | |
ac07aacd OZ |
645 | |
646 | if (p->stats.imp_routes != 0) | |
647 | log(L_ERR "Protocol %s is down but still has %d routes", p->name, p->stats.imp_routes); | |
925fe2d3 OZ |
648 | |
649 | bzero(&p->stats, sizeof(struct proto_stats)); | |
50fe90ed | 650 | rt_unlock_table(p->table); |
c8387626 | 651 | |
cfe34a31 OZ |
652 | if (p->proto->cleanup) |
653 | p->proto->cleanup(p); | |
c8387626 | 654 | |
67bd949a MM |
655 | proto_rethink_goal(p); |
656 | } | |
657 | ||
ac5d8012 MM |
658 | static void |
659 | proto_feed_more(void *P) | |
660 | { | |
661 | struct proto *p = P; | |
662 | ||
075898de MM |
663 | if (p->core_state != FS_FEEDING) |
664 | return; | |
fbde6c39 OZ |
665 | |
666 | DBG("Feeding protocol %s continued\n", p->name); | |
ac5d8012 MM |
667 | if (rt_feed_baby(p)) |
668 | { | |
669 | p->core_state = FS_HAPPY; | |
670 | proto_relink(p); | |
671 | DBG("Protocol %s up and running\n", p->name); | |
672 | } | |
673 | else | |
674 | { | |
675 | p->attn->hook = proto_feed_more; | |
676 | ev_schedule(p->attn); /* Will continue later... */ | |
677 | } | |
678 | } | |
679 | ||
8f6accb5 | 680 | static void |
bf47fe4b | 681 | proto_feed_initial(void *P) |
67bd949a MM |
682 | { |
683 | struct proto *p = P; | |
684 | ||
fbde6c39 OZ |
685 | if (p->core_state != FS_FEEDING) |
686 | return; | |
687 | ||
67bd949a | 688 | DBG("Feeding protocol %s\n", p->name); |
0e02abfd | 689 | proto_add_announce_hook(p, p->table); |
67bd949a | 690 | if_feed_baby(p); |
ac5d8012 | 691 | proto_feed_more(P); |
67bd949a MM |
692 | } |
693 | ||
d6a836f8 OZ |
694 | static void |
695 | proto_schedule_flush(struct proto *p) | |
696 | { | |
697 | /* Need to abort feeding */ | |
698 | if (p->core_state == FS_FEEDING) | |
699 | rt_feed_baby_abort(p); | |
700 | ||
701 | DBG("%s: Scheduling flush\n", p->name); | |
702 | p->core_state = FS_FLUSHING; | |
703 | proto_relink(p); | |
704 | proto_flush_hooks(p); | |
705 | ev_schedule(proto_flush_event); | |
706 | } | |
707 | ||
708 | static void | |
bf47fe4b | 709 | proto_schedule_feed(struct proto *p, int initial) |
d6a836f8 OZ |
710 | { |
711 | DBG("%s: Scheduling meal\n", p->name); | |
712 | p->core_state = FS_FEEDING; | |
11361a10 | 713 | p->refeeding = !initial; |
8a7fb885 OZ |
714 | |
715 | /* Hack: reset exp_routes during refeed, and do not decrease it later */ | |
716 | if (!initial) | |
717 | p->stats.exp_routes = 0; | |
718 | ||
d6a836f8 | 719 | proto_relink(p); |
bf47fe4b | 720 | p->attn->hook = initial ? proto_feed_initial : proto_feed_more; |
d6a836f8 OZ |
721 | ev_schedule(p->attn); |
722 | } | |
723 | ||
bf47fe4b OZ |
724 | /** |
725 | * proto_request_feeding - request feeding routes to the protocol | |
726 | * @p: given protocol | |
727 | * | |
728 | * Sometimes it is needed to send again all routes to the | |
729 | * protocol. This is called feeding and can be requested by this | |
730 | * function. This would cause protocol core state transition | |
731 | * to FS_FEEDING (during feeding) and when completed, it will | |
732 | * switch back to FS_HAPPY. This function can be called even | |
733 | * when feeding is already running, in that case it is restarted. | |
734 | */ | |
735 | void | |
736 | proto_request_feeding(struct proto *p) | |
737 | { | |
738 | ASSERT(p->proto_state == PS_UP); | |
739 | ||
740 | /* If we are already feeding, we want to restart it */ | |
741 | if (p->core_state == FS_FEEDING) | |
742 | { | |
743 | /* Unless feeding is in initial state */ | |
744 | if (p->attn->hook == proto_feed_initial) | |
745 | return; | |
746 | ||
747 | rt_feed_baby_abort(p); | |
748 | } | |
749 | ||
750 | proto_schedule_feed(p, 0); | |
751 | } | |
752 | ||
3c6269b8 MM |
753 | /** |
754 | * proto_notify_state - notify core about protocol state change | |
755 | * @p: protocol the state of which has changed | |
756 | * @ps: the new status | |
757 | * | |
758 | * Whenever a state of a protocol changes due to some event internal | |
759 | * to the protocol (i.e., not inside a start() or shutdown() hook), | |
760 | * it should immediately notify the core about the change by calling | |
761 | * proto_notify_state() which will write the new state to the &proto | |
762 | * structure and take all the actions necessary to adapt to the new | |
d6a836f8 OZ |
763 | * state. State change to PS_DOWN immediately frees resources of protocol |
764 | * and might execute start callback of protocol; therefore, | |
765 | * it should be used at tail positions of protocol callbacks. | |
3c6269b8 | 766 | */ |
67bd949a MM |
767 | void |
768 | proto_notify_state(struct proto *p, unsigned ps) | |
769 | { | |
770 | unsigned ops = p->proto_state; | |
771 | unsigned cs = p->core_state; | |
772 | ||
773 | DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]); | |
774 | if (ops == ps) | |
775 | return; | |
776 | ||
d6a836f8 OZ |
777 | p->proto_state = ps; |
778 | ||
67bd949a MM |
779 | switch (ps) |
780 | { | |
781 | case PS_DOWN: | |
a421ec33 | 782 | if ((cs == FS_FEEDING) || (cs == FS_HAPPY)) |
b807ef9a OZ |
783 | proto_schedule_flush(p); |
784 | ||
d6a836f8 OZ |
785 | neigh_prune(); // FIXME convert neighbors to resource? |
786 | rfree(p->pool); | |
787 | p->pool = NULL; | |
788 | ||
67bd949a | 789 | if (cs == FS_HUNGRY) /* Shutdown finished */ |
50fe90ed MM |
790 | { |
791 | proto_fell_down(p); | |
792 | return; /* The protocol might have ceased to exist */ | |
793 | } | |
67bd949a MM |
794 | break; |
795 | case PS_START: | |
796 | ASSERT(ops == PS_DOWN); | |
797 | ASSERT(cs == FS_HUNGRY); | |
798 | break; | |
799 | case PS_UP: | |
800 | ASSERT(ops == PS_DOWN || ops == PS_START); | |
801 | ASSERT(cs == FS_HUNGRY); | |
bf47fe4b | 802 | proto_schedule_feed(p, 1); |
67bd949a MM |
803 | break; |
804 | case PS_STOP: | |
a421ec33 | 805 | if ((cs == FS_FEEDING) || (cs == FS_HAPPY)) |
d6a836f8 | 806 | proto_schedule_flush(p); |
f4aabcee | 807 | break; |
67bd949a | 808 | default: |
67bd949a MM |
809 | bug("Invalid state transition for %s from %s/%s to */%s", p->name, c_states[cs], p_states[ops], p_states[ps]); |
810 | } | |
0432c017 | 811 | } |
1a54b1c6 | 812 | |
8f6accb5 | 813 | static void |
7c103b1e | 814 | proto_flush_all(void *unused UNUSED) |
1a54b1c6 MM |
815 | { |
816 | struct proto *p; | |
817 | ||
0e02abfd | 818 | rt_prune_all(); |
1a54b1c6 MM |
819 | while ((p = HEAD(flush_proto_list))->n.next) |
820 | { | |
53434e44 OZ |
821 | /* This will flush interfaces in the same manner |
822 | like rt_prune_all() flushes routes */ | |
823 | if (p->proto == &proto_unix_iface) | |
824 | if_flush_ifaces(p); | |
825 | ||
1a54b1c6 | 826 | DBG("Flushing protocol %s\n", p->name); |
1a54b1c6 MM |
827 | p->core_state = FS_HUNGRY; |
828 | proto_relink(p); | |
d6a836f8 OZ |
829 | if (p->proto_state == PS_DOWN) |
830 | proto_fell_down(p); | |
1a54b1c6 MM |
831 | } |
832 | } | |
ae97b946 | 833 | |
0d3e6bce MM |
834 | /* |
835 | * CLI Commands | |
836 | */ | |
837 | ||
838 | static char * | |
839 | proto_state_name(struct proto *p) | |
840 | { | |
841 | #define P(x,y) ((x << 4) | y) | |
842 | switch (P(p->proto_state, p->core_state)) | |
843 | { | |
844 | case P(PS_DOWN, FS_HUNGRY): return "down"; | |
845 | case P(PS_START, FS_HUNGRY): return "start"; | |
846 | case P(PS_UP, FS_HUNGRY): | |
847 | case P(PS_UP, FS_FEEDING): return "feed"; | |
848 | case P(PS_STOP, FS_HUNGRY): return "stop"; | |
849 | case P(PS_UP, FS_HAPPY): return "up"; | |
850 | case P(PS_STOP, FS_FLUSHING): | |
851 | case P(PS_DOWN, FS_FLUSHING): return "flush"; | |
852 | default: return "???"; | |
853 | } | |
854 | #undef P | |
855 | } | |
856 | ||
9db74169 OZ |
857 | static void |
858 | proto_do_show_stats(struct proto *p) | |
859 | { | |
860 | struct proto_stats *s = &p->stats; | |
861 | cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", | |
862 | s->imp_routes, s->exp_routes, s->pref_routes); | |
863 | cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); | |
864 | cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", | |
865 | s->imp_updates_received, s->imp_updates_invalid, | |
866 | s->imp_updates_filtered, s->imp_updates_ignored, | |
867 | s->imp_updates_accepted); | |
868 | cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u", | |
869 | s->imp_withdraws_received, s->imp_withdraws_invalid, | |
870 | s->imp_withdraws_ignored, s->imp_withdraws_accepted); | |
871 | cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u", | |
872 | s->exp_updates_received, s->exp_updates_rejected, | |
873 | s->exp_updates_filtered, s->exp_updates_accepted); | |
874 | cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u", | |
875 | s->exp_withdraws_received, s->exp_withdraws_accepted); | |
876 | } | |
877 | ||
ba130172 | 878 | #ifdef CONFIG_PIPE |
9db74169 OZ |
879 | static void |
880 | proto_do_show_pipe_stats(struct proto *p) | |
881 | { | |
882 | struct proto_stats *s1 = &p->stats; | |
883 | struct proto_stats *s2 = pipe_get_peer_stats(p); | |
884 | ||
885 | /* | |
886 | * Pipe stats (as anything related to pipes) are a bit tricky. There | |
887 | * are two sets of stats - s1 for routes going from the primary | |
888 | * routing table to the secondary routing table ('exported' from the | |
889 | * user point of view) and s2 for routes going in the other | |
890 | * direction ('imported' from the user point of view). | |
891 | * | |
892 | * Each route going through a pipe is, technically, first exported | |
893 | * to the pipe and then imported from that pipe and such operations | |
894 | * are counted in one set of stats according to the direction of the | |
895 | * route propagation. Filtering is done just in the first part | |
896 | * (export). Therefore, we compose stats for one directon for one | |
897 | * user direction from both import and export stats, skipping | |
898 | * immediate and irrelevant steps (exp_updates_accepted, | |
899 | * imp_updates_received, imp_updates_filtered, ...) | |
900 | */ | |
901 | ||
902 | cli_msg(-1006, " Routes: %u imported, %u exported", | |
903 | s2->imp_routes, s1->imp_routes); | |
904 | cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); | |
905 | cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", | |
906 | s2->exp_updates_received, s2->exp_updates_rejected + s2->imp_updates_invalid, | |
907 | s2->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted); | |
908 | cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u", | |
909 | s2->exp_withdraws_received, s2->imp_withdraws_invalid, | |
910 | s2->imp_withdraws_ignored, s2->imp_withdraws_accepted); | |
911 | cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u", | |
912 | s1->exp_updates_received, s1->exp_updates_rejected + s1->imp_updates_invalid, | |
913 | s1->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted); | |
914 | cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u", | |
915 | s1->exp_withdraws_received, s1->imp_withdraws_invalid, | |
916 | s1->imp_withdraws_ignored, s1->imp_withdraws_accepted); | |
917 | } | |
ba130172 | 918 | #endif |
9db74169 | 919 | |
e304fd4b OZ |
920 | void |
921 | proto_cmd_show(struct proto *p, unsigned int verbose, int cnt) | |
1d2664a4 | 922 | { |
c37e7851 | 923 | byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE]; |
9685deb9 | 924 | |
e304fd4b OZ |
925 | /* First protocol - show header */ |
926 | if (!cnt) | |
927 | cli_msg(-2002, "name proto table state since info"); | |
928 | ||
9685deb9 MM |
929 | buf[0] = 0; |
930 | if (p->proto->get_status) | |
931 | p->proto->get_status(p, buf); | |
c37e7851 OZ |
932 | tm_format_datetime(tbuf, &config->tf_proto, p->last_state_change); |
933 | cli_msg(-1002, "%-8s %-8s %-8s %-5s %-10s %s", | |
1d2664a4 MM |
934 | p->name, |
935 | p->proto->name, | |
936 | p->table->name, | |
937 | proto_state_name(p), | |
c37e7851 | 938 | tbuf, |
9685deb9 | 939 | buf); |
1d2664a4 MM |
940 | if (verbose) |
941 | { | |
e04555c0 OZ |
942 | if (p->cf->dsc) |
943 | cli_msg(-1006, " Description: %s", p->cf->dsc); | |
b8113a5e OZ |
944 | if (p->cf->router_id) |
945 | cli_msg(-1006, " Router ID: %R", p->cf->router_id); | |
925fe2d3 OZ |
946 | cli_msg(-1006, " Preference: %d", p->preference); |
947 | cli_msg(-1006, " Input filter: %s", filter_name(p->in_filter)); | |
948 | cli_msg(-1006, " Output filter: %s", filter_name(p->out_filter)); | |
949 | ||
950 | if (p->proto_state != PS_DOWN) | |
951 | { | |
9db74169 OZ |
952 | #ifdef CONFIG_PIPE |
953 | if (proto_is_pipe(p)) | |
954 | proto_do_show_pipe_stats(p); | |
955 | else | |
956 | #endif | |
957 | proto_do_show_stats(p); | |
925fe2d3 OZ |
958 | } |
959 | ||
b8113a5e OZ |
960 | if (p->proto->show_proto_info) |
961 | p->proto->show_proto_info(p); | |
962 | ||
925fe2d3 | 963 | cli_msg(-1006, ""); |
1d2664a4 MM |
964 | } |
965 | } | |
966 | ||
ae97b946 | 967 | void |
e304fd4b | 968 | proto_cmd_disable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) |
ae97b946 | 969 | { |
e304fd4b | 970 | if (p->disabled) |
1d2664a4 | 971 | { |
e304fd4b | 972 | cli_msg(-8, "%s: already disabled", p->name); |
1d2664a4 MM |
973 | return; |
974 | } | |
e304fd4b OZ |
975 | |
976 | log(L_INFO "Disabling protocol %s", p->name); | |
977 | p->disabled = 1; | |
978 | proto_rethink_goal(p); | |
979 | cli_msg(-9, "%s: disabled", p->name); | |
980 | } | |
981 | ||
982 | void | |
983 | proto_cmd_enable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) | |
984 | { | |
985 | if (!p->disabled) | |
986 | { | |
987 | cli_msg(-10, "%s: already enabled", p->name); | |
988 | return; | |
989 | } | |
990 | ||
991 | log(L_INFO "Enabling protocol %s", p->name); | |
992 | p->disabled = 0; | |
993 | proto_rethink_goal(p); | |
994 | cli_msg(-11, "%s: enabled", p->name); | |
995 | } | |
996 | ||
997 | void | |
998 | proto_cmd_restart(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) | |
999 | { | |
1000 | if (p->disabled) | |
1001 | { | |
1002 | cli_msg(-8, "%s: already disabled", p->name); | |
1003 | return; | |
1004 | } | |
1005 | ||
1006 | log(L_INFO "Restarting protocol %s", p->name); | |
1007 | p->disabled = 1; | |
1008 | proto_rethink_goal(p); | |
1009 | p->disabled = 0; | |
1010 | proto_rethink_goal(p); | |
1011 | cli_msg(-12, "%s: restarted", p->name); | |
1012 | } | |
1013 | ||
1014 | void | |
1015 | proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED) | |
1016 | { | |
1017 | if (p->disabled) | |
1018 | { | |
1019 | cli_msg(-8, "%s: already disabled", p->name); | |
1020 | return; | |
1021 | } | |
1022 | ||
1023 | /* If the protocol in not UP, it has no routes */ | |
1024 | if (p->proto_state != PS_UP) | |
1025 | return; | |
1026 | ||
1027 | log(L_INFO "Reloading protocol %s", p->name); | |
1028 | ||
1029 | /* re-importing routes */ | |
1030 | if (dir != CMD_RELOAD_OUT) | |
1031 | if (! (p->reload_routes && p->reload_routes(p))) | |
1032 | { | |
1033 | cli_msg(-8006, "%s: reload failed", p->name); | |
1034 | return; | |
1035 | } | |
1036 | ||
1037 | /* re-exporting routes */ | |
1038 | if (dir != CMD_RELOAD_IN) | |
1039 | proto_request_feeding(p); | |
1040 | ||
1041 | cli_msg(-15, "%s: reloading", p->name); | |
1042 | } | |
1043 | ||
1044 | void | |
1045 | proto_cmd_debug(struct proto *p, unsigned int mask, int cnt UNUSED) | |
1046 | { | |
1047 | p->debug = mask; | |
1048 | } | |
1049 | ||
1050 | void | |
1051 | proto_cmd_mrtdump(struct proto *p, unsigned int mask, int cnt UNUSED) | |
1052 | { | |
1053 | p->mrtdump = mask; | |
1054 | } | |
1055 | ||
1056 | static void | |
1057 | proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, unsigned int, int), unsigned int arg) | |
1058 | { | |
1059 | if (s->class != SYM_PROTO) | |
1d2664a4 | 1060 | { |
e304fd4b OZ |
1061 | cli_msg(9002, "%s is not a protocol", s->name); |
1062 | return; | |
1d2664a4 | 1063 | } |
e304fd4b OZ |
1064 | |
1065 | cmd(((struct proto_config *)s->def)->proto, arg, 0); | |
ae97b946 MM |
1066 | cli_msg(0, ""); |
1067 | } | |
02c1fbdd | 1068 | |
e304fd4b OZ |
1069 | static void |
1070 | proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, unsigned int, int), unsigned int arg) | |
1071 | { | |
1072 | int cnt = 0; | |
1073 | ||
1074 | node *nn; | |
1075 | WALK_LIST(nn, proto_list) | |
1076 | { | |
1077 | struct proto *p = SKIP_BACK(struct proto, glob_node, nn); | |
1078 | ||
1079 | if (!patt || patmatch(patt, p->name)) | |
1080 | cmd(p, arg, cnt++); | |
1081 | } | |
1082 | ||
1083 | if (!cnt) | |
1084 | cli_msg(8003, "No protocols match"); | |
1085 | else | |
1086 | cli_msg(0, ""); | |
1087 | } | |
1088 | ||
1089 | void | |
e0a45fb4 OZ |
1090 | proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, unsigned int, int), |
1091 | int restricted, unsigned int arg) | |
e304fd4b | 1092 | { |
e0a45fb4 OZ |
1093 | if (restricted && cli_access_restricted()) |
1094 | return; | |
1095 | ||
e304fd4b OZ |
1096 | if (ps.patt) |
1097 | proto_apply_cmd_patt(ps.ptr, cmd, arg); | |
1098 | else | |
1099 | proto_apply_cmd_symbol(ps.ptr, cmd, arg); | |
1100 | } | |
1101 | ||
02c1fbdd MM |
1102 | struct proto * |
1103 | proto_get_named(struct symbol *sym, struct protocol *pr) | |
1104 | { | |
1105 | struct proto *p, *q; | |
1106 | ||
1107 | if (sym) | |
1108 | { | |
1109 | if (sym->class != SYM_PROTO) | |
1110 | cf_error("%s: Not a protocol", sym->name); | |
1111 | p = ((struct proto_config *)sym->def)->proto; | |
1112 | if (!p || p->proto != pr) | |
1113 | cf_error("%s: Not a %s protocol", sym->name, pr->name); | |
1114 | } | |
1115 | else | |
1116 | { | |
1117 | p = NULL; | |
f14a4bec | 1118 | WALK_LIST(q, active_proto_list) |
02c1fbdd MM |
1119 | if (q->proto == pr) |
1120 | { | |
1121 | if (p) | |
1122 | cf_error("There are multiple %s protocols running", pr->name); | |
1123 | p = q; | |
1124 | } | |
1125 | if (!p) | |
1126 | cf_error("There is no %s protocol running", pr->name); | |
1127 | } | |
1128 | return p; | |
1129 | } |