Line data Source code
1 : /* $Id: gmpr_group.c 514187 2012-05-06 12:25:25Z ib-builder $
2 : *
3 : * gmpr_group.c - IGMP/MLD Router-Side group handling
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 :
18 :
19 0 : static void gmpr_log_event(gmpr_intf *intf , gmpr_limit_state_t state)
20 : {
21 0 : if(!intf) {
22 0 : return;
23 : }
24 :
25 0 : boolean logged = FALSE;
26 0 : switch(state) {
27 0 : case GMPR_ABOVE_LIMIT:
28 0 : gmpr_post_event(intf->rintf_instance, GMP_GROUP_LIMIT_EXCEED ,
29 : intf->rintf_id,
30 : intf->rintf_channel_limit,
31 : intf->rintf_channel_count );
32 0 : logged = TRUE;
33 0 : break;
34 0 : case GMPR_ABOVE_THRESHOLD_BELOW_LIMIT :
35 0 : gmpr_post_event(intf->rintf_instance, GMP_GROUP_THRESHOLD_EXCEED ,
36 : intf->rintf_id,
37 : intf->rintf_channel_threshold * intf->rintf_channel_limit/100 ,
38 : intf->rintf_channel_count );
39 0 : logged = TRUE;
40 0 : break;
41 0 : case GMPR_BELOW_THRESHOLD :
42 0 : gmpr_post_event(intf->rintf_instance, GMP_GROUP_LIMIT_BELOW,
43 : intf->rintf_id,
44 : intf->rintf_channel_threshold * intf->rintf_channel_limit/100 ,
45 : intf->rintf_channel_count );
46 0 : logged = TRUE;
47 0 : break;
48 0 : default :
49 0 : break;
50 : }
51 :
52 0 : if(logged && intf->rintf_log_interval) {
53 0 : time(&(intf->last_log_time));
54 : }
55 : }
56 :
57 : /* returns 1 if limit is exceeded when interface is null, 0 otherwise */
58 55 : boolean gmpr_check_grp_limit(gmpr_intf *intf, boolean incr)
59 : {
60 :
61 55 : if(!intf)
62 0 : return 1;
63 55 : gmpr_limit_state_t cur_state = GMPR_BELOW_THRESHOLD;
64 :
65 55 : uint32_t actual_ch_count = intf->rintf_channel_count;
66 55 : if(incr) {
67 0 : actual_ch_count = intf->rintf_channel_count + 1;
68 : }
69 :
70 55 : if((intf->rintf_channel_limit) &&
71 0 : (intf->rintf_channel_count >= intf->rintf_channel_limit)) {
72 0 : cur_state = GMPR_ABOVE_LIMIT;
73 0 : if(!intf->rintf_log_interval) {
74 0 : intf->rintf_limit_state = cur_state;
75 0 : gmpr_post_event(intf->rintf_instance, GMP_GROUP_LIMIT_EXCEED ,
76 : intf->rintf_id,
77 : intf->rintf_channel_limit,
78 : intf->rintf_channel_count );
79 : }
80 55 : }else if((intf->rintf_channel_limit) &&
81 0 : (intf->rintf_channel_threshold != 100 ) &&
82 0 : (actual_ch_count > ((intf->rintf_channel_threshold *
83 0 : intf->rintf_channel_limit)/100) )) {
84 0 : cur_state = GMPR_ABOVE_THRESHOLD_BELOW_LIMIT;
85 0 : if(!intf->rintf_log_interval) {
86 0 : intf->rintf_limit_state = cur_state;
87 0 : gmpr_post_event(intf->rintf_instance, GMP_GROUP_THRESHOLD_EXCEED ,
88 : intf->rintf_id,
89 : intf->rintf_channel_threshold * intf->rintf_channel_limit/100 ,
90 : actual_ch_count );
91 : }
92 55 : }else if (intf->rintf_channel_limit) {
93 0 : cur_state = GMPR_BELOW_THRESHOLD;
94 :
95 0 : if(!intf->rintf_log_interval &&
96 0 : (intf->rintf_limit_state != cur_state)) {
97 0 : intf->rintf_limit_state = cur_state;
98 0 : gmpr_post_event(intf->rintf_instance, GMP_GROUP_LIMIT_BELOW ,
99 : intf->rintf_id,
100 : intf->rintf_channel_threshold * intf->rintf_channel_limit/100 ,
101 : intf->rintf_channel_count );
102 : }
103 : }
104 :
105 55 : if(intf->rintf_log_interval) {
106 : /* If state different than previous state, a event to be logged has happened
107 : for the first time. Any first time state changes are to be logged */
108 :
109 0 : if(intf->rintf_limit_state != cur_state) {
110 0 : gmpr_log_event(intf, cur_state);
111 0 : intf->rintf_limit_state = cur_state;
112 : } else {
113 : /*
114 : * state is same as prev state, this is a repeat event
115 : * log messages if time elasped since since last log is
116 : * is greater than log_interval. BELOW_THRESHOLD repeat
117 : * events are not logged
118 : */
119 :
120 0 : if(cur_state) {
121 : time_t cur_time;
122 0 : time(&cur_time);
123 :
124 : /* There should be a last log time since this is a repeat event.
125 : * The last event should have been logged and hence last log
126 : * time should have been set to non zero value
127 : */
128 :
129 0 : double time_elasped = difftime(cur_time, intf->last_log_time);
130 0 : if (time_elasped > intf->rintf_log_interval) {
131 0 : gmpr_log_event(intf, cur_state);
132 : }
133 : }
134 : }
135 : }
136 :
137 55 : return ( cur_state == GMPR_ABOVE_LIMIT ? TRUE: FALSE);
138 : }
139 :
140 : /*
141 : * gmpr_next_intf_group
142 : *
143 : * Returns the next input group on an interface, given the previous one.
144 : * If the previous pointer is NULL, returns the first input group on the
145 : * interface.
146 : *
147 : * Returns a pointer to the group, or NULL if there are no more groups.
148 : */
149 : gmpr_group *
150 3480 : gmpr_next_intf_group (gmpr_intf *intf, gmpr_group *prev_group)
151 : {
152 : gmpr_group *next_group;
153 : gmpx_patnode *node;
154 :
155 : /* Look up the node. */
156 :
157 3480 : if (prev_group) {
158 16 : node = gmpx_patricia_get_next(intf->rintf_group_root,
159 : &prev_group->rgroup_intf_patnode);
160 : } else {
161 3464 : node = gmpx_patricia_lookup_least(intf->rintf_group_root);
162 : }
163 :
164 : /* Convert to a group pointer. The pointer may be NULL. */
165 :
166 3480 : next_group = gmpr_intf_patnode_to_group(node);
167 :
168 3480 : return next_group;
169 : }
170 :
171 :
172 : /*
173 : * gmpr_next_oif_group
174 : *
175 : * Returns the next output group on an interface, given the previous one.
176 : * If the previous pointer is NULL, returns the first output group on the
177 : * interface.
178 : *
179 : * Returns a pointer to the group, or NULL if there are no more groups.
180 : */
181 : gmpr_ogroup *
182 5159 : gmpr_next_oif_group (gmpr_intf *oif, gmpr_ogroup *prev_group)
183 : {
184 : gmpr_ogroup *next_group;
185 : gmpx_patnode *node;
186 :
187 : /* Look up the node. */
188 :
189 5159 : if (prev_group) {
190 16 : node = gmpx_patricia_get_next(oif->rintf_oif_group_root,
191 : &prev_group->rogroup_intf_patnode);
192 : } else {
193 5143 : node = gmpx_patricia_lookup_least(oif->rintf_oif_group_root);
194 : }
195 :
196 : /* Convert to a group pointer. The pointer may be NULL. */
197 :
198 5159 : next_group = gmpr_oif_patnode_to_group(node);
199 :
200 5159 : return next_group;
201 : }
202 :
203 :
204 : /*
205 : * gmpr_delink_oif_group
206 : *
207 : * Delink an input group from its OIF group, if any.
208 : */
209 : static void
210 91 : gmpr_delink_oif_group (gmpr_group *group)
211 : {
212 91 : if (group->rgroup_oif_group) {
213 22 : thread_remove(&group->rgroup_oif_thread);
214 22 : group->rgroup_oif_group = NULL;
215 : }
216 91 : }
217 :
218 :
219 : /*
220 : * gmpr_link_oif_group
221 : *
222 : * Link an input group to an OIF group.
223 : */
224 : static void
225 22 : gmpr_link_oif_group (gmpr_group *group, gmpr_ogroup *ogroup)
226 : {
227 : /* Better not be linked yet. */
228 :
229 22 : gmpx_assert(!group->rgroup_oif_group);
230 :
231 : /* Add it to the thread and set the pointer. */
232 :
233 22 : thread_circular_add_bottom(&ogroup->rogroup_oif_head,
234 : &group->rgroup_oif_thread);
235 22 : group->rgroup_oif_group = ogroup;
236 22 : }
237 :
238 :
239 : /*
240 : * gmpr_delink_oif_source
241 : *
242 : * Delink an input source from its OIF source, if any.
243 : *
244 : * Returns a pointer to the previously linked output group source, or NULL.
245 : */
246 : static gmpr_ogroup_addr_entry *
247 99 : gmpr_delink_oif_source (gmpr_group_addr_entry *group_addr)
248 : {
249 : gmpr_ogroup_addr_entry *ogroup_addr;
250 :
251 99 : ogroup_addr = group_addr->rgroup_addr_oif_addr;
252 99 : if (ogroup_addr) {
253 33 : thread_remove(&group_addr->rgroup_addr_oif_thread);
254 33 : group_addr->rgroup_addr_oif_addr = NULL;
255 :
256 : /*
257 : * If this was the last entry on the OIF source, flag it for
258 : * notification.
259 : */
260 33 : if (thread_circular_thread_empty(&ogroup_addr->rogroup_addr_oif_head))
261 33 : ogroup_addr->rogroup_notify = TRUE;
262 : }
263 :
264 99 : return ogroup_addr;
265 : }
266 :
267 :
268 : /*
269 : * gmpr_link_oif_source
270 : *
271 : * Link an input source to an OIF source.
272 : */
273 : static void
274 33 : gmpr_link_oif_source (gmpr_group_addr_entry *group_addr,
275 : gmpr_ogroup_addr_entry *ogroup_addr)
276 : {
277 : boolean was_empty;
278 :
279 : /* Better not be linked yet. */
280 :
281 33 : gmpx_assert(!group_addr->rgroup_addr_oif_addr);
282 :
283 : /* Add it to the thread and set the pointer. */
284 :
285 : was_empty =
286 33 : thread_circular_thread_empty(&ogroup_addr->rogroup_addr_oif_head);
287 33 : thread_circular_add_bottom(&ogroup_addr->rogroup_addr_oif_head,
288 : &group_addr->rgroup_addr_oif_thread);
289 33 : group_addr->rgroup_addr_oif_addr = ogroup_addr;
290 :
291 : /*
292 : * If we just added the first component source, flag the output source
293 : * for notification.
294 : */
295 33 : if (was_empty)
296 33 : ogroup_addr->rogroup_notify = TRUE;
297 33 : }
298 :
299 :
300 : /*
301 : * gmpr_alloc_ogroup_addr_entry
302 : *
303 : * Allocate an address entry for insertion into the group running-timer or
304 : * stopped-timer list.
305 : *
306 : * Returns a pointer to the embedded address list entry, or NULL if
307 : * out of memory.
308 : */
309 : static gmp_addr_list_entry *
310 33 : gmpr_alloc_ogroup_addr_entry (void *context)
311 : {
312 : gmpr_ogroup_addr_entry *group_addr_entry;
313 : gmpr_ogroup *group;
314 :
315 33 : group = context;
316 :
317 : /* Allocate a block. */
318 :
319 33 : group_addr_entry = gmpx_malloc_block(gmpr_ogroup_addr_entry_tag);
320 33 : if (!group_addr_entry)
321 0 : return NULL;
322 :
323 : /* Initialize it. */
324 :
325 33 : group_addr_entry->rogroup_addr_group = group;
326 33 : gmpr_set_notification_type(group_addr_entry->rogroup_addr_client_thread,
327 : GMPR_NOTIFY_SOURCE);
328 33 : thread_new_circular_thread(&group_addr_entry->rogroup_addr_oif_head);
329 :
330 33 : return &group_addr_entry->rogroup_addr_entry;
331 : }
332 :
333 :
334 : /*
335 : * gmpr_free_ogroup_addr_entry
336 : *
337 : * Callback to free an output group address entry.
338 : *
339 : * Any associated input group entries are delinked; it is up to that side of
340 : * the code to ensure they get cleaned up.
341 : */
342 : static void
343 33 : gmpr_free_ogroup_addr_entry (gmp_addr_list_entry *addr_entry)
344 : {
345 : gmpr_ogroup_addr_entry *ogroup_addr_entry;
346 : gmpr_group_addr_entry *group_addr_entry;
347 : task_thread *thread_ptr;
348 :
349 33 : ogroup_addr_entry = gmpr_addr_entry_to_ogroup_entry(addr_entry);
350 :
351 : /* Ensure that the entry isn't on any notification threads. */
352 :
353 33 : gmpr_flush_notifications(ogroup_addr_entry->rogroup_addr_client_thread,
354 : TRUE);
355 :
356 : /* Delink any input group address entries. */
357 :
358 33 : thread_ptr = NULL;
359 : while (TRUE) {
360 39 : thread_ptr = thread_circular_top(
361 : &ogroup_addr_entry->rogroup_addr_oif_head);
362 : group_addr_entry =
363 39 : gmpr_oif_thread_to_group_addr_entry(thread_ptr);
364 39 : if (!group_addr_entry)
365 33 : break;
366 6 : gmpr_delink_oif_source(group_addr_entry);
367 : }
368 :
369 : /* Free the block. */
370 :
371 33 : gmpx_free_block(gmpr_ogroup_addr_entry_tag, ogroup_addr_entry);
372 33 : }
373 :
374 :
375 : /*
376 : * gmpr_alloc_group_addr_entry
377 : *
378 : * Allocate an address entry for insertion into the input group
379 : * running-timer or stopped-timer list.
380 : *
381 : * Returns a pointer to the embedded address list entry, or NULL if
382 : * out of memory or the channel limit was reached.
383 : */
384 : static gmp_addr_list_entry *
385 33 : gmpr_alloc_group_addr_entry (void *context)
386 : {
387 : gmpr_group_addr_entry *group_addr_entry;
388 : gmpr_group *group;
389 : gmpr_intf *intf;
390 : gmpr_instance *instance;
391 :
392 33 : group = context;
393 33 : intf = group->rgroup_intf;
394 33 : instance = intf->rintf_instance;
395 :
396 : /*
397 : * If this was not the first source on the group, check the
398 : * channel limit. We don't count the first entry because the bare
399 : * group still counts as a channel.
400 : */
401 33 : if (!gmpr_all_group_lists_empty(group)) {
402 18 : if (intf->rintf_channel_limit) {
403 0 : if( gmpr_check_grp_limit(intf, TRUE)) {
404 0 : intf->rintf_chan_limit_drops++;
405 0 : return NULL;
406 : }
407 : }
408 18 : intf->rintf_channel_count++;
409 : }
410 :
411 : /* Allocate a block. */
412 :
413 33 : group_addr_entry = gmpx_malloc_block(gmpr_group_addr_entry_tag);
414 33 : if (!group_addr_entry)
415 0 : return NULL;
416 :
417 : /* Initialize it. */
418 :
419 33 : group_addr_entry->rgroup_addr_group = group;
420 33 : thread_new_circular_thread(&group_addr_entry->rgroup_host_addr_head);
421 :
422 : /* Allocate the source timer. */
423 :
424 33 : group_addr_entry->rgroup_addr_timer =
425 33 : gmpx_create_timer(instance->rinst_context, "GMP router source timer",
426 : gmpr_source_timer_expiry, group_addr_entry);
427 :
428 33 : return &group_addr_entry->rgroup_addr_entry;
429 : }
430 :
431 :
432 : /*
433 : * gmpr_free_group_addr_entry
434 : *
435 : * Callback to free an input group address entry.
436 : *
437 : * Frees the timer as well as the entry.
438 : *
439 : * Any associated host group entries are delinked; it is up to that side of
440 : * the code to ensure they get cleaned up.
441 : */
442 : static void
443 33 : gmpr_free_group_addr_entry (gmp_addr_list_entry *addr_entry)
444 : {
445 : gmpr_group_addr_entry *group_addr_entry;
446 : gmpr_host_group_addr *host_group_addr_entry;
447 : gmpr_group *group;
448 : gmpr_intf *intf;
449 : task_thread *thread_ptr;
450 :
451 33 : group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
452 :
453 : /* Delink any host address entries. */
454 :
455 33 : thread_ptr = NULL;
456 : while (TRUE) {
457 33 : thread_ptr = thread_circular_dequeue_top(
458 : &group_addr_entry->rgroup_host_addr_head);
459 : host_group_addr_entry =
460 33 : gmpr_thread_to_host_group_addr_entry(thread_ptr);
461 33 : if (!host_group_addr_entry)
462 33 : break;
463 :
464 0 : host_group_addr_entry->rhga_source = NULL;
465 : }
466 :
467 : /* Delink from the output address entry, if any. */
468 :
469 33 : gmpr_update_source_oif(group_addr_entry, OIF_DELETE);
470 :
471 : /*
472 : * If this was not the last source on the group, drop the channel
473 : * count. We don't count the last entry because the bare group
474 : * still counts as a channel.
475 : */
476 33 : group = group_addr_entry->rgroup_addr_group;
477 33 : intf = group->rgroup_intf;
478 :
479 33 : if (!gmpr_all_group_lists_empty(group)) {
480 18 : gmpx_assert(intf->rintf_channel_count > 0);
481 18 : intf->rintf_channel_count--;
482 18 : gmpr_check_grp_limit(intf, FALSE);
483 : }
484 :
485 : /* Destroy the timer and free the block. */
486 :
487 33 : gmpx_destroy_timer(group_addr_entry->rgroup_addr_timer);
488 33 : gmpx_free_block(gmpr_group_addr_entry_tag, group_addr_entry);
489 33 : }
490 :
491 :
492 : /*
493 : * gmpr_set_notification_type
494 : *
495 : * Initialize all notifications in an array with the notification type.
496 : */
497 : void
498 202 : gmpr_set_notification_type (gmpr_notify_block *notify_block,
499 : gmpr_notification_type notify_type)
500 : {
501 : ordinal_t client_ord;
502 606 : for (client_ord = 0; client_ord < GMPX_MAX_RTR_CLIENTS; client_ord++) {
503 404 : notify_block->gmpr_notify_type = notify_type;
504 404 : notify_block++;
505 : }
506 202 : }
507 :
508 :
509 : /*
510 : * gmpr_ogroup_lookup
511 : *
512 : * Look up an output group entry given the group address and the
513 : * output interface.
514 : *
515 : * Returns a pointer to the group entry, or NULL if it's not there.
516 : */
517 : gmpr_ogroup *
518 55 : gmpr_ogroup_lookup (gmpr_intf *intf, const uint8_t *group_addr)
519 : {
520 : gmpr_ogroup *group;
521 : patnode *node;
522 :
523 : /* Bail if there's no interface. */
524 :
525 55 : if (!intf)
526 0 : return NULL;
527 :
528 : /* Look up the entry. */
529 :
530 55 : node = gmpx_patricia_lookup(intf->rintf_oif_group_root, group_addr);
531 55 : group = gmpr_oif_patnode_to_group(node);
532 :
533 55 : return group;
534 : }
535 :
536 :
537 : /*
538 : * gmpr_group_lookup
539 : *
540 : * Look up an input group entry given the group address and the interface.
541 : *
542 : * Returns a pointer to the group entry, or NULL if it's not there.
543 : */
544 : gmpr_group *
545 140 : gmpr_group_lookup (gmpr_intf *intf, const uint8_t *group_addr)
546 : {
547 : gmpr_group *group;
548 : patnode *node;
549 :
550 : /* Look up the entry. */
551 :
552 140 : node = gmpx_patricia_lookup(intf->rintf_group_root, group_addr);
553 140 : group = gmpr_intf_patnode_to_group(node);
554 :
555 140 : return group;
556 : }
557 :
558 :
559 : /*
560 : * gmpr_group_notifications_active
561 : *
562 : * Returns TRUE if there are any active notifications on this group, or
563 : * FALSE if not.
564 : */
565 : static boolean
566 134 : gmpr_group_notifications_active (gmpr_ogroup *group)
567 : {
568 134 : return gmpr_notifications_active(group->rogroup_client_thread);
569 : }
570 :
571 :
572 : /*
573 : * gmpr_group_version
574 : *
575 : * Returns the GMP version to be used for this group. The version is
576 : * the lowest of that based on the versioning timers and the interface
577 : * version.
578 : *
579 : * If no group is passed, the interface version is returned.
580 : */
581 : gmp_version
582 314 : gmpr_group_version (gmpr_intf *intf, gmpr_group *group)
583 : {
584 : gmp_version version;
585 :
586 : /* Figure it out based on timers if a group is present. */
587 :
588 314 : version = GMP_VERSION_SOURCES;
589 314 : if (group) {
590 272 : if (gmpx_timer_running(group->rgroup_basic_host_present)) {
591 1 : version = GMP_VERSION_BASIC;
592 271 : } else if (gmpx_timer_running(group->rgroup_leaves_host_present)) {
593 129 : version = GMP_VERSION_LEAVES;
594 : }
595 : }
596 :
597 : /* Now cap it based on the configured interface version. */
598 :
599 314 : if (intf->rintf_ver < version)
600 50 : version = intf->rintf_ver;
601 :
602 314 : return version;
603 : }
604 :
605 :
606 : /*
607 : * gmpr_evaluate_group_version
608 : *
609 : * Evaluate the version compatibility mode for a group. We choose it based
610 : * on the status of the versioning timers.
611 : */
612 : void
613 174 : gmpr_evaluate_group_version (gmpr_group *group)
614 : {
615 : gmpr_intf *intf;
616 :
617 174 : intf = group->rgroup_intf;
618 :
619 : /* Let the common code do the heavy lifting. */
620 :
621 174 : group->rgroup_compatibility_mode = gmpr_group_version(intf, group);
622 174 : }
623 :
624 :
625 : /*
626 : * gmpr_group_version_timer_expiry
627 : *
628 : * A version timer (host present) has expired. We reevaluate the group
629 : * version.
630 : */
631 : static void
632 2 : gmpr_group_version_timer_expiry (gmpx_timer *timer, void *context)
633 : {
634 : gmpr_group *group;
635 :
636 2 : group = context;
637 :
638 : /* Stop the timer. */
639 :
640 2 : gmpx_stop_timer(timer);
641 :
642 : /* Reevaluate the group version. */
643 :
644 2 : gmpr_evaluate_group_version(group);
645 2 : }
646 :
647 :
648 : /*
649 : * gmpr_ogroup_create
650 : *
651 : * Create a new output group entry, given the group and interface, and
652 : * link it in.
653 : *
654 : * Returns a pointer to the group entry, or NULL if no memory or the
655 : * output interface is NULL.
656 : */
657 : static gmpr_ogroup *
658 37 : gmpr_ogroup_create (gmpr_intf *intf, gmp_addr_string *group_addr)
659 : {
660 : gmpr_ogroup *ogroup;
661 : gmpr_instance *instance;
662 :
663 : /* Bail if no output interface. */
664 :
665 37 : if (!intf)
666 0 : return NULL;
667 :
668 37 : instance = intf->rintf_instance;
669 :
670 : /* Allocate the block. */
671 :
672 37 : ogroup = gmpx_malloc_block(gmpr_ogroup_tag);
673 37 : if (!ogroup)
674 0 : return NULL; /* No memory */
675 :
676 : /* Initialize it. */
677 :
678 37 : ogroup->rogroup_intf = intf;
679 37 : memmove(ogroup->rogroup_addr.gmp_addr, group_addr->gmp_addr, instance->rinst_addrlen);
680 37 : ogroup->rogroup_filter_mode = GMP_FILTER_MODE_INCLUDE;
681 37 : thread_new_circular_thread(&ogroup->rogroup_oif_head);
682 :
683 37 : gmp_addr_list_init(&ogroup->rogroup_src_addr_deleted,
684 : &instance->rinst_addr_cat, gmpr_alloc_ogroup_addr_entry,
685 : gmpr_free_ogroup_addr_entry, ogroup);
686 37 : gmp_addr_list_init(&ogroup->rogroup_incl_src_addr,
687 : &instance->rinst_addr_cat, gmpr_alloc_ogroup_addr_entry,
688 : gmpr_free_ogroup_addr_entry, ogroup);
689 37 : gmp_addr_list_init(&ogroup->rogroup_excl_src_addr,
690 : &instance->rinst_addr_cat, gmpr_alloc_ogroup_addr_entry,
691 : gmpr_free_ogroup_addr_entry, ogroup);
692 :
693 37 : gmpr_set_notification_type(ogroup->rogroup_client_thread,
694 : GMPR_NOTIFY_GROUP);
695 :
696 : /* Link it in. */
697 :
698 37 : gmpx_assert(gmpx_patricia_add(intf->rintf_oif_group_root,
699 : &ogroup->rogroup_intf_patnode));
700 37 : gmpr_link_global_group(ogroup);
701 37 : intf->rintf_oif_group_count++;
702 :
703 : /* Notify the clients. */
704 :
705 37 : gmpr_group_notify_clients(ogroup);
706 :
707 37 : gmpr_trace(instance, GMPR_TRACE_GROUP, "Created OIF group %a, intf %i",
708 : ogroup->rogroup_addr.gmp_addr, intf->rintf_id);
709 :
710 37 : return ogroup;
711 : }
712 :
713 :
714 : /*
715 : * gmpr_group_create
716 : *
717 : * Create a new group entry, given the group and interface, and link it in.
718 : *
719 : * Returns a pointer to the group entry, or NULL if no memory or we've hit
720 : * the channel limit.
721 : */
722 : gmpr_group *
723 37 : gmpr_group_create (gmpr_intf *intf, const gmp_addr_string *group_addr)
724 : {
725 : gmpr_group *group;
726 : gmpr_instance *instance;
727 :
728 37 : instance = intf->rintf_instance;
729 :
730 : /* Bail if we've hit the channel limit. */
731 :
732 37 : if (intf->rintf_channel_limit) {
733 0 : if( gmpr_check_grp_limit(intf, TRUE)) {
734 0 : intf->rintf_chan_limit_drops++;
735 0 : return NULL;
736 : }
737 : }
738 :
739 : /* Allocate the block. */
740 :
741 37 : group = gmpx_malloc_block(gmpr_group_tag);
742 37 : if (!group)
743 0 : return NULL; /* No memory */
744 :
745 : /* Initialize it. */
746 :
747 37 : group->rgroup_intf = intf;
748 37 : intf->rintf_channel_count++;
749 37 : memmove(group->rgroup_addr.gmp_addr, group_addr->gmp_addr, instance->rinst_addrlen);
750 37 : group->rgroup_filter_mode = GMP_FILTER_MODE_INCLUDE;
751 37 : group->rgroup_compatibility_mode = GMP_VERSION_SOURCES;
752 37 : thread_new_circular_thread(&group->rgroup_host_group_head);
753 :
754 37 : gmp_addr_list_init(&group->rgroup_src_addr_running,
755 : &instance->rinst_addr_cat, gmpr_alloc_group_addr_entry,
756 : gmpr_free_group_addr_entry, group);
757 37 : gmp_addr_list_init(&group->rgroup_src_addr_stopped,
758 : &instance->rinst_addr_cat, gmpr_alloc_group_addr_entry,
759 : gmpr_free_group_addr_entry, group);
760 37 : gmp_addr_list_init(&group->rgroup_query_lo_timers,
761 : &instance->rinst_addr_cat,
762 : gmp_alloc_generic_addr_list_entry,
763 : gmp_free_generic_addr_list_entry, NULL);
764 37 : gmp_addr_list_init(&group->rgroup_query_hi_timers,
765 : &instance->rinst_addr_cat,
766 : gmp_alloc_generic_addr_list_entry,
767 : gmp_free_generic_addr_list_entry, NULL);
768 :
769 37 : group->rgroup_group_timer =
770 37 : gmpx_create_timer(instance->rinst_context, "GMP router group timer",
771 : gmpr_group_timer_expiry, group);
772 37 : group->rgroup_query_timer =
773 37 : gmpx_create_timer(instance->rinst_context, "GMP router group query",
774 : gmpr_group_query_timer_expiry, group);
775 37 : group->rgroup_gss_query_timer =
776 37 : gmpx_create_timer(instance->rinst_context, "GMP router GSS query",
777 : gmpr_gss_query_timer_expiry, group);
778 37 : group->rgroup_basic_host_present =
779 37 : gmpx_create_timer(instance->rinst_context,
780 : "GMP router group basic host",
781 : gmpr_group_version_timer_expiry, group);
782 37 : group->rgroup_leaves_host_present =
783 37 : gmpx_create_timer(instance->rinst_context,
784 : "GMP router group leave host",
785 : gmpr_group_version_timer_expiry, group);
786 :
787 : /* Set the group compatibility mode. */
788 :
789 37 : gmpr_evaluate_group_version(group);
790 :
791 : /* Link it in. */
792 :
793 37 : gmpx_assert(gmpx_patricia_add(intf->rintf_group_root,
794 : &group->rgroup_intf_patnode));
795 37 : intf->rintf_group_count++;
796 :
797 37 : gmpr_trace(instance, GMPR_TRACE_GROUP, "Created group %a, intf %i",
798 : group->rgroup_addr.gmp_addr, intf->rintf_id);
799 :
800 37 : return group;
801 : }
802 :
803 :
804 : /*
805 : * gmpr_destroy_ogroup
806 : *
807 : * Destroy an output group entry. Delinks it and frees memory.
808 : */
809 : static void
810 37 : gmpr_destroy_ogroup (gmpr_ogroup *ogroup)
811 : {
812 : gmpr_group *group;
813 : gmpr_intf *intf;
814 : task_thread *thread_ptr;
815 : gmpr_instance *instance;
816 :
817 37 : intf = ogroup->rogroup_intf;
818 37 : instance = intf->rintf_instance;
819 :
820 37 : gmpr_trace(instance, GMPR_TRACE_GROUP, "Deleted output group %a, intf %i",
821 : ogroup->rogroup_addr.gmp_addr, intf->rintf_id);
822 :
823 : /*
824 : * Pull the group off of any client notification thread. Normally this
825 : * should be empty, but if things are shutting down there may be
826 : * dangling notifications.
827 : */
828 37 : gmpr_flush_notifications(ogroup->rogroup_client_thread, TRUE);
829 :
830 : /* Walk the input group thread and delink them all. */
831 :
832 : while (TRUE) {
833 48 : thread_ptr = thread_circular_top(&ogroup->rogroup_oif_head);
834 48 : group = gmpr_oif_thread_to_group(thread_ptr);
835 48 : if (!group)
836 37 : break;
837 11 : gmpr_delink_oif_group(group);
838 : }
839 :
840 : /*
841 : * Flush the address lists. They should normally be empty, but may
842 : * not be if we're shutting down unceremoniously.
843 : */
844 37 : gmp_addr_list_clean(&ogroup->rogroup_incl_src_addr);
845 37 : gmp_addr_list_clean(&ogroup->rogroup_excl_src_addr);
846 37 : gmp_addr_list_clean(&ogroup->rogroup_src_addr_deleted);
847 :
848 : /* Remove ourselves from the interface tree. */
849 :
850 37 : gmpx_assert(gmpx_patricia_delete(intf->rintf_oif_group_root,
851 : &ogroup->rogroup_intf_patnode));
852 37 : gmpx_assert(intf->rintf_oif_group_count > 0);
853 37 : intf->rintf_oif_group_count--;
854 :
855 : /* Delink from the global tree. */
856 :
857 37 : gmpr_delink_global_group(ogroup);
858 :
859 : /* Free the block. */
860 :
861 37 : gmpx_free_block(gmpr_ogroup_tag, ogroup);
862 37 : }
863 :
864 :
865 : /*
866 : * gmpr_destroy_group
867 : *
868 : * Destroy a group entry. Delinks it and frees memory.
869 : */
870 : static void
871 37 : gmpr_destroy_group (gmpr_group *group)
872 : {
873 : gmpr_host_group *host_group;
874 : gmpr_intf *intf;
875 : task_thread *thread_ptr;
876 : gmpr_instance *instance;
877 :
878 37 : intf = group->rgroup_intf;
879 37 : instance = intf->rintf_instance;
880 :
881 37 : gmpr_trace(instance, GMPR_TRACE_GROUP, "Deleted group %a, intf %i",
882 : group->rgroup_addr.gmp_addr, intf->rintf_id);
883 :
884 : /* Walk the host group thread and delink them all. */
885 :
886 : while (TRUE) {
887 59 : thread_ptr = thread_circular_top(&group->rgroup_host_group_head);
888 59 : host_group = gmpr_thread_to_host_group(thread_ptr);
889 59 : if (!host_group)
890 37 : break;
891 22 : host_group->rhgroup_group = NULL;
892 22 : thread_remove(thread_ptr);
893 : }
894 :
895 : /* Delink from any output group. */
896 :
897 37 : gmpr_update_group_oif(group, OIF_DELETE);
898 :
899 : /*
900 : * Flush the address lists. They should normally be empty, but may
901 : * not be if we're shutting down unceremoniously.
902 : */
903 37 : gmp_addr_list_clean(&group->rgroup_src_addr_running);
904 37 : gmp_addr_list_clean(&group->rgroup_src_addr_stopped);
905 37 : gmp_addr_list_clean(&group->rgroup_query_lo_timers);
906 37 : gmp_addr_list_clean(&group->rgroup_query_hi_timers);
907 :
908 : /* Destroy the timers. */
909 :
910 37 : gmpx_destroy_timer(group->rgroup_query_timer);
911 37 : gmpx_destroy_timer(group->rgroup_group_timer);
912 37 : gmpx_destroy_timer(group->rgroup_gss_query_timer);
913 37 : gmpx_destroy_timer(group->rgroup_basic_host_present);
914 37 : gmpx_destroy_timer(group->rgroup_leaves_host_present);
915 :
916 : /* Delink from the interface transmit thread. */
917 :
918 37 : thread_remove(&group->rgroup_xmit_thread);
919 :
920 : /* Remove ourselves from the interface tree. */
921 :
922 37 : gmpx_assert(gmpx_patricia_delete(intf->rintf_group_root,
923 : &group->rgroup_intf_patnode));
924 37 : gmpx_assert(intf->rintf_group_count > 0);
925 37 : intf->rintf_group_count--;
926 37 : gmpx_assert(intf->rintf_channel_count > 0);
927 37 : intf->rintf_channel_count--;
928 37 : gmpr_check_grp_limit(intf, FALSE);
929 :
930 : /* Free the block. */
931 :
932 37 : gmpx_free_block(gmpr_group_tag, group);
933 37 : }
934 :
935 :
936 : /*
937 : * gmpr_destroy_intf_groups
938 : *
939 : * Unceremoniously destroy all groups on an interface.
940 : */
941 : void
942 1724 : gmpr_destroy_intf_groups (gmpr_intf *intf)
943 : {
944 : gmpr_group *group;
945 : gmpr_ogroup *ogroup;
946 :
947 : /* Walk all output groups on the interface. */
948 :
949 : while (TRUE) {
950 1724 : ogroup = gmpr_next_oif_group(intf, NULL);
951 1724 : if (!ogroup)
952 1708 : break;
953 :
954 : /* Destroy the group. */
955 :
956 16 : gmpr_destroy_ogroup(ogroup);
957 : }
958 :
959 : /* Walk all input groups on the interface. */
960 :
961 : while (TRUE) {
962 1724 : group = gmpr_next_intf_group(intf, NULL);
963 1724 : if (!group)
964 1708 : break;
965 :
966 : /* Destroy the group. */
967 :
968 16 : gmpr_destroy_group(group);
969 : }
970 1708 : }
971 :
972 :
973 : /*
974 : * gmpr_enqueue_group_xmit
975 : *
976 : * Enqueue a group entry for transmission of a Report message on an
977 : * interface.
978 : *
979 : * All timers and linkages are expected to be set.
980 : */
981 : void
982 44 : gmpr_enqueue_group_xmit (gmpr_group *group)
983 : {
984 : /* Enqueue it if it's not already on the queue. */
985 :
986 44 : if (!thread_node_on_thread(&group->rgroup_xmit_thread)) {
987 44 : thread_circular_add_bottom(&group->rgroup_intf->rintf_xmit_head,
988 : &group->rgroup_xmit_thread);
989 : }
990 44 : }
991 :
992 :
993 : /*
994 : * gmpr_first_group_xmit
995 : *
996 : * Get the first group entry on an interface transmit list.
997 : *
998 : * Returns a pointer to the group, or NULL if the list is empty.
999 : */
1000 : gmpr_group *
1001 2715 : gmpr_first_group_xmit (gmpr_intf *intf)
1002 : {
1003 : task_thread *thread_ptr;
1004 : gmpr_group *group;
1005 :
1006 2715 : thread_ptr = thread_circular_top(&intf->rintf_xmit_head);
1007 2715 : group = gmpr_xmit_thread_to_group(thread_ptr);
1008 :
1009 2715 : return group;
1010 : }
1011 :
1012 :
1013 : /*
1014 : * gmpr_dequeue_group_xmit
1015 : *
1016 : * Dequeue a group entry from an interface.
1017 : */
1018 : void
1019 44 : gmpr_dequeue_group_xmit (gmpr_group *group)
1020 : {
1021 44 : thread_remove(&group->rgroup_xmit_thread);
1022 44 : }
1023 :
1024 :
1025 : /*
1026 : * gmpr_attempt_ogroup_free
1027 : *
1028 : * Attempt to free the output group entry. We will do so if there is
1029 : * no client interest in the group and there's nothing more to send
1030 : * for the group.
1031 : *
1032 : * returns TRUE if gmpr_ogroup was freed. Otherwise FALSE.
1033 : */
1034 : boolean
1035 178 : gmpr_attempt_ogroup_free (gmpr_ogroup *ogroup)
1036 : {
1037 : /*
1038 : * Bail if we're in Exclude mode. Groups are only deleted when
1039 : * they're empty, and they're only empty when the group is
1040 : * in state Include {}.
1041 : */
1042 178 : if (ogroup->rogroup_filter_mode == GMP_FILTER_MODE_EXCLUDE)
1043 44 : return FALSE;
1044 :
1045 : /* Bail if there are any pending client notifications. */
1046 :
1047 134 : if (gmpr_group_notifications_active(ogroup))
1048 75 : return FALSE;
1049 :
1050 : /* Bail if there are any input groups bound to this group. */
1051 :
1052 59 : if (!thread_circular_thread_empty(&ogroup->rogroup_oif_head))
1053 0 : return FALSE;
1054 :
1055 : /* Bail if there's anything on any of the lists. */
1056 :
1057 59 : if (!gmp_addr_list_empty(&ogroup->rogroup_incl_src_addr))
1058 38 : return FALSE;
1059 21 : if (!gmp_addr_list_empty(&ogroup->rogroup_excl_src_addr))
1060 0 : return FALSE;
1061 21 : if (!gmp_addr_list_empty(&ogroup->rogroup_src_addr_deleted))
1062 0 : return FALSE;
1063 21 : if (!thread_circular_thread_empty(&ogroup->rogroup_oif_head))
1064 0 : return FALSE;
1065 :
1066 : /* Looks safe. Destroy the group. */
1067 :
1068 21 : gmpr_destroy_ogroup(ogroup);
1069 21 : return TRUE;
1070 : }
1071 :
1072 :
1073 : /*
1074 : * gmpr_attempt_group_free
1075 : *
1076 : * Attempt to free the group entry. We will do so if there is no client
1077 : * interest in the group and there's nothing more to send for the group.
1078 : */
1079 : void
1080 90 : gmpr_attempt_group_free (gmpr_group *group)
1081 : {
1082 : /*
1083 : * Bail if we're in Exclude mode. Groups are only deleted when
1084 : * they're empty, and they're only empty when the group is
1085 : * in state Include {}.
1086 : */
1087 90 : if (group->rgroup_filter_mode == GMP_FILTER_MODE_EXCLUDE)
1088 27 : return;
1089 :
1090 : /* Bail if this group is bound to an output group. */
1091 :
1092 63 : if (group->rgroup_oif_group)
1093 0 : return;
1094 :
1095 : /* Bail if there's anything on any of the lists. */
1096 :
1097 63 : if (!gmp_addr_list_empty(&group->rgroup_src_addr_running))
1098 42 : return;
1099 21 : if (!gmp_addr_list_empty(&group->rgroup_src_addr_stopped))
1100 0 : return;
1101 21 : if (!gmp_addr_list_empty(&group->rgroup_query_lo_timers))
1102 0 : return;
1103 21 : if (!gmp_addr_list_empty(&group->rgroup_query_hi_timers))
1104 0 : return;
1105 :
1106 : /* Bail if the group timer is running. */
1107 :
1108 21 : if (gmpx_timer_running(group->rgroup_group_timer))
1109 0 : return;
1110 :
1111 : /* Looks safe. Destroy the group. */
1112 :
1113 21 : gmpr_destroy_group(group);
1114 : }
1115 :
1116 :
1117 : /*
1118 : * gmpr_lookup_global_group
1119 : *
1120 : * Get the global group entry corresponding to the provided group entry.
1121 : *
1122 : * Returns a pointer to the global group entry, or NULL if it's not there.
1123 : */
1124 : gmpr_global_group *
1125 74 : gmpr_lookup_global_group (gmpr_instance *instance, uint8_t *group_addr)
1126 : {
1127 : gmpr_global_group *global_group;
1128 : gmpx_patnode *node;
1129 :
1130 : /* Look up the group in the tree. */
1131 :
1132 74 : node = gmpx_patricia_lookup(instance->rinst_global_state_root, group_addr);
1133 74 : global_group = gmpr_patnode_to_global_group(node);
1134 :
1135 74 : return global_group;
1136 : }
1137 :
1138 :
1139 : /*
1140 : * gmpr_get_global_group
1141 : *
1142 : * Get the global group entry corresponding to the provided group entry.
1143 : * Create one if it's not there.
1144 : *
1145 : * Returns a pointer to the global group entry, or NULL if out of memory.
1146 : */
1147 : static gmpr_global_group *
1148 37 : gmpr_get_global_group (gmpr_ogroup *group)
1149 : {
1150 : gmpr_instance *instance;
1151 : gmpr_global_group *global_group;
1152 :
1153 37 : instance = group->rogroup_intf->rintf_instance;
1154 :
1155 : /* Look up the group in the tree. */
1156 :
1157 37 : global_group = gmpr_lookup_global_group(instance,
1158 37 : group->rogroup_addr.gmp_addr);
1159 37 : if (!global_group) {
1160 :
1161 : /* Entry isn't there. Create one. */
1162 :
1163 34 : global_group = gmpx_malloc_block(gmpr_global_group_tag);
1164 34 : if (!global_group)
1165 0 : return NULL; /* Out of memory */
1166 :
1167 : /* Initialize. */
1168 :
1169 34 : thread_new_circular_thread(&global_group->global_group_head);
1170 34 : memmove(global_group->global_group_addr.gmp_addr,
1171 34 : group->rogroup_addr.gmp_addr,
1172 34 : instance->rinst_addrlen);
1173 :
1174 : /* Stick it into the tree. */
1175 :
1176 34 : gmpx_assert(gmpx_patricia_add(instance->rinst_global_state_root,
1177 : &global_group->global_group_node));
1178 : }
1179 :
1180 37 : return global_group;
1181 : }
1182 :
1183 :
1184 : /*
1185 : * gmpr_link_global_group
1186 : *
1187 : * Link a new group to the global group tree.
1188 : *
1189 : * Returns a pointer to the global group, or NULL if out of memory.
1190 : */
1191 : gmpr_global_group *
1192 37 : gmpr_link_global_group (gmpr_ogroup *group)
1193 : {
1194 : // gmpr_instance *instance;
1195 : gmpr_global_group *global_group;
1196 :
1197 : // instance = group->rogroup_intf->rintf_instance;
1198 :
1199 : /* Get the global group entry. One is created if necessary. */
1200 :
1201 37 : global_group = gmpr_get_global_group(group);
1202 37 : if (!global_group)
1203 0 : return NULL;
1204 :
1205 : /* Got it. Thread the new group to it. */
1206 :
1207 37 : thread_circular_add_bottom(&global_group->global_group_head,
1208 : &group->rogroup_global_thread);
1209 :
1210 37 : return global_group;
1211 : }
1212 :
1213 :
1214 : /*
1215 : * gmpr_attempt_delete_global_group
1216 : *
1217 : * Attempt to delete a global group entry. We do so if there are no
1218 : * groups or sources attached to the entry.
1219 : */
1220 : static void
1221 37 : gmpr_attempt_delete_global_group(gmpr_instance *instance,
1222 : gmpr_global_group *global_group)
1223 : {
1224 : /* See if there are any groups. */
1225 :
1226 37 : if (thread_circular_thread_empty(&global_group->global_group_head)) {
1227 :
1228 : /* No groups. Delink from the instance tree. */
1229 :
1230 34 : gmpx_assert(gmpx_patricia_delete(instance->rinst_global_state_root,
1231 : &global_group->global_group_node));
1232 :
1233 : /* Free the block. */
1234 :
1235 34 : gmpx_free_block(gmpr_global_group_tag, global_group);
1236 : }
1237 37 : }
1238 :
1239 :
1240 : /*
1241 : * gmpr_delink_global_group
1242 : *
1243 : * Delink a group from the global tree. Frees the global group entry if
1244 : * all groups are gone.
1245 : */
1246 : void
1247 37 : gmpr_delink_global_group (gmpr_ogroup *group)
1248 : {
1249 : gmpr_instance *instance;
1250 : gmpr_global_group *global_group;
1251 :
1252 37 : instance = group->rogroup_intf->rintf_instance;
1253 :
1254 : /* Bail if not threaded. */
1255 :
1256 37 : if (!thread_node_on_thread(&group->rogroup_global_thread))
1257 0 : return;
1258 :
1259 : /* Delink from the thread. */
1260 :
1261 37 : thread_remove(&group->rogroup_global_thread);
1262 :
1263 : /* Look up the group. */
1264 :
1265 37 : global_group = gmpr_lookup_global_group(instance,
1266 37 : group->rogroup_addr.gmp_addr);
1267 37 : gmpx_assert(global_group);
1268 :
1269 : /* Delete the global group if it's empty. */
1270 :
1271 37 : gmpr_attempt_delete_global_group(instance, global_group);
1272 : }
1273 :
1274 :
1275 : /*
1276 : * gmpr_group_forwards_all_sources
1277 : *
1278 : * Returns TRUE if the group forwards all sources (it's an Exclude{}), or
1279 : * FALSE if not.
1280 : */
1281 : boolean
1282 0 : gmpr_group_forwards_all_sources (gmpr_ogroup *group)
1283 : {
1284 : gmp_addr_vect result_vector;
1285 : boolean result;
1286 :
1287 : /* If not Exclude mode, we don't forward everything. */
1288 :
1289 0 : if (group->rogroup_filter_mode != GMP_FILTER_MODE_EXCLUDE)
1290 0 : return FALSE;
1291 :
1292 : /* If the exclude list is empty, we forward all sources. */
1293 :
1294 0 : if (gmp_addr_list_empty(&group->rogroup_excl_src_addr))
1295 0 : return TRUE;
1296 :
1297 : /*
1298 : * The exclude list isn't empty. Subtract the include list from
1299 : * the exclude list. If the result is empty, the group forwards
1300 : * all sources. Otherwise, it doesn't.
1301 : */
1302 0 : gmp_init_addr_vector(&result_vector, NULL);
1303 0 : gmp_addr_vect_minus(&group->rogroup_excl_src_addr.addr_vect,
1304 : &group->rogroup_incl_src_addr.addr_vect,
1305 : &result_vector, NULL, NULL, 0);
1306 0 : result = gmp_addr_vect_empty(&result_vector);
1307 0 : gmp_addr_vect_clean(&result_vector);
1308 0 : return result;
1309 : }
1310 :
1311 :
1312 : /*
1313 : * gmpr_group_forwards_source
1314 : *
1315 : * Returns TRUE if the source is being forwarded as part of the group, or
1316 : * FALSE if not.
1317 : */
1318 : boolean
1319 0 : gmpr_group_forwards_source (gmpr_ogroup *group, const uint8_t *source_addr)
1320 : {
1321 : gmpr_instance *instance;
1322 : gmp_addr_cat_entry *cat_entry;
1323 : boolean active;
1324 : boolean result;
1325 :
1326 0 : instance = group->rogroup_intf->rintf_instance;
1327 :
1328 : /* Look up the catalog entry. It may or may not be there. */
1329 :
1330 0 : cat_entry = gmp_lookup_addr_cat_entry(&instance->rinst_addr_cat,
1331 : source_addr);
1332 :
1333 : /*
1334 : * If the catalog entry is there, see if the source is active. If the
1335 : * catalog entry isn't there, the source isn't active.
1336 : */
1337 0 : if (cat_entry) {
1338 0 : active = gmpr_source_ord_is_active(group, cat_entry->adcat_ent_ord);
1339 : } else {
1340 0 : active = FALSE;
1341 : }
1342 :
1343 : /* Check out the filter mode. */
1344 :
1345 0 : if (group->rogroup_filter_mode == GMP_FILTER_MODE_INCLUDE) {
1346 :
1347 : /* Include mode. We're forwarding if the source is active. */
1348 :
1349 0 : result = active;
1350 :
1351 : } else {
1352 :
1353 : /* Exclude mode. We're forwarding if the source is *not* active. */
1354 :
1355 0 : result = !active;
1356 : }
1357 :
1358 0 : return result;
1359 : }
1360 :
1361 :
1362 : /*
1363 : * gmpr_wipe_source_timer
1364 : *
1365 : * Callback from a vector walk to prematurely time out a source timer.
1366 : */
1367 : static boolean
1368 6 : gmpr_wipe_source_timer (void *context, bv_bitnum_t bitnum,
1369 : boolean new_value GMPX_UNUSED,
1370 : boolean old_value GMPX_UNUSED)
1371 : {
1372 : gmpr_group *group;
1373 : gmp_addr_list_entry *addr_entry;
1374 : gmpr_group_addr_entry *group_addr_entry;
1375 :
1376 6 : group = context;
1377 :
1378 : /* Look up the entry. */
1379 :
1380 6 : addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
1381 : bitnum);
1382 6 : gmpx_assert(addr_entry);
1383 6 : group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
1384 :
1385 : /* Whack the timer. */
1386 :
1387 6 : gmpx_start_timer(group_addr_entry->rgroup_addr_timer, 0, 0);
1388 :
1389 6 : return FALSE;
1390 : }
1391 :
1392 :
1393 : /*
1394 : * gmpr_timeout_group
1395 : *
1396 : * Prematurely time out a group.
1397 : */
1398 : void
1399 16 : gmpr_timeout_group (gmpr_group *group)
1400 : {
1401 : /* If the group is in Exclude mode, just whack the group timer. */
1402 :
1403 16 : if (group->rgroup_filter_mode == GMP_FILTER_MODE_EXCLUDE) {
1404 11 : gmpx_start_timer(group->rgroup_group_timer, 0, 0);
1405 :
1406 : } else {
1407 :
1408 : /*
1409 : * Include mode. We need to walk all of the sources and
1410 : * whack their individual timers.
1411 : */
1412 5 : gmp_addr_vect_walk(&group->rgroup_src_addr_running.addr_vect,
1413 : gmpr_wipe_source_timer, group);
1414 : }
1415 16 : }
1416 :
1417 :
1418 : /*
1419 : * gmpr_evaluate_oif_group
1420 : *
1421 : * State has possibly changed on an OIF group. Send any necessary
1422 : * notifications.
1423 : */
1424 : static void
1425 162 : gmpr_evaluate_oif_group (gmpr_ogroup *ogroup)
1426 : {
1427 : gmp_filter_mode new_mode;
1428 :
1429 : /* Tolerate NULL pointers. */
1430 :
1431 162 : if (!ogroup)
1432 69 : return;
1433 :
1434 : /*
1435 : * See whether the filter mode has changed. The filter mode is
1436 : * Include if the Exclude list is empty and there are no input
1437 : * groups linked to the group, or is Exclude otherwise.
1438 : */
1439 186 : if (gmp_addr_list_empty(&ogroup->rogroup_excl_src_addr) &&
1440 93 : thread_circular_thread_empty(&ogroup->rogroup_oif_head)) {
1441 71 : new_mode = GMP_FILTER_MODE_INCLUDE;
1442 : } else {
1443 22 : new_mode = GMP_FILTER_MODE_EXCLUDE;
1444 : }
1445 93 : if (new_mode != ogroup->rogroup_filter_mode) {
1446 :
1447 : /* The mode changed. Switch it and do the notifications. */
1448 :
1449 33 : ogroup->rogroup_filter_mode = new_mode;
1450 33 : gmpr_mode_change_notify_clients(ogroup);
1451 :
1452 : } else {
1453 :
1454 : /*
1455 : * The mode didn't change. See if the group has become inactive
1456 : * (we may have just deleted the final source) and notify
1457 : * the clients if so.
1458 : */
1459 60 : if (!gmpr_ogroup_is_active(ogroup)) {
1460 10 : gmpr_group_notify_clients(ogroup);
1461 : }
1462 : }
1463 :
1464 : /* Try to free the entry, just in case. */
1465 :
1466 93 : gmpr_attempt_ogroup_free(ogroup);
1467 : }
1468 :
1469 :
1470 : /*
1471 : * gmpr_evaluate_oif_source
1472 : *
1473 : * State has possibly changed on an OIF source. Do The Right Thing, which
1474 : * includes possibly updating the OIF group and/or sending notifications.
1475 : */
1476 : static void
1477 126 : gmpr_evaluate_oif_source (gmpr_ogroup_addr_entry *ogroup_addr)
1478 : {
1479 : gmpr_ogroup *ogroup;
1480 : gmpr_ogroup_addr_entry *other_ogroup_addr;
1481 : gmp_addr_list *addr_list;
1482 : gmp_addr_list_entry *addr_entry;
1483 :
1484 : /* Tolerate NULL pointers. */
1485 :
1486 126 : if (!ogroup_addr)
1487 66 : return;
1488 :
1489 : /* See if this source has any components left. */
1490 :
1491 60 : ogroup = ogroup_addr->rogroup_addr_group;
1492 60 : if (thread_circular_thread_empty(&ogroup_addr->rogroup_addr_oif_head)) {
1493 :
1494 : /*
1495 : * No components left. Check to see if the same address is
1496 : * present on the other list (include/exclude).
1497 : */
1498 27 : if (gmpr_group_addr_mode(ogroup_addr) == GMP_FILTER_MODE_INCLUDE) {
1499 27 : addr_list = gmpr_ogroup_source_list_by_mode(ogroup,
1500 : GMP_FILTER_MODE_EXCLUDE);
1501 : } else {
1502 0 : addr_list = gmpr_ogroup_source_list_by_mode(ogroup,
1503 : GMP_FILTER_MODE_INCLUDE);
1504 : }
1505 27 : addr_entry = gmp_lookup_addr_entry(addr_list,
1506 : ogroup_addr->rogroup_addr_entry.addr_ent_ord);
1507 27 : other_ogroup_addr = gmpr_addr_entry_to_ogroup_entry(addr_entry);
1508 :
1509 27 : if (other_ogroup_addr) {
1510 :
1511 : /*
1512 : * The same address exists on the other list. Simply destroy
1513 : * this one, and post a notification for the other one.
1514 : */
1515 0 : gmp_delete_addr_list_entry(&ogroup_addr->rogroup_addr_entry);
1516 0 : ogroup_addr = other_ogroup_addr;
1517 0 : ogroup_addr->rogroup_notify = TRUE;
1518 :
1519 : } else {
1520 :
1521 : /*
1522 : * This source doesn't exist on the other list. Move the
1523 : * source to the delete list.
1524 : */
1525 27 : gmp_move_addr_list_entry(&ogroup->rogroup_src_addr_deleted,
1526 : &ogroup_addr->rogroup_addr_entry);
1527 : }
1528 : }
1529 60 : gmpr_source_notify_clients(ogroup_addr, NOTIFY_CONDITIONAL);
1530 :
1531 : /*
1532 : * Now evaluate the group. The change in source may have caused a
1533 : * mode change.
1534 : */
1535 60 : gmpr_evaluate_oif_group(ogroup);
1536 : }
1537 :
1538 :
1539 : /*
1540 : * gmpr_link_oif_group_intf
1541 : *
1542 : * Link an input group to an OIF group by interface. If the OIF group
1543 : * doesn't exist, create it.
1544 : *
1545 : * Returns a pointer to the OIF group, or NULL if out of memory, or if the
1546 : * OIF pointer is NULL.
1547 : */
1548 : static gmpr_ogroup *
1549 22 : gmpr_link_oif_group_intf (gmpr_group *group, gmpr_intf *oif)
1550 : {
1551 : gmpr_ogroup *ogroup;
1552 :
1553 : /* Bail if no interface. */
1554 :
1555 22 : if (!oif)
1556 0 : return NULL;
1557 :
1558 : /* Look up the output group. */
1559 :
1560 22 : ogroup = gmpr_ogroup_lookup(oif, group->rgroup_addr.gmp_addr);
1561 22 : if (!ogroup) {
1562 :
1563 : /* The output group doesn't exist. Create it. */
1564 :
1565 22 : ogroup = gmpr_ogroup_create(oif, &group->rgroup_addr);
1566 22 : if (!ogroup)
1567 0 : return NULL; /* Out of memory */
1568 : }
1569 :
1570 : /* Link the input and output groups together. */
1571 :
1572 22 : gmpr_link_oif_group(group, ogroup);
1573 :
1574 22 : return ogroup;
1575 : }
1576 :
1577 :
1578 : /*
1579 : * gmpr_lookup_oif_source
1580 : *
1581 : * Look up an OIF source given the input source and OIF.
1582 : *
1583 : * Returns a pointer to the OIF source, or NULL if it doesn't exist.
1584 : * Also returns a pointer to the OIF group if that exists.
1585 : */
1586 : static gmpr_ogroup_addr_entry *
1587 33 : gmpr_lookup_oif_source (gmpr_intf *oif, gmpr_group_addr_entry *group_addr,
1588 : gmpr_ogroup **ogroup)
1589 : {
1590 : gmpr_ogroup_addr_entry *ogroup_addr_entry;
1591 : gmpr_group *group;
1592 : gmp_addr_list *addr_list;
1593 : gmp_addr_list_entry *addr_entry;
1594 :
1595 : /* If there's no OIF, there's no output group. */
1596 :
1597 33 : if (!oif) {
1598 0 : *ogroup = NULL;
1599 0 : return NULL;
1600 : }
1601 :
1602 : /* Look up the output group. */
1603 :
1604 33 : group = group_addr->rgroup_addr_group;
1605 33 : *ogroup = gmpr_ogroup_lookup(oif, group->rgroup_addr.gmp_addr);
1606 33 : if (!*ogroup)
1607 15 : return NULL; /* Ain't here. */
1608 :
1609 : /* Look up the output source. */
1610 :
1611 18 : addr_list = gmpr_ogroup_source_list_by_mode(*ogroup,
1612 : group->rgroup_filter_mode);
1613 18 : addr_entry = gmp_lookup_addr_entry(addr_list,
1614 : group_addr->rgroup_addr_entry.addr_ent_ord);
1615 18 : ogroup_addr_entry = gmpr_addr_entry_to_ogroup_entry(addr_entry);
1616 :
1617 18 : return ogroup_addr_entry;
1618 : }
1619 :
1620 :
1621 : /*
1622 : * gmpr_link_oif_source_intf
1623 : *
1624 : * Link an input source to an OIF source by interface. If the OIF
1625 : * group and/or source don't exist, create them.
1626 : *
1627 : * Returns a pointer to the new OIF source, or NULL if out of memory or the
1628 : * OIF pointer was NULL.
1629 : *
1630 : * Note that we will harvest a source from the deleted list if there, in order
1631 : * to avoid sending redundant notifications, and to ensure that the same
1632 : * source is never on both an active (include/exclude) list and the deleted
1633 : * list.
1634 : */
1635 : static gmpr_ogroup_addr_entry *
1636 33 : gmpr_link_oif_source_intf (gmpr_group_addr_entry *group_addr, gmpr_intf *oif)
1637 : {
1638 : gmpr_ogroup_addr_entry *ogroup_addr;
1639 : gmpr_ogroup *ogroup;
1640 : gmpr_group *group;
1641 : gmp_addr_list *addr_list;
1642 : gmp_addr_list_entry *addr_entry;
1643 : ordinal_t addr_ord;
1644 :
1645 33 : addr_ord = group_addr->rgroup_addr_entry.addr_ent_ord;
1646 :
1647 : /* Bail if no output interface. */
1648 :
1649 33 : if (!oif)
1650 0 : return NULL;
1651 :
1652 : /* Look up the output group and source. */
1653 :
1654 33 : group = group_addr->rgroup_addr_group;
1655 33 : ogroup_addr = gmpr_lookup_oif_source(oif, group_addr, &ogroup);
1656 33 : if (!ogroup) {
1657 :
1658 : /* The output group doesn't exist. Create it. */
1659 :
1660 15 : ogroup = gmpr_ogroup_create(oif, &group->rgroup_addr);
1661 15 : if (!ogroup)
1662 0 : return NULL; /* Out of memory */
1663 : }
1664 :
1665 : /* If the output source doesn't exist, get one. */
1666 :
1667 33 : if (!ogroup_addr) {
1668 :
1669 : /* Figure out which list it's going to. */
1670 :
1671 33 : addr_list = gmpr_ogroup_source_list_by_mode(ogroup,
1672 : group->rgroup_filter_mode);
1673 : /* See if we can get it from the deleted list. */
1674 :
1675 33 : addr_entry = gmp_lookup_addr_entry(&ogroup->rogroup_src_addr_deleted,
1676 : addr_ord);
1677 33 : if (addr_entry) {
1678 :
1679 : /* On the deleted list. Move it to the active list. */
1680 :
1681 0 : gmp_move_addr_list_entry(addr_list, addr_entry);
1682 :
1683 : } else {
1684 :
1685 : /* Not there. Create one. */
1686 :
1687 33 : addr_entry = gmp_create_addr_list_entry(addr_list, addr_ord);
1688 : }
1689 :
1690 33 : ogroup_addr = gmpr_addr_entry_to_ogroup_entry(addr_entry);
1691 33 : if (!ogroup_addr)
1692 0 : return NULL; /* Out of memory */
1693 : }
1694 :
1695 : /*
1696 : * Link the input and output sources together. This will trigger
1697 : * a notification.
1698 : */
1699 33 : gmpr_link_oif_source(group_addr, ogroup_addr);
1700 :
1701 33 : return ogroup_addr;
1702 : }
1703 :
1704 :
1705 : /*
1706 : * gmpr_get_current_source_oif
1707 : *
1708 : * Returns the current OIF for a source, or NULL if it has none.
1709 : *
1710 : * Tolerates NULL pointers.
1711 : */
1712 : static gmpr_intf *
1713 33 : gmpr_get_current_source_oif (gmpr_group_addr_entry *group_addr)
1714 : {
1715 : gmpr_ogroup_addr_entry *ogroup_addr;
1716 : gmpr_intf *oif;
1717 : gmpr_ogroup *ogroup;
1718 :
1719 33 : if (!group_addr)
1720 0 : return NULL;
1721 :
1722 : /* Pick up the current output source entry, if any. */
1723 :
1724 33 : ogroup_addr = group_addr->rgroup_addr_oif_addr;
1725 33 : if (ogroup_addr) {
1726 0 : ogroup = ogroup_addr->rogroup_addr_group;
1727 0 : oif = ogroup->rogroup_intf;
1728 : } else {
1729 33 : oif = NULL;
1730 : }
1731 :
1732 33 : return oif;
1733 : }
1734 :
1735 :
1736 : /*
1737 : * gmpr_get_current_group_oif
1738 : *
1739 : * Returns the current OIF for a group, or NULL if it has none.
1740 : *
1741 : * Tolerates NULL pointers.
1742 : */
1743 : static gmpr_intf *
1744 22 : gmpr_get_current_group_oif (gmpr_group *group)
1745 : {
1746 : gmpr_intf *oif;
1747 : gmpr_ogroup *ogroup;
1748 :
1749 22 : if (!group)
1750 0 : return NULL;
1751 :
1752 : /* Grab the current output group, if any, and get the interface. */
1753 :
1754 22 : ogroup = group->rgroup_oif_group;
1755 22 : if (ogroup) {
1756 0 : oif = ogroup->rogroup_intf;
1757 : } else {
1758 22 : oif = NULL;
1759 : }
1760 :
1761 22 : return oif;
1762 : }
1763 :
1764 :
1765 : /*
1766 : * gmpr_get_mapped_oif
1767 : *
1768 : * Get the mapped OIF for a (*,G) or (S,G). Returns a pointer to the OIF
1769 : * (which may be the same as the input interface) or NULL if there is
1770 : * no output interface.
1771 : */
1772 : static gmpr_intf *
1773 55 : gmpr_get_mapped_oif (gmpr_intf *intf, uint8_t *group, uint8_t *source)
1774 : {
1775 : gmpr_intf *oif;
1776 : gmpx_intf_id oif_id;
1777 : gmpr_instance *instance;
1778 : gmpr_instance_context *ctx;
1779 : boolean oif_ok;
1780 :
1781 55 : instance = intf->rintf_instance;
1782 55 : ctx = &instance->rinst_cb_context;
1783 55 : if (ctx->rctx_oif_map_cb) {
1784 55 : oif_ok = (*ctx->rctx_oif_map_cb)(instance->rinst_context,
1785 : intf->rintf_id, group, source,
1786 : &oif_id);
1787 55 : if (oif_ok) {
1788 55 : oif = gmpr_intf_lookup(instance, oif_id); /* May be NULL */
1789 : } else {
1790 0 : oif = NULL;
1791 : }
1792 : } else {
1793 0 : oif = intf;
1794 : }
1795 :
1796 55 : return oif;
1797 : }
1798 :
1799 :
1800 : /*
1801 : * gmpr_map_group_oif
1802 : *
1803 : * Do the work to (re)map a group to an output interface.
1804 : *
1805 : * If the output interface is changing, we clean up the old interface.
1806 : */
1807 : static void
1808 22 : gmpr_map_group_oif (gmpr_group *group)
1809 : {
1810 : gmpr_intf *oif;
1811 : gmpr_intf *old_oif;
1812 : gmpr_ogroup *ogroup;
1813 :
1814 : /*
1815 : * Validity checks. We have to be active, in Exclude mode, and have
1816 : * no sources.
1817 : */
1818 22 : gmpx_assert(gmpr_group_is_active(group));
1819 22 : gmpx_assert(group->rgroup_filter_mode == GMP_FILTER_MODE_EXCLUDE);
1820 22 : gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
1821 :
1822 : /* Get the current and new mapped output interfaces. */
1823 :
1824 22 : ogroup = group->rgroup_oif_group;
1825 22 : old_oif = gmpr_get_current_group_oif(group);
1826 22 : oif = gmpr_get_mapped_oif(group->rgroup_intf, group->rgroup_addr.gmp_addr,
1827 : NULL);
1828 :
1829 : /*
1830 : * If the output interface has changed, delink from the old one
1831 : * and link to the new one. This handles the NULL cases properly.
1832 : */
1833 22 : if (oif != old_oif) {
1834 22 : gmpr_delink_oif_group(group);
1835 22 : gmpr_evaluate_oif_group(ogroup);
1836 22 : ogroup = gmpr_link_oif_group_intf(group, oif);
1837 : }
1838 :
1839 : /* Evaluate the current OIF group. */
1840 :
1841 22 : gmpr_evaluate_oif_group(ogroup);
1842 22 : }
1843 :
1844 :
1845 : /*
1846 : * gmpr_update_group_oif
1847 : *
1848 : * Update the OIF information for an input group.
1849 : */
1850 : void
1851 80 : gmpr_update_group_oif (gmpr_group *group, oif_update_type update_type)
1852 : {
1853 : gmpr_ogroup *ogroup;
1854 : // gmpr_intf *intf;
1855 : // gmpr_instance *instance;
1856 :
1857 : // intf = group->rgroup_intf;
1858 : // instance = intf->rintf_instance;
1859 80 : ogroup = group->rgroup_oif_group;
1860 :
1861 : /* Switch based on the operation (Update or Delete). */
1862 :
1863 80 : switch (update_type) {
1864 22 : case OIF_UPDATE:
1865 :
1866 : /*
1867 : * See if the group has any sources. If so, it should not be
1868 : * linked to an OIF (since the sources are individually linked.)
1869 : * This can happen if a group was originally (*,G) but switched
1870 : * to having sources.
1871 : */
1872 44 : if (!gmp_addr_list_empty(&group->rgroup_src_addr_stopped) ||
1873 22 : !gmp_addr_list_empty(&group->rgroup_src_addr_running)) {
1874 :
1875 : /* Sources present. Delink us. */
1876 :
1877 0 : gmpr_delink_oif_group(group);
1878 0 : gmpr_evaluate_oif_group(ogroup);
1879 :
1880 : } else {
1881 :
1882 : /* No sources present. Remap the group. */
1883 :
1884 22 : gmpr_map_group_oif(group);
1885 : }
1886 22 : break;
1887 :
1888 58 : case OIF_DELETE:
1889 :
1890 : /* Delink from the output group and then reevaluate it. */
1891 :
1892 58 : gmpr_delink_oif_group(group);
1893 58 : gmpr_evaluate_oif_group(ogroup);
1894 58 : break;
1895 :
1896 0 : default:
1897 0 : gmpx_assert(FALSE);
1898 : }
1899 80 : }
1900 :
1901 :
1902 : /*
1903 : * gmpr_map_source_oif
1904 : *
1905 : * (Re)map the output interface for a source.
1906 : *
1907 : * If the output interface is changing, we clean up the old one.
1908 : */
1909 : static void
1910 33 : gmpr_map_source_oif (gmpr_group_addr_entry *group_addr)
1911 : {
1912 : gmpr_group *group;
1913 : gmpr_ogroup *ogroup;
1914 : gmpr_ogroup_addr_entry *old_ogroup_addr;
1915 : gmpr_ogroup_addr_entry *ogroup_addr;
1916 : gmpr_intf *intf;
1917 : gmpr_intf *oif;
1918 : gmpr_intf *old_oif;
1919 : gmpr_instance *instance;
1920 : gmp_addr_cat_entry *cat_entry;
1921 :
1922 33 : group = group_addr->rgroup_addr_group;
1923 33 : intf = group->rgroup_intf;
1924 33 : instance = intf->rintf_instance;
1925 33 : old_ogroup_addr = group_addr->rgroup_addr_oif_addr;
1926 33 : old_oif = gmpr_get_current_source_oif(group_addr);
1927 :
1928 : /*
1929 : * Look up the output interface. We may be given an intf_id
1930 : * that we don't know about, or the policy may tell us to
1931 : * block the group. In either of those cases we will end
1932 : * up with a NULL OIF pointer, and the right thing will happen.
1933 : */
1934 33 : cat_entry = gmp_get_addr_cat_by_ordinal(&instance->rinst_addr_cat,
1935 : group_addr->rgroup_addr_entry.addr_ent_ord);
1936 33 : gmpx_assert(cat_entry);
1937 33 : oif = gmpr_get_mapped_oif(intf, group->rgroup_addr.gmp_addr,
1938 33 : cat_entry->adcat_ent_addr.gmp_addr);
1939 :
1940 : /*
1941 : * If the output interface has changed, delink from the old
1942 : * one and link to the new one. Note that either of the oif pointers
1943 : * may be NULL.
1944 : */
1945 33 : if (oif != old_oif) {
1946 33 : gmpr_delink_oif_source(group_addr);
1947 33 : gmpr_evaluate_oif_source(old_ogroup_addr);
1948 33 : ogroup_addr = gmpr_link_oif_source_intf(group_addr, oif);
1949 :
1950 : } else {
1951 :
1952 : /* Output interface unchanged. Check the output source. */
1953 :
1954 0 : ogroup_addr = gmpr_lookup_oif_source(oif, group_addr, &ogroup);
1955 :
1956 : /*
1957 : * If the output source has changed, delink from the old one
1958 : * and link to the new one. Note that the call to
1959 : * gmpr_link_oif_source_intf() will find the same output group
1960 : * address if it exists, or create one if it doesn't. Note also that
1961 : * either the new or old output group address may be NULL.
1962 : */
1963 0 : if (ogroup_addr != old_ogroup_addr) {
1964 0 : gmpr_delink_oif_source(group_addr);
1965 0 : gmpr_evaluate_oif_source(old_ogroup_addr);
1966 0 : ogroup_addr = gmpr_link_oif_source_intf(group_addr, oif);
1967 : }
1968 : }
1969 :
1970 : /* Evaluate the source. */
1971 :
1972 33 : gmpr_evaluate_oif_source(ogroup_addr);
1973 33 : }
1974 :
1975 :
1976 : /*
1977 : * gmpr_update_source_oif
1978 : *
1979 : * Update the OIF information for an input source.
1980 : */
1981 : void
1982 93 : gmpr_update_source_oif (gmpr_group_addr_entry *group_addr,
1983 : oif_update_type update_type)
1984 : {
1985 : gmpr_ogroup_addr_entry *old_ogroup_addr;
1986 :
1987 : /* Switch based on the operation (Update or Delete). */
1988 :
1989 93 : switch (update_type) {
1990 33 : case OIF_UPDATE:
1991 :
1992 : /* Remap the output interface. */
1993 :
1994 33 : gmpr_map_source_oif(group_addr);
1995 33 : break;
1996 :
1997 60 : case OIF_DELETE:
1998 :
1999 : /* Deleting the source. Delink it and reevaluate the output source. */
2000 :
2001 60 : old_ogroup_addr = gmpr_delink_oif_source(group_addr);
2002 60 : gmpr_evaluate_oif_source(old_ogroup_addr);
2003 60 : break;
2004 :
2005 0 : default:
2006 0 : gmpx_assert(FALSE);
2007 : }
2008 93 : }
2009 :
2010 :
2011 : /*
2012 : * gmpr_update_all_group_source_oif
2013 : *
2014 : * Update the OIF information for all sources that are part of a group.
2015 : */
2016 : static void
2017 22 : gmpr_update_all_group_source_oif (gmpr_group *group,
2018 : oif_update_type update_type)
2019 : {
2020 : gmpr_group_addr_entry *group_addr;
2021 : gmp_addr_list *addr_list;
2022 : gmp_addr_list_entry *addr_entry;
2023 :
2024 : /* Walk all of the sources on the group. */
2025 :
2026 22 : addr_entry = NULL;
2027 22 : addr_list = gmpr_group_source_list(group);
2028 : while (TRUE) {
2029 22 : addr_entry = gmp_addr_list_next_entry(addr_list, addr_entry);
2030 22 : group_addr = gmpr_addr_entry_to_group_entry(addr_entry);
2031 :
2032 : /* Bail if all done. */
2033 :
2034 22 : if (!group_addr)
2035 22 : break;
2036 :
2037 : /* Got an entry. Update it. */
2038 :
2039 0 : gmpr_update_source_oif(group_addr, update_type);
2040 : }
2041 22 : }
2042 :
2043 :
2044 : /*
2045 : * gmpr_update_oif_mode_change
2046 : *
2047 : * Update the OIF info for an input group when the group changes
2048 : * filter modes (from Include to Exclude, or vice versa.)
2049 : *
2050 : * This routine is also called when a (*,G) group is created or destroyed,
2051 : * since a mode change takes place for both of those.
2052 : *
2053 : * The OIF info is updated as necessary.
2054 : *
2055 : * If appropriate, an attempt is made to delete the input group as well.
2056 : */
2057 : void
2058 33 : gmpr_update_oif_mode_change (gmpr_group *group)
2059 : {
2060 : /* See if the group is active (Exclude mode, or sources.) */
2061 :
2062 33 : if (gmpr_group_is_active(group)) {
2063 :
2064 : /*
2065 : * The group is active. Update the OIF for the group and all of the
2066 : * sources.
2067 : */
2068 22 : gmpr_update_group_oif(group, OIF_UPDATE);
2069 22 : gmpr_update_all_group_source_oif(group, OIF_UPDATE);
2070 :
2071 : } else {
2072 :
2073 : /*
2074 : * Group is no longer active. Delink the OIF and try to free
2075 : * the group.
2076 : */
2077 11 : gmpr_update_group_oif(group, OIF_DELETE);
2078 11 : gmpr_attempt_group_free(group);
2079 : }
2080 33 : }
2081 :
2082 :
2083 : /*
2084 : * gmpr_notify_oif_map_change_internal
2085 : *
2086 : * An OIF map has changed. We need to reevaluate all of the groups and
2087 : * sources on the interface.
2088 : */
2089 : void
2090 0 : gmpr_notify_oif_map_change_internal (gmpr_intf *intf)
2091 : {
2092 : gmpr_group *group;
2093 : gmpr_group_addr_entry *group_addr;
2094 : gmp_addr_list *source_list;
2095 : gmp_addr_list_entry *addr_entry;
2096 :
2097 : /* Walk all of the groups on the interface. */
2098 :
2099 0 : group = NULL;
2100 : while (TRUE) {
2101 :
2102 : /* Get the next group. Bail if we've run out. */
2103 :
2104 0 : group = gmpr_next_intf_group(intf, group);
2105 0 : if (!group)
2106 0 : break;
2107 :
2108 : /* Process only active groups. */
2109 :
2110 0 : if (gmpr_group_is_active(group)) {
2111 :
2112 : /* Get the active source list. */
2113 :
2114 0 : source_list = gmpr_group_source_list(group);
2115 :
2116 : /* If there are sources on the list, process them. */
2117 :
2118 0 : if (!gmp_addr_list_empty(source_list)) {
2119 :
2120 0 : addr_entry = NULL;
2121 : while (TRUE) {
2122 0 : addr_entry =
2123 0 : gmp_addr_list_next_entry(source_list, addr_entry);
2124 0 : group_addr = gmpr_addr_entry_to_group_entry(addr_entry);
2125 :
2126 : /* Bail if out of sources. */
2127 :
2128 0 : if (!group_addr)
2129 0 : break;
2130 :
2131 : /* Remap the source. */
2132 :
2133 0 : gmpr_map_source_oif(group_addr);
2134 : }
2135 :
2136 : } else {
2137 :
2138 : /* It's a (*,G) entry. Remap it. */
2139 :
2140 0 : gmpr_map_group_oif(group);
2141 : }
2142 :
2143 : /* CAC wants host notifications for an oif change */
2144 :
2145 0 : gmpr_host_notify_oif_map_change(group);
2146 : }
2147 : }
2148 :
2149 : /* Alert the clients to any changes. */
2150 :
2151 0 : gmpr_alert_clients(intf->rintf_instance);
2152 0 : }
2153 :
2154 :
2155 : /*
2156 : * gmpr_update_intf_output_groups
2157 : *
2158 : * Update the output groups on an interface when the interface goes up
2159 : * and down.
2160 : *
2161 : * We send delete notifications when they go down, and add
2162 : * notifications when they come up (the notification code teases this
2163 : * out from the interface status.)
2164 : *
2165 : * We only care about groups with a different output interface than
2166 : * input interface, since the groups with the same interface will be
2167 : * ripped out anyhow (since the input interface will be going down.)
2168 : */
2169 : void
2170 3416 : gmpr_update_intf_output_groups (gmpr_intf *intf)
2171 : {
2172 : gmpr_ogroup *ogroup;
2173 : gmpr_group *group;
2174 : task_thread *thread_ptr;
2175 :
2176 : /* Walk all output groups on the interface. */
2177 :
2178 3416 : ogroup = NULL;
2179 : while (TRUE) {
2180 3432 : ogroup = gmpr_next_oif_group(intf, ogroup);
2181 3432 : if (!ogroup)
2182 3416 : break;
2183 :
2184 : /*
2185 : * Got an output group. Process it if any input interface doesn't
2186 : * match the output interface.
2187 : */
2188 16 : thread_ptr = NULL;
2189 : while (TRUE) {
2190 :
2191 : /* Grab the next input group. */
2192 :
2193 27 : thread_ptr = thread_circular_thread_next(&ogroup->rogroup_oif_head,
2194 : thread_ptr);
2195 27 : group = gmpr_oif_thread_to_group(thread_ptr);
2196 :
2197 : /* Bail if we're done. */
2198 :
2199 27 : if (!group)
2200 16 : break;
2201 :
2202 : /* Process it if the input interface doesn't match the oif. */
2203 :
2204 11 : if (group->rgroup_intf != ogroup->rogroup_intf) {
2205 :
2206 : /*
2207 : * Interface mismatch. If we're going down, flush all source
2208 : * notifications for the group and enqueue the group (which
2209 : * will turn into a group_delete notification.) If we're
2210 : * coming up, enqueue the group and all of the sources.
2211 : */
2212 0 : if (ogroup->rogroup_intf->rintf_up) {
2213 :
2214 : /* Coming up. */
2215 :
2216 0 : gmpr_group_notify_clients(ogroup);
2217 0 : gmpr_enqueue_all_source_notifications(ogroup, NULL);
2218 :
2219 : } else {
2220 :
2221 : /* Going down. */
2222 :
2223 0 : gmpr_flush_notifications_group(ogroup);
2224 0 : gmpr_group_notify_clients(ogroup);
2225 : }
2226 :
2227 : /* No need to look at any further input groups. */
2228 :
2229 0 : break;
2230 : }
2231 : }
2232 : }
2233 3416 : }
2234 :
2235 :
2236 : /*
2237 : * gmpr_flush_intf_input_groups
2238 : *
2239 : * Flush the input groups from an interface when it goes down.
2240 : */
2241 : void
2242 1708 : gmpr_flush_intf_input_groups (gmpr_intf *intf)
2243 : {
2244 : gmpr_group *group;
2245 :
2246 : /* Walk all input groups on the interface. */
2247 :
2248 1708 : group = NULL;
2249 : while (TRUE) {
2250 1724 : group = gmpr_next_intf_group(intf, group);
2251 1724 : if (!group)
2252 1708 : break;
2253 :
2254 : /* Got a group. Force an early timeout. */
2255 :
2256 16 : gmpr_timeout_group(group);
2257 : }
2258 1708 : }
|