LCOV - code coverage report
Current view: top level - vnsw/agent/services/multicast/grpmgmt - gmpr_group.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 489 638 76.6 %
Date: 2026-08-03 02:19:58 Functions: 51 55 92.7 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.14