LCOV - code coverage report
Current view: top level - vnsw/agent/services/multicast/grpmgmt - gmpp_proto.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 125 164 76.2 %
Date: 2026-08-03 02:19:58 Functions: 14 16 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* $Id: gmpp_proto.c 346474 2009-11-14 10:18:58Z ssiano $
       2             :  *
       3             :  * gmpp_proto.c - GMP generic packet handling
       4             :  *
       5             :  * Dave Katz, March 2008
       6             :  *
       7             :  * Copyright (c) 2008, Juniper Networks, Inc.
       8             :  * All rights reserved.
       9             :  *
      10             :  * This module handles generic packet I/O for GMP host and router functions.
      11             :  * It acts as an interface between the host/router modules on one side and
      12             :  * the IGMP/MLD modules on the other side.
      13             :  *
      14             :  * There are two instantiations of this function, one for each role
      15             :  * (Host and Router.)
      16             :  *
      17             :  * This function has no visibility into host- and router-specific data
      18             :  * structures, so that we can operate with only one side compiled in if
      19             :  * desired.
      20             :  *
      21             :  *
      22             :  * Registration
      23             :  *
      24             :  *   The client (host/router) must first register via gmpp_register.
      25             :  *   This establishes links between the two sides.
      26             :  *
      27             :  * Transmission
      28             :  *
      29             :  *   When a client wishes to transmit a packet, it informs this module
      30             :  *   via gmpp_start_xmit.  This module then informs the protocol
      31             :  *   module, which ultimately informs the I/O environment.  A callback
      32             :  *   percolates back through here to the client xmit_ready callback,
      33             :  *   requesting a packet.  The client passes back a pointer to a
      34             :  *   generic packet.
      35             :  *
      36             :  *   This module forms a packet out of some or all of the generic
      37             :  *   packet data passed by the client, with the help of the
      38             :  *   protocol-specific modules, and passes the packet to the I/O
      39             :  *   environment.  We delink each source address from its transmit
      40             :  *   list as we process it.  We call back the client group_done
      41             :  *   routine as we finish processing the data for each group.
      42             :  *
      43             :  *   In some cases, a single group may span a packet (an Include list
      44             :  *   with a very large number of sources.)  In this case we don't call
      45             :  *   the group_done entry and leave some of the source addresses still
      46             :  *   enqueued.  When we call back for the next packet, the client will
      47             :  *   pass us the same group again and we continue.
      48             :  *
      49             :  * Reception
      50             :  *
      51             :  *   Packet reception is driven from the I/O environment.  We receive
      52             :  *   an indication from there and parse the packet, with the help of
      53             :  *   the protocol modules, and pass the parsed packet to the client's
      54             :  *   packet_rcv callback.  The client is expected to process the
      55             :  *   entire parsed packet atomically, as we free the parsed packet
      56             :  *   immediately.
      57             :  *
      58             :  */
      59             : #include "gmpx_basic_types.h"
      60             : #include "gmp.h"
      61             : #include "gmpx_environment.h"
      62             : #include "gmp_externs.h"
      63             : #include "gmp_private.h"
      64             : #include "gmpp_private.h"
      65             : #include "igmp_protocol.h"
      66             : #include "mld_proto.h"
      67             : 
      68             : /*
      69             :  * Context blocks
      70             :  * 
      71             :  * Statically allocated, indexed by client role (host/router).
      72             :  */
      73             : static gmpp_context gmpp_context_block[GMP_NUM_ROLES];
      74             : static gmpp_io_context gmpp_io_context_block[GMP_NUM_PROTOS][GMP_NUM_ROLES];
      75             : static gmpx_block_tag gmpp_packet_header_tag;
      76             : static gmpx_block_tag gmpp_group_record_tag;
      77             : static gmpx_block_tag gmpp_io_exception_tag;
      78             : 
      79             : static boolean gmpp_initialized;
      80             : 
      81             : /*
      82             :  * gmpp_init
      83             :  *
      84             :  * Do basic initialization.
      85             :  */
      86             : void
      87         125 : gmpp_init (void)
      88             : {
      89         125 :     if (!gmpp_initialized) {
      90             : 
      91             :         /* Set up memory blocks. */
      92             : 
      93         125 :         gmpp_packet_header_tag =
      94         125 :             gmpx_malloc_block_create(sizeof(gmp_packet),
      95             :                                      "GMP generic packet header");
      96         125 :         gmpp_group_record_tag =
      97         125 :             gmpx_malloc_block_create(sizeof(gmp_report_group_record),
      98             :                                      "GMP generic packet group record");
      99         125 :         gmpp_io_exception_tag =
     100         125 :             gmpx_malloc_block_create(sizeof(gmpp_io_exception),
     101             :                                      "GMP I/O exception record");
     102         125 :         gmpp_initialized = TRUE;
     103             :     }
     104         125 : }
     105             : 
     106             : 
     107             : /*
     108             :  * gmpp_create_group_record
     109             :  *
     110             :  * Create a group record, initialize it, and link it into the packet.
     111             :  *
     112             :  * Returns a pointer to the group record, or NULL if out of memory.
     113             :  */
     114             : gmp_report_group_record *
     115         140 : gmpp_create_group_record (gmp_report_packet *report_packet, void *group_id,
     116             :                           const uint8_t *group_addr, uint32_t addr_len)
     117             : {
     118             :     gmp_report_group_record *group_record;
     119             : 
     120             :     /* Allocate the block. */
     121             : 
     122         140 :     group_record = gmpx_malloc_block(gmpp_group_record_tag);
     123         140 :     if (!group_record)
     124           0 :         return NULL;                    /* Out of memory */
     125             : 
     126             :     /* Initialize it. */
     127             : 
     128         140 :     group_record->gmp_rpt_group_id = group_id;
     129         140 :     memmove(group_record->gmp_rpt_group.gmp_addr, group_addr, addr_len);
     130             : 
     131             :     /* Put it into the thread. */
     132             : 
     133         140 :     thread_circular_add_bottom(&report_packet->gmp_report_group_head,
     134             :                                &group_record->gmp_rpt_thread);
     135         140 :     report_packet->gmp_report_group_count++;
     136             : 
     137         140 :     return group_record;
     138             : }
     139             : 
     140             : 
     141             : /*
     142             :  * gmpp_create_packet_header
     143             :  *
     144             :  * Create a packet header.
     145             :  *
     146             :  * Returns a pointer to the packet header, or NULL if out of memory.
     147             :  */
     148             : gmp_packet *
     149        1055 : gmpp_create_packet_header (gmp_version version, gmp_message_type message_type,
     150             :                            gmp_proto proto)
     151             : {
     152             :     gmp_packet *packet;
     153             :     gmp_report_packet *report_packet;
     154             : 
     155        1055 :     packet = gmpx_malloc_block(gmpp_packet_header_tag);
     156        1055 :     if (packet) {
     157             : 
     158             :         /* Initialize it. */
     159             : 
     160        1055 :         packet->gmp_packet_version = version;
     161        1055 :         packet->gmp_packet_type = message_type;
     162        1055 :         packet->gmp_packet_proto = proto;
     163             : 
     164        1055 :         switch (message_type) {
     165             : 
     166         919 :           case GMP_QUERY_PACKET:
     167         919 :             break;
     168             : 
     169         136 :           case GMP_REPORT_PACKET:
     170         136 :             report_packet = &packet->gmp_packet_contents.gmp_packet_report;
     171         136 :             thread_new_circular_thread(&report_packet->gmp_report_group_head);
     172         136 :             break;
     173             : 
     174           0 :           default:
     175           0 :             gmpx_assert(FALSE);
     176             :         }
     177             :     }
     178             : 
     179        1055 :     return packet;
     180             : }
     181             : 
     182             : 
     183             : /*
     184             :  * gmpp_destroy_packet
     185             :  *
     186             :  * Destroy a generic packet.  We clean up the contents and free it.
     187             :  *
     188             :  * Note that the address list pointers in query packets and group records
     189             :  * point to address lists owned elsewhere, and are not freed.  The address
     190             :  * threads pointed to are destroyed, however.
     191             :  */
     192             : void
     193        1055 : gmpp_destroy_packet (gmp_packet *packet)
     194             : {
     195             :     gmp_report_group_record *group_record;
     196             :     gmp_report_packet *report_packet;
     197             :     gmp_query_packet *query_packet;
     198             :     task_thread *thread_ptr;
     199             : 
     200             :     /* Clean up based on the packet type. */
     201             : 
     202        1055 :     switch (packet->gmp_packet_type) {
     203             : 
     204         919 :       case GMP_QUERY_PACKET:
     205             : 
     206             :         /* Query packet.  Delete the address thread. */
     207             : 
     208         919 :         query_packet = &packet->gmp_packet_contents.gmp_packet_query;
     209         919 :         gmp_destroy_addr_thread(query_packet->gmp_query_rcv_srcs);
     210         919 :         break;
     211             :         
     212         136 :       case GMP_REPORT_PACKET:
     213             : 
     214             :         /* Report packet, toss all of the group records. */
     215             : 
     216         136 :         report_packet = &packet->gmp_packet_contents.gmp_packet_report;
     217             : 
     218             :         /* Walk all group records, and free them. */
     219             : 
     220             :         while (TRUE) {
     221         140 :             thread_ptr =
     222         276 :                 thread_circular_top(&report_packet->gmp_report_group_head);
     223         276 :             if (!thread_ptr)
     224         136 :                 break;
     225         140 :             thread_remove(thread_ptr);
     226         140 :             group_record = gmp_thread_to_report_group_record(thread_ptr);
     227         140 :             gmp_destroy_addr_thread(group_record->gmp_rpt_rcv_srcs);
     228         140 :             gmpx_free_block(gmpp_group_record_tag, group_record);
     229             :         }
     230         136 :         break;
     231             : 
     232           0 :       default:
     233           0 :         gmpx_assert(FALSE);
     234             :         break;
     235             :     }
     236             : 
     237             :     /* Now toss the packet header and we're done. */
     238             : 
     239        1055 :     gmpx_free_block(gmpp_packet_header_tag, packet);
     240        1055 : }
     241             : 
     242             :     
     243             : /*
     244             :  * gmpp_start_xmit
     245             :  * 
     246             :  * Initiate transmission on an interface.
     247             :  */
     248             : void
     249        2627 : gmpp_start_xmit (gmp_role role, gmp_proto proto, gmpx_intf_id intf_id)
     250             : {
     251             :     gmpp_io_context *io_ctx;
     252             :     gmpp_io_exception *io_except;
     253             :     patnode *node;
     254             : 
     255             :     /* Validate the parameters. */
     256             : 
     257        2627 :     gmpx_assert(role < GMP_NUM_ROLES);
     258        2627 :     gmpx_assert(proto < GMP_NUM_PROTOS);
     259             : 
     260             :     /* Call the registered callback. */
     261             : 
     262        2627 :     io_ctx = &gmpp_io_context_block[proto][role];
     263        2627 :     gmpx_assert(io_ctx->io_ctx_xmitready_cb);
     264             : 
     265             :     /* See if there's an exception for this interface.  If so, use it. */
     266             : 
     267        2627 :     node = gmpx_patricia_lookup(io_ctx->io_ctx_exceptions, &intf_id);
     268        2627 :     io_except = gmpp_patnode_to_io_ex(node);
     269             : 
     270        2627 :     if (io_except) {
     271           0 :         (*io_except->io_ctx_alt_cb)(role, proto, intf_id);
     272             :     } else {
     273             : 
     274             :         /* No exception.  use the general callback. */
     275             : 
     276        2627 :         (*io_ctx->io_ctx_xmitready_cb)(role, proto, intf_id);
     277             :     }
     278        2627 : }
     279             : 
     280             : 
     281             : /*
     282             :  * gmpp_register
     283             :  *
     284             :  * Register a client.
     285             :  */
     286             : void
     287         125 : gmpp_register (gmp_role role, gmp_xmit_callback_func xmit_callback,
     288             :                gmp_rcv_callback_func rcv_callback,
     289             :                gmp_group_done_callback_func group_done_callback,
     290             :                gmp_packet_free_callback_func packet_free_callback)
     291             : {
     292             :     gmpp_context *ctx;
     293             : 
     294             :     /* Make sure the role is in range. */
     295             : 
     296         125 :     gmpx_assert(role < GMP_NUM_ROLES);
     297             : 
     298         125 :     ctx = &gmpp_context_block[role];
     299             : 
     300             :     /* Make sure we're not getting any duplicates. */
     301             : 
     302         125 :     gmpx_assert(ctx->ctx_xmit_cb == NULL);
     303             : 
     304             :     /* Squirrel away all of the context. */
     305             : 
     306         125 :     ctx->ctx_xmit_cb = xmit_callback;
     307         125 :     ctx->ctx_rcv_cb = rcv_callback;
     308         125 :     ctx->ctx_group_cb = group_done_callback;
     309         125 :     ctx->ctx_pkt_free_cb = packet_free_callback;
     310         125 : }
     311             : 
     312             : 
     313             : /*
     314             :  * gmpp_deregister
     315             :  *
     316             :  * Deregister a client.
     317             :  */
     318             : void
     319         125 : gmpp_deregister (gmp_role role)
     320             : {
     321             :     gmpp_context *ctx;
     322             : 
     323             :     /* Make sure the role is in range. */
     324             : 
     325         125 :     gmpx_assert(role < GMP_NUM_ROLES);
     326             : 
     327         125 :     ctx = &gmpp_context_block[role];
     328             : 
     329             :     /* It better have been registered. */
     330             : 
     331         125 :     gmpx_assert(ctx->ctx_xmit_cb);
     332             : 
     333             :     /* Zap the context block. */
     334             : 
     335         125 :     memset(ctx, 0, sizeof(gmpp_context));
     336         125 : }
     337             : 
     338             : 
     339             : /*
     340             :  * gmpp_enab_disab_proto
     341             :  *
     342             :  * Enable or disable protocol processing for a client.
     343             :  */
     344             : void
     345         250 : gmpp_enab_disab_proto (gmp_role role, gmp_proto proto, boolean enabled)
     346             : {
     347             :     /* Ensure that everything is in range. */
     348             : 
     349         250 :     gmpx_assert(role < GMP_NUM_ROLES && proto < GMP_NUM_PROTOS);
     350             : 
     351             :     /* Set the flag appropriately. */
     352             : 
     353         250 :     gmpp_context_block[role].ctx_proto_active[proto] = enabled;
     354         250 : }
     355             : 
     356             : 
     357             : /*
     358             :  * gmp_register_io
     359             :  *
     360             :  * Register packet I/O.  This is called by the protocol formatting routines
     361             :  * to register themselves.
     362             :  */
     363             : void
     364         125 : gmp_register_io (gmp_role role, gmp_proto proto,
     365             :                  gmpp_xmit_ready_func xmit_ready)
     366             : {
     367             :     gmpp_io_context *io_ctx;
     368             : 
     369             :     /* Validate the parameters. */
     370             : 
     371         125 :     gmpx_assert(role < GMP_NUM_ROLES);
     372         125 :     gmpx_assert(proto < GMP_NUM_PROTOS);
     373             : 
     374             :     /* Set the context appropriately. */
     375             : 
     376         125 :     io_ctx = &gmpp_io_context_block[proto][role];
     377             : 
     378             :     /* Set the transmit-ready callback. */
     379             : 
     380         125 :     io_ctx->io_ctx_xmitready_cb = xmit_ready;
     381             : 
     382             :     /* Set up the patricia tree for exception interfaces. */
     383             : 
     384         125 :     if (!io_ctx->io_ctx_exceptions) {
     385         125 :         io_ctx->io_ctx_exceptions =
     386         125 :             gmpx_patroot_init(sizeof(gmpx_intf_id),
     387             :                               GMPX_PATRICIA_OFFSET(gmpp_io_exception,
     388             :                                                    io_ctx_node, io_ctx_intf));
     389             :     }
     390         125 : }
     391             : 
     392             : 
     393             : /*
     394             :  * gmp_register_io_exception
     395             :  *
     396             :  * Register an exception to the I/O transmit callback for a particular
     397             :  * interface.  Transmit-ready notifications on the indicated interface
     398             :  * will go to the exception routine instead of the normal one.
     399             :  */
     400             : void
     401           0 : gmp_register_io_exception (gmp_role role, gmp_proto proto,
     402             :                            gmpx_intf_id intf_id,
     403             :                            gmpp_xmit_ready_func xmit_ready)
     404             : {
     405             :     gmpp_io_context *io_ctx;
     406             :     gmpp_io_exception *io_except;
     407             :     gmpx_patnode *node;
     408             : 
     409             :     /*
     410             :      * We may get called before initialization has taken place.  Cheat
     411             :      * and try to initialize now (it'll be a no-op if this has already
     412             :      * taken place.)
     413             :      */
     414           0 :     gmpp_init();
     415             : 
     416             :     /* Validate the parameters. */
     417             : 
     418           0 :     gmpx_assert(role < GMP_NUM_ROLES);
     419           0 :     gmpx_assert(proto < GMP_NUM_PROTOS);
     420             : 
     421             :     /* Get the context block. */
     422             : 
     423           0 :     io_ctx = &gmpp_io_context_block[proto][role];
     424             : 
     425             :     /* We better have already initialized it. */
     426             : 
     427           0 :     gmpx_assert(io_ctx->io_ctx_exceptions);
     428             : 
     429             :     /* Look up the interface.  Use it if it's there. */
     430             : 
     431           0 :     node = gmpx_patricia_lookup(io_ctx->io_ctx_exceptions, &intf_id);
     432           0 :     io_except = gmpp_patnode_to_io_ex(node);
     433             : 
     434             :     /* If there's no exception block, allocate it and link it in. */
     435             : 
     436           0 :     if (!io_except) {
     437           0 :         io_except = gmpx_malloc_block(gmpp_io_exception_tag);
     438           0 :         io_except->io_ctx_intf = intf_id;
     439           0 :         gmpx_assert(gmpx_patricia_add(io_ctx->io_ctx_exceptions,
     440             :                                       &io_except->io_ctx_node));
     441             :     }
     442             : 
     443             :     /* Fill in the field. */
     444             : 
     445           0 :     io_except->io_ctx_alt_cb = xmit_ready;
     446           0 : }
     447             : 
     448             : 
     449             : /*
     450             :  * gmp_register_peek_function
     451             :  *
     452             :  * Register a transmit peek function for a client.  This routine is called
     453             :  * whenever the client engine generates a generic packet to send.
     454             :  */
     455             : void
     456         125 : gmp_register_peek_function (gmp_role role,
     457             :                             gmp_xmit_peek_callback_func xmit_peek_cb,
     458             :                             gmp_rcv_peek_callback_func rcv_peek_cb)
     459             : {
     460         125 :     gmpp_context_block[role].ctx_xmit_peek_cb = xmit_peek_cb;
     461         125 :     gmpp_context_block[role].ctx_rcv_peek_cb = rcv_peek_cb;
     462         125 : }
     463             : 
     464             : 
     465             : /*
     466             :  * gmpp_next_xmit_packet
     467             :  *
     468             :  * Get the next generic packet to transmit, given the interface, protocol,
     469             :  * and role.  Returns a pointer to the generic packet, or NULL if there's
     470             :  * nothing to send.
     471             :  *
     472             :  * The buffer length is passed to the transmit callback to help it figure
     473             :  * out how many groups can fit within a packet.  Zero values are tolerated,
     474             :  * meaning that the buffer size is unknown;  the callback routines will always
     475             :  * return a single group in that case.
     476             :  */
     477             : gmp_packet *
     478        3546 : gmpp_next_xmit_packet (gmp_role role, gmp_proto proto, gmpx_intf_id intf_id,
     479             :                        uint32_t buffer_len)
     480             : {
     481             :     gmpp_context *ctx;
     482             :     gmp_packet *packet;
     483             : 
     484        3546 :     gmpx_assert(role < GMP_NUM_ROLES);
     485        3546 :     gmpx_assert(proto < GMP_NUM_PROTOS);
     486             : 
     487             :     /* Look up the context block. */
     488             : 
     489        3546 :     ctx = &gmpp_context_block[role];
     490        3546 :     gmpx_assert(ctx->ctx_proto_active[proto]);
     491             : 
     492             :     /* Call the callback to do the job. */
     493             : 
     494        3546 :     packet = (*ctx->ctx_xmit_cb)(intf_id, proto, buffer_len);
     495             : 
     496             :     /* If there is a transmit peek function, let it peek at the packet. */
     497             : 
     498        3546 :     if (packet && ctx->ctx_xmit_peek_cb)
     499         919 :         (*ctx->ctx_xmit_peek_cb)(intf_id, proto, packet);
     500             : 
     501        3546 :     return packet;
     502             : }
     503             : 
     504             : 
     505             : /*
     506             :  * gmpp_group_done
     507             :  *
     508             :  * Called by the protocol-specific routine to indicate that it is done
     509             :  * processing a group.  We call the client engine callback routine in turn.
     510             :  */
     511             : void
     512          44 : gmpp_group_done (gmp_role role, gmp_proto proto, void *group_id)
     513             : {
     514             :     gmpp_context *ctx;
     515             : 
     516          44 :     gmpx_assert(role < GMP_NUM_ROLES);
     517          44 :     gmpx_assert(proto < GMP_NUM_PROTOS);
     518             : 
     519          44 :     ctx = &gmpp_context_block[role];
     520          44 :     gmpx_assert(ctx->ctx_proto_active[proto]);
     521             : 
     522          44 :     (*ctx->ctx_group_cb)(group_id);
     523          44 : }
     524             : 
     525             : 
     526             : /*
     527             :  * gmpp_packet_done
     528             :  *
     529             :  * Called by the protocol-specific routine to indicate that it is done
     530             :  * with a packet.  We call the client engine callback routine in turn.
     531             :  */
     532             : void
     533         919 : gmpp_packet_done (gmp_role role, gmp_proto proto, gmp_packet *packet)
     534             : {
     535             :     gmpp_context *ctx;
     536             : 
     537         919 :     gmpx_assert(role < GMP_NUM_ROLES);
     538         919 :     gmpx_assert(proto < GMP_NUM_PROTOS);
     539             : 
     540         919 :     ctx = &gmpp_context_block[role];
     541         919 :     gmpx_assert(ctx->ctx_proto_active[proto]);
     542             : 
     543         919 :     (*ctx->ctx_pkt_free_cb)(packet);
     544         919 : }
     545             : 
     546             : 
     547             : /*
     548             :  * gmpp_process_rcv_packet
     549             :  *
     550             :  * Called by the protocol-specific routine when a packet is received.
     551             :  * The packet parsed cleanly, and we're passed a generic packet.  We
     552             :  * pass it to each client, and to any registered peek routine.
     553             :  */
     554             : void
     555         136 : gmpp_process_rcv_packet (gmp_packet *packet, gmpx_intf_id intf_id)
     556             : {
     557             :     gmp_role role;
     558             :     gmpp_context *ctx;
     559             : 
     560         136 :     gmpx_assert(packet->gmp_packet_proto < GMP_NUM_PROTOS);
     561             : 
     562         408 :     for (role = 0; role < GMP_NUM_ROLES; role++) {
     563         272 :         ctx = &gmpp_context_block[role];
     564         272 :         if (ctx->ctx_proto_active[packet->gmp_packet_proto]) {
     565         136 :             if (ctx->ctx_rcv_peek_cb) {
     566         136 :                 (*ctx->ctx_rcv_peek_cb)(intf_id, packet->gmp_packet_proto,
     567             :                                         packet);
     568             :             }
     569         136 :             (*ctx->ctx_rcv_cb)(intf_id, packet);
     570             :         }
     571             :     }
     572         136 : }
     573             : 
     574             : 
     575             : /*
     576             :  * gmpp_max_group_count
     577             :  *
     578             :  * Get the max group count possible for a packet, given the protocol,
     579             :  * version, packet type, and buffer length.
     580             :  *
     581             :  * This is calculated based on having no sources, so it may well return
     582             :  * a number larger than what the packet can actually carry.  This is OK,
     583             :  * as this is simply providing an upper bound for the packet building code,
     584             :  * which will only put into the packet what will fit.
     585             :  *
     586             :  * This doesn't exactly belong here, since it is multi-protocol specific
     587             :  * rather than generic, but by putting it here we can still compile only
     588             :  * MLD or IGMP without difficulty.
     589             :  *
     590             :  * If the buffer length is zero, we always return one group.
     591             :  */
     592             : uint32_t
     593           0 : gmpp_max_group_count (gmp_proto proto, gmp_version version,
     594             :                       gmp_message_type msg_type, uint32_t buffer_len)
     595             : {
     596             :     uint32_t overhead;
     597             :     uint32_t group_len;
     598             :     uint32_t max_group_count;
     599             : 
     600             :     /*
     601             :      * The only packets that carry more than one group are IGMPv3/
     602             :      * MLDv2 Report packets.  Bail on the others first, and bail if
     603             :      * the buffer length is zero (meaning that we should always return
     604             :      * one group.)
     605             :      */
     606           0 :     if (version != GMP_VERSION_SOURCES || msg_type != GMP_REPORT_PACKET ||
     607             :         !buffer_len) {
     608           0 :         return 1;
     609             :     }
     610             : 
     611             :     /*
     612             :      * It's a sources-version report packet.  Load up the overhead and
     613             :      * per-group cost based on the protocol.
     614             :      */
     615           0 :     switch (proto) {
     616           0 :       case GMP_PROTO_IGMP:
     617           0 :         overhead = sizeof(igmp_v3_report);
     618           0 :         group_len = sizeof(igmp_v3_rpt_rcrd);
     619           0 :         break;
     620             : 
     621           0 :       case GMP_PROTO_MLD:
     622           0 :         overhead = sizeof(mld_v2_report);
     623           0 :         group_len = sizeof(mld_v2_rpt_rcrd);
     624           0 :         break;
     625             : 
     626           0 :       default:
     627           0 :         overhead = 0;                   /* Satisfy the compiler. */
     628           0 :         group_len = 0;
     629           0 :         gmpx_assert(FALSE);
     630             :         break;
     631             :     }
     632             : 
     633             :     /* Got the parameters.  Do the simple calculation. */
     634             : 
     635           0 :     gmpx_assert(buffer_len >= group_len + overhead);
     636           0 :     max_group_count = (buffer_len - overhead) / group_len;
     637             : 
     638           0 :     return max_group_count;
     639             : }

Generated by: LCOV version 1.14