LCOV - code coverage report
Current view: top level - vnsw/agent/services/multicast/grpmgmt - gmpr_engine.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 350 597 58.6 %
Date: 2026-08-03 02:19:58 Functions: 28 44 63.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* $Id: gmpr_engine.c 514187 2012-05-06 12:25:25Z ib-builder $
       2             :  *
       3             :  * gmpr_engine.c - IGMP/MLD Router-Side generic protocol engine
       4             :  *
       5             :  * Dave Katz, March 2008
       6             :  *
       7             :  * Copyright (c) 2008, Juniper Networks, Inc.
       8             :  * All rights reserved.
       9             :  *
      10             :  * This module contains the protocol engine for router-side GMP.
      11             :  */
      12             : #include "gmpx_basic_types.h"
      13             : #include "gmp.h"
      14             : #include "gmpx_environment.h"
      15             : #include "gmp_externs.h"
      16             : #include "gmp_private.h"
      17             : #include "gmp_router.h"
      18             : #include "gmpr_private.h"
      19             : #include "gmpr_trace.h"
      20             : 
      21             : 
      22             : /*
      23             :  * gmpr_process_query_packet
      24             :  *
      25             :  * Process a received query packet.
      26             :  */
      27             : static void
      28           0 : gmpr_process_query_packet(gmpr_intf *intf, gmp_packet *packet)
      29             : {
      30             :     gmpr_instance *instance;
      31             :     gmp_query_packet *query_pkt;
      32             :     gmpr_group *group;
      33             :     gmp_addr_thread_entry *addr_thread_entry;
      34             :     gmp_addr_string *source_addr;
      35             :     gmp_addr_cat_entry *cat_entry;
      36             :     gmp_addr_list_entry *addr_entry;
      37             :     gmpr_group_addr_entry *group_addr;
      38             :     int addr_compare;
      39             : 
      40             :     /* Bail if this is IGMPv1;  we ignore queries in this case. */
      41             : 
      42           0 :     if (intf->rintf_ver == GMP_VERSION_BASIC)
      43           0 :         return;
      44             : 
      45           0 :     instance = intf->rintf_instance;
      46           0 :     query_pkt = &packet->gmp_packet_contents.gmp_packet_query;
      47             : 
      48             :     /* Post a warning if the version does not match. */
      49             : 
      50           0 :     if (intf->rintf_ver != packet->gmp_packet_version ) {
      51           0 :         gmpr_post_event(instance, GMP_VERSION_MISMATCH, intf->rintf_id, 
      52             :                         gmp_untranslate_version(instance->rinst_proto,
      53             :                                                 intf->rintf_ver),
      54             :                         gmp_untranslate_version(instance->rinst_proto,
      55             :                                                 packet->gmp_packet_version));
      56             :     }
      57             : 
      58             :     /*
      59             :      * If the source of the query is all zeroes, ignore it.  It's a snooping
      60             :      * switch trying to elicit state from hosts, and is not the elected
      61             :      * querier.
      62             :      */
      63           0 :     if (gmp_addr_is_zero(&packet->gmp_packet_src_addr,
      64             :                          instance->rinst_addrlen)) {
      65           0 :         return;
      66             :     }
      67             : 
      68             :     /*
      69             :      * The source has a real address.  If we have one too, do the election.
      70             :      * If we don't have an address, it means that we're a snooping switch
      71             :      * and don't do our own queries, so we elect the other guy as querier.
      72             :      */
      73           0 :     if (!gmp_addr_is_zero(&intf->rintf_local_addr, instance->rinst_addrlen)) {
      74             : 
      75             :         /* Compare addresses with the querier. */
      76             : 
      77           0 :         addr_compare = memcmp(packet->gmp_packet_src_addr.gmp_addr,
      78           0 :                               intf->rintf_local_addr.gmp_addr,
      79           0 :                               instance->rinst_addrlen);
      80             : 
      81             :         /* If the querier has a higher address, or is us, bail. */
      82             : 
      83           0 :         if (addr_compare >= 0)
      84           0 :             return;
      85             :     }
      86             : 
      87             :     /* Update the querier status. */
      88             : 
      89           0 :     gmpr_update_querier(intf, &packet->gmp_packet_src_addr, FALSE);
      90             : 
      91             :     /* Update the robustness variable. */
      92             : 
      93           0 :     gmpr_intf_update_robustness(intf, query_pkt->gmp_query_qrv);
      94             : 
      95             :     /* Update the query interval if this packet carries QQI. */
      96             : 
      97           0 :     if (packet->gmp_packet_version == GMP_VERSION_SOURCES)
      98           0 :         gmpr_intf_update_query_ivl(intf, query_pkt->gmp_query_qqi);
      99             : 
     100             :     /* Start the other-querier timer now that we've updated the intervals. */
     101             : 
     102           0 :     gmpx_start_timer(intf->rintf_other_querier_present,
     103             :                      intf->rintf_other_querier_ivl, 0);
     104             : 
     105             :     /*
     106             :      * If the suppress-router-side-processing flag is clear and a
     107             :      * group is present, take a look at the rest of the packet.
     108             :      */
     109           0 :     if (!query_pkt->gmp_query_suppress && query_pkt->gmp_query_group_query) {
     110             : 
     111             :         /* Look up the group. */
     112             : 
     113           0 :         group = gmpr_group_lookup(intf, query_pkt->gmp_query_group.gmp_addr);
     114           0 :         if (group) {
     115             : 
     116             :             /* Got a group.  See if this is a GSS query. */
     117             : 
     118           0 :             if (query_pkt->gmp_query_rcv_srcs) {
     119             : 
     120             :                 /*
     121             :                  * GSS query.  Walk the source address list, updating
     122             :                  * the source timers.
     123             :                  */
     124           0 :                 addr_thread_entry = NULL;
     125             :                 while (TRUE) {
     126           0 :                     source_addr = gmp_next_addr_thread_addr(
     127             :                                             query_pkt->gmp_query_rcv_srcs,
     128             :                                             &addr_thread_entry);
     129           0 :                     if (!source_addr)
     130           0 :                         break;
     131             : 
     132             :                     /* Got a source address.  Look up the catalog entry. */
     133             : 
     134             :                     cat_entry =
     135           0 :                         gmp_lookup_addr_cat_entry(&instance->rinst_addr_cat,
     136           0 :                                                   source_addr->gmp_addr);
     137             : 
     138             :                     /*
     139             :                      * If there's a catalog entry, look up the source
     140             :                      * on the running-timer list.
     141             :                      */
     142           0 :                     if (cat_entry) {
     143           0 :                         addr_entry = gmp_lookup_addr_entry(
     144             :                                            &group->rgroup_src_addr_running,
     145             :                                            cat_entry->adcat_ent_ord);
     146             : 
     147             :                         /* If the entry is found, update the timer. */
     148             : 
     149           0 :                         if (addr_entry) {
     150             :                             group_addr =
     151           0 :                                 gmpr_addr_entry_to_group_entry(addr_entry);
     152           0 :                             gmpx_start_timer(group_addr->rgroup_addr_timer,
     153             :                                              intf->rintf_lmqt, 0);
     154             :                         }
     155             :                     }
     156             :                 }
     157             : 
     158             :             } else {
     159             : 
     160             :                 /* Group-specific only.  Update the group timer. */
     161             : 
     162           0 :                 gmpx_start_timer(group->rgroup_group_timer, intf->rintf_lmqt,
     163             :                                  0);
     164             :             }
     165             :         }
     166             :     }
     167             : }
     168             : 
     169             : 
     170             : /*
     171             :  * gmpr_enqueue_group_query
     172             :  *
     173             :  * Enqueues a group query.
     174             :  */
     175             : static void
     176          27 : gmpr_enqueue_group_query (gmpr_group *group)
     177             : {
     178             :     gmpr_intf *intf;
     179             : 
     180          27 :     intf = group->rgroup_intf;
     181             : 
     182             :     /* Bail if this version doesn't support group queries. */
     183             : 
     184          27 :     if (group->rgroup_compatibility_mode < GMP_VERSION_LEAVES)
     185           0 :         return;
     186             : 
     187             :     /* Bail if we're doing fast leaves on this interface. */
     188             : 
     189          27 :     if (intf->rintf_fast_leaves)
     190           0 :         return;
     191             : 
     192             :     /* Lower the group timer to LMQT. */
     193             : 
     194          27 :     if (!gmpx_timer_running(group->rgroup_group_timer) || 
     195          27 :         (gmpx_timer_time_remaining(group->rgroup_group_timer) >
     196          27 :          intf->rintf_lmqt)) {
     197          16 :         gmpx_start_timer(group->rgroup_group_timer, intf->rintf_lmqt, 0);
     198             :     }
     199             : 
     200             :     /* Bail if we're suppressing GS/GSS queries on this interface. */
     201             : 
     202          27 :     if (intf->rintf_suppress_gs_query)
     203           0 :         return;
     204             : 
     205             :     /* Set the retransmission count. */
     206             : 
     207          27 :     group->rgroup_query_rexmit_count = intf->rintf_lmq_count;
     208             : 
     209             :     /* Kick the query timer. */
     210             : 
     211          27 :     gmpx_start_timer(group->rgroup_query_timer, 0, 0);
     212             : }
     213             : 
     214             : 
     215             : /*
     216             :  * gmpr_create_running_list_entry
     217             :  *
     218             :  * Create a new entry for the running-timer list and create a timer
     219             :  * for it.
     220             :  *
     221             :  * Returns a pointer to the new entry, or NULL if out of memory or the
     222             :  * channel limit was hit.
     223             :  */
     224             : static gmpr_group_addr_entry *
     225          33 : gmpr_create_running_list_entry (gmpr_group *group, bv_bitnum_t bitnum)
     226             : {
     227             :     gmp_addr_list_entry *addr_entry;
     228             : 
     229             :     /* Enqueue a new entry. */
     230             : 
     231          33 :     addr_entry = gmp_create_addr_list_entry(&group->rgroup_src_addr_running,
     232             :                                             bitnum);
     233          33 :     return gmpr_addr_entry_to_group_entry(addr_entry);
     234             : }
     235             : 
     236             : 
     237             : /*
     238             :  * gmpr_copy_reporter
     239             :  *
     240             :  * Copy the last reporter address from the group to a source.  Whenever
     241             :  * a group is mentioned in a report, the reporter address is written into
     242             :  * the group entry.  When set operations result, they are peppered with calls
     243             :  * to this routine to put the reporter's address into the source.  The group
     244             :  * happens to be a convenient place that we know was just updated.
     245             :  */
     246             : static void
     247         121 : gmpr_copy_reporter (gmpr_group *group,
     248             :                     gmpr_group_addr_entry *group_addr_entry)
     249             : {
     250         121 :     memmove(group_addr_entry->rgroup_addr_last_reporter.gmp_addr,
     251         121 :         group->rgroup_last_reporter.gmp_addr,
     252         121 :         group->rgroup_intf->rintf_instance->rinst_addrlen);
     253         121 : }
     254             : 
     255             : 
     256             : /*
     257             :  * gmpr_move_include_cb
     258             :  *
     259             :  * Vector walk callback to move entries to the include list.  We look
     260             :  * up the address in the stopped-timer list, and move it to the
     261             :  * running-timer list if it's there.  If the entry is in the
     262             :  * running-timer list, we update the timer.  If it's not there, we add
     263             :  * it.
     264             :  */
     265             : static boolean
     266           0 : gmpr_move_include_cb (void *context, bv_bitnum_t bitnum,
     267             :                       boolean new_val GMPX_UNUSED,
     268             :                       boolean old_val GMPX_UNUSED)
     269             : {
     270             :     gmpr_group *group;
     271             :     gmp_addr_list_entry *addr_entry;
     272             :     gmpr_group_addr_entry *group_addr_entry;
     273             : 
     274           0 :     group = context;
     275             : 
     276             :     /* See if the address is present on the stopped-timer list. */
     277             : 
     278           0 :     if (gmp_addr_in_list(&group->rgroup_src_addr_stopped, bitnum)) {
     279             : 
     280             :         /* Entry is in the stopped list list.  Look it up. */
     281             : 
     282           0 :         addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_stopped,
     283             :                                            bitnum);
     284           0 :         gmpx_assert(addr_entry);
     285             : 
     286             :         /* Move it to the running list. */
     287             : 
     288           0 :         gmp_move_addr_list_entry(&group->rgroup_src_addr_running, addr_entry);
     289             : 
     290             :         /* Update the OIF, as the source is no longer excluded. */
     291             : 
     292           0 :         group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
     293           0 :         gmpr_update_source_oif(group_addr_entry, OIF_DELETE);
     294             : 
     295             :     } else {
     296             : 
     297             :         /*
     298             :          * Not in the stopped-timer list.  See if the address is
     299             :          * present in the running-timer list.  If it is, look it up.
     300             :          * If not, allocate a new entry and put it into the list.
     301             :          */
     302           0 :         if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
     303             : 
     304             :             /* Not in the list.  Allocate a new one and put it in there. */
     305             : 
     306           0 :             group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
     307           0 :             if (!group_addr_entry)
     308           0 :                 return FALSE;           /* Out of memory or limit hit */
     309             : 
     310             :         } else {
     311             : 
     312             :             /* Entry is in the list.  Look it up. */
     313             : 
     314           0 :             addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
     315             :                                                bitnum);
     316           0 :             gmpx_assert(addr_entry);
     317           0 :             group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
     318             :         }
     319             :     }
     320             : 
     321             :     /* Bump the timer. */
     322             : 
     323           0 :     gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
     324           0 :                      group->rgroup_intf->rintf_group_membership_ivl, 0);
     325             : 
     326             :     /* Copy the reporter address from the group. */
     327             : 
     328           0 :     gmpr_copy_reporter(group, group_addr_entry);
     329             : 
     330           0 :     return FALSE;
     331             : }
     332             : 
     333             : 
     334             : /*
     335             :  * gmpr_move_include
     336             :  *
     337             :  * Move the source list from the stopped-timer to the running-timer
     338             :  * list, creating new entries for any sources not present on either list.
     339             :  *
     340             :  * Bumps the source timer of each source listed in the record.
     341             :  */
     342             : static void
     343           0 : gmpr_move_include (gmpr_group *group, gmp_addr_vect *source_vect)
     344             : {
     345             :     /* Walk the vector.  The callback does all the work. */
     346             : 
     347           0 :     gmp_addr_vect_walk(source_vect, gmpr_move_include_cb, group);
     348           0 : }
     349             : 
     350             : 
     351             : /*
     352             :  * gmpr_delete_include_cb
     353             :  *
     354             :  * Vector walk for culling the running-timer list.  We delete any
     355             :  * entry that we're passed.
     356             :  */
     357             : static boolean
     358           0 : gmpr_delete_include_cb (void *context, bv_bitnum_t bitnum,
     359             :                         boolean new_val GMPX_UNUSED,
     360             :                         boolean old_val GMPX_UNUSED)
     361             : {
     362             :     gmpr_group *group;
     363             :     gmp_addr_list_entry *addr_entry;
     364             : 
     365           0 :     group = context;
     366             : 
     367             :     /* Look up the entry. */
     368             : 
     369           0 :     addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
     370             :                                        bitnum);
     371           0 :     gmpx_assert(addr_entry);
     372             : 
     373             :     /* Delete it.  The callback will free the timer. */
     374             : 
     375           0 :     gmp_delete_addr_list_entry(addr_entry);
     376             : 
     377           0 :     return FALSE;
     378             : }
     379             : 
     380             : 
     381             : /*
     382             :  * gmpr_delete_exclude_cb
     383             :  *
     384             :  * Vector walk for culling the stopped-timer list.  We move any
     385             :  * entry we're passed to the deleted list and notify the clients,
     386             :  * since the source is no longer being excluded.
     387             :  */
     388             : static boolean
     389           0 : gmpr_delete_exclude_cb (void *context, bv_bitnum_t bitnum,
     390             :                         boolean new_val GMPX_UNUSED,
     391             :                         boolean old_val GMPX_UNUSED)
     392             : {
     393             :     gmpr_group *group;
     394             :     gmp_addr_list_entry *addr_entry;
     395             :     gmpr_group_addr_entry *group_addr;
     396             : 
     397           0 :     group = context;
     398             : 
     399             :     /* Look up the entry. */
     400             : 
     401           0 :     addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_stopped,
     402             :                                        bitnum);
     403           0 :     gmpx_assert(addr_entry);
     404             : 
     405             :     /* Delink any OIF entry. */
     406             : 
     407           0 :     group_addr = gmpr_addr_entry_to_group_entry(addr_entry);
     408           0 :     gmpr_update_source_oif(group_addr, OIF_DELETE);
     409             : 
     410             :     /* Delete the source. */
     411             : 
     412           0 :     gmp_delete_addr_list_entry(addr_entry);
     413             : 
     414           0 :     return FALSE;
     415             : }
     416             : 
     417             : 
     418             : /*
     419             :  * gmpr_add_exclude_cb
     420             :  *
     421             :  * Vector walk callback for adding a source to the exclude
     422             :  * (stopped-timer) list.  We assume that this is a new source.
     423             :  */
     424             : static boolean
     425           0 : gmpr_add_exclude_cb (void *context, bv_bitnum_t bitnum,
     426             :                      boolean new_val GMPX_UNUSED,
     427             :                      boolean old_val GMPX_UNUSED)
     428             : {
     429             :     gmpr_group *group;
     430             :     gmp_addr_list_entry *addr_entry;
     431             :     gmpr_group_addr_entry *group_addr;
     432             : 
     433           0 :     group = context;
     434             : 
     435             :     /* Allocate a new entry and put it in the stopped list. */
     436             : 
     437           0 :     addr_entry = gmp_create_addr_list_entry(&group->rgroup_src_addr_stopped,
     438             :                                             bitnum);
     439           0 :     if (!addr_entry)
     440           0 :         return FALSE;                   /* No memory or limit hit */
     441             : 
     442             :     /* Copy the reporter address from the group. */
     443             : 
     444           0 :     group_addr = gmpr_addr_entry_to_group_entry(addr_entry);
     445           0 :     gmpr_copy_reporter(group, group_addr);
     446             : 
     447           0 :     return FALSE;
     448             : }
     449             : 
     450             : 
     451             : /*
     452             :  * gmpr_add_include_cb
     453             :  *
     454             :  * Vector walk callback for adding a set of sources to the include
     455             :  * list.  We look up the address in the timer-running list, add it if
     456             :  * it's not there, and bump up the timer.
     457             :  */
     458             : static boolean
     459          59 : gmpr_add_include_cb (void *context, bv_bitnum_t bitnum,
     460             :                      boolean new_val GMPX_UNUSED,
     461             :                      boolean old_val GMPX_UNUSED)
     462             : {
     463             :     gmpr_group *group;
     464             :     gmp_addr_list_entry *addr_entry;
     465             :     gmpr_group_addr_entry *group_addr_entry;
     466             : 
     467          59 :     group = context;
     468             : 
     469             :     /*
     470             :      * See if the address is present in the running-timer list.  If it is,
     471             :      * look it up.  If not, allocate a new entry and put it into the list.
     472             :      */
     473          59 :     if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
     474             : 
     475             :         /* Not in the list.  Allocate a new one and put it in there. */
     476             : 
     477          33 :         group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
     478          33 :         if (!group_addr_entry)
     479           0 :             return FALSE;               /* Out of memory or limit hit */
     480          33 :         addr_entry = &group_addr_entry->rgroup_addr_entry;
     481             : 
     482             :         /* Update the OIF. */
     483             : 
     484          33 :         gmpr_update_source_oif(group_addr_entry, OIF_UPDATE);
     485             : 
     486             :     } else {
     487             : 
     488             :         /* Entry is in the list.  Look it up. */
     489             : 
     490          26 :         addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
     491             :                                            bitnum);
     492          26 :         gmpx_assert(addr_entry);
     493             :     }
     494             : 
     495          59 :     group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
     496             : 
     497             :     /* Bump the timer. */
     498             : 
     499          59 :     gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
     500          59 :                      group->rgroup_intf->rintf_group_membership_ivl, 0);
     501             : 
     502             :     /* Copy the reporter address from the group. */
     503             : 
     504          59 :     gmpr_copy_reporter(group, group_addr_entry);
     505             : 
     506          59 :     return FALSE;
     507             : }
     508             : 
     509             : 
     510             : /*
     511             :  * gmpr_enqueue_gss_query
     512             :  *
     513             :  * Do the work to set up a GSS query for a (group,source) pair.
     514             :  */
     515             : static void
     516          39 : gmpr_enqueue_gss_query (gmpr_group *group,
     517             :                         gmpr_group_addr_entry *group_addr_entry)
     518             : {
     519             :     gmpr_intf *intf;
     520             : 
     521          39 :     intf = group->rgroup_intf;
     522             : 
     523             :     /* Drop the source timer if appropriate. */
     524             : 
     525          39 :     if (!gmpx_timer_running(group_addr_entry->rgroup_addr_timer) ||
     526          39 :         (gmpx_timer_time_remaining(group_addr_entry->rgroup_addr_timer) >
     527          39 :                                   intf->rintf_lmqt)) {
     528          39 :         gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
     529             :                          intf->rintf_lmqt, 0);
     530             :     }
     531             : 
     532             :     /* Bail if the current version doesn't support GSS queries. */
     533             : 
     534          39 :     if (group->rgroup_compatibility_mode < GMP_VERSION_SOURCES)
     535           0 :         return;
     536             : 
     537             :     /* Bail if we're doing fast leaves on this interface. */
     538             : 
     539          39 :     if (intf->rintf_fast_leaves)
     540           0 :         return;
     541             : 
     542             :     /* Bail if we're suppressing GS and GSS queries. */
     543             : 
     544          39 :     if (intf->rintf_suppress_gs_query)
     545           0 :         return;
     546             : 
     547             :     /* Bump the rexmit count back up. */
     548             : 
     549          39 :     group_addr_entry->rgroup_addr_rexmit_count = intf->rintf_lmq_count;
     550             : 
     551             :     /* Set the GSS timer to expire immediately if it's not already running. */
     552             : 
     553          39 :     if (!gmpx_timer_running(group->rgroup_gss_query_timer))
     554          15 :         gmpx_start_timer(group->rgroup_gss_query_timer, 0, 0);
     555             : }
     556             : 
     557             : 
     558             : /*
     559             :  * gmpr_send_gss_query_cb
     560             :  *
     561             :  * Vector callback to send a group-and-source specific query.
     562             :  */
     563             : static boolean
     564          62 : gmpr_send_gss_query_cb (void *context, bv_bitnum_t bitnum,
     565             :                         boolean new_val GMPX_UNUSED,
     566             :                         boolean old_val GMPX_UNUSED)
     567             : {
     568             :     gmpr_group *group;
     569             :     gmp_addr_list_entry *addr_entry;
     570             :     gmpr_group_addr_entry *group_addr_entry;
     571             :     gmpr_intf *intf;
     572             : 
     573          62 :     group = context;
     574          62 :     intf = group->rgroup_intf;
     575             : 
     576             :     /* Look up the entry in the running-timer list.  It better be there. */
     577             : 
     578          62 :     addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
     579             :                                        bitnum);
     580          62 :     gmpx_assert(addr_entry);
     581          62 :     group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
     582             : 
     583             :     /* Process the source if the source timer is greater than LMQT. */
     584             : 
     585          62 :     if (gmpx_timer_time_remaining(group_addr_entry->rgroup_addr_timer) >
     586          62 :         intf->rintf_lmqt) {
     587             : 
     588             :         /* Enqueue the source for the GSS query. */
     589             : 
     590          39 :         gmpr_enqueue_gss_query(group, group_addr_entry);
     591             :     }
     592             : 
     593             :     /* Copy the reporter address from the group. */
     594             : 
     595          62 :     gmpr_copy_reporter(group, group_addr_entry);
     596             : 
     597          62 :     return FALSE;
     598             : }
     599             : 
     600             : 
     601             : /*
     602             :  * gmpr_process_state_chg_ex_in
     603             :  *
     604             :  * Process a state-change record of TO_IN type with a filter state of
     605             :  * Exclude.
     606             :  *
     607             :  * We do a bunch of set math.
     608             :  */
     609             : static void
     610          27 : gmpr_process_state_chg_ex_in (gmpr_group *group, gmp_addr_vect *source_vect)
     611             : {
     612             :     /*
     613             :      * First, form the set (X-A) and send a query out on each member.
     614             :      * These are all of the addresses on the running-timer list that
     615             :      * were not mentioned in the record.
     616             :      */
     617          27 :     if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
     618             :                             source_vect, NULL, gmpr_send_gss_query_cb, group,
     619             :                             BV_CALL_SET) < 0)
     620           0 :         return;                         /* Out of memory */
     621             : 
     622             :     /*
     623             :      * Now walk the source list, moving any matching entry from the
     624             :      * stopped-timer list to the running-timer list, and creating
     625             :      * new entries for anything not found in either list.
     626             :      */
     627          27 :     gmp_addr_vect_walk(source_vect, gmpr_move_include_cb, group);
     628             : 
     629             :     /* Finally, enqueue a group-specific query. */
     630             : 
     631          27 :     gmpr_enqueue_group_query(group);
     632             : }
     633             : 
     634             : 
     635             : /*
     636             :  * gmpr_chg_ex_ex_cb
     637             :  *
     638             :  * Vector walk to determine new running-timer entries for the
     639             :  * Exclude/Exclude state-change case.  We are given an entry on the
     640             :  * new source list, and add it to the running-timer list if it is not
     641             :  * found in either the running-timer or stopped-timer lists.
     642             :  *
     643             :  * This does not trigger a state change, since the source will still be
     644             :  * received whether the entry is in the running-timer list or it is not
     645             :  * in any list.
     646             :  */
     647             : static boolean
     648           0 : gmpr_chg_ex_ex_cb (void *context, bv_bitnum_t bitnum,
     649             :                    boolean new_val GMPX_UNUSED, boolean old_val GMPX_UNUSED)
     650             : {
     651             :     gmpr_group *group;
     652             :     gmpr_group_addr_entry *group_addr_entry;
     653             : 
     654           0 :     group = context;
     655             : 
     656             :     /* See if the address is present in the running-timer list. */
     657             : 
     658           0 :     if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
     659             : 
     660             :         /*
     661             :          * Not in the running-timer list.  See if it's in the
     662             :          * stopped-timer list.
     663             :          */
     664           0 :         if (!gmp_addr_in_list(&group->rgroup_src_addr_stopped, bitnum)) {
     665             : 
     666             :             /*
     667             :              * Not in either list.  Allocate a new one and put it in
     668             :              * the running-timer list.
     669             :              */
     670           0 :             group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
     671           0 :             if (!group_addr_entry)
     672           0 :                 return FALSE;           /* Out of memory or limit hit */
     673           0 :             gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
     674             :                      gmpx_timer_time_remaining(group->rgroup_group_timer), 0);
     675             : 
     676             :             /* Copy the reporter address from the group. */
     677             : 
     678           0 :             gmpr_copy_reporter(group, group_addr_entry);
     679             :         }
     680             :     }
     681             : 
     682           0 :     return FALSE;
     683             : }
     684             : 
     685             : 
     686             : /*
     687             :  * gmpr_process_state_chg_ex_ex
     688             :  *
     689             :  * Process a state-change record of TO_EX type with a filter state of
     690             :  * Exclude.
     691             :  *
     692             :  * We do a bunch of set math.
     693             :  */
     694             : static void
     695           0 : gmpr_process_state_chg_ex_ex (gmpr_group *group, gmp_addr_vect *source_vect)
     696             : {
     697             :     /*
     698             :      * OK, this one is really nutty.  By the spec, set A is the set of
     699             :      * addresses in the new report, X is the set in the running-timer
     700             :      * list, and Y is the set in the stopped-timer list.  By definition,
     701             :      * X and Y are non-overlapping.
     702             :      *
     703             :      * First, we delete X-A.  This eliminates all addresses from the
     704             :      * running-timer list, except for (A*X).
     705             :      */
     706           0 :     if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
     707             :                             source_vect, NULL, gmpr_delete_include_cb, group,
     708             :                             BV_CALL_SET) < 0)
     709           0 :         return;                         /* Out of memory */
     710             : 
     711             :     /*
     712             :      * Now, delete Y-A.  This eliminates all addresses from the stopped-timer
     713             :      * list, except for (A*Y).
     714             :      */
     715           0 :     if (gmp_addr_vect_minus(&group->rgroup_src_addr_stopped.addr_vect,
     716             :                             source_vect, NULL, gmpr_delete_exclude_cb, group,
     717             :                             BV_CALL_SET) < 0)
     718           0 :         return;                         /* Out of memory */
     719             : 
     720             :     /*
     721             :      * Next, walk A, adding to the running list any address not found
     722             :      * on what remains of the running and stopped lists.  This forms the
     723             :      * set (A-X-Y).  The net result is that the running list contains
     724             :      * (A-X-Y) + (A*X), which is the same as (A-Y).  Whew.
     725             :      */
     726           0 :     gmp_addr_vect_walk(source_vect, gmpr_chg_ex_ex_cb, group);
     727             : 
     728             :     /* Now send a query on everything on the running list, which is (A-Y). */
     729             : 
     730           0 :     gmp_addr_vect_walk(&group->rgroup_src_addr_running.addr_vect,
     731             :                        gmpr_send_gss_query_cb, group);
     732             : 
     733             :     /* Finally, restart the group timer. */
     734             : 
     735           0 :     gmpx_start_timer(group->rgroup_group_timer,
     736           0 :                      group->rgroup_intf->rintf_group_membership_ivl, 0);
     737             : }
     738             : 
     739             : 
     740             : /*
     741             :  * gmpr_state_chg_ex_block_cb
     742             :  *
     743             :  * Vector callback for the receipt of a BLOCK record in Exclude state.
     744             :  *
     745             :  * We're called for each source in the BLOCK list that is not in the
     746             :  * stopped-timer list.
     747             :  */
     748             : static boolean
     749           0 : gmpr_state_chg_ex_block_cb (void *context, bv_bitnum_t bitnum,
     750             :                             boolean new_val GMPX_UNUSED,
     751             :                             boolean old_val GMPX_UNUSED)
     752             : {
     753             :     gmpr_group *group;
     754             :     gmp_addr_list_entry *addr_entry;
     755             :     gmpr_group_addr_entry *group_addr_entry;
     756             :     gmpr_intf *intf;
     757             : 
     758           0 :     group = context;
     759           0 :     intf = group->rgroup_intf;
     760             : 
     761             :     /* If the entry is not in the running-timer list, create it. */
     762             : 
     763           0 :     if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
     764             : 
     765             :         /*
     766             :          * Not in the list.  Allocate a new one and put it in the
     767             :          * running-timer list.
     768             :          */
     769           0 :         group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
     770           0 :         if (!group_addr_entry)
     771           0 :             return FALSE;               /* Out of memory or limit hit */
     772           0 :         addr_entry = &group_addr_entry->rgroup_addr_entry;
     773           0 :         gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
     774             :                          gmpx_timer_time_remaining(group->rgroup_group_timer),
     775             :                          0);
     776             : 
     777             :     } else {
     778             : 
     779             :         /* Look up the entry in the running-timer list.  It better be there. */
     780             : 
     781           0 :         addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
     782             :                                            bitnum);
     783           0 :         gmpx_assert(addr_entry);
     784           0 :         group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
     785             :     }
     786             : 
     787             :     /* Send a query if the source if the source timer is greater than LMQT. */
     788             : 
     789           0 :     if (gmpx_timer_time_remaining(group_addr_entry->rgroup_addr_timer) >
     790           0 :         intf->rintf_lmqt) {
     791             : 
     792             :         /* Enqueue the source for the GSS query. */
     793             : 
     794           0 :         gmpr_enqueue_gss_query(group, group_addr_entry);
     795             :     }
     796             : 
     797             :     /* Copy the reporter address from the group. */
     798             : 
     799           0 :     gmpr_copy_reporter(group, group_addr_entry);
     800             : 
     801           0 :     return FALSE;
     802             : }
     803             : 
     804             : 
     805             : /*
     806             :  * gmpr_process_state_chg_ex_block
     807             :  *
     808             :  * Process a state-change record of type BLOCK with a filter state of
     809             :  * Exclude.
     810             :  */
     811             : static void
     812           0 : gmpr_process_state_chg_ex_block (gmpr_group *group, gmp_addr_vect *source_vect)
     813             : {
     814             :     /*
     815             :      * Form the set (A-Y) (all addresses mentioned in the BLOCK that are
     816             :      * not in the stopped-timer list.)  Add these to the running-timer list,
     817             :      * setting the timers on those not already in that list.  Also, send
     818             :      * a query on each of them.
     819             :      */
     820           0 :     if (gmp_addr_vect_minus(source_vect,
     821             :                             &group->rgroup_src_addr_stopped.addr_vect,
     822             :                             NULL, gmpr_state_chg_ex_block_cb, group,
     823             :                             BV_CALL_SET) < 0)
     824           0 :         return;                         /* Out of memory */
     825             : }
     826             : 
     827             : 
     828             : /*
     829             :  * gmpr_process_state_chg_in_ex
     830             :  *
     831             :  * Process a state-change record of type TO_EX with a filter state of
     832             :  * Include.
     833             :  */
     834             : static void
     835           0 : gmpr_process_state_chg_in_ex (gmpr_group *group, gmp_addr_vect *source_vect)
     836             : {
     837           0 :     gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
     838             : 
     839             :     /* Change the filter mode. */
     840             : 
     841           0 :     group->rgroup_filter_mode = GMP_FILTER_MODE_EXCLUDE;
     842             : 
     843             :     /*
     844             :      * Form the set (B-A) (all newly-mentioned sources) and stick them
     845             :      * straight into the stopped-timer list.
     846             :      */
     847           0 :     if (gmp_addr_vect_minus(source_vect,
     848             :                             &group->rgroup_src_addr_running.addr_vect, NULL,
     849             :                             gmpr_add_exclude_cb, group, BV_CALL_SET) < 0)
     850           0 :         return;                         /* Out of memory */
     851             : 
     852             :     /*
     853             :      * Form the set (A-B) (all addresses mentioned in the Include set
     854             :      * that are not in the TO_IN set) and delete them.
     855             :      */
     856           0 :     if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
     857             :                             source_vect, NULL, gmpr_delete_include_cb, group,
     858             :                             BV_CALL_SET) < 0)
     859           0 :         return;                         /* Out of memory */
     860             : 
     861             :     /*
     862             :      * Send a query for (A*B), which is what's left on the running-timer
     863             :      * list.
     864             :      */
     865           0 :     gmp_addr_vect_walk(&group->rgroup_src_addr_running.addr_vect,
     866             :                        gmpr_send_gss_query_cb, group);
     867             : 
     868             :     /* Restart the group timer. */
     869             : 
     870           0 :     gmpx_start_timer(group->rgroup_group_timer,
     871           0 :                      group->rgroup_intf->rintf_group_membership_ivl, 0);
     872             : 
     873             :     /* Update the OIF. */
     874             : 
     875           0 :     gmpr_update_oif_mode_change(group);
     876             : }
     877             : 
     878             : 
     879             : /*
     880             :  * gmpr_process_state_chg_in_in
     881             :  *
     882             :  * Process a state-change record of type TO_IN with a filter state of
     883             :  * Include.
     884             :  */
     885             : static void
     886           0 : gmpr_process_state_chg_in_in (gmpr_group *group, gmp_addr_vect *source_vect)
     887             : {
     888           0 :     gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
     889             : 
     890             :     /* Add any new sources to the Include (running-timer) list. */
     891             : 
     892           0 :     gmp_addr_vect_walk(source_vect, gmpr_add_include_cb, group);
     893             : 
     894             :     /*
     895             :      * Form the set (A-B) (all addresses mentioned in the Include set
     896             :      * that are not in the TO_IN set) and send a Query on them.
     897             :      */
     898           0 :     if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
     899             :                             source_vect, NULL, gmpr_send_gss_query_cb, group,
     900             :                             BV_CALL_SET) < 0)
     901           0 :         return;                         /* Out of memory */
     902             : }
     903             : 
     904             : 
     905             : /*
     906             :  * gmpr_process_state_chg_in_block
     907             :  *
     908             :  * Process a state-change record of type BLOCK with a filter state of
     909             :  * Include.
     910             :  */
     911             : static void
     912          30 : gmpr_process_state_chg_in_block (gmpr_group *group, gmp_addr_vect *source_vect)
     913             : {
     914             :     /*
     915             :      * Form the set (A*B) (all addresses mentioned in the BLOCK that are
     916             :      * currently in the Include set) and send a Query on them.
     917             :      */
     918          30 :     gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
     919          30 :     if (gmp_addr_vect_inter(&group->rgroup_src_addr_running.addr_vect,
     920             :                             source_vect, NULL, gmpr_send_gss_query_cb, group,
     921             :                             BV_CALL_SET) < 0)
     922           0 :         return;                         /* Out of memory */
     923             : }
     924             : 
     925             : 
     926             : /*
     927             :  * gmpr_add_include
     928             :  *
     929             :  * Process a current-state record of type IS_IN or a state-change
     930             :  * record of ALLOW with a filter state of Include.
     931             :  *
     932             :  * Update the timer-running list to include any new sources in the
     933             :  * record, and bump the source timer of each source listed in the
     934             :  * record.
     935             :  */
     936             : static void
     937          29 : gmpr_add_include (gmpr_group *group, gmp_addr_vect *source_vect)
     938             : {
     939             :     /* Walk the vector.  The callback does all the work. */
     940             : 
     941          29 :     gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
     942          29 :     gmp_addr_vect_walk(source_vect, gmpr_add_include_cb, group);
     943          29 : }
     944             : 
     945             : 
     946             : /*
     947             :  * gmpr_process_state_chg_rcrd
     948             :  *
     949             :  * Process a state-change record.
     950             :  */
     951             : static void
     952          57 : gmpr_process_state_chg_rcrd (gmpr_group *group,
     953             :                              gmp_report_rectype rec_type,
     954             :                              gmp_addr_vect *source_vect)
     955             : {
     956             :     /*
     957             :      * Process based on our current filter state, combined with the
     958             :      * received record type.
     959             :      */
     960          57 :     switch (group->rgroup_filter_mode) {
     961          30 :       case GMP_FILTER_MODE_INCLUDE:
     962             :         switch (rec_type) {
     963           0 :           case GMP_RPT_ALLOW:
     964           0 :             gmpr_add_include(group, source_vect);
     965           0 :             break;
     966             : 
     967          30 :           case GMP_RPT_BLOCK:
     968          30 :             gmpr_process_state_chg_in_block(group, source_vect);
     969          30 :             break;
     970             : 
     971           0 :           case GMP_RPT_TO_IN:
     972           0 :             gmpr_process_state_chg_in_in(group, source_vect);
     973           0 :             break;
     974             : 
     975           0 :           case GMP_RPT_TO_EX:
     976           0 :             gmpr_process_state_chg_in_ex(group, source_vect);
     977           0 :             break;
     978             : 
     979           0 :           default:
     980           0 :             gmpx_assert(FALSE);
     981             :             break;
     982             :         }
     983          30 :         break;
     984             : 
     985          27 :       case GMP_FILTER_MODE_EXCLUDE:
     986             :         switch (rec_type) {
     987           0 :           case GMP_RPT_ALLOW:
     988           0 :             gmpr_move_include(group, source_vect);
     989           0 :             break;
     990             : 
     991           0 :           case GMP_RPT_BLOCK:
     992           0 :             gmpr_process_state_chg_ex_block(group, source_vect);
     993           0 :             break;
     994             : 
     995          27 :           case GMP_RPT_TO_IN:
     996          27 :             gmpr_process_state_chg_ex_in(group, source_vect);
     997          27 :             break;
     998             : 
     999           0 :           case GMP_RPT_TO_EX:
    1000           0 :             gmpr_process_state_chg_ex_ex(group, source_vect);
    1001           0 :             break;
    1002             : 
    1003           0 :           default:
    1004           0 :             gmpx_assert(FALSE);
    1005             :             break;
    1006             :         }
    1007          27 :         break;
    1008             : 
    1009           0 :       default:
    1010           0 :         gmpx_assert(FALSE);
    1011             :     }
    1012          57 : }
    1013             : 
    1014             : 
    1015             : /*
    1016             :  * gmpr_ex_ex_new_cb
    1017             :  *
    1018             :  * Vector walk to determine new running-timer entries for the
    1019             :  * Exclude/Exclude case.  We are given an entry on the new source
    1020             :  * list, and add it to the running-timer list if it is not found in
    1021             :  * either the running-timer or stopped-timer lists.
    1022             :  *
    1023             :  * This does not trigger a state change, since the source will still be
    1024             :  * received whether the entry is in the running-timer list or it is not
    1025             :  * in any list.
    1026             :  */
    1027             : static boolean
    1028           0 : gmpr_ex_ex_new_cb (void *context, bv_bitnum_t bitnum,
    1029             :                    boolean new_val GMPX_UNUSED, boolean old_val GMPX_UNUSED)
    1030             : {
    1031             :     gmpr_group *group;
    1032             :     gmpr_group_addr_entry *group_addr_entry;
    1033             : 
    1034           0 :     group = context;
    1035             : 
    1036             :     /* See if the address is present in the running-timer list. */
    1037             : 
    1038           0 :     if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
    1039             : 
    1040             :         /*
    1041             :          * Not in the running-timer list.  See if it's in the
    1042             :          * stopped-timer list.
    1043             :          */
    1044           0 :         if (!gmp_addr_in_list(&group->rgroup_src_addr_stopped, bitnum)) {
    1045             : 
    1046             :             /*
    1047             :              * Not in either list.  Allocate a new one and put it in
    1048             :              * the running-timer list.
    1049             :              */
    1050           0 :             group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
    1051           0 :             if (!group_addr_entry)
    1052           0 :                 return FALSE;           /* Out of memory or limit hit */
    1053           0 :             gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
    1054           0 :                              group->rgroup_intf->rintf_group_membership_ivl,
    1055             :                              0);
    1056             : 
    1057             :             /* Copy the reporter address from the group. */
    1058             : 
    1059           0 :             gmpr_copy_reporter(group, group_addr_entry);
    1060             :         }
    1061             :     }
    1062             : 
    1063           0 :     return FALSE;
    1064             : }
    1065             : 
    1066             : 
    1067             : /*
    1068             :  * gmpr_process_cur_state_ex_ex
    1069             :  *
    1070             :  * Process a current-state record of IS_EX type with a filter state of
    1071             :  * Exclude.
    1072             :  *
    1073             :  * We do a bunch of set math.
    1074             :  */
    1075             : static void
    1076          27 : gmpr_process_cur_state_ex_ex (gmpr_group *group, gmp_addr_vect *source_vect)
    1077             : {
    1078             :     /*
    1079             :      * OK, this one is really nutty.  By the spec, set A is the set of
    1080             :      * addresses in the new report, X is the set in the running-timer
    1081             :      * list, and Y is the set in the stopped-timer list.  By definition,
    1082             :      * X and Y are non-overlapping.
    1083             :      *
    1084             :      * First, we delete X-A.  This eliminates all addresses from the
    1085             :      * running-timer list, except for (A*X).
    1086             :      */
    1087          27 :     if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
    1088             :                             source_vect, NULL, gmpr_delete_include_cb, group,
    1089             :                             BV_CALL_SET) < 0)
    1090           0 :         return;                         /* Out of memory */
    1091             : 
    1092             :     /*
    1093             :      * Now, delete Y-A.  This eliminates all addresses from the stopped-timer
    1094             :      * list, except for (A*Y).
    1095             :      */
    1096          27 :     if (gmp_addr_vect_minus(&group->rgroup_src_addr_stopped.addr_vect,
    1097             :                             source_vect, NULL, gmpr_delete_exclude_cb, group,
    1098             :                             BV_CALL_SET) < 0)
    1099           0 :         return;                         /* Out of memory */
    1100             : 
    1101             :     /*
    1102             :      * Next, walk A, adding to the running list any address not found
    1103             :      * on what remains of the running and stopped lists.  This forms the
    1104             :      * set (A-X-Y).  The net result is that the running list contains
    1105             :      * (A-X-Y) + (A*X), which is the same as (A-Y).  Whew.
    1106             :      */
    1107          27 :     gmp_addr_vect_walk(source_vect, gmpr_ex_ex_new_cb, group);
    1108             : 
    1109             :     /* Finally, restart the group timer. */
    1110             : 
    1111          27 :     gmpx_start_timer(group->rgroup_group_timer,
    1112          27 :                      group->rgroup_intf->rintf_group_membership_ivl, 0);
    1113             : }
    1114             : 
    1115             : 
    1116             : /*
    1117             :  * gmpr_in_ex_running_cb
    1118             :  *
    1119             :  * Vector walk callback for Include/Exclude current-state.  We're getting
    1120             :  * called for any entry on the running-timer list that's not on the exclude
    1121             :  * list.  We delete such entries from the running-timer list.
    1122             :  */
    1123             : static boolean
    1124           0 : gmpr_in_ex_running_cb (void *context, bv_bitnum_t bitnum,
    1125             :                        boolean new_val GMPX_UNUSED,
    1126             :                        boolean old_val GMPX_UNUSED)
    1127             : {
    1128             :     gmpr_group *group;
    1129             :     gmp_addr_list_entry *addr_entry;
    1130             : 
    1131           0 :     group = context;
    1132             : 
    1133             :     /* Look up the entry. */
    1134             : 
    1135           0 :     addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
    1136             :                                        bitnum);
    1137           0 :     gmpx_assert(addr_entry);
    1138             : 
    1139             :     /* Delete it.  The callback will free the timer. */
    1140             : 
    1141           0 :     gmp_delete_addr_list_entry(addr_entry);
    1142             : 
    1143           0 :     return FALSE;
    1144             : }
    1145             : 
    1146             : 
    1147             : /*
    1148             :  * gmpr_process_cur_state_in_ex
    1149             :  *
    1150             :  * Process a current-state record of IS_EX type with a filter state of
    1151             :  * Include.
    1152             :  *
    1153             :  * We switch filter modes from Include to Exclude and do a bunch of
    1154             :  * set math.
    1155             :  */
    1156             : static void
    1157          22 : gmpr_process_cur_state_in_ex (gmpr_group *group, gmp_addr_vect *source_vect)
    1158             : {
    1159             :     /* Change the filter mode to Exclude. */
    1160             : 
    1161          22 :     group->rgroup_filter_mode = GMP_FILTER_MODE_EXCLUDE;
    1162             : 
    1163             :     /*
    1164             :      * Calculate (B-A), where B is the set of sources in the new record
    1165             :      * and A is the set of sources in the running-timer list.  These
    1166             :      * new entries are put into the stopped-timer list.
    1167             :      */
    1168          22 :     gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
    1169          22 :     if (gmp_addr_vect_minus(source_vect,
    1170             :                             &group->rgroup_src_addr_running.addr_vect, NULL,
    1171             :                             gmpr_add_exclude_cb, group, BV_CALL_SET) < 0)
    1172           0 :         return;                         /* Out of memory */
    1173             : 
    1174             :     /*
    1175             :      * Now calculate (A-B).  The resultant entries are deleted from
    1176             :      * the running list.
    1177             :      */
    1178          22 :     if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
    1179             :                             source_vect, NULL, gmpr_in_ex_running_cb, group,
    1180             :                             BV_CALL_SET) < 0)
    1181           0 :         return;                         /* Out of memory */
    1182             : 
    1183             :     /* Restart the group timer. */
    1184             : 
    1185          22 :     gmpx_start_timer(group->rgroup_group_timer,
    1186          22 :                      group->rgroup_intf->rintf_group_membership_ivl, 0);
    1187             : 
    1188             :     /* Update the OIF. */
    1189             : 
    1190          22 :     gmpr_update_oif_mode_change(group);
    1191             : }
    1192             : 
    1193             : 
    1194             : /*
    1195             :  * gmpr_process_cur_state_rcrd
    1196             :  *
    1197             :  * Process a current-state record.
    1198             :  */
    1199             : static void
    1200          78 : gmpr_process_cur_state_rcrd (gmpr_group *group,
    1201             :                              gmp_report_rectype rec_type,
    1202             :                              gmp_addr_vect *source_vect)
    1203             : {
    1204             :     /*
    1205             :      * Process based on our current filter state, combined with the
    1206             :      * received record type.
    1207             :      */
    1208          78 :     switch (group->rgroup_filter_mode) {
    1209          51 :       case GMP_FILTER_MODE_INCLUDE:
    1210             : 
    1211             :         switch (rec_type) {
    1212          29 :           case GMP_RPT_IS_IN:
    1213          29 :             gmpr_add_include(group, source_vect);
    1214          29 :             break;
    1215             : 
    1216          22 :           case GMP_RPT_IS_EX:
    1217          22 :             gmpr_process_cur_state_in_ex(group, source_vect);
    1218          22 :             break;
    1219             : 
    1220           0 :           default:
    1221           0 :             gmpx_assert(FALSE);
    1222             :             break;
    1223             :         }
    1224          51 :         break;
    1225             : 
    1226          27 :       case GMP_FILTER_MODE_EXCLUDE:
    1227             : 
    1228             :         switch (rec_type) {
    1229           0 :           case GMP_RPT_IS_IN:
    1230           0 :             gmpr_move_include(group, source_vect);
    1231           0 :             break;
    1232             : 
    1233          27 :           case GMP_RPT_IS_EX:
    1234          27 :             gmpr_process_cur_state_ex_ex(group, source_vect);
    1235          27 :             break;
    1236             : 
    1237           0 :           default:
    1238           0 :             gmpx_assert(FALSE);
    1239             :             break;
    1240             :         }
    1241          27 :         break;
    1242             : 
    1243           0 :       default:
    1244           0 :         gmpx_assert(FALSE);
    1245             :     }
    1246          78 : }
    1247             : 
    1248             : 
    1249             : /*
    1250             :  * gmpr_update_version_compatibility_mode
    1251             :  *
    1252             :  * Update the version compatibility mode for this group based on the
    1253             :  * received packet.
    1254             :  */
    1255             : static void
    1256         135 : gmpr_update_version_compatibility_mode (gmpr_group *group, gmp_version ver)
    1257             : {
    1258             :     gmpr_intf *intf;
    1259             :     uint32_t old_host_ivl;
    1260             : 
    1261         135 :     intf = group->rgroup_intf;
    1262             : 
    1263             :     /* Calculate the older-host-present interval. */
    1264             : 
    1265         135 :     old_host_ivl = (intf->rintf_robustness * intf->rintf_query_ivl) +
    1266         135 :         intf->rintf_query_resp_ivl;
    1267             : 
    1268             :     /* Start the appropriate timer. */
    1269             : 
    1270         135 :     if (ver == GMP_VERSION_BASIC) {
    1271           1 :         gmpx_start_timer(group->rgroup_basic_host_present, old_host_ivl, 0);
    1272         134 :     } else if (ver == GMP_VERSION_LEAVES) {
    1273          75 :         gmpx_start_timer(group->rgroup_leaves_host_present, old_host_ivl, 0);
    1274             :     }
    1275             : 
    1276             :     /* Evaluate the group version. */
    1277             : 
    1278         135 :     gmpr_evaluate_group_version(group);
    1279         135 : }
    1280             : 
    1281             : 
    1282             : /*
    1283             :  * gmpr_harmonize_report_version
    1284             :  *
    1285             :  * Harmonize the report based on the current group compatibility version.
    1286             :  * We may modify the report contents, or even discard it.
    1287             :  *
    1288             :  * Returns TRUE if the group record should continue to be processed, or
    1289             :  * FALSE if it should be ignored.
    1290             :  */
    1291             : static boolean
    1292         140 : gmpr_harmonize_report_version (gmp_version group_version,
    1293             :                                gmp_report_group_record *group_rcrd)
    1294             : {
    1295             :     /* Bail if we're running the latest. */
    1296             : 
    1297         140 :     if (group_version == GMP_VERSION_SOURCES)
    1298          60 :         return TRUE;
    1299             : 
    1300             :     /* LEAVES or BASIC version.  Ignore any BLOCK messages. */
    1301             : 
    1302          80 :     if (group_rcrd->gmp_rpt_type == GMP_RPT_BLOCK)
    1303           0 :         return FALSE;
    1304             : 
    1305             :     /*
    1306             :      * If the record is a non-null TO_IN or IS_IN or ALLOW, change it to a null
    1307             :      * TO_EX.
    1308             :      */
    1309          80 :     if (group_rcrd->gmp_rpt_rcv_srcs &&
    1310           0 :         (group_rcrd->gmp_rpt_type == GMP_RPT_TO_IN ||
    1311           0 :          group_rcrd->gmp_rpt_type == GMP_RPT_IS_IN ||
    1312           0 :          group_rcrd->gmp_rpt_type == GMP_RPT_ALLOW)) {
    1313           0 :         gmp_destroy_addr_thread(group_rcrd->gmp_rpt_rcv_srcs);
    1314           0 :         group_rcrd->gmp_rpt_rcv_srcs = NULL;
    1315           0 :         group_rcrd->gmp_rpt_type = GMP_RPT_TO_EX;
    1316             :     }
    1317             : 
    1318             :     /* If the record is a non-null TO_EX or IS_EX, strip the sources. */
    1319             : 
    1320          80 :     if (group_rcrd->gmp_rpt_rcv_srcs &&
    1321           0 :         (group_rcrd->gmp_rpt_type == GMP_RPT_TO_EX ||
    1322           0 :          group_rcrd->gmp_rpt_type == GMP_RPT_IS_EX)) {
    1323           0 :         gmp_destroy_addr_thread(group_rcrd->gmp_rpt_rcv_srcs);
    1324           0 :         group_rcrd->gmp_rpt_rcv_srcs = NULL;
    1325             :     }
    1326             : 
    1327             :     /* If we're using the BASIC version, ignore any TO_IN or IS_IN messages. */
    1328             : 
    1329          80 :     if (group_version == GMP_VERSION_BASIC) {
    1330           1 :         if (group_rcrd->gmp_rpt_type == GMP_RPT_TO_IN ||
    1331           1 :             group_rcrd->gmp_rpt_type == GMP_RPT_IS_IN) {
    1332           0 :             return FALSE;
    1333             :         }
    1334             :     }
    1335             : 
    1336          80 :     return TRUE;
    1337             : }
    1338             : 
    1339             : 
    1340             : /*
    1341             :  * gmpr_process_report_packet
    1342             :  *
    1343             :  * Process a received report packet.
    1344             :  */
    1345             : static void
    1346         136 : gmpr_process_report_packet(gmpr_intf *intf, gmp_packet *packet)
    1347             : {
    1348             :     gmpr_instance *instance;
    1349             :     gmpr_group *group;
    1350             :     gmp_report_packet *rpt_pkt;
    1351             :     gmp_report_group_record *group_rcrd;
    1352             :     task_thread *thread_ptr;
    1353             :     gmp_addr_vect source_vect;
    1354             :     gmp_addr_thread_entry *thread_entry;
    1355             :     gmp_addr_string *addr;
    1356             :     uint8_t *group_addr;
    1357             :     gmpr_instance_context *ctx;
    1358             :     gmp_version group_version;
    1359             :     boolean got_sources;
    1360             : 
    1361         136 :     instance = intf->rintf_instance;
    1362         136 :     ctx = &instance->rinst_cb_context;
    1363         136 :     rpt_pkt = &packet->gmp_packet_contents.gmp_packet_report;
    1364         136 :     gmp_init_addr_vector(&source_vect, &instance->rinst_addr_cat);
    1365             : 
    1366         136 :     gmpr_trace_agent("Process report packet : file : %s, line : %.",
    1367             :                             __FILE__, __LINE__);
    1368             : 
    1369             :     /* Walk all of the groups in the report. */
    1370             : 
    1371         276 :     FOR_ALL_CIRCULAR_THREAD_ENTRIES(&rpt_pkt->gmp_report_group_head,
    1372             :                                     thread_ptr) {
    1373         140 :         group_rcrd = gmp_thread_to_report_group_record(thread_ptr);
    1374             : 
    1375             :         /* Look up the group.  It may not be there. */
    1376             : 
    1377         140 :         group_addr = group_rcrd->gmp_rpt_group.gmp_addr;
    1378         140 :         group = gmpr_group_lookup(intf, group_addr);
    1379             : 
    1380             :         /*
    1381             :          * Modify the received report based on the compatibility mode.
    1382             :          * We will skip the record entirely if it says to.
    1383             :          */
    1384         140 :         group_version = gmpr_group_version(intf, group);
    1385         140 :         if (gmpr_harmonize_report_version(group_version, group_rcrd)) {
    1386             : 
    1387             :             /*
    1388             :              * We didn't toss the record.  If there are any sources
    1389             :              * present, walk them.
    1390             :              */
    1391         140 :             if (group_rcrd->gmp_rpt_rcv_srcs &&
    1392          59 :                 group_rcrd->gmp_rpt_rcv_srcs->gmp_addr_thread_count) {
    1393             : 
    1394          59 :                 thread_entry = NULL;
    1395             :                 while (TRUE) {
    1396         134 :                     addr =
    1397         193 :                         gmp_next_addr_thread_addr(group_rcrd->gmp_rpt_rcv_srcs,
    1398             :                                                   &thread_entry);
    1399         193 :                     if (!addr)
    1400          59 :                         break;
    1401             : 
    1402             :                     /* See if the group and source pass policy. */
    1403             : 
    1404         134 :                     if (ctx->rctx_policy_cb) {
    1405         134 :                         if (!(*ctx->rctx_policy_cb)(instance->rinst_context,
    1406             :                                                     intf->rintf_id, group_addr,
    1407         134 :                                                     addr->gmp_addr,
    1408             :                                                     packet->gmp_packet_attr)) {
    1409          12 :                             continue;
    1410             :                         }
    1411             :                     }
    1412             : 
    1413             :                     /* Got an address.  Add it to the vector. */
    1414             : 
    1415         122 :                     if (gmp_addr_vect_set(&source_vect, addr) < 0)
    1416           0 :                         return;         /* Out of memory */
    1417             :                 }
    1418             : 
    1419             :                 /*
    1420             :                  * If we get here without any sources, it means that they
    1421             :                  * were all blocked by policy, so we should skip the
    1422             :                  * record and go on.
    1423             :                  */
    1424          59 :                 if (gmp_addr_vect_empty(&source_vect))
    1425           0 :                     continue;
    1426             : 
    1427             :             } else {
    1428             : 
    1429             :                 /* No sources.  See if the group passes policy. */
    1430             : 
    1431          81 :                 if (ctx->rctx_policy_cb) {
    1432          81 :                     if (!(*ctx->rctx_policy_cb)(instance->rinst_context,
    1433             :                                                 intf->rintf_id, group_addr,
    1434             :                                                 NULL,
    1435             :                                                 packet->gmp_packet_attr)) {
    1436           0 :                         continue;
    1437             :                     }
    1438             :                 }
    1439             : 
    1440             :                 /*
    1441             :                  * Ignore the group if this is a join and the group is
    1442             :                  * SSM-only.
    1443             :                  */
    1444          81 :                 if (group_rcrd->gmp_rpt_type == GMP_RPT_TO_EX ||
    1445          81 :                     group_rcrd->gmp_rpt_type == GMP_RPT_IS_EX) {
    1446          49 :                     if (ctx->rctx_ssm_check_cb) {
    1447          49 :                         if (!(*ctx->rctx_ssm_check_cb)(instance->rinst_context,
    1448             :                                                        intf->rintf_id,
    1449             :                                                        group_addr)) {
    1450           0 :                             continue;
    1451             :                         }
    1452             :                     }
    1453             :                 }
    1454             :             }
    1455             : 
    1456             :             /*
    1457             :              * If we get here, the record is valid.  If there is no group,
    1458             :              * and the record is some kind of leave, skip to the next
    1459             :              * record without creating anything.
    1460             :              */
    1461         140 :             got_sources = !gmp_addr_vect_empty(&source_vect);
    1462         140 :             if (!group &&
    1463          42 :                 ((group_rcrd->gmp_rpt_type == GMP_RPT_TO_IN && !got_sources) ||
    1464          37 :                  (group_rcrd->gmp_rpt_type == GMP_RPT_IS_IN && !got_sources) ||
    1465          37 :                  (group_rcrd->gmp_rpt_type == GMP_RPT_BLOCK))) {
    1466           5 :                 goto next_record;
    1467             :             }
    1468             : 
    1469             :             /* Create a group if we don't have one yet. */
    1470             : 
    1471         135 :             if (!group)
    1472          37 :                 group = gmpr_group_create(intf, &group_rcrd->gmp_rpt_group);
    1473             : 
    1474             :             /*
    1475             :              * Now check to see if we have a group.  If we do, process
    1476             :              * the record.  If not, skip it, as we are out of memory or have
    1477             :              * hit the group limit for the interface.
    1478             :              */
    1479         135 :             if (group) {
    1480             : 
    1481             :                 /* Update the version compatibility mode. */
    1482             : 
    1483         135 :                 gmpr_update_version_compatibility_mode(group,
    1484             :                                                packet->gmp_packet_version);
    1485             : 
    1486             :                 /* Note the reporter's address. */
    1487             : 
    1488         135 :                 memmove(group->rgroup_last_reporter.gmp_addr,
    1489         135 :             packet->gmp_packet_src_addr.gmp_addr,
    1490         135 :             instance->rinst_addrlen);
    1491             :  
    1492             :                 /* See if this is a current-state or state-change record. */
    1493             : 
    1494         135 :                 if (group_rcrd->gmp_rpt_type == GMP_RPT_IS_IN ||
    1495         106 :                     group_rcrd->gmp_rpt_type == GMP_RPT_IS_EX) {
    1496             : 
    1497             :                     /* Current-state record.  Process it. */
    1498             : 
    1499          78 :                     gmpr_process_cur_state_rcrd(group, group_rcrd->gmp_rpt_type,
    1500             :                                                 &source_vect);
    1501             : 
    1502             :                 } else {
    1503             : 
    1504             :                     /* State-change record.  Process it. */
    1505             : 
    1506          57 :                     gmpr_process_state_chg_rcrd(group, group_rcrd->gmp_rpt_type,
    1507             :                                                 &source_vect);
    1508             :                 }
    1509             : 
    1510             :                 /* Do host processing, if appropriate. */
    1511             : 
    1512         135 :                 gmpr_host_process_report(packet->gmp_packet_src_addr.gmp_addr,
    1513             :                                          group_rcrd->gmp_rpt_type, group,
    1514             :                                          &source_vect);
    1515             :             }
    1516             : 
    1517           0 :           next_record:
    1518             : 
    1519             :             /* Clean up the source vector. */
    1520             : 
    1521         140 :             gmp_addr_vect_clean(&source_vect);
    1522             :         }
    1523             :     }
    1524             : }
    1525             : 
    1526             : 
    1527             : /*
    1528             :  * gmpr_packet_rcv_callback
    1529             :  *
    1530             :  * Callback from generic packet handling to process a packet, provided
    1531             :  * in the generic form.  All syntax checking has already occurred.
    1532             :  */
    1533             : static void
    1534         136 : gmpr_packet_rcv_callback (gmpx_intf_id intf_id, gmp_packet *packet)
    1535             : {
    1536             :     gmpr_intf *intf;
    1537             : 
    1538             :     /* Look up the interface. */
    1539             : 
    1540         136 :     intf = gmpr_intf_lookup_global(packet->gmp_packet_proto, intf_id);
    1541             : 
    1542             :     /* Bail if no interface. */
    1543             : 
    1544         136 :     if (!intf)
    1545           0 :         return;
    1546             : 
    1547             :     /* Return if passive receive. */
    1548             : 
    1549         136 :     if (intf->rintf_passive_receive)
    1550           0 :         return;
    1551             : 
    1552             :     /* Tease them apart by type. */
    1553             : 
    1554         136 :     switch (packet->gmp_packet_type) {
    1555           0 :       case GMP_QUERY_PACKET:
    1556           0 :         gmpr_process_query_packet(intf, packet);
    1557           0 :         break;
    1558             : 
    1559         136 :       case GMP_REPORT_PACKET:
    1560         136 :         gmpr_process_report_packet(intf, packet);
    1561         136 :         break;
    1562             : 
    1563           0 :       default:
    1564           0 :         gmpx_assert(FALSE);
    1565             :     }
    1566             : 
    1567             :     /* Pass along any notifications. */
    1568             : 
    1569         136 :     gmpr_alert_clients(intf->rintf_instance);
    1570         136 :     gmpr_alert_host_clients(intf->rintf_instance);
    1571             : }
    1572             : 
    1573             : 
    1574             : /*
    1575             :  * gmpr_send_gss_query
    1576             :  *
    1577             :  * Send a GSS query on an interface, if appropriate.
    1578             :  *
    1579             :  * Returns a pointer to the packet, or NULL if out of memory.
    1580             :  */
    1581             : static gmp_packet *
    1582          88 : gmpr_send_gss_query (gmpr_group *group)
    1583             : {
    1584             :     gmpr_instance *instance;
    1585             :     gmpr_intf *intf;
    1586             :     gmp_addr_list *addr_list;
    1587             :     gmp_packet *packet;
    1588             :     gmp_query_packet *query_packet;
    1589             :     boolean hi_timer;
    1590             : 
    1591          88 :     intf = group->rgroup_intf;
    1592             : 
    1593             :     /* Bail if we don't need to send a GSS query. */
    1594             : 
    1595          88 :     if (!group->rgroup_send_gss_query)
    1596          54 :         return NULL;
    1597             : 
    1598             :     /* Bail if we're not the querier. */
    1599             : 
    1600          34 :     if (!intf->rintf_querier) {
    1601           0 :         group->rgroup_send_gss_query = FALSE;
    1602           0 :         return NULL;
    1603             :     }
    1604             : 
    1605             :     /*
    1606             :      * There are two lists of sources to query, the lo-timer list and the
    1607             :      * hi-timer list.  Take a look at the lo-timer list first.
    1608             :      */
    1609          34 :     addr_list = &group->rgroup_query_lo_timers;
    1610          34 :     if (!gmp_xmit_addr_list_empty(addr_list)) {
    1611             : 
    1612             :         /* Something on the low timer list.  Flag it. */
    1613             : 
    1614          17 :         hi_timer = FALSE;
    1615             : 
    1616             :     } else {
    1617             : 
    1618             :         /* Try the hi-timer list. */
    1619             : 
    1620          17 :         addr_list = &group->rgroup_query_hi_timers;
    1621             : 
    1622          17 :         if (!gmp_xmit_addr_list_empty(addr_list)) {
    1623             : 
    1624             :             /* Something on the high timer list.  Flag it. */
    1625             : 
    1626           0 :             hi_timer = TRUE;
    1627             : 
    1628             :         } else {
    1629             : 
    1630             :             /* Nothing found.  Bail. */
    1631             : 
    1632          17 :             group->rgroup_send_gss_query = FALSE;
    1633          17 :             return NULL;
    1634             :         }
    1635             :     }
    1636             :     
    1637             :     /* Something to send.  Get a packet header and initialize it. */
    1638             : 
    1639          17 :     instance = intf->rintf_instance;
    1640          17 :     packet = gmpp_create_packet_header(group->rgroup_compatibility_mode,
    1641             :                                        GMP_QUERY_PACKET,
    1642             :                                        instance->rinst_proto);
    1643          17 :     if (!packet)
    1644           0 :         return NULL;                    /* Out of memory */
    1645             : 
    1646             :     /*
    1647             :      * Fill in the packet.  We set the S bit if we're sending the high-timer
    1648             :      * list.
    1649             :      */
    1650          17 :     query_packet = &packet->gmp_packet_contents.gmp_packet_query;
    1651          17 :     query_packet->gmp_query_max_resp = intf->rintf_lmq_ivl;
    1652          17 :     query_packet->gmp_query_group_query = TRUE;
    1653          17 :     memmove(&query_packet->gmp_query_group, &group->rgroup_addr, sizeof(gmp_addr_string));
    1654          17 :     query_packet->gmp_query_qrv = intf->rintf_robustness;
    1655          17 :     query_packet->gmp_query_qqi = intf->rintf_query_ivl;
    1656          17 :     query_packet->gmp_query_suppress = hi_timer;
    1657          17 :     query_packet->gmp_query_xmit_srcs = addr_list;
    1658          17 :     query_packet->gmp_query_group_id = group;
    1659             : 
    1660          17 :     return packet;
    1661             : }
    1662             : 
    1663             : 
    1664             : /*
    1665             :  * gmpr_send_group_query
    1666             :  *
    1667             :  * Send a group query on an interface, if appropriate.
    1668             :  *
    1669             :  * Returns a pointer to the packet, or NULL if out of memory.
    1670             :  */
    1671             : static gmp_packet *
    1672          71 : gmpr_send_group_query (gmpr_group *group)
    1673             : {
    1674             :     gmpr_instance *instance;
    1675             :     gmpr_intf *intf;
    1676             :     gmp_packet *packet;
    1677             :     gmp_query_packet *query_packet;
    1678             : 
    1679          71 :     intf = group->rgroup_intf;
    1680             : 
    1681             :     /* Bail if we don't need to send a group query. */
    1682             : 
    1683          71 :     if (!group->rgroup_send_group_query)
    1684          44 :         return NULL;
    1685             : 
    1686          27 :     group->rgroup_send_group_query = FALSE;
    1687             : 
    1688             :     /* Bail if we're not the querier. */
    1689             : 
    1690          27 :     if (!intf->rintf_querier)
    1691           0 :         return NULL;
    1692             :     
    1693             :     /* Something to send.  Get a packet header and initialize it. */
    1694             : 
    1695          27 :     instance = intf->rintf_instance;
    1696          27 :     packet = gmpp_create_packet_header(group->rgroup_compatibility_mode,
    1697             :                                        GMP_QUERY_PACKET,
    1698             :                                        instance->rinst_proto);
    1699          27 :     if (!packet)
    1700           0 :         return NULL;                    /* Out of memory */
    1701             : 
    1702             :     /* Fill in the packet. */
    1703             : 
    1704          27 :     query_packet = &packet->gmp_packet_contents.gmp_packet_query;
    1705          27 :     query_packet->gmp_query_max_resp = intf->rintf_lmq_ivl;
    1706          27 :     query_packet->gmp_query_group_query = TRUE;
    1707          27 :     memmove(&query_packet->gmp_query_group, &group->rgroup_addr, sizeof(gmp_addr_string));
    1708          27 :     query_packet->gmp_query_qrv = intf->rintf_robustness;
    1709          27 :     query_packet->gmp_query_qqi = intf->rintf_query_ivl;
    1710          27 :     query_packet->gmp_query_group_id = group;
    1711             : 
    1712             :     /* Set the S bit if the group timer is larger than LMQT. */
    1713             : 
    1714          27 :     query_packet->gmp_query_suppress =
    1715          27 :         (gmpx_timer_time_remaining(group->rgroup_group_timer) >
    1716          27 :                                    intf->rintf_lmqt);
    1717             : 
    1718          27 :     return packet;
    1719             : }
    1720             : 
    1721             : 
    1722             : /*
    1723             :  * gmpr_send_gen_query
    1724             :  *
    1725             :  * Send a general query packet on an interface, if appropriate.
    1726             :  *
    1727             :  * Returns a pointer to the packet, or NULL if out of memory.
    1728             :  */
    1729             : static gmp_packet *
    1730        3546 : gmpr_send_gen_query (gmpr_intf *intf)
    1731             : {
    1732             :     gmpr_instance *instance;
    1733             :     gmp_packet *packet;
    1734             :     gmp_query_packet *query_packet;
    1735             : 
    1736        3546 :     packet = NULL;
    1737             : 
    1738             :     /* Do so only if we need to. */
    1739             : 
    1740        3546 :     if (intf->rintf_send_gen_query) {
    1741             : 
    1742             :         /*
    1743             :          * See if we're supposed to be querier.  This is the case
    1744             :          * the other_querier_present timer is stopped, and if we're not
    1745             :          * disabled and running IGMP version 1.
    1746             :          */
    1747         875 :         if (intf->rintf_querier &&
    1748         875 :             !(intf->rintf_ver == GMP_VERSION_BASIC &&
    1749           6 :               !intf->rintf_querier_enabled)) {
    1750             : 
    1751             :             /* Guess we need to.  Get a packet header and initialize it. */
    1752             : 
    1753         875 :             instance = intf->rintf_instance;
    1754         875 :             packet = gmpp_create_packet_header(intf->rintf_ver,
    1755             :                                                GMP_QUERY_PACKET,
    1756             :                                                instance->rinst_proto);
    1757         875 :             if (!packet)
    1758           0 :                 return NULL;            /* Out of memory */
    1759             : 
    1760         875 :             query_packet = &packet->gmp_packet_contents.gmp_packet_query;
    1761         875 :             query_packet->gmp_query_max_resp = intf->rintf_query_resp_ivl;
    1762         875 :             query_packet->gmp_query_group_query = FALSE;
    1763         875 :             query_packet->gmp_query_qrv = intf->rintf_robustness;
    1764         875 :             query_packet->gmp_query_qqi = intf->rintf_query_ivl;
    1765             :         }
    1766             : 
    1767             :     }
    1768        3546 :     intf->rintf_send_gen_query = FALSE;
    1769        3546 :     return packet;
    1770             : }
    1771             : 
    1772             : 
    1773             : /*
    1774             :  * gmpr_packet_free_callback
    1775             :  *
    1776             :  * Callback from the packet handler when it is done with a packet structure.
    1777             :  */
    1778             : static void
    1779         919 : gmpr_packet_free_callback (gmp_packet *packet)
    1780             : {
    1781             :     /* Free the packet. */
    1782             : 
    1783         919 :     gmpp_destroy_packet(packet);
    1784         919 : }
    1785             : 
    1786             : 
    1787             : /*
    1788             :  * gmpr_group_done_callback
    1789             :  *
    1790             :  * Callback from the packet handler when it is done processing a group.
    1791             :  */
    1792             : static void
    1793          44 : gmpr_group_done_callback (void *group_id)
    1794             : {
    1795             :     gmpr_group *group;
    1796             : 
    1797          44 :     group = group_id;
    1798             : 
    1799             :     /* Flush the query lists if there's nothing left to send. */
    1800             : 
    1801          44 :     if (gmp_xmit_addr_list_empty(&group->rgroup_query_hi_timers))
    1802          44 :         gmp_flush_addr_list(&group->rgroup_query_hi_timers);
    1803          44 :     if (gmp_xmit_addr_list_empty(&group->rgroup_query_lo_timers))
    1804          44 :         gmp_flush_addr_list(&group->rgroup_query_lo_timers);
    1805             : 
    1806             :     /*
    1807             :      * Try to free the group.  This will happen if there's no longer any
    1808             :      * interest in this group and we've sent all of the necessary messages.
    1809             :      */
    1810          44 :     gmpr_attempt_group_free(group);
    1811          44 : }
    1812             : 
    1813             : 
    1814             : /*
    1815             :  * gmpr_xmit_callback
    1816             :  *
    1817             :  * Callback from the packet handler when it is ready to send a packet.
    1818             :  *
    1819             :  * Returns a pointer to a generic packet to send, or NULL if there's nothing
    1820             :  * to send.
    1821             :  *
    1822             :  * Also returns a pointer to a packet to send.
    1823             :  */
    1824             : static gmp_packet *
    1825        3546 : gmpr_xmit_callback (gmpx_intf_id intf_id, gmp_proto proto,
    1826             :                     uint32_t buffer_len GMPX_UNUSED)
    1827             : {
    1828             :     gmpr_intf *intf;
    1829             :     gmpr_group *group;
    1830             :     gmp_packet *packet;
    1831             : 
    1832             :     /* Look up the interface. */
    1833             : 
    1834        3546 :     intf = gmpr_intf_lookup_global(proto, intf_id);
    1835        3546 :     if (!intf)                          /* No interface! */
    1836           0 :         return NULL;
    1837             : 
    1838             :     /*
    1839             :      * Got an interface.  By default, turn off the "xmit pending" flag.
    1840             :      * We'll turn it back on if we end up returning a real packet.
    1841             :      */
    1842        3546 :     packet = NULL;
    1843        3546 :     intf->rintf_xmit_pending = FALSE;
    1844             : 
    1845             :     /* If we need to send a general query, do so. */
    1846             : 
    1847        3546 :     packet = gmpr_send_gen_query(intf);
    1848             : 
    1849             :     /* If nothing yet, take a look at the group transmit list. */
    1850             : 
    1851        3546 :     if (!packet) {
    1852             : 
    1853             :         /*
    1854             :          * Start pulling groups off of the interface transmit list.  Normally
    1855             :          * we will use the first one, but it's possible for a group to be
    1856             :          * overtaken by events and no longer have anything to say.
    1857             :          */
    1858             :         while (TRUE) {
    1859             : 
    1860             :             /* Grab the first group off of the transmit list. */
    1861             : 
    1862        2715 :             group = gmpr_first_group_xmit(intf);
    1863        2715 :             if (!group)
    1864        2627 :                 break;
    1865             : 
    1866             :             /* Got a group.  See if we need to send a gss query. */
    1867             : 
    1868          88 :             packet = gmpr_send_gss_query(group);
    1869             : 
    1870             :             /* If nothing yet, see if we need to send a group query. */
    1871             : 
    1872          88 :             if (!packet)
    1873          71 :                 packet = gmpr_send_group_query(group);
    1874             : 
    1875             :             /*
    1876             :              * If we've got a packet, bail from the loop.  Otherwise,
    1877             :              * it looks like there was nothing for this group after all,
    1878             :              * so we dequeue it and try the next one.
    1879             :              */
    1880          88 :             if (packet)
    1881          44 :                 break;
    1882          44 :             gmpr_dequeue_group_xmit(group);
    1883             :         }
    1884             :     }
    1885             : 
    1886             :     /*
    1887             :      * If we're actually returning a packet, note that we still have
    1888             :      * a transmission pending.
    1889             :      */
    1890        3546 :     if (packet)
    1891         919 :         intf->rintf_xmit_pending = TRUE;
    1892             : 
    1893        3546 :     return packet;
    1894             : }
    1895             : 
    1896             : 
    1897             : /*
    1898             :  * gmpr_register_packet_handler
    1899             :  *
    1900             :  * Register us with the generic packet handler.
    1901             :  */
    1902             : void
    1903         125 : gmpr_register_packet_handler (void)
    1904             : {
    1905             :     /* Call the packet handler registration routines with the right stuff. */
    1906             : 
    1907         125 :     gmpp_register(GMP_ROLE_ROUTER, gmpr_xmit_callback,
    1908             :                   gmpr_packet_rcv_callback, gmpr_group_done_callback,
    1909             :                   gmpr_packet_free_callback);
    1910         125 : }
    1911             : 
    1912             : 
    1913             : /*
    1914             :  * gmpr_group_timer_expiry
    1915             :  *
    1916             :  * Called when a group timer expires.
    1917             :  */
    1918             : void
    1919          11 : gmpr_group_timer_expiry (gmpx_timer *timer, void *context)
    1920             : {
    1921             :     gmpr_instance *instance;
    1922             :     gmpr_group *group;
    1923             : 
    1924          11 :     group = context;
    1925          11 :     instance = group->rgroup_intf->rintf_instance;
    1926          11 :     gmpx_stop_timer(timer);
    1927             : 
    1928          11 :     gmpr_trace_agent("Group Timer Expiry : file : %s, line : %.",
    1929             :                             __FILE__, __LINE__);
    1930             : 
    1931             :     /*
    1932             :      * A group timer expiry means that there are no more active
    1933             :      * Exclude callers out there.  We flush the stopped-timer list,
    1934             :      * switch to Include mode, and update the OIF.  If the
    1935             :      * running-timer list is empty, the group will be deleted after
    1936             :      * the OIF update.
    1937             :      */
    1938          11 :     gmp_flush_addr_list(&group->rgroup_src_addr_stopped);
    1939          11 :     group->rgroup_filter_mode = GMP_FILTER_MODE_INCLUDE;
    1940          11 :     gmpr_update_oif_mode_change(group);
    1941          11 :     gmpr_alert_clients(instance);
    1942          11 :     instance->rinst_group_timeout++;
    1943          11 : }
    1944             : 
    1945             : 
    1946             : /*
    1947             :  * gmpr_source_timer_expiry
    1948             :  *
    1949             :  * Called when a source timer expires.
    1950             :  */
    1951             : void
    1952          27 : gmpr_source_timer_expiry (gmpx_timer *timer, void *context)
    1953             : {
    1954             :     gmpr_group *group;
    1955             :     gmpr_group_addr_entry *group_addr_entry;
    1956             :     gmp_addr_list_entry *addr_entry;
    1957             :     gmpr_instance *instance;
    1958             : 
    1959          27 :     gmpx_stop_timer(timer);
    1960          27 :     group_addr_entry = context;
    1961          27 :     addr_entry = &group_addr_entry->rgroup_addr_entry;
    1962          27 :     group = group_addr_entry->rgroup_addr_group;
    1963          27 :     instance = group->rgroup_intf->rintf_instance;
    1964             : 
    1965             :     /* Check the filter mode. */
    1966             : 
    1967          27 :     gmpr_trace_agent("Source Timer Expiry : file : %s, line : %.",
    1968             :                             __FILE__, __LINE__);
    1969             : 
    1970          27 :     if (group->rgroup_filter_mode == GMP_FILTER_MODE_INCLUDE) {
    1971             : 
    1972             :         /*
    1973             :          * Include mode.  The source is going away.  Update the OIF and
    1974             :          * delete the entry.
    1975             :          */
    1976          27 :         gmpr_update_source_oif(group_addr_entry, OIF_DELETE);
    1977          27 :         gmp_delete_addr_list_entry(addr_entry);
    1978             : 
    1979             :         /* Now see if there's anything left in the running list. */
    1980             : 
    1981          27 :         if (gmp_addr_list_empty(&group->rgroup_src_addr_running)) {
    1982             : 
    1983             :             /*
    1984             :              * The running list is now empty.  This means that the last source
    1985             :              * went away, and we should instead delete the whole group.
    1986             :              */
    1987          10 :             gmpr_update_group_oif(group, OIF_DELETE);
    1988          10 :             gmpr_attempt_group_free(group);
    1989             :         }
    1990             : 
    1991             :     } else {
    1992             : 
    1993             :         /*
    1994             :          * Exclude mode.  Move the entry from the running timer list
    1995             :          * to the stopped timer list.
    1996             :          */
    1997           0 :         gmp_move_addr_list_entry(&group->rgroup_src_addr_stopped, addr_entry);
    1998           0 :         gmpr_update_source_oif(group_addr_entry, OIF_UPDATE);
    1999             :     }
    2000             : 
    2001          27 :     gmpr_alert_clients(instance);
    2002          27 : }
    2003             : 
    2004             : 
    2005             : /*
    2006             :  * gmpr_gss_query_timer_expiry
    2007             :  *
    2008             :  * Called when a GSS query transmission timer expires.
    2009             :  */
    2010             : void
    2011          25 : gmpr_gss_query_timer_expiry (gmpx_timer *timer, void *context)
    2012             : {
    2013             :     gmpr_intf *intf;
    2014             :     gmpr_group *group;
    2015             :     gmp_addr_list *addr_list;
    2016             :     gmp_addr_list *running_list;
    2017             :     gmp_addr_list_entry *addr_entry, *new_addr_entry;
    2018             :     gmpr_group_addr_entry *group_addr;
    2019             :     boolean found_something;
    2020             : 
    2021          25 :     gmpx_stop_timer(timer);
    2022          25 :     group = context;
    2023          25 :     intf = group->rgroup_intf;
    2024             : 
    2025             :     /*
    2026             :      * Flush the low-timer and high-timer query lists.  They're probably
    2027             :      * empty anyhow.
    2028             :      */
    2029          25 :     gmp_flush_addr_list(&group->rgroup_query_lo_timers);
    2030          25 :     gmp_flush_addr_list(&group->rgroup_query_hi_timers);
    2031             : 
    2032             :     /*
    2033             :      * Walk the running-timer list, enqueueing entries onto the low-timer
    2034             :      * or high-timer query lists for each entry with a nonzero retransmit
    2035             :      * count.
    2036             :      */
    2037          25 :     found_something = FALSE;
    2038          25 :     running_list = &group->rgroup_src_addr_running;
    2039          25 :     addr_entry = NULL;
    2040             :     while (TRUE) {
    2041          85 :         addr_entry = gmp_addr_list_next_entry(running_list, addr_entry);
    2042          85 :         group_addr = gmpr_addr_entry_to_group_entry(addr_entry);
    2043          85 :         if (!group_addr)
    2044          25 :             break;
    2045             : 
    2046             :         /* Process entries with nonzero retransmit counts. */
    2047             : 
    2048          60 :         if (group_addr->rgroup_addr_rexmit_count) {
    2049             : 
    2050          36 :             found_something = TRUE;
    2051             : 
    2052             :             /* Decrement the retransmission count. */
    2053             : 
    2054          36 :             group_addr->rgroup_addr_rexmit_count--;
    2055             : 
    2056             :             /*
    2057             :              * Stick the entry into the low or high timer lists,
    2058             :              * depending on the remaining time compared to LMQT.
    2059             :              */
    2060          36 :             if (gmpx_timer_time_remaining(group_addr->rgroup_addr_timer) <=
    2061          36 :                 intf->rintf_lmqt) {
    2062          36 :                 addr_list = &group->rgroup_query_lo_timers;
    2063             :             } else {
    2064           0 :                 addr_list = &group->rgroup_query_hi_timers;
    2065             :             }
    2066             :             new_addr_entry =
    2067          36 :                 gmp_create_addr_list_entry(addr_list,
    2068             :                                            addr_entry->addr_ent_ord);
    2069          36 :             gmp_enqueue_xmit_addr_entry(new_addr_entry);
    2070             :         }
    2071             :     }
    2072             : 
    2073             :     /*
    2074             :      * Enqueue the group, set the flag, kick the transmitter if we
    2075             :      * found something.
    2076             :      */
    2077          25 :     if (found_something) {
    2078          17 :         gmpr_enqueue_group_xmit(group);
    2079          17 :         group->rgroup_send_gss_query = TRUE;
    2080          17 :         gmpr_kick_xmit(group->rgroup_intf);
    2081          17 :         gmpx_start_timer(group->rgroup_gss_query_timer,
    2082          17 :                          group->rgroup_intf->rintf_lmq_ivl, 0);
    2083             :     }
    2084             : 
    2085             :     /*
    2086             :      * Try tossing the group.  We may have just cleaned up the last
    2087             :      * bits keeping it alive by flushing the lo and hi timer lists.
    2088             :      */
    2089          25 :     gmpr_attempt_group_free(group);
    2090          25 : }
    2091             : 
    2092             : 
    2093             : /*
    2094             :  * gmpr_group_query_timer_expiry
    2095             :  *
    2096             :  * Called when a group query transmission timer expires.
    2097             :  */
    2098             : void
    2099          27 : gmpr_group_query_timer_expiry (gmpx_timer *timer, void *context)
    2100             : {
    2101             :     gmpr_group *group;
    2102             :     gmpr_intf *intf;
    2103             : 
    2104          27 :     gmpx_stop_timer(timer);
    2105          27 :     group = context;
    2106          27 :     intf = group->rgroup_intf;
    2107             : 
    2108             :     /* Decrement the retransmit count. */
    2109             : 
    2110          27 :     gmpx_assert(group->rgroup_query_rexmit_count);
    2111          27 :     group->rgroup_query_rexmit_count--;
    2112             : 
    2113             :     /*
    2114             :      * Enqueue the group, set the flag and kick the transmitter.  When
    2115             :      * we get called back, we'll actually form the packet.
    2116             :      */
    2117          27 :     gmpr_enqueue_group_xmit(group);
    2118          27 :     group->rgroup_send_group_query = TRUE;
    2119          27 :     gmpr_kick_xmit(intf);
    2120             : 
    2121             :     /* Restart the timer if the rexmit count is still nonzero. */
    2122             : 
    2123          27 :     if (group->rgroup_query_rexmit_count)
    2124           0 :         gmpx_start_timer(group->rgroup_query_timer, intf->rintf_lmq_ivl, 0);
    2125          27 : }
    2126             : 
    2127             : 
    2128             : /*
    2129             :  * gmpr_last_host_addr_ref_gone
    2130             :  *
    2131             :  * Called when the last host reference to a (S,G) is going away and we're
    2132             :  * doing fast leave processing.  We act as if the source timer has expired.
    2133             :  * We can get away with this because we only track host sources when in
    2134             :  * Include mode.
    2135             :  */
    2136             : void
    2137           0 : gmpr_last_host_addr_ref_gone (gmpr_group_addr_entry *group_addr_entry)
    2138             : {
    2139             :     /* Just set the source timer to expire immediately. */
    2140             : 
    2141           0 :     gmpx_start_timer(group_addr_entry->rgroup_addr_timer, 0, 0);
    2142           0 : }
    2143             : 
    2144             : 
    2145             : /*
    2146             :  * gmpr_last_host_group_ref_gone
    2147             :  *
    2148             :  * Called when the last host reference to a group is going away and we're
    2149             :  * doing fast leave processing.  We switch to Include mode, flush the
    2150             :  * source lists, and post the group notification.
    2151             :  */
    2152             : void
    2153           0 : gmpr_last_host_group_ref_gone (gmpr_group *group)
    2154             : {
    2155             :     gmpr_instance *instance;
    2156             : 
    2157             :     /* Bail if the group is already gone. */
    2158             : 
    2159           0 :     if (!gmpr_group_is_active(group))
    2160           0 :         return;
    2161             : 
    2162           0 :     instance = group->rgroup_intf->rintf_instance;
    2163             : 
    2164             :     /* Force the group to Include{} state. */
    2165             : 
    2166           0 :     gmp_flush_addr_list(&group->rgroup_src_addr_running);
    2167           0 :     gmp_flush_addr_list(&group->rgroup_src_addr_stopped);
    2168           0 :     group->rgroup_filter_mode = GMP_FILTER_MODE_INCLUDE;
    2169           0 :     gmpx_stop_timer(group->rgroup_group_timer);
    2170             : 
    2171             :     /* Update the OIF and try to delete the group. */
    2172             : 
    2173           0 :     gmpr_update_oif_mode_change(group);
    2174           0 :     gmpr_alert_clients(instance);
    2175             : }

Generated by: LCOV version 1.14