Line data Source code
1 : /* $Id: gmpr_intf.c 514187 2012-05-06 12:25:25Z ib-builder $
2 : *
3 : * gmpr_intf.c - IGMP/MLD Router-Side Interface Routines
4 : *
5 : * Dave Katz, March 2008
6 : *
7 : * Copyright (c) 2008, Juniper Networks, Inc.
8 : * All rights reserved.
9 : */
10 : #include "gmpx_basic_types.h"
11 : #include "gmp.h"
12 : #include "gmpx_environment.h"
13 : #include "gmp_private.h"
14 : #include "gmp_router.h"
15 : #include "gmpr_private.h"
16 : #include "gmpr_trace.h"
17 : /* Global tree of all interfaces, per protocol. */
18 :
19 : gmpx_patroot *gmpr_global_intf_tree[GMP_NUM_PROTOS];
20 :
21 : /* Forward references */
22 :
23 : static void gmpr_intf_query_timer_expiry(gmpx_timer *timer, void *context);
24 :
25 : /*
26 : * gmpr_next_instance_intf
27 : *
28 : * Returns the next interface on an instance, given the previous one.
29 : * If the previous pointer is NULL, returns the first interface on the
30 : * instance.
31 : *
32 : * Returns a pointer to the interface, or NULL if there are no more
33 : * interfaces.
34 : */
35 : gmpr_intf *
36 381 : gmpr_next_instance_intf (gmpr_instance *instance, gmpr_intf *prev_intf)
37 : {
38 : gmpr_intf *next_intf;
39 : gmpx_patnode *node;
40 :
41 : /* Look up the node. */
42 :
43 381 : if (prev_intf) {
44 6 : node = gmpx_patricia_get_next(instance->rinst_intfs,
45 : &prev_intf->rintf_inst_patnode);
46 : } else {
47 375 : node = gmpx_patricia_lookup_least(instance->rinst_intfs);
48 : }
49 :
50 : /* Convert to a intf pointer. The pointer may be NULL. */
51 :
52 381 : next_intf = gmpr_inst_patnode_to_intf(node);
53 :
54 381 : return next_intf;
55 : }
56 :
57 :
58 : /*
59 : * gmpr_intf_lookup
60 : *
61 : * Look up an interface, given the instance and interface ID.
62 : *
63 : * Returns a pointer to the interface structure, or NULL if not there.
64 : */
65 : gmpr_intf *
66 6887 : gmpr_intf_lookup (gmpr_instance *instance, gmpx_intf_id intf_id)
67 : {
68 : gmpr_intf *intf;
69 : gmpx_patnode *node;
70 :
71 : /* Look up the interface in the tree. */
72 :
73 6887 : node = gmpx_patricia_lookup(instance->rinst_intfs, &intf_id);
74 6887 : intf = gmpr_inst_patnode_to_intf(node);
75 :
76 6887 : return intf;
77 : }
78 :
79 :
80 : /*
81 : * gmpr_intf_lookup_global
82 : *
83 : * Look up an interface, given the protocol and interface ID.
84 : *
85 : * Returns a pointer to the interface structure, or NULL if not there.
86 : */
87 : gmpr_intf *
88 5390 : gmpr_intf_lookup_global (gmp_proto proto, gmpx_intf_id intf_id)
89 : {
90 : gmpr_intf *intf;
91 : gmpx_patnode *node;
92 :
93 : /* Look up the interface in the tree. */
94 :
95 5390 : gmpx_assert(proto < GMP_NUM_PROTOS);
96 5390 : node = gmpx_patricia_lookup(gmpr_global_intf_tree[proto], &intf_id);
97 5390 : intf = gmpr_global_patnode_to_intf(node);
98 :
99 5390 : return intf;
100 : }
101 :
102 :
103 : /*
104 : * gmpr_destroy_intf
105 : *
106 : * Free an interface.
107 : */
108 : static void
109 1708 : gmpr_destroy_intf (gmpr_intf *intf)
110 : {
111 : gmpr_instance *instance;
112 :
113 : /* Toss the input group tree. It better be empty. */
114 :
115 1708 : gmpx_assert(gmpx_patricia_lookup_least(intf->rintf_group_root) == NULL);
116 1708 : gmpx_patroot_destroy(intf->rintf_group_root);
117 1708 : intf->rintf_group_root = NULL;
118 :
119 : /* Toss the output group tree. It better be empty. */
120 :
121 1708 : gmpx_assert(gmpx_patricia_lookup_least(intf->rintf_oif_group_root) ==
122 : NULL);
123 1708 : gmpx_patroot_destroy(intf->rintf_oif_group_root);
124 1708 : intf->rintf_oif_group_root = NULL;
125 :
126 : /* Toss the host tree. It better be empty. */
127 :
128 1708 : gmpx_assert(gmpx_patricia_lookup_least(intf->rintf_host_root) == NULL);
129 1708 : gmpx_patroot_destroy(intf->rintf_host_root);
130 1708 : intf->rintf_host_root = NULL;
131 :
132 : /* The group transmit thread better be empty. */
133 :
134 1708 : gmpx_assert(thread_circular_thread_empty(&intf->rintf_xmit_head));
135 :
136 : /* Destroy the timers. */
137 :
138 1708 : gmpx_destroy_timer(intf->rintf_query_timer);
139 1708 : gmpx_destroy_timer(intf->rintf_other_querier_present);
140 :
141 : /* Delete the interface from the threads. */
142 :
143 1708 : instance = intf->rintf_instance;
144 1708 : gmpx_assert(gmpx_patricia_delete(instance->rinst_intfs,
145 : &intf->rintf_inst_patnode));
146 1708 : gmpx_assert(
147 : gmpx_patricia_delete(gmpr_global_intf_tree[instance->rinst_proto],
148 : &intf->rintf_global_patnode));
149 1708 : thread_remove(&intf->rintf_startup_thread);
150 :
151 : /* Free the interface entry itself. */
152 :
153 1708 : gmpx_free_block(gmpr_intf_tag, intf);
154 1708 : }
155 :
156 :
157 : /*
158 : * gmpr_destroy_instance_intfs
159 : *
160 : * Destroy all interfaces bound to an instance. This is all quite
161 : * unceremonious; we simply blast all of the state.
162 : */
163 : void
164 125 : gmpr_destroy_instance_intfs (gmpr_instance *instance)
165 : {
166 : gmpr_intf *intf;
167 :
168 : /* Walk each interface on the instance and destroy it. */
169 :
170 : while (TRUE) {
171 :
172 125 : intf = gmpr_next_instance_intf(instance, NULL);
173 125 : if (!intf)
174 125 : break; /* All done */
175 :
176 : /* Got an interface. Blast all the hosts on it. */
177 :
178 0 : gmpr_destroy_intf_hosts(intf);
179 :
180 : /* Blast all the groups on it. */
181 :
182 0 : gmpr_destroy_intf_groups(intf);
183 :
184 : /* Now blast the interface itself. */
185 :
186 0 : gmpr_destroy_intf(intf);
187 : }
188 125 : }
189 :
190 :
191 : /*
192 : * gmpr_intf_update_gmi
193 : *
194 : * Update the group membership interval
195 : */
196 : static void
197 10248 : gmpr_intf_update_gmi (gmpr_intf *intf)
198 : {
199 10248 : intf->rintf_group_membership_ivl =
200 10248 : (intf->rintf_robustness * intf->rintf_query_ivl) +
201 10248 : intf->rintf_query_resp_ivl;
202 10248 : }
203 :
204 :
205 : /*
206 : * gmpr_intf_update_other_querier_interval
207 : *
208 : * Update the other-querier-present interval.
209 : */
210 : static void
211 10248 : gmpr_intf_update_other_querier_interval (gmpr_intf *intf)
212 : {
213 10248 : intf->rintf_other_querier_ivl =
214 10248 : (intf->rintf_robustness * intf->rintf_query_ivl) +
215 10248 : (intf->rintf_query_resp_ivl / 2);
216 10248 : }
217 :
218 :
219 : /*
220 : * gmpr_intf_update_lmqt
221 : *
222 : * Update the Last Member Query Time. The spec says to make it twice the
223 : * query interval, but this means that the timers will expire just before
224 : * the last query is sent, so we add half of a query interval as well.
225 : */
226 : static void
227 3416 : gmpr_intf_update_lmqt (gmpr_intf *intf)
228 : {
229 3416 : intf->rintf_lmqt = (intf->rintf_lmq_ivl * intf->rintf_lmq_count) +
230 3416 : (intf->rintf_lmq_ivl / 2);
231 3416 : }
232 :
233 :
234 : /*
235 : * gmpr_update_lmq_count
236 : *
237 : * Update the LMQ count variable.
238 : */
239 : static void
240 1708 : gmpr_intf_update_lmq_count (gmpr_intf *intf)
241 : {
242 : /* Use the robustness variable. */
243 :
244 1708 : intf->rintf_lmq_count = intf->rintf_robustness;
245 :
246 : /* Update dependent variables. */
247 :
248 1708 : gmpr_intf_update_lmqt(intf);
249 1708 : }
250 :
251 :
252 : /*
253 : * gmp_intf_update_robustness
254 : *
255 : * Update the robustness variable.
256 : */
257 : void
258 1708 : gmpr_intf_update_robustness (gmpr_intf *intf, uint32_t robustness)
259 : {
260 : /* If the value is zero, use the local value. */
261 :
262 1708 : if (robustness == 0)
263 0 : robustness = intf->rintf_local_robustness;
264 :
265 : /* Save the new value. */
266 :
267 1708 : intf->rintf_robustness = robustness;
268 :
269 : /* Update dependent variables. */
270 :
271 1708 : gmpr_intf_update_lmq_count(intf);
272 1708 : gmpr_intf_update_gmi(intf);
273 1708 : gmpr_intf_update_other_querier_interval(intf);
274 1708 : }
275 :
276 :
277 : /*
278 : * gmpr_intf_update_query_resp_ivl
279 : *
280 : * Update the query response interval variable.
281 : */
282 : static void
283 3416 : gmpr_intf_update_query_resp_ivl (gmpr_intf *intf)
284 : {
285 : uint32_t ivl;
286 : uint32_t ivl_target;
287 : gmpr_instance *instance;
288 :
289 3416 : instance = intf->rintf_instance;
290 :
291 : /* Try the local value first. */
292 :
293 3416 : ivl = intf->rintf_local_query_resp_ivl;
294 :
295 : /*
296 : * The value must be at least a second less than the query
297 : * interval. Adjust it accordingly.
298 : */
299 3416 : if (intf->rintf_query_ivl > MSECS_PER_SEC)
300 3416 : ivl_target = intf->rintf_query_ivl - MSECS_PER_SEC;
301 : else
302 0 : ivl_target = intf->rintf_query_ivl / 2;
303 :
304 3416 : if (ivl > ivl_target)
305 0 : ivl = ivl_target;
306 :
307 : /* It also has to be at least the minimum for the protocol. */
308 :
309 3416 : if (ivl < instance->rinst_min_max_resp)
310 0 : ivl = instance->rinst_min_max_resp;
311 :
312 : /* Save the updated value. */
313 :
314 3416 : intf->rintf_query_resp_ivl = ivl;
315 :
316 : /* Update dependent variables. */
317 :
318 3416 : gmpr_intf_update_gmi(intf);
319 3416 : gmpr_intf_update_other_querier_interval(intf);
320 3416 : }
321 :
322 :
323 : /*
324 : * gmpr_intf_update_query_ivl
325 : *
326 : * Update the query interval variable.
327 : */
328 : void
329 3416 : gmpr_intf_update_query_ivl (gmpr_intf *intf, uint32_t query_ivl)
330 : {
331 : /* If the value is zero, use the default value. */
332 :
333 3416 : if (query_ivl == 0)
334 0 : query_ivl = GMPR_QUERY_IVL_DEFAULT;
335 :
336 : /* Save the new value. */
337 :
338 3416 : intf->rintf_query_ivl = query_ivl;
339 :
340 : /* Update dependent variables. */
341 :
342 3416 : gmpr_intf_update_query_resp_ivl(intf);
343 3416 : gmpr_intf_update_gmi(intf);
344 3416 : gmpr_intf_update_other_querier_interval(intf);
345 3416 : }
346 :
347 :
348 : /*
349 : * gmpr_other_querier_present_expiry
350 : *
351 : * The other-querier-present timer has expired, which means that we're the
352 : * querier again. Send a general query.
353 : */
354 : static void
355 0 : gmpr_other_querier_present_expiry (gmpx_timer *timer, void *context)
356 : {
357 : gmpr_intf *intf;
358 :
359 0 : gmpx_stop_timer(timer);
360 0 : intf = context;
361 :
362 0 : gmpr_update_querier(intf, &intf->rintf_local_addr, TRUE);
363 0 : gmpx_start_timer(intf->rintf_query_timer, 0, 0);
364 0 : }
365 :
366 :
367 : /*
368 : * gmpr_restart_query_timer
369 : *
370 : * Restart (or stop) the query timer for an interface. We stop it if
371 : * general queries are suppressed, unless the gen_query_requested flag
372 : * is set.
373 : */
374 : static void
375 2583 : gmpr_restart_query_timer (gmpr_intf *intf)
376 : {
377 : gmpr_instance *instance;
378 : uint32_t ivl;
379 : int intf_count;
380 : task_thread *thread_ptr;
381 :
382 2583 : instance = intf->rintf_instance;
383 :
384 : /*
385 : * If queries are being suppressed and are not being explicitly
386 : * requested, stop the timer, clean up, and bail.
387 : */
388 2583 : if (intf->rintf_suppress_gen_query && !intf->rintf_gen_query_requested) {
389 0 : gmpx_stop_timer(intf->rintf_query_timer);
390 0 : thread_remove(&intf->rintf_startup_thread);
391 0 : intf->rintf_startup_query_count = 0;
392 0 : intf->rintf_first_query_pending = FALSE;
393 0 : return;
394 : }
395 :
396 : /*
397 : * We need to start the timer. See if we're sending the first packet
398 : * in a startup sequence (which generally happens very quickly.)
399 : */
400 2583 : if (intf->rintf_first_query_pending) {
401 :
402 : /*
403 : * First packet in the sequence. See if there are a lot
404 : * of interfaces all queued up at the same time.
405 : */
406 1708 : intf->rintf_first_query_pending = FALSE;
407 1708 : intf_count = GMPX_MANY_INTFS + 1;
408 5365 : FOR_ALL_CIRCULAR_THREAD_ENTRIES(&instance->rinst_startup_intf_thread,
409 : thread_ptr) {
410 3657 : intf_count--;
411 3657 : if (intf_count == 0)
412 0 : break;
413 : }
414 :
415 : /*
416 : * If there are lots of interfaces starting up
417 : * simultaneously, use the standard query interval. If
418 : * not, use a very short interval to speed things up.
419 : * Jitter is unnecessary, since we are going to smear the
420 : * timers after setting them.
421 : */
422 1708 : if (intf_count == 0) { /* Too many interfaces */
423 0 : ivl = intf->rintf_query_ivl;
424 : } else {
425 1708 : ivl = GMPR_INIT_FIRST_QUERY_IVL;
426 : }
427 :
428 875 : } else if (intf->rintf_startup_query_count) {
429 :
430 : /*
431 : * Not the first packet in the sequence. If we're still part
432 : * of the startup sequence, send this one with a fairly short
433 : * fixed interval.
434 : */
435 832 : ivl = GMPR_INIT_LATER_QUERY_IVL;
436 :
437 : } else {
438 :
439 : /* Not part of a startup sequence. Use the regular interval. */
440 :
441 43 : ivl = intf->rintf_query_ivl;
442 : }
443 :
444 : /* Start the timer. */
445 :
446 2583 : gmpx_start_timer(intf->rintf_query_timer, ivl, 0);
447 : }
448 :
449 :
450 : /*
451 : * gmpr_setup_initial_query_timer_internal
452 : *
453 : * Set up the query timer to send a sequence of queries. The number
454 : * of queries in the sequence is provided by the caller.
455 : *
456 : * If general queries are being suppressed, this will stop the query
457 : * timer unless the gen_query_requested flag is set (meaning that the
458 : * client really really really wants queries sent.)
459 : *
460 : * If there aren't too many interfaces starting up simultaneously,
461 : * start it with a short interval. If there are lots, start it on its
462 : * regular interval to avoid bunching. (The timers will be smeared to
463 : * smooth things out.)
464 : */
465 : static void
466 3484 : gmpr_setup_initial_query_timer_internal (gmpr_intf *intf, uint32_t query_count)
467 : {
468 :
469 : gmpr_instance *instance;
470 :
471 3484 : instance = intf->rintf_instance;
472 :
473 : /*
474 : * Bail if there's already a query sequence being transmitted and
475 : * the number of pending queries is at least as large as that
476 : * being requested, as there's no reason to start another.
477 : */
478 3484 : if (intf->rintf_startup_query_count >= query_count)
479 1776 : return;
480 :
481 : /* Set the startup query count and set the "first_query" flag. */
482 :
483 1708 : intf->rintf_startup_query_count = query_count;
484 1708 : intf->rintf_first_query_pending = TRUE;
485 :
486 : /*
487 : * Put the interface on the thread of startup interfaces. This will
488 : * make us throttle the rate at which we send queries if the interface
489 : * count is high.
490 : */
491 1708 : thread_remove(&intf->rintf_startup_thread); /* Just in case. */
492 1708 : thread_circular_add_top(&instance->rinst_startup_intf_thread,
493 : &intf->rintf_startup_thread);
494 :
495 : /* Restart (or stop) the query timer. */
496 :
497 1708 : gmpr_restart_query_timer(intf);
498 :
499 : /* Schedule a smearing of the query timers. */
500 :
501 1708 : gmpr_accelerate_query_smear(instance);
502 : }
503 :
504 :
505 : /*
506 : * gmpr_setup_initial_query_timer
507 : *
508 : * Set up the query timer to send a sequence of queries. This is done
509 : * when an interface is first created, or when the client requests it,
510 : * or when the query interval is changed (and probably other times as
511 : * well).
512 : */
513 : void
514 3484 : gmpr_setup_initial_query_timer (gmpr_intf *intf)
515 : {
516 3484 : gmpr_setup_initial_query_timer_internal(intf, intf->rintf_robustness);
517 3484 : }
518 :
519 :
520 : /*
521 : * gmpr_trigger_one_query
522 : *
523 : * Trigger the transmission of a single fast query. We simulate a startup
524 : * sequence with a single packet, so it will be throttled appropriately if
525 : * there are lots of interfaces starting up.
526 : */
527 : void
528 0 : gmpr_trigger_one_query (gmpr_intf *intf)
529 : {
530 0 : gmpr_setup_initial_query_timer_internal(intf, 1);
531 0 : }
532 :
533 : /*
534 : * gmpr_create_intf
535 : *
536 : * Create an interface structure and add it to the instance tree.
537 : *
538 : * Returns a pointer to the interface structure, or NULL if no memory.
539 : */
540 : static gmpr_intf *
541 1708 : gmpr_create_intf (gmpr_instance *instance, gmpx_intf_id intf_id)
542 : {
543 : gmpr_intf *intf;
544 :
545 : /* Allocate the block. */
546 :
547 1708 : intf = gmpx_malloc_block(gmpr_intf_tag);
548 1708 : if (!intf)
549 0 : return NULL;
550 :
551 : /* Initialize the input group tree. */
552 :
553 1708 : intf->rintf_group_root =
554 1708 : gmpx_patroot_init(instance->rinst_addrlen,
555 : GMPX_PATRICIA_OFFSET(gmpr_group, rgroup_intf_patnode,
556 : rgroup_addr));
557 1708 : if (!intf->rintf_group_root) { /* No memory */
558 0 : gmpx_free_block(gmpr_intf_tag, intf);
559 0 : return NULL;
560 : }
561 :
562 : /* Initialize the output group tree. */
563 :
564 1708 : intf->rintf_oif_group_root =
565 1708 : gmpx_patroot_init(instance->rinst_addrlen,
566 : GMPX_PATRICIA_OFFSET(gmpr_ogroup,
567 : rogroup_intf_patnode,
568 : rogroup_addr));
569 1708 : if (!intf->rintf_oif_group_root) { /* No memory */
570 0 : gmpx_patroot_destroy(intf->rintf_group_root);
571 0 : gmpx_free_block(gmpr_intf_tag, intf);
572 0 : return NULL;
573 : }
574 :
575 : /* Create the host tree. */
576 :
577 1708 : intf->rintf_host_root =
578 1708 : gmpx_patroot_init(instance->rinst_addrlen,
579 : GMPX_PATRICIA_OFFSET(gmpr_host, rhost_node,
580 : rhost_addr));
581 1708 : if (!intf->rintf_host_root) { /* No memory */
582 0 : gmpx_patroot_destroy(intf->rintf_group_root);
583 0 : gmpx_patroot_destroy(intf->rintf_oif_group_root);
584 0 : gmpx_free_block(gmpr_intf_tag, intf);
585 0 : return NULL;
586 : }
587 :
588 : /* Initialize the transmit queue. */
589 :
590 1708 : thread_new_circular_thread(&intf->rintf_xmit_head);
591 :
592 : /* Put the interface into the instance tree. */
593 :
594 1708 : intf->rintf_id = intf_id;
595 1708 : gmpx_assert(gmpx_patricia_add(instance->rinst_intfs,
596 : &intf->rintf_inst_patnode));
597 :
598 : /* Put the interface into the global tree. */
599 :
600 1708 : gmpx_assert(
601 : gmpx_patricia_add(gmpr_global_intf_tree[instance->rinst_proto],
602 : &intf->rintf_global_patnode));
603 :
604 : /* Initialize a few more things. */
605 :
606 1708 : intf->rintf_instance = instance;
607 1708 : intf->rintf_ver = GMP_VERSION_DEFAULT;
608 1708 : intf->rintf_query_ivl = GMPR_QUERY_IVL_DEFAULT;
609 1708 : intf->rintf_local_query_ivl = GMPR_QUERY_IVL_DEFAULT;
610 1708 : intf->rintf_query_resp_ivl = GMPR_QUERY_RESP_IVL_DEFAULT;
611 1708 : intf->rintf_local_query_resp_ivl = GMPR_QUERY_RESP_IVL_DEFAULT;
612 1708 : intf->rintf_robustness = GMP_ROBUSTNESS_DEFAULT;
613 1708 : intf->rintf_local_robustness = GMP_ROBUSTNESS_DEFAULT;
614 1708 : intf->rintf_lmq_ivl = GMPR_LMQI_DEFAULT;
615 1708 : intf->rintf_lmq_count = GMP_ROBUSTNESS_DEFAULT;
616 1708 : intf->rintf_querier = FALSE;
617 1708 : intf->rintf_querier_enabled = TRUE;
618 :
619 : /* Update the group membership interval. */
620 :
621 1708 : gmpr_intf_update_gmi(intf);
622 :
623 : /* Update the other querier present interval. */
624 :
625 1708 : gmpr_intf_update_other_querier_interval(intf);
626 :
627 : /* Update the Last Member Query Time. */
628 :
629 1708 : gmpr_intf_update_lmqt(intf);
630 :
631 : /* Create the timers. */
632 :
633 1708 : intf->rintf_query_timer =
634 1708 : gmpx_create_grouped_timer(GMP_TIMER_GROUP_GEN_QUERY,
635 : instance->rinst_context,
636 : "GMP router general query",
637 : gmpr_intf_query_timer_expiry, intf);
638 1708 : intf->rintf_other_querier_present =
639 1708 : gmpx_create_timer(instance->rinst_context,
640 : "GMP router other querier present",
641 : gmpr_other_querier_present_expiry, intf);
642 :
643 : /* Initialized to null, will be created if log-interval is configured */
644 :
645 : /* Set up the query timer. */
646 :
647 1708 : gmpr_setup_initial_query_timer(intf);
648 :
649 1708 : return intf;
650 : }
651 :
652 :
653 : /*
654 : * gmpr_attach_intf_internal
655 : *
656 : * Create an interface entry based on the instance and interface ID.
657 : *
658 : * Returns 0 if all OK, -1 if out of memory, or 1 if the interface already
659 : * exists.
660 : */
661 : int
662 1708 : gmpr_attach_intf_internal (gmpr_instance *instance, gmpx_intf_id intf_id)
663 : {
664 : gmpr_intf *intf;
665 :
666 : /* Look up the interface. Bail if it's already there. */
667 :
668 1708 : intf = gmpr_intf_lookup_global(instance->rinst_proto, intf_id);
669 1708 : if (intf)
670 0 : return 1; /* Already exists */
671 :
672 : /* Create a new one. */
673 :
674 1708 : intf = gmpr_create_intf(instance, intf_id);
675 1708 : if (!intf)
676 0 : return -1; /* Out of memory */
677 :
678 1708 : return 0;
679 : }
680 :
681 :
682 : /*
683 : * gmpr_detach_intf_internal
684 : *
685 : * Get rid of an interface. Under normal circumstances, the interface will
686 : * be inactive, but we tolerate unceremonious deletions by cleaning up all
687 : * of the attached state.
688 : *
689 : * Returns 0 if all OK, or 1 if the interface doesn't exist.
690 : */
691 : int
692 1708 : gmpr_detach_intf_internal (gmpr_instance *instance, gmpx_intf_id intf_id)
693 : {
694 : gmpr_intf *intf;
695 :
696 : /* Look up the interface. Bail if it doesn't exist. */
697 :
698 1708 : intf = gmpr_intf_lookup(instance, intf_id);
699 1708 : if (!intf)
700 0 : return 1; /* Doesn't exist */
701 :
702 : /* Blast all of the groups on the interface. */
703 :
704 1708 : gmpr_destroy_intf_groups(intf);
705 :
706 : /* Blast all of the hosts on the interface. */
707 :
708 1708 : gmpr_destroy_intf_hosts(intf);
709 :
710 : /* Should be clean now. Destroy the interface. */
711 :
712 1708 : gmpr_destroy_intf(intf);
713 :
714 1708 : return 0;
715 : }
716 :
717 : /*
718 : * gmpr_intf_set_params
719 : *
720 : * Set parameters for an interface.
721 : *
722 : * Returns 0 if OK, -1 if out of memory, or 1 if the interface doesn't exist.
723 : */
724 : int
725 1708 : gmpr_intf_set_params (gmpr_instance *instance, gmpx_intf_id intf_id,
726 : gmpr_intf_params *params)
727 : {
728 : gmpr_intf *intf;
729 : gmpr_group *group;
730 : boolean version_changed;
731 : gmp_version new_version;
732 : uint32_t old_query_ivl;
733 : uint32_t old_robustness;
734 : boolean send_query;
735 :
736 : /* Get the interface. */
737 :
738 1708 : intf = gmpr_intf_lookup(instance, intf_id);
739 1708 : if (!intf)
740 0 : return 1; /* Doesn't exist. */
741 :
742 : /* Set the parameters. */
743 :
744 1708 : new_version = gmp_translate_version(instance->rinst_proto,
745 1708 : params->gmpr_ifparm_version);
746 1708 : version_changed = (intf->rintf_ver != new_version);
747 1708 : intf->rintf_ver = new_version;
748 :
749 : /* If the version changed, update all group versions. */
750 :
751 1708 : if (version_changed) {
752 :
753 : /* Walk all groups on the interface. */
754 :
755 32 : group = NULL;
756 :
757 : while (TRUE) {
758 32 : group = gmpr_next_intf_group(intf, group);
759 :
760 : /* Bail if done. */
761 :
762 32 : if (!group)
763 32 : break;
764 :
765 : /* Update the compatibility mode. */
766 :
767 0 : gmpr_evaluate_group_version(group);
768 : }
769 : }
770 :
771 : /* Remember if the suppression parameters are changing. */
772 :
773 1708 : send_query = FALSE;
774 1708 : if (intf->rintf_suppress_gen_query !=
775 1708 : params->gmpr_ifparm_suppress_gen_query) {
776 0 : send_query = TRUE;
777 : }
778 :
779 : /* Set the parameters. */
780 :
781 1708 : old_query_ivl = intf->rintf_local_query_ivl;
782 1708 : intf->rintf_local_query_ivl = params->gmpr_ifparm_qivl;
783 1708 : intf->rintf_local_query_resp_ivl = params->gmpr_ifparm_qrivl;
784 1708 : intf->rintf_lmq_ivl = params->gmpr_ifparm_lmqi;
785 1708 : intf->rintf_fast_leaves = params->gmpr_ifparm_fast_leave;
786 1708 : intf->rintf_channel_limit = params->gmpr_ifparm_chan_limit;
787 :
788 1708 : if(intf->rintf_channel_count > intf->rintf_channel_limit)
789 0 : intf->rintf_limit_state = GMPR_ABOVE_LIMIT;
790 : else
791 1708 : intf->rintf_limit_state = GMPR_BELOW_THRESHOLD;
792 :
793 : /* threshold and log-interval can be configured only
794 : when channel_limit is configured */
795 :
796 1708 : if(intf->rintf_channel_limit) {
797 0 : intf->rintf_channel_threshold = params->gmpr_ifparm_chan_threshold;
798 0 : intf->rintf_log_interval = params->gmpr_ifparm_log_interval;
799 0 : if(intf->rintf_channel_count > intf->rintf_channel_threshold)
800 0 : intf->rintf_limit_state = GMPR_ABOVE_THRESHOLD_BELOW_LIMIT;
801 : else
802 0 : intf->rintf_limit_state = GMPR_BELOW_THRESHOLD;
803 : } else {
804 : /* Threshold and log interval does not have a significance without limit */
805 1708 : intf->rintf_channel_threshold = 0 ;
806 1708 : intf->rintf_log_interval = 0;
807 : }
808 :
809 1708 : intf->rintf_passive_receive = params->gmpr_ifparm_passive_receive;
810 1708 : intf->rintf_suppress_gen_query = params->gmpr_ifparm_suppress_gen_query;
811 1708 : intf->rintf_suppress_gs_query = params->gmpr_ifparm_suppress_gs_query;
812 1708 : intf->rintf_local_robustness = params->gmpr_ifparm_robustness;
813 :
814 : /* If we're doing fast leaves, force host tracking. */
815 :
816 1708 : if (intf->rintf_fast_leaves)
817 0 : instance->rinst_host_tracking = TRUE;
818 :
819 : /* Update dependent parameters. */
820 :
821 1708 : old_robustness = intf->rintf_robustness;
822 1708 : gmpr_intf_update_robustness(intf, params->gmpr_ifparm_robustness);
823 1708 : gmpr_intf_update_query_ivl(intf, params->gmpr_ifparm_qivl);
824 :
825 : /* Restart the query timer if the interval changed. */
826 :
827 1708 : if (old_query_ivl != intf->rintf_local_query_ivl)
828 68 : send_query = TRUE;
829 :
830 : /*
831 : * If the channel limit is now less than the number of channels on the
832 : * interface, blow away the interface state and issue a query to rebuild
833 : * it.
834 : */
835 1708 : if (intf->rintf_channel_limit &&
836 0 : intf->rintf_channel_count > intf->rintf_channel_limit) {
837 :
838 : /* Walk all groups on the interface. */
839 0 : intf->rintf_limit_state = GMPR_ABOVE_LIMIT;
840 0 : group = NULL;
841 :
842 : while (TRUE) {
843 0 : group = gmpr_next_intf_group(intf, group);
844 :
845 : /* Bail if done. */
846 :
847 0 : if (!group)
848 0 : break;
849 :
850 : /* Make the group go away. */
851 :
852 0 : gmpr_timeout_group(group);
853 : }
854 0 : send_query = TRUE;
855 1708 : } else if(intf->rintf_channel_limit &&
856 0 : intf->rintf_channel_count > intf->rintf_channel_threshold *
857 0 : intf->rintf_channel_limit/100 ) {
858 0 : intf->rintf_limit_state = GMPR_ABOVE_THRESHOLD_BELOW_LIMIT;
859 : } else {
860 1708 : intf->rintf_limit_state = GMPR_BELOW_THRESHOLD;
861 : }
862 :
863 : /*
864 : * If queries are suppressed, zap the startup query counter,
865 : * since it isn't relevant and may get in the way later.
866 : */
867 1708 : if (intf->rintf_suppress_gen_query)
868 0 : intf->rintf_startup_query_count = 0;
869 :
870 : /*
871 : * Kick the query timer if necessary (which may stop it.)
872 : * If the robustness variable changed, send a whole query sequence
873 : * instead.
874 : */
875 1708 : if (old_robustness != intf->rintf_robustness) {
876 68 : gmpr_setup_initial_query_timer(intf);
877 1640 : } else if (send_query) {
878 0 : gmpr_trigger_one_query(intf);
879 : }
880 :
881 1708 : return 0;
882 : }
883 :
884 :
885 : /*
886 : * gmpr_intf_query_timer_expiry
887 : *
888 : * The interface general query timer has expired.
889 : */
890 : static void
891 875 : gmpr_intf_query_timer_expiry (gmpx_timer *timer GMPX_UNUSED, void *context)
892 : {
893 : gmpr_intf *intf;
894 :
895 875 : intf = context;
896 :
897 : /*
898 : * Set the flag saying that we need to send a query, and kick the
899 : * transmitter.
900 : */
901 875 : intf->rintf_send_gen_query = TRUE;
902 875 : gmpr_kick_xmit(intf);
903 :
904 : /* If we're in an initial query sequence, drop the query count. */
905 :
906 875 : if (intf->rintf_startup_query_count)
907 867 : intf->rintf_startup_query_count--;
908 :
909 : /*
910 : * If the startup query count is zero, clear the "query requested"
911 : * flag, as any requested query sequence is complete. Likewise,
912 : * take the interface off of the startup thread.
913 : */
914 875 : if (!intf->rintf_startup_query_count) {
915 43 : intf->rintf_gen_query_requested = FALSE;
916 43 : thread_remove(&intf->rintf_startup_thread);
917 : }
918 875 : gmpr_restart_query_timer(intf);
919 875 : }
920 :
921 :
922 : /*
923 : * gmpr_kick_xmit
924 : *
925 : * Kick the transmission state machinery for an interface.
926 : */
927 : void
928 2627 : gmpr_kick_xmit (gmpr_intf *intf)
929 : {
930 : /*
931 : * Do it only if we don't have a pending transmission and the
932 : * interface is up.
933 : */
934 :
935 2627 : if (!intf->rintf_xmit_pending && intf->rintf_up) {
936 :
937 : /* Set the flag and kick the I/O. */
938 :
939 2627 : intf->rintf_xmit_pending = TRUE;
940 2627 : gmpp_start_xmit(GMP_ROLE_ROUTER, intf->rintf_instance->rinst_proto,
941 : intf->rintf_id);
942 : }
943 2627 : }
944 :
945 :
946 : /*
947 : * gmpr_update_querier
948 : *
949 : * Update querier status on this interface. If it's changing, post a
950 : * notification if enabled.
951 : *
952 : * The address parameter will be NULL if we're the querier.
953 : */
954 : void
955 1708 : gmpr_update_querier (gmpr_intf *intf, gmp_addr_string *addr, boolean querier)
956 : {
957 : gmpr_instance *instance;
958 : gmpr_client *client;
959 : task_thread *thread_ptr;
960 :
961 1708 : instance = intf->rintf_instance;
962 :
963 : /* Bail if nothing has changed. */
964 :
965 1708 : if (querier == intf->rintf_querier &&
966 0 : (!addr || !memcmp(addr->gmp_addr, intf->rintf_querier_addr.gmp_addr,
967 0 : instance->rinst_addrlen))) {
968 0 : return;
969 : }
970 :
971 : /* Update the status. */
972 :
973 1708 : intf->rintf_querier = querier;
974 1708 : memmove(intf->rintf_querier_addr.gmp_addr, addr->gmp_addr,
975 1708 : instance->rinst_addrlen);
976 :
977 : /*
978 : * If we're becoming querier, update the query interval to the
979 : * configured value.
980 : */
981 1708 : if (querier)
982 1708 : gmpr_intf_update_query_ivl(intf, intf->rintf_local_query_ivl);
983 :
984 : /* Call the callback for any client, if present. */
985 :
986 3416 : FOR_ALL_CIRCULAR_THREAD_ENTRIES(&instance->rinst_client_thread,
987 : thread_ptr) {
988 1708 : client = gmpr_thread_to_client(thread_ptr);
989 1708 : if (client->rclient_cb_context.rctx_querier_cb)
990 1708 : (*client->rclient_cb_context.rctx_querier_cb)(
991 : instance->rinst_context,
992 : intf->rintf_id,
993 : intf->rintf_querier,
994 1708 : intf->rintf_querier_addr.gmp_addr);
995 : }
996 : }
|