LCOV - code coverage report
Current view: top level - vnsw/agent/services/multicast/grpmgmt - gmpr_client.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 260 344 75.6 %
Date: 2026-08-03 02:19:58 Functions: 27 29 93.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* $Id: gmpr_client.c 374940 2010-04-20 04:55:18Z weesan $
       2             :  *
       3             :  * gmpr_client.c - IGMP/MLD Router-Side Client Routines
       4             :  *
       5             :  * Dave Katz, March 2008
       6             :  *
       7             :  * Copyright (c) 2008, Juniper Networks, Inc.
       8             :  * All rights reserved.
       9             :  */
      10             : 
      11             : /*
      12             :  * A note on the notification mechanism
      13             :  *
      14             :  * In order to keep state bounded, we provide notifications to the
      15             :  * clients by threading output groups and/or sources onto those
      16             :  * clients' notification threads.  When the client calls
      17             :  * gmpr_client_get_notification(), we allocate client notification
      18             :  * blocks and pass them back to the client (with at most one client
      19             :  * notification block outstanding, so as to not grow memory.)
      20             :  *
      21             :  * There are two types of notifications--full notifications and
      22             :  * deltas.  Full notifications always contain the group and all
      23             :  * sources in one structure.  Deltas return per-source changes as
      24             :  * appropriate.
      25             :  *
      26             :  * The gmpr engine assumes that deltas are always in effect, and only
      27             :  * threads groups and/or sources that are changing.  If only delta
      28             :  * notifications are being requested, these groups and sources are
      29             :  * translated one-to-one into notifications in the obvious way.
      30             :  *
      31             :  * If full notifications are in effect, the group send_full_notif flag
      32             :  * is set for the group, regardless of whether the entity being
      33             :  * enqueued is a group or a source.  When notifications are generated,
      34             :  * this flag is used to trigger the delivery of a full notification.
      35             :  *
      36             :  * If *only* full notifications are in effect, the group is always
      37             :  * enqueued rather than the source, for efficiency (since we don't
      38             :  * care about the status of individual sources.)
      39             :  *
      40             :  * The net result of all of this is that a bunch of deltas enqueued
      41             :  * synchronously (on the receipt of a new Report with multiple
      42             :  * sources, for instance) will result in only a single full
      43             :  * notification being passed.
      44             :  */
      45             : 
      46             : #include "gmpx_basic_types.h"
      47             : #include "gmp.h"
      48             : #include "gmpx_environment.h"
      49             : #include "gmp_externs.h"
      50             : #include "gmp_private.h"
      51             : #include "gmp_router.h"
      52             : #include "gmpr_private.h"
      53             : #include "gmp_trace.h"
      54             : #include "gmpr_trace.h"
      55             : 
      56             : /* Forward references... */
      57             : 
      58             : static void gmpr_flush_notifications_client(gmpr_client *client);
      59             : 
      60             : 
      61             : /*
      62             :  * gmpr_get_client
      63             :  * 
      64             :  * Return an client pointer, given a client ID.
      65             :  *
      66             :  * Verifies that the client ID is valid.
      67             :  */
      68             : gmpr_client *
      69         747 : gmpr_get_client (gmp_client_id client_id)
      70             : {
      71             :     gmpr_client *client;
      72             : 
      73             :     /* Do the (trivial) conversion. */
      74             : 
      75         747 :     client = client_id;
      76             : 
      77             :     /* Verify the magic number. */
      78             : 
      79         747 :     gmpx_assert(client->rclient_magic == GMPR_CLIENT_MAGIC);
      80             : 
      81         747 :     return client;
      82             : }
      83             : 
      84             : 
      85             : /*
      86             :  * gmpr_client_startup_expiry
      87             :  *
      88             :  * Called when the client startup timer expires.  We enqueue everything
      89             :  * for the client.
      90             :  */
      91             : static void
      92         125 : gmpr_client_startup_expiry (gmpx_timer *timer, void *context)
      93             : {
      94             :     gmpr_client *client;
      95             : 
      96         125 :     client = context;
      97             : 
      98         125 :     gmpx_destroy_timer(timer);
      99         125 :     client->rclient_startup_timer = NULL;
     100             : 
     101             :     /* Enqueue everything for the client. */
     102             : 
     103         125 :     gmpr_client_enqueue_all_groups(client, TRUE);
     104         125 :     gmpr_alert_clients(client->rclient_instance);
     105         125 :     gmpr_client_enqueue_all_host_groups(client);
     106         125 :     gmpr_alert_host_clients(client->rclient_instance);
     107         125 : }
     108             : 
     109             : 
     110             : /*
     111             :  * gmpr_create_client
     112             :  *
     113             :  * Create a client entry.
     114             :  *
     115             :  * Returns a pointer to the client entry, or NULL if no memory.
     116             :  */
     117             : gmpr_client *
     118         125 : gmpr_create_client (gmpr_instance *instance)
     119             : {
     120             :     gmpr_client *client;
     121             :     ordinal_t next_ord;
     122             : 
     123             :     /* Grab the next ordinal. */
     124             : 
     125         125 :     next_ord = ord_get_ordinal(instance->rinst_ord_handle);
     126         125 :     if (next_ord == ORD_BAD_ORDINAL)
     127           0 :         return NULL;                    /* Out of memory */
     128             : 
     129             :     /* If we've got too many clients, bail. */
     130             : 
     131         125 :     if (next_ord >= GMPX_MAX_RTR_CLIENTS) {
     132           0 :         ord_free_ordinal(instance->rinst_ord_handle, next_ord);
     133           0 :         return NULL;                    /* Too many clients */
     134             :     }
     135             : 
     136             :     /* Allocate a client block. */
     137             : 
     138         125 :     client = gmpx_malloc_block(gmpr_client_tag);
     139         125 :     if (!client)                        /* No memory */
     140           0 :         return NULL;
     141             : 
     142             :     /* Link the client into the instance. */
     143             : 
     144         125 :     client->rclient_magic = GMPR_CLIENT_MAGIC;
     145         125 :     thread_circular_add_top(&instance->rinst_client_thread,
     146             :                             &client->rclient_thread);
     147         125 :     client->rclient_instance = instance;
     148         125 :     client->rclient_ordinal = next_ord;
     149             : 
     150             :     /* Initialize the notification threads. */
     151             : 
     152         125 :     thread_new_circular_thread(&client->rclient_notif_head);
     153         125 :     thread_new_circular_thread(&client->rclient_host_notif_head);
     154             : 
     155             :     /* Initialize the end-of-refresh notification block. */
     156             : 
     157         125 :     client->rclient_refresh_end_notif.gmpr_notify_type =
     158             :         GMPR_NOTIFY_REFRESH_END;
     159             : 
     160             :     /*
     161             :      * Create a timer and launch it with a zero delay.  This is a cheap
     162             :      * way of deferring.  The callback will enqueue all notifications for
     163             :      * the client.
     164             :      */
     165         125 :     client->rclient_startup_timer =
     166         125 :         gmpx_create_timer(instance->rinst_context, "GMP client startup_timer",
     167             :                           gmpr_client_startup_expiry, client);
     168         125 :     if (client->rclient_startup_timer)
     169         125 :         gmpx_start_timer(client->rclient_startup_timer, 0, 0);
     170             : 
     171         125 :     return client;
     172             : }
     173             : 
     174             : 
     175             : /*
     176             :  * gmpr_destroy_client
     177             :  *
     178             :  * Destroy a client entry.  Cleans up appropriately.
     179             :  */
     180             : void
     181         125 : gmpr_destroy_client (gmpr_client *client)
     182             : {
     183             :     gmpr_instance *instance;
     184             : 
     185         125 :     instance = client->rclient_instance;
     186             : 
     187             :     /* Free the ordinal. */
     188             : 
     189         125 :     ord_free_ordinal(instance->rinst_ord_handle, client->rclient_ordinal);
     190             : 
     191             :     /* Flush the notification lists. */
     192             : 
     193         125 :     gmpr_flush_notifications_client(client);
     194         125 :     gmpr_flush_host_notifications_client(client);
     195             : 
     196             :     /* Destroy any timers. */
     197             : 
     198         125 :     gmpx_destroy_timer(client->rclient_startup_timer);
     199             : 
     200             :     /* Delink the block and free it. */
     201             : 
     202         125 :     thread_remove(&client->rclient_thread);
     203         125 :     client->rclient_instance = NULL;
     204         125 :     gmpx_free_block(gmpr_client_tag, client);
     205         125 : }
     206             : 
     207             : 
     208             : /*
     209             :  * gmpr_destroy_instance_clients
     210             :  *
     211             :  * Destroy all clients on an instance.
     212             :  */
     213             : void
     214         125 : gmpr_destroy_instance_clients (gmpr_instance *instance)
     215             : {
     216             :     task_thread *thread_ptr;
     217             :     gmpr_client *client;
     218             : 
     219             :     /* Walk all clients on the instance. */
     220             : 
     221             :     while (TRUE) {
     222         125 :         thread_ptr = thread_circular_top(&instance->rinst_client_thread);
     223         125 :         client = gmpr_thread_to_client(thread_ptr);
     224         125 :         if (!thread_ptr)
     225         125 :             break;
     226             : 
     227             :         /* Destroy the client. */
     228             : 
     229           0 :         gmpr_destroy_client(client);
     230             :     }
     231         125 : }
     232             : 
     233             : 
     234             : /*
     235             :  * gmpr_notifications_active
     236             :  *
     237             :  * Returns TRUE if there are any active notifications on this
     238             :  * notification block array, or FALSE if not.
     239             :  */
     240             : boolean
     241         422 : gmpr_notifications_active (gmpr_notify_block *notify_block)
     242             : {
     243             :     uint32_t client_ord;
     244             : 
     245             :     /* Walk the notification array. */
     246             : 
     247         940 :     for (client_ord = 0; client_ord < GMPX_MAX_RTR_CLIENTS; client_ord++) {
     248         681 :         if (thread_node_on_thread(&notify_block->gmpr_notify_thread))
     249         163 :             return TRUE;
     250         518 :         notify_block++;
     251             :     }
     252             : 
     253         259 :     return FALSE;
     254             : }
     255             : 
     256             : 
     257             : /*
     258             :  * gmpr_source_notifications_active
     259             :  *
     260             :  * Returns TRUE if there are any active notifications on this source, or
     261             :  * FALSE if not.
     262             :  */
     263             : static boolean
     264          27 : gmpr_source_notifications_active (gmpr_ogroup_addr_entry *group_addr)
     265             : {
     266          27 :     return gmpr_notifications_active(group_addr->rogroup_addr_client_thread);
     267             : }
     268             : 
     269             : 
     270             : /*
     271             :  * gmpr_attempt_free_deleted_addr_entry
     272             :  *
     273             :  * Attempt to free a deleted address entry.  We assume that it is on
     274             :  * the group deleted list.
     275             :  *
     276             :  * The entry is free if there are no pending notifications left.
     277             :  *
     278             :  * returns TRUE if the gmpr_ogroup_addr_entry was freed. Otherwise FALSE.
     279             :  */
     280             : static boolean
     281          27 : gmpr_attempt_free_deleted_addr_entry (gmpr_ogroup_addr_entry *group_addr)
     282             : {
     283             :     gmpr_ogroup *group;
     284          27 :     boolean deleted_addr_entry = FALSE;
     285             : 
     286          27 :     group = group_addr->rogroup_addr_group;
     287             : 
     288             :     /* Do it if there are no active notifications. */
     289             : 
     290          27 :     if (!gmpr_source_notifications_active(group_addr)) {
     291          27 :         deleted_addr_entry = TRUE;
     292          27 :         gmp_delete_addr_list_entry(&group_addr->rogroup_addr_entry);
     293             : 
     294             :         /* Try to free the group as well. */
     295             : 
     296          27 :         gmpr_attempt_ogroup_free(group);
     297             :     }
     298          27 :     return deleted_addr_entry;
     299             : }
     300             : 
     301             : 
     302             : /*
     303             :  * gmpr_delete_notification
     304             :  *
     305             :  * Delete a client notification, it having been removed from the
     306             :  * client notification list.  Notifications aren't actually "deleted"
     307             :  * since they are embedded in other data structures.  But we do any
     308             :  * necessary cleanup.
     309             :  *
     310             :  * return true is the gmpr_notify_block memory has been freed.  This will
     311             :  * be true if the outer structure that the gmpr_notify_block is embedded
     312             :  * in is freed.  Otherwise return false.
     313             :  */
     314             : static boolean
     315         118 : gmpr_delete_notification (gmpr_notify_block *notification,
     316             :                           ordinal_t client_ord)
     317             : {
     318             :     gmpr_ogroup *group;
     319             :     gmpr_ogroup_addr_entry *group_addr;
     320         118 :     boolean notify_block_freed = FALSE;
     321             : 
     322             :     /* See whether it is a group or source notification. */
     323             : 
     324         118 :     switch (notification->gmpr_notify_type) {
     325          58 :       case GMPR_NOTIFY_GROUP:
     326             : 
     327             :         /*
     328             :          * Group notification.  Try to free the group, as we may have just
     329             :          * cleaned up the last thing keeping the group alive.
     330             :          */
     331          58 :         group = gmpr_client_notification_to_group(notification, client_ord);
     332          58 :         notify_block_freed = gmpr_attempt_ogroup_free(group);
     333          58 :         break;
     334             : 
     335          60 :       case GMPR_NOTIFY_SOURCE:
     336             : 
     337             :         /*
     338             :          * Source notification.  If the entry is on the deleted list
     339             :          * (meaning that we're done with it other than notifications),
     340             :          * try to free the address entry if it is no longer on any
     341             :          * client notification list.
     342             :          */
     343          60 :         group_addr = gmpr_client_notification_to_addr_entry(notification,
     344             :                                                             client_ord);
     345          60 :         if (gmpr_group_addr_deleted(group_addr)) {
     346             :              notify_block_freed = 
     347          27 :                  gmpr_attempt_free_deleted_addr_entry(group_addr);
     348             :         }
     349          60 :         break;
     350             : 
     351           0 :       case GMPR_NOTIFY_REFRESH_END:
     352             : 
     353             :         /* Refresh end.  Nothing to do. */
     354             : 
     355           0 :         break;
     356             : 
     357           0 :       default:
     358           0 :         gmpx_assert(FALSE);
     359             :     }
     360         118 :     return notify_block_freed;
     361             : }
     362             : 
     363             : 
     364             : /*
     365             :  * gmpr_flush_notifications
     366             :  *
     367             :  * Flush all pending client notifications on a notification block array.
     368             :  *
     369             :  * If just_delink is TRUE, we simply delink the entries.  If FALSE, we
     370             :  * call gmpr_delete_notification to try to clean up whatever the
     371             :  * client has embedded.
     372             :  */
     373             : void
     374          70 : gmpr_flush_notifications (gmpr_notify_block *notify_block, boolean just_delink)
     375             : {
     376             :     ordinal_t client_ord;
     377             :     boolean nb_freed;
     378             : 
     379         210 :     for (client_ord = 0; client_ord < GMPX_MAX_RTR_CLIENTS; client_ord++) {
     380         140 :         if (thread_node_on_thread(&notify_block->gmpr_notify_thread)) {
     381           0 :             thread_remove(&notify_block->gmpr_notify_thread);
     382           0 :             if (!just_delink){
     383           0 :                 nb_freed = gmpr_delete_notification(notify_block, client_ord);
     384           0 :                 if (nb_freed){
     385           0 :                     return;
     386             :                 }
     387             :             }
     388             :         }
     389         140 :         notify_block++;
     390             :     }
     391             : }
     392             : 
     393             : 
     394             : /*
     395             :  * gmpr_flush_notifications_group_list
     396             :  *
     397             :  * Flush all pending client source notifications on a group address list.
     398             :  */
     399             : static void
     400          99 : gmpr_flush_notifications_group_list (gmp_addr_list *addr_list)
     401             : {
     402             :     gmp_addr_list_entry *addr_entry, *addr_entry_next;
     403             :     gmpr_ogroup_addr_entry *group_addr;
     404             : 
     405          99 :     addr_entry = gmp_addr_list_next_entry(addr_list, NULL);
     406          99 :     addr_entry_next = NULL;
     407             :     /* Walk the list. */
     408             : 
     409             :     while (TRUE) {
     410          99 :         if (addr_entry)
     411           0 :             addr_entry_next = gmp_addr_list_next_entry(addr_list, addr_entry);
     412             : 
     413          99 :         group_addr = gmpr_addr_entry_to_ogroup_entry(addr_entry);
     414          99 :         if (!group_addr)
     415          99 :             break;
     416             : 
     417             :         /* Got an entry.  Delink it from each client. */
     418             : 
     419           0 :         gmpr_flush_notifications(group_addr->rogroup_addr_client_thread,
     420             :                                  FALSE);
     421           0 :         addr_entry = addr_entry_next;
     422             :     }
     423          99 : }
     424             : 
     425             : 
     426             : /*
     427             :  * gmpr_flush_notifications_group
     428             :  *
     429             :  * Flush all pending client source notifications for a group.  Note that it
     430             :  * does not remove the group itself from any notification list if it happens
     431             :  * to be there.
     432             :  */
     433             : void
     434          33 : gmpr_flush_notifications_group (gmpr_ogroup *ogroup)
     435             : {
     436             :     /* Flush each of the lists where notifications may lie. */
     437             : 
     438          33 :     gmpr_flush_notifications_group_list(&ogroup->rogroup_incl_src_addr);
     439          33 :     gmpr_flush_notifications_group_list(&ogroup->rogroup_excl_src_addr);
     440          33 :     gmpr_flush_notifications_group_list(&ogroup->rogroup_src_addr_deleted);
     441          33 : }
     442             : 
     443             : 
     444             : /*
     445             :  * gmpr_flush_notifications_client
     446             :  *
     447             :  * Flush all pending notifications for a client.
     448             :  */
     449             : static void
     450         125 : gmpr_flush_notifications_client (gmpr_client *client)
     451             : {
     452             :     gmpr_notify_block *notification;
     453             : 
     454             :     task_thread *thread_ptr;
     455             : 
     456             :     /* Walk the client notification list. */
     457             : 
     458             :     while (TRUE) {
     459         125 :         thread_ptr = thread_circular_dequeue_top(&client->rclient_notif_head);
     460         125 :         notification = gmpr_thread_to_notify_block(thread_ptr);
     461         125 :         if (!notification)
     462         125 :             break;
     463             : 
     464             :         /* Got a notification.  Delete it. */
     465             : 
     466           0 :         gmpr_delete_notification(notification, client->rclient_ordinal);
     467             :     }
     468         125 : }
     469             : 
     470             : 
     471             : /*
     472             :  * gmpr_update_client_notify
     473             :  *
     474             :  * Update the notify-client flag in advance of starting to enqueue
     475             :  * notifications.  We set it if it was previously clear, and if the
     476             :  * notification queue is currently empty.  The net effect is that we
     477             :  * set it when enqueueing the first notification.
     478             :  */
     479             : static void
     480         140 : gmpr_update_client_notify (gmpr_client *client)
     481             : {
     482         140 :     if (!client->rclient_notify) {
     483          82 :         client->rclient_notify =
     484          82 :             thread_circular_thread_empty(&client->rclient_notif_head);
     485          82 :         gmpr_trace(client->rclient_instance, GMPR_TRACE_CLIENT_NOTIFY,
     486             :                    "Client %u notify set to %u", client->rclient_ordinal,
     487             :                    client->rclient_notify);
     488             :     }
     489         140 : }
     490             : 
     491             : 
     492             : /*
     493             :  * gmpr_enqueue_refresh_end
     494             :  *
     495             :  * Enqueue a refresh end marker for a client.  We use a notification
     496             :  * block embedded in the client block to carry it.  We dequeue it and
     497             :  * move it to the end, in the off chance that it is already enqueued.
     498             :  */
     499             : void
     500           0 : gmpr_enqueue_refresh_end (gmpr_client *client)
     501             : {
     502             :     gmpr_notify_block *notif;
     503             : 
     504             :     /* Grab the block, based on the client ID. */
     505             : 
     506           0 :     notif = &client->rclient_refresh_end_notif;
     507             : 
     508             :     /* Dequeue it, just in case. */
     509             : 
     510           0 :     thread_remove(&notif->gmpr_notify_thread);
     511             : 
     512             :     /* Update the notification flag. */
     513             : 
     514           0 :     gmpr_update_client_notify(client);
     515             : 
     516             :     /* Enqueue it. */
     517             : 
     518           0 :     thread_circular_add_bottom(&client->rclient_notif_head,
     519             :                                &notif->gmpr_notify_thread);
     520           0 : }
     521             : 
     522             : 
     523             : /*
     524             :  * gmpr_client_enqueue_group
     525             :  *
     526             :  * Enqueue one output group onto a client notification thread.
     527             :  *
     528             :  * If it was already enqueued, it is delinked and moved to the end.
     529             :  */
     530             : static void
     531          80 : gmpr_client_enqueue_group (gmpr_client *client, gmpr_ogroup *group)
     532             : {
     533             :     ordinal_t client_ord;
     534             :     task_thread *thread_ptr;
     535             : 
     536             :     /*
     537             :      * Bail if the client startup timer is running.  We'll be doing a full
     538             :      * state enqueue when it expires.
     539             :      */
     540          80 :     if (client->rclient_startup_timer)
     541           0 :         return;
     542             : 
     543             :     /* Note if the client's notification thread was empty. */
     544             : 
     545          80 :     gmpr_update_client_notify(client);
     546             : 
     547             :     /*
     548             :      * Delink the group from the client task_thread, in case it was already
     549             :      * on there, and then requeue it at the end.
     550             :      */
     551          80 :     client_ord = client->rclient_ordinal;
     552          80 :     thread_ptr = &group->rogroup_client_thread[client_ord].gmpr_notify_thread;
     553          80 :     thread_remove(thread_ptr);
     554          80 :     thread_circular_add_bottom(&client->rclient_notif_head, thread_ptr);
     555             : 
     556             :     /* If we're doing full notifications, flag that we need one. */
     557             : 
     558          80 :     if (client->rclient_cb_context.rctx_full_notifications)
     559           0 :         group->rogroup_send_full_notif[client->rclient_ordinal] = TRUE;
     560             : }
     561             : 
     562             : 
     563             : /*
     564             :  * gmpr_client_enqueue_source
     565             :  *
     566             :  * Enqueue one source address onto a client notification thread.
     567             :  *
     568             :  * If it was already enqueued, it is delinked and moved to the end.
     569             :  *
     570             :  * If we're doing only full notifications (and no deltas) we instead
     571             :  * enqueue the group.  The net result of this is that the notification
     572             :  * thread will consist solely of group entries (and no sources.)
     573             :  */
     574             : static void
     575          60 : gmpr_client_enqueue_source (gmpr_client *client,
     576             :                             gmpr_ogroup_addr_entry *group_addr)
     577             : {
     578             :     gmpr_ogroup *group;
     579             :     ordinal_t client_ord;
     580             :     task_thread *thread_ptr;
     581             : 
     582             :     /*
     583             :      * If we're only doing full notifications, enqueue the group instead
     584             :      * and bail.
     585             :      */
     586          60 :     if (client->rclient_cb_context.rctx_full_notifications &&
     587           0 :         (!client->rclient_cb_context.rctx_delta_notifications)) {
     588           0 :         gmpr_client_enqueue_group(client, group_addr->rogroup_addr_group);
     589           0 :         return;
     590             :     }
     591             : 
     592             :     /*
     593             :      * Bail if the client startup timer is running.  We'll be doing a full
     594             :      * state enqueue when it expires.
     595             :      */
     596          60 :     if (client->rclient_startup_timer)
     597           0 :         return;
     598             : 
     599             :     /* Note if the client's notification thread was empty. */
     600             : 
     601          60 :     gmpr_update_client_notify(client);
     602             : 
     603             :     /*
     604             :      * Delink the group from the client thread, in case it was already
     605             :      * on there, and then requeue it at the end.
     606             :      */
     607          60 :     client_ord = client->rclient_ordinal;
     608          60 :     thread_ptr =
     609             :         &group_addr->rogroup_addr_client_thread[client_ord].gmpr_notify_thread;
     610          60 :     thread_remove(thread_ptr);
     611          60 :     thread_circular_add_bottom(&client->rclient_notif_head, thread_ptr);
     612             : 
     613             :     /* If we're doing full notifications, flag that we need one. */
     614             : 
     615          60 :     if (client->rclient_cb_context.rctx_full_notifications) {
     616           0 :         group = group_addr->rogroup_addr_group;
     617           0 :         group->rogroup_send_full_notif[client->rclient_ordinal] = TRUE;
     618             :     }
     619             : }
     620             : 
     621             : 
     622             : /*
     623             :  * gmpr_group_notify_clients
     624             :  *
     625             :  * Enqueue a group notification for all clients.
     626             :  *
     627             :  * The group is threaded onto the notification thread for each client,
     628             :  * and a notification callback is made if the thread was previously
     629             :  * empty.
     630             :  */
     631             : void
     632          80 : gmpr_group_notify_clients (gmpr_ogroup *group)
     633             : {
     634             :     gmpr_instance *instance;
     635             :     gmpr_client *client;
     636             :     task_thread *thread_ptr;
     637             : 
     638          80 :     instance = group->rogroup_intf->rintf_instance;
     639             : 
     640             :     /* Walk all clients. */
     641             : 
     642         160 :     FOR_ALL_CIRCULAR_THREAD_ENTRIES(&instance->rinst_client_thread,
     643             :                                     thread_ptr) {
     644          80 :         client = gmpr_thread_to_client(thread_ptr);
     645             : 
     646             :         /* Enqueue the group. */
     647             : 
     648          80 :         gmpr_client_enqueue_group(client, group);
     649             :     }
     650          80 : }
     651             : 
     652             : 
     653             : /*
     654             :  * gmpr_source_notify_clients
     655             :  *
     656             :  * Notify all clients that a source has changed state.
     657             :  *
     658             :  * The "flag" parameter says if the notification is conditional (based
     659             :  * on the setting of the rogroup_notify flag) or unconditional.
     660             :  *
     661             :  * The source is threaded onto the notification thread for each client,
     662             :  * and a notification callback is made if the thread was previously
     663             :  * empty.
     664             :  */
     665             : void
     666          60 : gmpr_source_notify_clients (gmpr_ogroup_addr_entry *group_addr,
     667             :                             gmpr_source_notify_flag flag)
     668             : {
     669             :     gmpr_instance *instance;
     670             :     gmpr_client *client;
     671             :     task_thread *thread_ptr;
     672             :     boolean client_found;
     673             : 
     674          60 :     instance = group_addr->rogroup_addr_group->rogroup_intf->rintf_instance;
     675             : 
     676             :     /* Do it if we're supposed to. */
     677             : 
     678          60 :     client_found = FALSE;
     679          60 :     if ((flag == NOTIFY_UNCONDITIONAL) || group_addr->rogroup_notify) {
     680             : 
     681             :         /* Walk all clients. */
     682             : 
     683         120 :         FOR_ALL_CIRCULAR_THREAD_ENTRIES(&instance->rinst_client_thread,
     684             :                                         thread_ptr) {
     685          60 :             client = gmpr_thread_to_client(thread_ptr);
     686          60 :             client_found = TRUE;
     687             : 
     688             :             /* Enqueue the notification. */
     689             : 
     690          60 :             gmpr_client_enqueue_source(client, group_addr);
     691             :         }
     692             :     }
     693          60 :     group_addr->rogroup_notify = FALSE;
     694             : 
     695             :     /*
     696             :      * If there were no clients, go ahead and attempt to free the
     697             :      * entry if it was deleted.  This is a paranoia check, since a
     698             :      * lingering deleted entry will block ever freeing the group.
     699             :      */
     700          60 :     if (!client_found && gmpr_group_addr_deleted(group_addr)) {
     701           0 :         gmpr_attempt_free_deleted_addr_entry(group_addr);
     702             :     }
     703          60 : }
     704             : 
     705             : 
     706             : /*
     707             :  * gmpr_enqueue_all_source_notifications
     708             :  *
     709             :  * Enqueue all appropriate source notifications for an output group.
     710             :  *
     711             :  * We ignore the deleted list, and only look at the include or exclude
     712             :  * list as appropriate.
     713             :  *
     714             :  * If client is non-NULL, the notifications are enqueued only for that
     715             :  * client.  Otherwise they are enqueued for all clients.
     716             :  */
     717             : void
     718          33 : gmpr_enqueue_all_source_notifications (gmpr_ogroup *group, gmpr_client *client)
     719             : {
     720             :     gmp_addr_list *addr_list;
     721             :     gmpr_ogroup_addr_entry *group_addr;
     722             :     gmp_addr_list_entry *addr_entry;
     723             : 
     724             :     /* Select the address list based on the group filter mode. */
     725             : 
     726          33 :     addr_list = gmpr_ogroup_source_list(group);
     727             : 
     728             :     /* Walk the address list, enqueueing each active entry. */
     729             : 
     730          33 :     addr_entry = NULL;
     731             :     while (TRUE) {
     732          33 :         addr_entry = gmp_addr_list_next_entry(addr_list, addr_entry);
     733          33 :         group_addr = gmpr_addr_entry_to_ogroup_entry(addr_entry);
     734          33 :         if (!group_addr)
     735          33 :             break;
     736           0 :         if (gmpr_source_is_active(group, group_addr)) {
     737           0 :             if (client) {
     738           0 :                 gmpr_client_enqueue_source(client, group_addr);
     739             :             } else {
     740           0 :                 gmpr_source_notify_clients(group_addr, NOTIFY_UNCONDITIONAL);
     741             :             }
     742             :         }
     743             :     }
     744          33 : }
     745             : 
     746             : 
     747             : /*
     748             :  * gmpr_mode_change_notify_clients
     749             :  *
     750             :  * Notify all clients about an interface mode change for a group.  This
     751             :  * consists of flushing all pending notifications for that group, and then
     752             :  * enqueueing the group and any sources.
     753             :  */
     754             : void
     755          33 : gmpr_mode_change_notify_clients (gmpr_ogroup *group)
     756             : {
     757             :     /* Enqueue the group. */
     758             : 
     759          33 :     gmpr_group_notify_clients(group);
     760             : 
     761             :     /* Flush any pending notifications. */
     762             : 
     763          33 :     gmpr_flush_notifications_group(group);
     764             : 
     765             :     /* Enqueue all appropriate source notifications. */
     766             : 
     767          33 :     gmpr_enqueue_all_source_notifications(group, NULL);
     768          33 : }
     769             : 
     770             : 
     771             : /*
     772             :  * gmpr_client_enqueue_all_intf_groups
     773             :  *
     774             :  * Enqueue all groups and sources associated with a single interface onto
     775             :  * a client notification thread.
     776             :  *
     777             :  * If flush is TRUE, we flush out all notifications for the groups's
     778             :  * sources first.  This cleans up any lingering source deletions.
     779             :  */
     780             : void
     781           3 : gmpr_client_enqueue_all_intf_groups (gmpr_client *client, gmpr_intf *intf,
     782             :                                      boolean flush)
     783             : {
     784             :     gmpr_ogroup *group;
     785             : 
     786             :     /* Walk all groups on the interface. */
     787             : 
     788           3 :     group = NULL;
     789             : 
     790             :     while (TRUE) {
     791           3 :         group = gmpr_next_oif_group(intf, group);
     792             : 
     793             :         /* Bail if done. */
     794             : 
     795           3 :         if (!group)
     796           3 :             break;
     797             : 
     798             :         /*
     799             :          * If we're asked to, flush any notifications for this group
     800             :          * first.  This serves to remove any pending source delete
     801             :          * notifications (which will be freed as a side effect).
     802             :          */
     803           0 :         if (flush)
     804           0 :             gmpr_flush_notifications_group(group);
     805             : 
     806             :         /* Got a group.  Enqueue it. */
     807             : 
     808           0 :         gmpr_client_enqueue_group(client, group);
     809             : 
     810             :         /* Enqueue all of the sources as well. */
     811             : 
     812           0 :         gmpr_enqueue_all_source_notifications(group, client);
     813             :     }
     814           3 : }
     815             : 
     816             : 
     817             : /*
     818             :  * gmpr_client_enqueue_all_groups
     819             :  *
     820             :  * Enqueue all groups and sources onto a client notification thread.
     821             :  * We call this when a new client appears.  Clients also use this to
     822             :  * refresh their state if they have to.
     823             :  *
     824             :  * If flush is TRUE, we flush out all source notifications first.  This
     825             :  * gets rid of any pending source deletions.
     826             :  */
     827             : void
     828         125 : gmpr_client_enqueue_all_groups (gmpr_client *client, boolean flush)
     829             : {
     830             :     gmpr_instance *instance;
     831             :     gmpr_intf *intf;
     832             : 
     833         125 :     instance = client->rclient_instance;
     834             : 
     835             :     /* Walk all interfaces on the instance. */
     836             : 
     837         125 :     intf = NULL;
     838             : 
     839             :     while (TRUE) {
     840         128 :         intf = gmpr_next_instance_intf(instance, intf);
     841         128 :         if (!intf)
     842         125 :             break;
     843             : 
     844             :         /* Enqueue everything on the interface. */
     845             : 
     846           3 :         gmpr_client_enqueue_all_intf_groups(client, intf, flush);
     847             :     }
     848         125 : }
     849             : 
     850             : 
     851             : /*
     852             :  * gmpr_free_notification
     853             :  *
     854             :  * Free up a notification.
     855             :  */
     856             : void
     857          62 : gmpr_free_notification (gmpr_client_notification *notification)
     858             : {
     859          62 :     gmp_destroy_addr_thread(notification->notif_addr_thread);
     860          62 :     gmpx_free_block(gmpr_notification_tag, notification);
     861          62 : }
     862             : 
     863             : 
     864             : /*
     865             :  * gmpr_build_full_notification
     866             :  *
     867             :  * Build a full notification.  We build an address thread from the source
     868             :  * addresses, if any.
     869             :  */
     870             : static void
     871           0 : gmpr_build_full_notification (gmpr_instance *instance,
     872             :                               gmpr_client_notification *client_notif,
     873             :                               gmpr_ogroup *group)
     874             : {
     875             :     gmp_addr_list *addr_list;
     876             :     gmp_addr_thread *addr_thread;
     877             :     gmp_addr_list_entry *addr_entry;
     878             :     gmpr_ogroup_addr_entry *group_addr;
     879             :     gmp_addr_cat_entry *cat_entry;
     880             : 
     881             :     /* Set the notification type. */
     882             : 
     883           0 :     client_notif->notif_type = GMPR_NOTIF_GROUP_STATE;
     884             : 
     885             :     /* Create an address thread with all active sources, if present. */
     886             : 
     887           0 :     addr_list = gmpr_ogroup_source_list(group);
     888           0 :     if (!gmp_addr_list_empty(addr_list)) {
     889             : 
     890             :         /* List is there.  Create an address thread. */
     891             : 
     892           0 :         addr_thread = gmp_alloc_addr_thread();
     893           0 :         client_notif->notif_addr_thread = addr_thread;
     894             : 
     895             :         /* Walk the address list. */
     896             : 
     897           0 :         addr_entry = NULL;
     898             : 
     899             :         while (TRUE) {
     900           0 :             addr_entry = gmp_addr_list_next_entry(addr_list, addr_entry);
     901           0 :             group_addr = gmpr_addr_entry_to_ogroup_entry(addr_entry);
     902           0 :             if (!group_addr)
     903           0 :                 break;
     904             : 
     905             :             /*
     906             :              * Got a source address entry.  Stick it in the thread if
     907             :              * it's active.
     908             :              */
     909           0 :             if (gmpr_source_is_active(group, group_addr)) {
     910             :                 cat_entry =
     911           0 :                     gmp_get_addr_cat_by_ordinal(&instance->rinst_addr_cat,
     912             :                                                 addr_entry->addr_ent_ord);
     913           0 :                 gmpx_assert(cat_entry);
     914           0 :                 gmp_enqueue_addr_thread_addr(addr_thread,
     915           0 :                                          cat_entry->adcat_ent_addr.gmp_addr,
     916             :                                          instance->rinst_addrlen);
     917             :             }
     918             :         }
     919             :     }
     920           0 : }
     921             : 
     922             : 
     923             : /*
     924             :  * gmpr_build_delta_notification
     925             :  * 
     926             :  * Build a delta notification.
     927             :  */
     928             : static void
     929         118 : gmpr_build_delta_notification (gmpr_instance *instance,
     930             :                                gmpr_notify_block *notification,
     931             :                                gmpr_client_notification *client_notif,
     932             :                                gmpr_ogroup *group,
     933             :                                gmpr_ogroup_addr_entry *group_addr)
     934             : {
     935             :     gmp_addr_list_entry *addr_entry;
     936             :     gmp_addr_cat_entry *cat_entry;
     937             : 
     938             :    /* Switch based on notification type. */
     939             : 
     940         118 :     switch (notification->gmpr_notify_type) {
     941          58 :       case GMPR_NOTIFY_GROUP:
     942             : 
     943             :         /*
     944             :          * We've got a group notification.  If the group state is
     945             :          * Include {}, or the interface is down, we're deleting the
     946             :          * group.  Otherwise, we're adding the group in either Include
     947             :          * or Exclude state.
     948             :          */
     949          58 :         if (gmpr_ogroup_is_active(group) && group->rogroup_intf->rintf_up) {
     950          37 :             if (group->rogroup_filter_mode == GMP_FILTER_MODE_INCLUDE) {
     951          15 :                 client_notif->notif_type = GMPR_NOTIF_GROUP_ADD_INCL;
     952             :             } else {
     953          22 :                 client_notif->notif_type = GMPR_NOTIF_GROUP_ADD_EXCL;
     954             :             }
     955             :         } else {
     956          21 :             client_notif->notif_type = GMPR_NOTIF_GROUP_DELETE;
     957             :         }
     958          58 :         break;
     959             : 
     960          60 :       case GMPR_NOTIFY_SOURCE:
     961             : 
     962             :         /*
     963             :          * We've got a client notification.  Look up the source
     964             :          * address and copy it to the client notification.
     965             :          */
     966          60 :         addr_entry = &group_addr->rogroup_addr_entry;
     967          60 :         cat_entry = gmp_get_addr_cat_by_ordinal(&instance->rinst_addr_cat,
     968             :                                                 addr_entry->addr_ent_ord);
     969          60 :         gmpx_assert(cat_entry);
     970          60 :         memmove(client_notif->notif_source_addr.gmp_addr,
     971          60 :         cat_entry->adcat_ent_addr.gmp_addr,
     972          60 :         instance->rinst_addrlen);
     973             : 
     974             :         /*
     975             :          * Now split out in each combination of the current filter
     976             :          * mode, and which list the address entry is on in order to
     977             :          * determine the notification type.  We treat a source as deleted
     978             :          * if the output interface is down.
     979             :          */
     980          60 :         switch (group->rogroup_filter_mode) {
     981          60 :           case GMP_FILTER_MODE_INCLUDE:
     982             : 
     983             :             /* Include mode.  See which list the entry is on. */
     984             : 
     985          60 :             if (gmpr_group_addr_deleted(group_addr) ||
     986          33 :                 !group->rogroup_intf->rintf_up) {
     987             : 
     988             :                 /* Deleted.  Send a BLOCK event. */
     989             : 
     990          27 :                 client_notif->notif_type = GMPR_NOTIF_BLOCK_SOURCE;
     991             : 
     992          33 :             } else if (gmpr_group_addr_included(group_addr)) {
     993             : 
     994             :                 /* Included.  Send an ALLOW event. */
     995             : 
     996          33 :                 client_notif->notif_type = GMPR_NOTIF_ALLOW_SOURCE;
     997             : 
     998             :             } else {
     999             : 
    1000             :                 /*
    1001             :                 * There can't be anything on the Exclude list in
    1002             :                 * Include mode.
    1003             :                 */
    1004           0 :                 gmpx_assert(FALSE);
    1005             :             }
    1006          60 :             break;
    1007             : 
    1008           0 :           case GMP_FILTER_MODE_EXCLUDE:
    1009             : 
    1010             :             /* Exclude mode.  See which list the entry is on. */
    1011             : 
    1012           0 :             if (gmpr_group_addr_deleted(group_addr) ||
    1013           0 :                 gmpr_group_addr_included(group_addr) ||
    1014           0 :                 !group->rogroup_intf->rintf_up) {
    1015             : 
    1016             :                 /*
    1017             :                  * Deleted or included or interface down.  Send an
    1018             :                  * ALLOW event.
    1019             :                  */
    1020           0 :                 client_notif->notif_type = GMPR_NOTIF_ALLOW_SOURCE;
    1021             : 
    1022             :             } else {
    1023             : 
    1024             :                 /* Excluded.  Send a BLOCK event. */
    1025             : 
    1026           0 :                 client_notif->notif_type = GMPR_NOTIF_BLOCK_SOURCE;
    1027             :             }
    1028           0 :             break;
    1029             : 
    1030           0 :           default:
    1031           0 :             gmpx_assert(FALSE);
    1032             :             break;
    1033             :         }
    1034             : 
    1035          60 :         break;
    1036             : 
    1037           0 :       case GMPR_NOTIFY_REFRESH_END:
    1038             : 
    1039             :         /* Refresh end marker.  Pass a refresh end notification. */
    1040             : 
    1041           0 :         client_notif->notif_type = GMPR_NOTIF_REFRESH_END;
    1042           0 :         break;
    1043             : 
    1044           0 :       default:
    1045           0 :         gmpx_assert(FALSE);
    1046             :         break;
    1047             :     }
    1048         118 : }
    1049             : 
    1050             : 
    1051             : /*
    1052             :  * gmpr_fill_client_notification
    1053             :  *
    1054             :  * Fill in the non-common client notification fields based on our internal
    1055             :  * notification type and other state information.
    1056             :  *
    1057             :  * Returns a pointer to the group entry.
    1058             :  */
    1059             : static gmpr_ogroup *
    1060         118 : gmpr_fill_client_notification (gmpr_instance *instance,
    1061             :                                gmpr_notify_block *notification,
    1062             :                                gmpr_client *client,
    1063             :                                gmpr_client_notification *client_notif)
    1064             : {
    1065             :     gmpr_ogroup *group;
    1066             :     gmpr_ogroup_addr_entry *group_addr;
    1067             : 
    1068             :     /* First, figure out the group. */
    1069             : 
    1070         118 :     switch (notification->gmpr_notify_type) {
    1071          58 :       case GMPR_NOTIFY_GROUP:   
    1072          58 :         group_addr = NULL;
    1073          58 :         group = gmpr_client_notification_to_group(notification,
    1074             :                                                   client->rclient_ordinal);
    1075          58 :         break;
    1076             : 
    1077          60 :       case GMPR_NOTIFY_SOURCE:
    1078             :         group_addr =
    1079          60 :             gmpr_client_notification_to_addr_entry(notification,
    1080             :                                                    client->rclient_ordinal);
    1081          60 :         group = group_addr->rogroup_addr_group;
    1082          60 :         break;
    1083             : 
    1084           0 :       case GMPR_NOTIFY_REFRESH_END:
    1085           0 :         group_addr = NULL;
    1086           0 :         group = NULL;
    1087           0 :         break;
    1088             : 
    1089           0 :       default:
    1090           0 :         gmpx_assert(FALSE);
    1091             :         group = NULL;                   /* Quiet the compiler. */
    1092             :         group_addr = NULL;
    1093             :         break;
    1094             : 
    1095             :     }
    1096             : 
    1097             :     /*
    1098             :      * Got the group.  If the send_full_notif flag is set for this
    1099             :      * client,bthe group is active, and the interface is up, build a
    1100             :      * full notification with all of the trimmings.
    1101             :      */
    1102         118 :     if (group && gmpr_ogroup_is_active(group) &&
    1103          74 :         group->rogroup_intf->rintf_up &&
    1104          74 :         group->rogroup_send_full_notif[client->rclient_ordinal]) {
    1105             : 
    1106             :         /* Clear the flag to show we're doing it. */
    1107             : 
    1108           0 :         group->rogroup_send_full_notif[client->rclient_ordinal] = FALSE;
    1109             : 
    1110             :         /* Build the notification. */
    1111             : 
    1112           0 :         gmpr_build_full_notification(instance, client_notif, group);
    1113             : 
    1114             :     } else {
    1115             : 
    1116             :         /* Not sending a full notification.  Build a delta (or deletion). */
    1117             : 
    1118         118 :         gmpr_build_delta_notification(instance, notification, client_notif,
    1119             :                                       group, group_addr);
    1120             :     }
    1121             : 
    1122         118 :     return group;
    1123             : }
    1124             : 
    1125             : 
    1126             : /*
    1127             :  * gmpr_alert_clients
    1128             :  *
    1129             :  * Process pending notifications for all clients.  This consists of
    1130             :  * calling the client callback for any clients flagged as needing to be
    1131             :  * called.
    1132             :  *
    1133             :  * We do this step separately so that we don't end up calling back the
    1134             :  * client from deep within our message processing.
    1135             :  */
    1136             : void
    1137         299 : gmpr_alert_clients (gmpr_instance *instance)
    1138             : {
    1139             :     gmpr_client *client;
    1140             :     gmpr_client_context *cli_ctx;
    1141             :     task_thread *thread_ptr;
    1142             : 
    1143         299 :     gmpr_trace_agent("Alert Client : file : %s, line : %.",
    1144             :                             __FILE__, __LINE__);
    1145             : 
    1146             :     /* Walk all clients. */
    1147             : 
    1148         598 :     FOR_ALL_CIRCULAR_THREAD_ENTRIES(&instance->rinst_client_thread,
    1149             :                                     thread_ptr) {
    1150         299 :         client = gmpr_thread_to_client(thread_ptr);
    1151             : 
    1152             :         /* If this is the first notification for the client, wake it up. */
    1153             : 
    1154         299 :         if (client->rclient_notify) {
    1155          62 :             gmpr_trace(client->rclient_instance, GMPR_TRACE_CLIENT_NOTIFY,
    1156             :                        "Client %u callback", client->rclient_ordinal);
    1157          62 :             client->rclient_notify = FALSE;
    1158          62 :             cli_ctx = &client->rclient_cb_context;
    1159          62 :             (*cli_ctx->rctx_notif_cb)(client->rclient_context);
    1160             :         }
    1161             :     }
    1162         299 : }
    1163             : 
    1164             : 
    1165             : /*
    1166             :  * gmpr_client_get_notification
    1167             :  *
    1168             :  * Get the next notification for a client.
    1169             :  *
    1170             :  * Returns a pointer to the notification block, or NULL if there's nothing
    1171             :  * there.
    1172             :  *
    1173             :  * We do some funny games here if the client is interested in both
    1174             :  * delta and full notifications.  Regardless of the notification type
    1175             :  * (group or source) we look at the group send_full_notif flag, and if
    1176             :  * TRUE, we send a full notification to the client, clear the flag,
    1177             :  * and leave the notification entry on the thread.  The next time we
    1178             :  * get called we'll deliver the deltas.
    1179             :  */
    1180             : gmpr_client_notification *
    1181         272 : gmpr_client_get_notification (gmpr_client *client,
    1182             :                               gmpr_client_notification *last_notification)
    1183             : {
    1184             :     task_thread *thread_ptr;
    1185             :     gmpr_ogroup *group;
    1186             :     gmpr_instance *instance;
    1187             :     gmpr_client_notification *client_notif;
    1188             :     gmpr_notify_block *notification;
    1189             :     boolean both_notifs;
    1190             : 
    1191         272 :     instance = client->rclient_instance;
    1192             : 
    1193             :     /* If there is an old client notification there, reuse it. */
    1194             : 
    1195         272 :     client_notif = NULL;
    1196         272 :     if (last_notification) {
    1197         118 :         client_notif = last_notification;
    1198         118 :         gmp_destroy_addr_thread(client_notif->notif_addr_thread);
    1199         118 :         memset(client_notif, 0, sizeof(gmpr_client_notification));
    1200             :     }
    1201             : 
    1202             :     /* Note whether we're doing both kinds of notifications. */
    1203             : 
    1204         544 :     both_notifs = (client->rclient_cb_context.rctx_delta_notifications &&
    1205         272 :                    client->rclient_cb_context.rctx_full_notifications);
    1206             : 
    1207             :     /* Pick up the top of the notification thread. */
    1208             :  
    1209         272 :     thread_ptr = thread_circular_top(&client->rclient_notif_head);
    1210         272 :     notification = gmpr_thread_to_notify_block(thread_ptr);
    1211             : 
    1212             :     /* Bail if there's nothing there. */
    1213             : 
    1214         272 :     if (!notification) {
    1215             : 
    1216             :         /* Free any old client notification. */
    1217             : 
    1218         154 :         if (client_notif)
    1219          62 :             gmpr_free_notification(client_notif);
    1220             : 
    1221         154 :         return NULL;
    1222             :     }
    1223             : 
    1224             :     /* If we don't have a client notification block, get one now. */
    1225             : 
    1226         118 :     if (!client_notif) {
    1227          62 :         client_notif = gmpx_malloc_block(gmpr_notification_tag);
    1228          62 :         if (!client_notif)
    1229           0 :             return NULL;                /* Out of memory */
    1230             :     }
    1231             : 
    1232             :     /* Fill in the non-common fields. */
    1233             :     
    1234         118 :     group = gmpr_fill_client_notification(instance, notification, client,
    1235             :                                           client_notif);
    1236             : 
    1237             :     /* Fill in the common fields. */
    1238             : 
    1239         118 :     if (group) {
    1240         118 :         client_notif->notif_intf_id = group->rogroup_intf->rintf_id;
    1241         118 :         memmove(client_notif->notif_group_addr.gmp_addr,
    1242         118 :         group->rogroup_addr.gmp_addr,
    1243         118 :         instance->rinst_addrlen);
    1244         118 :         client_notif->notif_filter_mode = group->rogroup_filter_mode;
    1245             :     }
    1246             : 
    1247             :     /*
    1248             :      * If the client has requested both kinds of notifications and we're
    1249             :      * delivering a full notification, we leave the notification block
    1250             :      * on the thread (so that we'll generate the delta the next time around.)
    1251             :      * Otherwise, we dequeue the notification block and attempt to free
    1252             :      * its contents.
    1253             :      */
    1254         118 :     if (!both_notifs || client_notif->notif_type != GMPR_NOTIF_GROUP_STATE) {
    1255         118 :         thread_circular_dequeue_top(&client->rclient_notif_head);
    1256         118 :         gmpr_delete_notification(notification, client->rclient_ordinal);
    1257             :     }
    1258             : 
    1259             :     /*
    1260             :      * A couple of notes for PR 509013:
    1261             :      * 1. We add logic here to give hints to the clients, such as
    1262             :      *    IGMP, about the last notification of the same group so that
    1263             :      *    the clients would know to process a few more notifications
    1264             :      *    even when its quantum has been reached.
    1265             :      * 2. This block of code is deliberately separated from the code
    1266             :      *    above to maintain its independence and modularity.
    1267             :      * 3. mcsnoopd is a client that asks for both kinds of
    1268             :      *    notifications.  The fix here should work for mcsnoopd as
    1269             :      *    well.  However, in this set of fix, only IGMP and MLD are
    1270             :      *    changed to take advantage of this.  A separate PR is needed
    1271             :      *    in order to add similar logic to mcsnoopd.
    1272             :      */
    1273             : 
    1274             :     /*
    1275             :      * Take a peek at the next notification to see if it is
    1276             :      * for a new (*,g).  If yes, mark the current notification as the
    1277             :      * last (s,g).
    1278             :      */
    1279         118 :     client_notif->notif_last_sg = FALSE;
    1280         118 :     thread_ptr = thread_circular_top(&client->rclient_notif_head);
    1281             :     /*
    1282             :      * If the client has requested both kinds of notifications, the
    1283             :      * notification block is not popped from the top of the thread
    1284             :      * (see comments above), as such, we need to look further to its
    1285             :      * next block in the thread.
    1286             :      */
    1287         118 :     if (thread_ptr &&
    1288           0 :         both_notifs && client_notif->notif_type == GMPR_NOTIF_GROUP_STATE) {
    1289           0 :         thread_ptr = thread_circular_thread_next(&client->rclient_notif_head,
    1290             :                                                  thread_ptr);
    1291             :     }
    1292         118 :     if (thread_ptr) {
    1293          56 :         notification = gmpr_thread_to_notify_block(thread_ptr);
    1294          56 :         if (notification && 
    1295          56 :             notification->gmpr_notify_type == GMPR_NOTIFY_GROUP) {
    1296          11 :             client_notif->notif_last_sg = TRUE;
    1297             :         }
    1298             :     } else {
    1299             :         /* Last notification will always be the last (s,g). */
    1300          62 :         client_notif->notif_last_sg = TRUE;
    1301             :     }
    1302             : 
    1303             :     /* Trace it. */
    1304             : 
    1305         118 :     gmpr_trace(instance, GMPR_TRACE_CLIENT_NOTIFY,
    1306             :                "Client %u notif %i %a %s %a",
    1307             :                client->rclient_ordinal, client_notif->notif_intf_id,
    1308             :                client_notif->notif_group_addr.gmp_addr,
    1309             :                gmpr_client_notif_string(client_notif->notif_type),
    1310             :                client_notif->notif_source_addr.gmp_addr);
    1311             : 
    1312         118 :     return client_notif;
    1313             : }

Generated by: LCOV version 1.14