LCOV - code coverage report
Current view: top level - vnsw/agent/services/multicast/grpmgmt - igmp_protocol.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 227 558 40.7 %
Date: 2026-08-03 02:19:58 Functions: 13 19 68.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* $Id: igmp_proto.c 346474 2009-11-14 10:18:58Z ssiano $
       2             :  *
       3             :  * igmp_proto.c - IGMP protocol encode/decode routines
       4             :  *
       5             :  * Dave Katz, March 2008
       6             :  *
       7             :  * Copyright (c) 2008, Juniper Networks, Inc.
       8             :  * All rights reserved.
       9             :  */
      10             : #include "gmpx_basic_types.h"
      11             : #include "gmp.h"
      12             : #include "gmpx_environment.h"
      13             : #include "gmp_externs.h"
      14             : #include "gmp_private.h"
      15             : #include "gmpp_private.h"
      16             : #include "gmp_trace.h"
      17             : #include "igmp_protocol.h"
      18             : 
      19             : /*
      20             :  * Destination addresses
      21             :  */
      22             : static uint8_t igmp_all_hosts[IPV4_ADDR_LEN] = {224, 0, 0, 1};
      23             : static uint8_t igmp_all_routers[IPV4_ADDR_LEN] = {224, 0, 0, 2};
      24             : static uint8_t igmp_all_v3_routers[IPV4_ADDR_LEN] = {224, 0, 0, 22};
      25             : 
      26             : 
      27             : 
      28             : /*
      29             :  * igmp_packet_type_string
      30             :  *
      31             :  * Returns the IGMP packet type string given the packet type.
      32             :  */
      33             : static const char *
      34           0 : igmp_packet_type_string (igmp_hdr *hdr, uint32_t len)
      35             : {
      36           0 :     switch (hdr->igmp_hdr_type) {
      37           0 :       case IGMP_TYPE_QUERY:
      38           0 :         if (len > sizeof(igmp_v1v2_pkt))
      39           0 :             return "V3 Query";
      40           0 :         if (hdr->igmp_hdr_maxresp)
      41           0 :             return "V2 Query";
      42           0 :         return "V1 Query";
      43             : 
      44           0 :       case IGMP_TYPE_V1_REPORT:
      45           0 :         return "V1 Report";
      46             : 
      47           0 :       case IGMP_TYPE_V2_REPORT:
      48           0 :         return "V2 Report";
      49             : 
      50           0 :       case IGMP_TYPE_V2_LEAVE:
      51           0 :         return "V2 Leave";
      52             : 
      53           0 :       case IGMP_TYPE_V3_REPORT:
      54           0 :         return "V3 Report";
      55             : 
      56           0 :       default:
      57           0 :         return "Invalid";
      58             :     }
      59             : }
      60             : 
      61             : 
      62             : /*
      63             :  * igmp_decode_fixfloat
      64             :  *
      65             :  * Decodes a fix/float field (Max Resp or QQIC).  Returns the value in
      66             :  * native units.
      67             :  */
      68             : static uint32_t
      69           0 : igmp_decode_fixfloat (uint8_t value)
      70             : {
      71             :     uint32_t mant, exp, result;
      72             : 
      73             :     /* If the flag is set, decode the value as floating point. */
      74             : 
      75           0 :     if (value & IGMP_FIXFLOAT_FLAG) {
      76           0 :         mant = (value & IGMP_FLOAT_MANT_MASK) >> IGMP_FLOAT_MANT_SHIFT;
      77           0 :         exp = (value & IGMP_FLOAT_EXP_MASK) >> IGMP_FLOAT_EXP_SHIFT;
      78           0 :         result = (mant | IGMP_FLOAT_MANT_HIGHBIT) <<
      79           0 :             (exp + IGMP_FLOAT_EXP_OFFSET);
      80             :     } else {
      81           0 :         result = value;
      82             :     }
      83             : 
      84           0 :     return result;
      85             : }
      86             : 
      87             : 
      88             : /*
      89             :  * igmp_generic_version
      90             :  *
      91             :  * Determine the generic version number of an IGMP packet
      92             :  *
      93             :  * Returns the version number, or GMP_VERSION_INVALID if an error occurs.
      94             :  *
      95             :  * Also returns the generic message type.
      96             :  */
      97             : static gmp_version
      98         136 : igmp_generic_version (igmp_packet *pkt, uint32_t pkt_len,
      99             :                       gmp_message_type *msg_type)
     100             : {
     101             :     gmp_version version;
     102             : 
     103             :     /* First, branch by packet type. */
     104             : 
     105         136 :     *msg_type = GMP_REPORT_PACKET;      /* Reasonable default. */
     106         136 :     switch (pkt->igmp_pkt_naked.igmp_naked_header_hdr.igmp_hdr_type) {
     107             : 
     108             :         /* Do the easy ones first. */
     109             : 
     110           1 :       case IGMP_TYPE_V1_REPORT:
     111           1 :         version = GMP_VERSION_BASIC;
     112           1 :         break;
     113             : 
     114          79 :       case IGMP_TYPE_V2_REPORT:
     115             :       case IGMP_TYPE_V2_LEAVE:
     116          79 :         version = GMP_VERSION_LEAVES;
     117          79 :         break;
     118             : 
     119          56 :       case IGMP_TYPE_V3_REPORT:
     120          56 :         version = GMP_VERSION_SOURCES;
     121          56 :         break;
     122             : 
     123           0 :       case IGMP_TYPE_QUERY:
     124             : 
     125             :         /*
     126             :          * Now the queries.  Queries are distinguished by length and
     127             :          * the Max Resp field.
     128             :          */
     129           0 :         *msg_type = GMP_QUERY_PACKET;
     130           0 :         if (pkt_len > sizeof(igmp_v1v2_pkt)) /* Long packet */
     131           0 :             version = GMP_VERSION_SOURCES;
     132           0 :         else if (pkt->igmp_pkt_v1v2.igmp_v1v2_pkt_hdr.igmp_hdr_maxresp != 0)
     133           0 :             version = GMP_VERSION_LEAVES;
     134             :         else
     135           0 :             version = GMP_VERSION_BASIC;
     136           0 :         break;
     137             : 
     138           0 :       default:
     139           0 :         version = GMP_VERSION_INVALID;
     140           0 :         break;
     141             :     }
     142             : 
     143         136 :     return version;
     144             : }
     145             : 
     146             : 
     147             : /*
     148             :  * igmp_max_resp_value
     149             :  *
     150             :  * Returns the value of the max resp field, in standard units (100 msec.)
     151             :  * If the value is zero, it returns the default value (for IGMP v1.)
     152             :  */
     153           0 : static uint32_t igmp_max_resp_value (igmp_packet *pkt, gmp_version version)
     154             : {
     155             :     uint32_t max_resp_field, value;
     156             : 
     157           0 :     max_resp_field =
     158           0 :         pkt->igmp_pkt_naked.igmp_naked_header_hdr.igmp_hdr_maxresp;
     159             : 
     160             :     /* Switch by IGMP version. */
     161             : 
     162           0 :     switch (version) {
     163           0 :       case GMP_VERSION_BASIC:
     164           0 :         value = IGMP_MAX_RESP_DEFAULT;
     165           0 :         break;
     166             : 
     167           0 :       case GMP_VERSION_LEAVES:
     168           0 :         value = max_resp_field;
     169           0 :         break;
     170             : 
     171           0 :       case GMP_VERSION_SOURCES:
     172           0 :         value = igmp_decode_fixfloat(max_resp_field);
     173           0 :         break;
     174             : 
     175           0 :       default:
     176           0 :         value = 0;                      /* Invalid */
     177             :     }
     178             : 
     179           0 :     return value;
     180             : }
     181             : 
     182             : 
     183             : /*
     184             :  * gmp_igmp_trace_bad_pkt
     185             :  *
     186             :  * Trace a bad IGMP packet
     187             :  */
     188             : void
     189           0 : gmp_igmp_trace_bad_pkt(uint32_t len, const uint8_t *addr, gmpx_intf_id intf_id,
     190             :                        void *trace_context, uint32_t trace_flags)
     191             : {
     192             :     /* Bail if not tracing bad packets. */
     193             : 
     194           0 :     if (!gmp_trace_set(trace_flags, GMP_TRACE_PACKET_BAD))
     195           0 :         return;
     196             : 
     197           0 :     gmpx_trace(trace_context, "RCV bad IGMP packet, len %u, from %a intf %i",
     198             :                len, addr, intf_id);
     199             : }
     200             : 
     201             : 
     202             : /*
     203             :  * gmp_igmp_trace_pkt
     204             :  * 
     205             :  * Trace an IGMP packet
     206             :  */
     207             : void
     208        1055 : gmp_igmp_trace_pkt (void *packet, uint32_t len, const uint8_t *addr,
     209             :                     gmpx_intf_id intf_id, boolean receive,
     210             :                     void *trace_context, uint32_t trace_flags)
     211             : {
     212             :     const char *direction;
     213             :     const char *op;
     214             :     igmp_hdr *hdr;
     215             :     igmp_v1v2_pkt *v1v2_pkt;
     216             :     igmp_v3_query *v3_query;
     217             :     igmp_v3_report *v3_report;
     218             :     igmp_v3_rpt_rcrd *rpt_rcrd;
     219             :     gmp_version version;
     220             :     gmp_message_type msg_type;
     221             :     uint8_t *byte_ptr;
     222             :     uint32_t source_count;
     223             :     uint32_t record_count;
     224             :     boolean detail;
     225             :     igmp_packet *pkt;
     226             : 
     227        1055 :     pkt = packet;
     228             : 
     229             :     /* See if this packet should be traced at all. */
     230             : 
     231        1055 :     if (!gmp_trace_set(trace_flags, GMP_TRACE_PACKET))
     232        1055 :         return;
     233             : 
     234           0 :     hdr = &pkt->igmp_pkt_naked.igmp_naked_header_hdr;
     235           0 :     detail = FALSE;
     236           0 :     switch (hdr->igmp_hdr_type) {
     237           0 :       case IGMP_TYPE_QUERY:
     238           0 :         if (!gmp_trace_set(trace_flags, GMP_TRACE_QUERY))
     239           0 :             return;
     240           0 :         if (gmp_trace_set(trace_flags, GMP_TRACE_QUERY_DETAIL))
     241           0 :             detail = TRUE;
     242           0 :         break;
     243             : 
     244           0 :       case IGMP_TYPE_V2_LEAVE:
     245           0 :         if (!gmp_trace_set(trace_flags, GMP_TRACE_LEAVE))
     246           0 :             return;
     247           0 :         if (gmp_trace_set(trace_flags, GMP_TRACE_LEAVE_DETAIL))
     248           0 :             detail = TRUE;
     249           0 :         break;
     250             : 
     251           0 :       case IGMP_TYPE_V1_REPORT:
     252             :       case IGMP_TYPE_V2_REPORT:
     253             :       case IGMP_TYPE_V3_REPORT:
     254           0 :         if (!gmp_trace_set(trace_flags, GMP_TRACE_REPORT))
     255           0 :             return;
     256           0 :         if (gmp_trace_set(trace_flags, GMP_TRACE_REPORT_DETAIL))
     257           0 :             detail = TRUE;
     258           0 :         break;
     259             : 
     260           0 :       default:
     261           0 :         if (!gmp_trace_set(trace_flags, GMP_TRACE_PACKET_BAD))
     262           0 :             return;
     263           0 :         break;
     264             :     }
     265             : 
     266             :     /*
     267             :      * If we've gotten this far, we're doing basic tracing of the packet.
     268             :      * Do so.
     269             :      */
     270           0 :     v1v2_pkt = &pkt->igmp_pkt_v1v2;
     271           0 :     v3_query = &pkt->igmp_pkt_v3_query;
     272           0 :     v3_report = &pkt->igmp_pkt_v3_rpt;
     273           0 :     version = igmp_generic_version(pkt, len, &msg_type);
     274             : 
     275           0 :     if (receive) {
     276           0 :         direction = "from";
     277           0 :         op = "RCV";
     278             :     } else {
     279           0 :         direction = "to";
     280           0 :         op = "XMT";
     281             :     }
     282           0 :     gmpx_trace(trace_context, "%s IGMP %s len %u %s %a intf %i", op,
     283             :                igmp_packet_type_string(hdr, len), len, direction, addr,
     284             :                intf_id);
     285             : 
     286             :     /* Bail if no detailed tracing going on. */
     287             : 
     288           0 :     if (!detail)
     289           0 :         return;
     290             : 
     291             :     /* Do any detailed tracing based on packet type. */
     292             : 
     293           0 :     switch (hdr->igmp_hdr_type) {
     294           0 :       case IGMP_TYPE_QUERY:
     295           0 :         gmpx_trace(trace_context, "  Max_resp 0x%x (%u), group %a",
     296           0 :                    hdr->igmp_hdr_maxresp, igmp_max_resp_value(pkt, version),
     297           0 :                    v1v2_pkt->igmp_v1v2_pkt_group);
     298           0 :         if (version == GMP_VERSION_SOURCES) {
     299           0 :             source_count = get_short(&v3_query->igmp_v3_query_num_srcs);
     300           0 :             gmpx_trace(trace_context,
     301             :                        "  S %u, QRV %u, QQIC 0x%x (%u), sources %u",
     302           0 :                        ((v3_query->igmp_v3_query_s_qrv &
     303             :                          IGMP_SUPP_RTR_PROC_MASK) != 0),
     304           0 :                        v3_query->igmp_v3_query_s_qrv & IGMP_QRV_MASK,
     305           0 :                        v3_query->igmp_v3_query_qqic,
     306           0 :                        igmp_decode_fixfloat(v3_query->igmp_v3_query_qqic),
     307             :                        source_count);
     308           0 :             byte_ptr = v3_query->igmp_v3_query_source;
     309           0 :             while (source_count--) {
     310           0 :                 gmpx_trace(trace_context, "    Source %a", byte_ptr);
     311           0 :                 byte_ptr += IPV4_ADDR_LEN;
     312             :             }
     313             :         }
     314           0 :         break;
     315             : 
     316           0 :       case IGMP_TYPE_V1_REPORT:
     317             :       case IGMP_TYPE_V2_REPORT:
     318             :       case IGMP_TYPE_V2_LEAVE:
     319           0 :         gmpx_trace(trace_context, "  Group %a", v1v2_pkt->igmp_v1v2_pkt_group);
     320           0 :         break;
     321             : 
     322           0 :       case IGMP_TYPE_V3_REPORT:
     323           0 :         record_count = get_short(&v3_report->igmp_v3_report_num_rcrds);
     324           0 :         gmpx_trace(trace_context, "  Records %u", record_count);
     325             : 
     326           0 :         rpt_rcrd = v3_report->igmp_v3_report_rcrd;
     327           0 :         while (record_count--) {
     328           0 :             source_count = get_short(&rpt_rcrd->igmp_v3_rpt_num_srcs);
     329           0 :             gmpx_trace(trace_context,
     330             :                        "    Group %a, type %s, aux_len %u, sources %u",
     331           0 :                        rpt_rcrd->igmp_v3_rpt_group,
     332           0 :                        gmp_report_type_string(rpt_rcrd->igmp_v3_rpt_rec_type),
     333           0 :                        rpt_rcrd->igmp_v3_rpt_aux_len, source_count);
     334           0 :             byte_ptr = get_igmp_v3_rpt_source(rpt_rcrd);
     335           0 :             while (source_count--) {
     336           0 :                 gmpx_trace(trace_context, "      Source %a", byte_ptr);
     337           0 :                 byte_ptr += IPV4_ADDR_LEN;
     338             :             }
     339           0 :             byte_ptr += rpt_rcrd->igmp_v3_rpt_aux_len;
     340           0 :             rpt_rcrd = (igmp_v3_rpt_rcrd *) byte_ptr;
     341             :         }
     342           0 :         break;
     343             : 
     344             :     }
     345             : }
     346             : 
     347             : 
     348             : /*
     349             :  * igmp_encode_fixfloat
     350             :  *
     351             :  * Encodes a fix/float field (Max Resp or QQIC), given the value in native
     352             :  * units.  Returns the encoded value, or zero if the value is too large
     353             :  * to encode.
     354             :  *
     355             :  * The encoded value will be less than or equal to the provided value (since
     356             :  * we lose granularity in the encoding.)
     357             :  */
     358             : static uint8_t
     359        1700 : igmp_encode_fixfloat (uint32_t value)
     360             : {
     361             :     uint32_t mant, exp;
     362             : 
     363             :     /* If the value is small enough, just use it. */
     364             : 
     365        1700 :     if (value < IGMP_FIXFLOAT_FLAG)
     366        1700 :         return (uint8_t) value;
     367             : 
     368             :     /* If the value is too big, bail. */
     369             : 
     370           0 :     if (value > IGMP_MAX_FLOAT_ENCODABLE)
     371           0 :         return 0;
     372             : 
     373             :     /* Big enough to encode.  Do the job. */
     374             : 
     375           0 :     mant = value >> IGMP_FLOAT_EXP_OFFSET;
     376           0 :     for (exp = 0; exp <= IGMP_FLOAT_MAX_EXP; exp++) {
     377             : 
     378             :         /* Bail if the mantissa is small enough now. */
     379             : 
     380           0 :         if (mant <= IGMP_FLOAT_MAX_MANT)
     381           0 :             break;
     382             : 
     383             :         /* Shift the mantissa down a bit and try again. */
     384             : 
     385           0 :         mant >>= 1;
     386             :     }
     387             : 
     388             :     /* Got it.  Form the value and return it. */
     389             : 
     390           0 :     return (IGMP_FIXFLOAT_FLAG | (exp << IGMP_FLOAT_EXP_SHIFT) |
     391           0 :             (mant & IGMP_FLOAT_MANT_MASK));
     392             : }
     393             : 
     394             : 
     395             : /*
     396             :  * igmp_format_v1_packet
     397             :  *
     398             :  * Format an IGMP V1 packet.
     399             :  *
     400             :  * Returns the formatted packet length, or 0 if nothing to send.
     401             :  */
     402             : static uint32_t
     403           6 : igmp_format_v1_packet (gmp_role role, gmp_packet *gen_packet,
     404             :                        igmp_packet *packet, uint32_t packet_len)
     405             : {
     406             :     igmp_v1v2_pkt *v1v2_pkt;
     407             :     igmp_hdr *pkt_hdr;
     408             :     gmp_query_packet *query_pkt;
     409             :     task_thread *thread_ptr;
     410             :     gmp_report_group_record *group_record;
     411             :     gmp_report_packet *report_pkt;
     412             :     uint32_t formatted_len;
     413             : 
     414             :     /* Version 1 packet.  Pretty straightforward. */
     415             : 
     416           6 :     gmpx_assert(packet_len >= sizeof(igmp_v1v2_pkt));
     417           6 :     v1v2_pkt = &packet->igmp_pkt_v1v2;
     418           6 :     pkt_hdr = &v1v2_pkt->igmp_v1v2_pkt_hdr;
     419           6 :     memset(pkt_hdr, 0, sizeof(igmp_hdr));
     420           6 :     formatted_len = sizeof(igmp_v1v2_pkt);
     421             : 
     422           6 :     switch (gen_packet->gmp_packet_type) {
     423           6 :       case GMP_QUERY_PACKET:
     424             : 
     425             :         /* Format a query packet. */
     426             : 
     427           6 :         pkt_hdr->igmp_hdr_type = IGMP_TYPE_QUERY;
     428           6 :         query_pkt = &gen_packet->gmp_packet_contents.gmp_packet_query;
     429           6 :         memset(v1v2_pkt->igmp_v1v2_pkt_group, 0, IPV4_ADDR_LEN);
     430           6 :         memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, igmp_all_hosts, IPV4_ADDR_LEN);
     431             :         /*
     432             :          * Flush the source list.  We may end up here with a GSS query due
     433             :          * to a race condition while changing versions.
     434             :          */
     435           6 :         gmp_flush_xmit_list(query_pkt->gmp_query_xmit_srcs);
     436           6 :         break;
     437             : 
     438           0 :       case GMP_REPORT_PACKET:
     439             :             
     440             :         /*
     441             :          * Format a report packet.  Note that there may be multiple
     442             :          * group records on this (since V3 supports that).  We just
     443             :          * pull off the first one and format it.  We'll be called back
     444             :          * later to send more packets for other groups.
     445             :          */
     446           0 :         pkt_hdr->igmp_hdr_type = IGMP_TYPE_V1_REPORT;
     447           0 :         report_pkt = &gen_packet->gmp_packet_contents.gmp_packet_report;
     448           0 :         gmpx_assert(report_pkt->gmp_report_group_count >= 1);
     449           0 :         thread_ptr = thread_circular_top(&report_pkt->gmp_report_group_head);
     450           0 :         group_record = gmp_thread_to_report_group_record(thread_ptr);
     451           0 :         gmpx_assert(group_record);
     452             : 
     453             :         /*
     454             :          * Don't send anything if this is a leave-equivalent (IS_IN or TO_IN
     455             :          * with a null list, or a BLOCK.
     456             :          */
     457           0 :         if (group_record->gmp_rpt_type == GMP_RPT_BLOCK ||
     458           0 :             ((group_record->gmp_rpt_type == GMP_RPT_IS_IN ||
     459           0 :               group_record->gmp_rpt_type == GMP_RPT_TO_IN) &&
     460           0 :              gmp_addr_list_empty(group_record->gmp_rpt_xmit_srcs))) {
     461           0 :             formatted_len = 0;
     462             :         }
     463           0 :         memmove(v1v2_pkt->igmp_v1v2_pkt_group, group_record->gmp_rpt_group.gmp_addr, IPV4_ADDR_LEN);
     464           0 :         memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, group_record->gmp_rpt_group.gmp_addr, IPV4_ADDR_LEN);
     465             : 
     466             :         /* Flush the source list and flag that we're done with this group. */
     467             : 
     468           0 :         gmp_flush_xmit_list(group_record->gmp_rpt_xmit_srcs);
     469           0 :         gmpp_group_done(role, GMP_PROTO_IGMP, group_record->gmp_rpt_group_id);
     470           0 :         break;
     471             : 
     472           0 :       default:
     473           0 :         gmpx_assert(FALSE);
     474             :     }
     475             : 
     476           6 :     return formatted_len;
     477             : }
     478             : 
     479             : 
     480             : /*
     481             :  * igmp_format_v2_packet
     482             :  *
     483             :  * Format an IGMP V2 packet.
     484             :  *
     485             :  * Returns the formatted packet length, or 0 if nothing to send.
     486             :  */
     487             : static uint32_t
     488          63 : igmp_format_v2_packet (gmp_role role, gmp_packet *gen_packet,
     489             :                        igmp_packet *packet, uint32_t packet_len)
     490             : {
     491             :     igmp_v1v2_pkt *v1v2_pkt;
     492             :     igmp_hdr *pkt_hdr;
     493             :     gmp_query_packet *query_pkt;
     494             :     gmp_report_packet *report_pkt;
     495             :     task_thread *thread_ptr;
     496             :     gmp_report_group_record *group_record;
     497             :     uint32_t max_resp;
     498             :     uint32_t formatted_len;
     499             : 
     500             :     /* Initialize and idiot-proof. */
     501             : 
     502          63 :     gmpx_assert(packet_len >= sizeof(igmp_v1v2_pkt));
     503          63 :     v1v2_pkt = &packet->igmp_pkt_v1v2;
     504          63 :     pkt_hdr = &v1v2_pkt->igmp_v1v2_pkt_hdr;
     505          63 :     memset(pkt_hdr, 0, sizeof(igmp_hdr));
     506          63 :     formatted_len = sizeof(igmp_v1v2_pkt);
     507             : 
     508          63 :     switch (gen_packet->gmp_packet_type) {
     509             : 
     510          63 :       case GMP_QUERY_PACKET:
     511             : 
     512             :         /* Format a query packet. */
     513             : 
     514          63 :         pkt_hdr->igmp_hdr_type = IGMP_TYPE_QUERY;
     515          63 :         query_pkt = &gen_packet->gmp_packet_contents.gmp_packet_query;
     516             : 
     517             :         /*
     518             :          * If this is a group query, copy in the group address.  Otherwise,
     519             :          * zero it.  Set the destination address accordingly.
     520             :          */
     521          63 :         if (query_pkt->gmp_query_group_query) {
     522          27 :             memmove(v1v2_pkt->igmp_v1v2_pkt_group, query_pkt->gmp_query_group.gmp_addr,
     523             :                 IPV4_ADDR_LEN);
     524          27 :             memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, query_pkt->gmp_query_group.gmp_addr,
     525             :                 IPV4_ADDR_LEN);
     526             :         } else {
     527          36 :             memset(v1v2_pkt->igmp_v1v2_pkt_group, 0, IPV4_ADDR_LEN);
     528          36 :             memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, igmp_all_hosts, IPV4_ADDR_LEN);
     529             :         }
     530             : 
     531             :         /* Store the max resp value. */
     532             : 
     533          63 :         max_resp = query_pkt->gmp_query_max_resp / IGMP_MAX_RESP_MSEC;
     534          63 :         if (max_resp > IGMP_V2_MAX_MAX_RESP)
     535           0 :             max_resp = IGMP_V2_MAX_MAX_RESP;
     536          63 :         pkt_hdr->igmp_hdr_maxresp = max_resp;
     537             : 
     538             :         /*
     539             :          * Flush the source list.  We may end up here with a GSS query due
     540             :          * to a race condition while changing versions.
     541             :          */
     542          63 :         gmp_flush_xmit_list(query_pkt->gmp_query_xmit_srcs);
     543             :   
     544          63 :         if (query_pkt->gmp_query_group_query) {
     545          27 :             gmpp_group_done(role, GMP_PROTO_IGMP,
     546             :                             query_pkt->gmp_query_group_id);
     547             :         }
     548             : 
     549          63 :         break;
     550             : 
     551           0 :       case GMP_REPORT_PACKET:
     552             :             
     553             :         /*
     554             :          * Format a report packet.  Note that there may be multiple
     555             :          * group records on this (since V3 supports that).  We just
     556             :          * pull off the first one and format it.  We'll be called back
     557             :          * later to send more packets for other groups.
     558             :          */
     559           0 :         report_pkt = &gen_packet->gmp_packet_contents.gmp_packet_report;
     560           0 :         gmpx_assert(report_pkt->gmp_report_group_count >= 1);
     561           0 :         thread_ptr = thread_circular_top(&report_pkt->gmp_report_group_head);
     562           0 :         group_record = gmp_thread_to_report_group_record(thread_ptr);
     563           0 :         gmpx_assert(group_record);
     564             : 
     565             :         /*
     566             :          * If the record type is TO_IN or IS_IN with an empty address
     567             :          * list, this is a Leave.  Otherwise, it is a report.  Set the
     568             :          * destination address accordingly.
     569             :          */
     570           0 :         if ((group_record->gmp_rpt_type == GMP_RPT_TO_IN ||
     571           0 :              group_record->gmp_rpt_type == GMP_RPT_IS_IN) &&
     572           0 :             gmp_addr_list_empty(group_record->gmp_rpt_xmit_srcs)) {
     573           0 :             pkt_hdr->igmp_hdr_type = IGMP_TYPE_V2_LEAVE;
     574           0 :             memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, igmp_all_routers, IPV4_ADDR_LEN);
     575             :         } else {
     576           0 :             pkt_hdr->igmp_hdr_type = IGMP_TYPE_V2_REPORT;
     577           0 :             memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, group_record->gmp_rpt_group.gmp_addr,
     578             :                 IPV4_ADDR_LEN);
     579             :         }
     580             : 
     581           0 :         memmove(v1v2_pkt->igmp_v1v2_pkt_group, group_record->gmp_rpt_group.gmp_addr, IPV4_ADDR_LEN);
     582             : 
     583             :         /* If this is a BLOCK record, don't send anything. */
     584             : 
     585           0 :         if (group_record->gmp_rpt_type == GMP_RPT_BLOCK)
     586           0 :             formatted_len = 0;
     587             : 
     588             :         /* Flush the source list and flag that we're done with this group. */
     589             : 
     590           0 :         gmp_flush_xmit_list(group_record->gmp_rpt_xmit_srcs);
     591           0 :         gmpp_group_done(role, GMP_PROTO_IGMP, group_record->gmp_rpt_group_id);
     592           0 :         break;
     593             : 
     594           0 :       default:
     595           0 :         gmpx_assert(FALSE);
     596             :     }
     597             : 
     598          63 :     return formatted_len;
     599             : }
     600             : 
     601             : 
     602             : /*
     603             :  * igmp_format_v3_query
     604             :  *
     605             :  * Format an IGMPv3 Query packet.
     606             :  *
     607             :  * Returns the formatted packet length, or 0 if nothing to send.
     608             :  */
     609             : static uint32_t
     610         850 : igmp_format_v3_query (gmp_role role, gmp_packet *gen_packet,
     611             :                       igmp_packet *packet, uint32_t packet_len)
     612             : {
     613             :     igmp_v3_query *v3_query_pkt;
     614             :     igmp_hdr *pkt_hdr;
     615             :     gmp_query_packet *query_pkt;
     616             :     uint32_t formatted_len;
     617             :     uint32_t source_count;
     618             :     gmp_addr_list_entry *addr_entry;
     619             :     gmp_addr_list *addr_list;
     620             :     uint8_t *addr_ptr;
     621             :     gmp_addr_cat_entry *cat_entry;
     622             :     // boolean gss_query;
     623             :     boolean group_done;
     624             : 
     625             :     /* Format a query packet. */
     626             : 
     627         850 :     gmpx_assert(packet_len >= sizeof(igmp_v3_query) + IPV4_ADDR_LEN);
     628         850 :     query_pkt = &gen_packet->gmp_packet_contents.gmp_packet_query;
     629         850 :     v3_query_pkt = &packet->igmp_pkt_v3_query;
     630         850 :     memset(v3_query_pkt, 0, sizeof(igmp_v3_query));
     631         850 :     pkt_hdr = &v3_query_pkt->igmp_v3_query_hdr;
     632             : 
     633             :     /* Set up the header. */
     634             : 
     635         850 :     pkt_hdr->igmp_hdr_type = IGMP_TYPE_QUERY;
     636         850 :     pkt_hdr->igmp_hdr_maxresp =
     637         850 :         igmp_encode_fixfloat(query_pkt->gmp_query_max_resp /
     638             :                              IGMP_MAX_RESP_MSEC);
     639             : 
     640             :     /* Note whether this is a GSS query. */
     641             : 
     642             :     // gss_query = query_pkt->gmp_query_group_query &&
     643             :         // query_pkt->gmp_query_xmit_srcs;
     644             : 
     645             :     /*
     646             :      * If this is a group query, copy in the group address.  Otherwise,
     647             :      * zero it.  Set the destination address accordingly.
     648             :      */
     649         850 :     if (query_pkt->gmp_query_group_query) {
     650          17 :         memmove(v3_query_pkt->igmp_v3_query_group, query_pkt->gmp_query_group.gmp_addr,
     651             :             IPV4_ADDR_LEN);
     652          17 :         memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, query_pkt->gmp_query_group.gmp_addr,
     653             :             IPV4_ADDR_LEN);
     654             :     } else {
     655         833 :         memset(v3_query_pkt->igmp_v3_query_group, 0, IPV4_ADDR_LEN);
     656         833 :         memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, igmp_all_hosts, IPV4_ADDR_LEN);
     657             :     }
     658             : 
     659             :     /* Set the S and QRV fields. */
     660             : 
     661         850 :     v3_query_pkt->igmp_v3_query_s_qrv = query_pkt->gmp_query_qrv;
     662         850 :     if (query_pkt->gmp_query_suppress)
     663           0 :         v3_query_pkt->igmp_v3_query_s_qrv |= IGMP_SUPP_RTR_PROC_MASK;
     664             : 
     665             :     /* Set the QQIC field. */
     666             : 
     667         850 :     v3_query_pkt->igmp_v3_query_qqic =
     668         850 :         igmp_encode_fixfloat(query_pkt->gmp_query_qqi / MSECS_PER_SEC);
     669             : 
     670         850 :     formatted_len = sizeof(igmp_v3_query);
     671         850 :     packet_len -= sizeof(igmp_v3_query);
     672         850 :     source_count = 0;
     673         850 :     addr_ptr = v3_query_pkt->igmp_v3_query_source;
     674             : 
     675             :     /* Add as may sources as possible if they are present. */
     676             : 
     677         850 :     addr_list = query_pkt->gmp_query_xmit_srcs;
     678         850 :     if (addr_list) {
     679             : 
     680             :         /*
     681             :          * Top of source loop.  Add sources and delink them from the
     682             :          * transmit chain until either there are no more sources or
     683             :          * we've run out of space in the packet.
     684             :          */
     685          17 :         group_done = TRUE;
     686             :         while (TRUE) {
     687             : 
     688             :             /* Bail if out of space in the packet. */
     689             : 
     690          53 :             if (packet_len < IPV4_ADDR_LEN) {
     691           0 :                 group_done = FALSE;
     692           0 :                 break;
     693             :             }
     694             : 
     695             :             /* Got space.  Grab the next address. */
     696             : 
     697          53 :             addr_entry = gmp_first_xmit_addr_entry(addr_list);
     698             : 
     699             :             /* Bail if there's nothing there. */
     700             : 
     701          53 :             if (!addr_entry)
     702          17 :                 break;
     703             : 
     704             :             /*
     705             :              * We've got an address entry.  Look up the catalog entry
     706             :              * and copy the address into the packet.
     707             :              */
     708             :             cat_entry =
     709          36 :                 gmp_get_addr_cat_by_ordinal(addr_list->addr_vect.av_catalog,
     710             :                                             addr_entry->addr_ent_ord);
     711          36 :             gmpx_assert(cat_entry);
     712          36 :             memmove(addr_ptr, cat_entry->adcat_ent_addr.gmp_addr, IPV4_ADDR_LEN);
     713             : 
     714             :             /* Delink the address entry. */
     715             : 
     716          36 :             gmp_dequeue_xmit_addr_entry(addr_entry);
     717             : 
     718             :             /* Do housekeeping. */
     719             : 
     720          36 :             addr_ptr += IPV4_ADDR_LEN;
     721          36 :             formatted_len += IPV4_ADDR_LEN;
     722          36 :             packet_len -= IPV4_ADDR_LEN;
     723          36 :             source_count++;
     724             :         }
     725             : 
     726             :         /* If we're done with the group, call back. */
     727             : 
     728          17 :         if (group_done) {
     729          17 :             gmpp_group_done(role, GMP_PROTO_IGMP,
     730             :                             query_pkt->gmp_query_group_id);
     731             :         }
     732             : 
     733             :     }
     734             : 
     735             :     /* Update the source count. */
     736             : 
     737         850 :     put_short(&v3_query_pkt->igmp_v3_query_num_srcs, source_count);
     738             : 
     739         850 :     return formatted_len;
     740             : }
     741             : 
     742             : /*
     743             :  * igmp_format_v3_report
     744             :  *
     745             :  * Format an IGMPv3 Report packet.
     746             :  *
     747             :  * Returns the formatted packet length, or 0 if nothing to send.
     748             :  */
     749             : static uint32_t
     750           0 : igmp_format_v3_report (gmp_role role, gmp_packet *gen_packet,
     751             :                        igmp_packet *packet, uint32_t packet_len)
     752             : {
     753             :     igmp_v3_report *v3_rpt_pkt;
     754             :     igmp_v3_rpt_rcrd *v3_group_rcrd;
     755             :     igmp_hdr *pkt_hdr;
     756             :     gmp_report_packet *report_pkt;
     757             :     gmp_report_group_record *group_record;
     758             :     uint32_t formatted_len;
     759             :     uint32_t source_count;
     760             :     uint32_t group_count;
     761             :     uint32_t groups_remaining;
     762             :     uint32_t group_space;
     763             :     gmp_addr_list_entry *addr_entry;
     764             :     gmp_addr_list *addr_list;
     765             :     uint8_t *byte_ptr;
     766             :     gmp_addr_cat_entry *cat_entry;
     767             :     task_thread *thread_ptr;
     768             :     boolean group_done;
     769             : 
     770             :     /* Format a report packet. */
     771             : 
     772           0 :     gmpx_assert(packet_len >= sizeof(igmp_v3_report) +
     773             :                 sizeof(igmp_v3_rpt_rcrd) + IPV4_ADDR_LEN);
     774           0 :     report_pkt = &gen_packet->gmp_packet_contents.gmp_packet_report;
     775           0 :     v3_rpt_pkt = &packet->igmp_pkt_v3_rpt;
     776           0 :     memset(v3_rpt_pkt, 0, sizeof(igmp_v3_report));
     777           0 :     pkt_hdr = &v3_rpt_pkt->igmp_v3_report_hdr;
     778             : 
     779             :     /* Set up the header. */
     780             : 
     781           0 :     pkt_hdr->igmp_hdr_type = IGMP_TYPE_V3_REPORT;
     782             : 
     783           0 :     packet_len -= sizeof(igmp_v3_report);
     784           0 :     formatted_len = sizeof(igmp_v3_report);
     785           0 :     group_count = 0;
     786           0 :     v3_group_rcrd = v3_rpt_pkt->igmp_v3_report_rcrd;
     787           0 :     groups_remaining = report_pkt->gmp_report_group_count;
     788           0 :     thread_ptr = NULL;
     789             : 
     790             :     /*
     791             :      * Top of the group loop.  Walk the groups until we run out of
     792             :      * groups, or run out of space in the packet.
     793             :      */
     794             :     while (TRUE) {
     795             : 
     796             :         /* Bail if there are no more groups. */
     797             : 
     798           0 :         if (!groups_remaining)
     799           0 :             break;
     800             : 
     801             :         /* Grab the next group. */
     802             : 
     803             :         thread_ptr =
     804           0 :             thread_circular_thread_next(&report_pkt->gmp_report_group_head,
     805             :                                         thread_ptr);
     806           0 :         group_record = gmp_thread_to_report_group_record(thread_ptr);
     807           0 :         gmpx_assert(group_record);
     808             : 
     809             :         /*
     810             :          * Got the group.  Calculate the size of the group record we'll
     811             :          * generate, and see if it will fit.
     812             :          */
     813           0 :         group_space = sizeof(igmp_v3_rpt_rcrd);
     814           0 :         if (group_record->gmp_rpt_xmit_srcs) {
     815           0 :             group_space += group_record->gmp_rpt_xmit_srcs->xmit_addr_count *
     816             :                 IPV4_ADDR_LEN;
     817             :         }
     818             : 
     819           0 :         if (group_space > packet_len) {
     820             : 
     821             :             /*
     822             :              * It's not going to fit.  If this is not the first group,
     823             :              * bail.  Otherwise, go ahead with it anyhow.  We'll end
     824             :              * up truncating the list of sources in this case.
     825             :              */
     826           0 :             if (group_count)
     827           0 :                 break;
     828             :         }
     829             : 
     830             :         /* Create the group record header. */
     831             : 
     832           0 :         memset(v3_group_rcrd, 0, sizeof(igmp_v3_rpt_rcrd));
     833           0 :         v3_group_rcrd->igmp_v3_rpt_rec_type = group_record->gmp_rpt_type;
     834           0 :         memmove(v3_group_rcrd->igmp_v3_rpt_group, group_record->gmp_rpt_group.gmp_addr,
     835             :             IPV4_ADDR_LEN);
     836           0 :         packet_len -= sizeof(igmp_v3_rpt_rcrd);
     837           0 :         formatted_len += sizeof(igmp_v3_rpt_rcrd);
     838           0 :         byte_ptr = get_igmp_v3_rpt_source(v3_group_rcrd);
     839           0 :         group_count++;
     840             : 
     841             :         /* Now walk the sources, if any, and add them to the record. */
     842             : 
     843           0 :         source_count = 0;
     844           0 :         addr_list = group_record->gmp_rpt_xmit_srcs;
     845           0 :         group_done = TRUE;
     846           0 :         if (addr_list) {
     847             :             while (TRUE) {
     848             : 
     849             :                 /* Bail if out of space in the packet. */
     850             : 
     851           0 :                 if (packet_len < IPV4_ADDR_LEN)
     852           0 :                     break;
     853             : 
     854             :                 /* Got space.  Grab the next address. */
     855             : 
     856           0 :                 addr_entry = gmp_first_xmit_addr_entry(addr_list);
     857             : 
     858             :                 /* Bail if there's nothing there. */
     859             : 
     860           0 :                 if (!addr_entry)
     861           0 :                     break;
     862             : 
     863             :                 /*
     864             :                  * We've got an address entry.  Look up the catalog
     865             :                  * entry and copy the address into the packet.
     866             :                  */
     867             :                 cat_entry =
     868           0 :                     gmp_get_addr_cat_by_ordinal(
     869             :                                         addr_list->addr_vect.av_catalog,
     870             :                                         addr_entry->addr_ent_ord);
     871           0 :                 gmpx_assert(cat_entry);
     872           0 :                 memmove(byte_ptr, cat_entry->adcat_ent_addr.gmp_addr, IPV4_ADDR_LEN);
     873             : 
     874             :                 /* Delink the address entry. */
     875             : 
     876           0 :                 gmp_dequeue_xmit_addr_entry(addr_entry);
     877             : 
     878             :                 /* Do housekeeping. */
     879             : 
     880           0 :                 byte_ptr += IPV4_ADDR_LEN;
     881           0 :                 formatted_len += IPV4_ADDR_LEN;
     882           0 :                 packet_len -= IPV4_ADDR_LEN;
     883           0 :                 source_count++;
     884             :             }
     885             : 
     886             :             /* Update the source count in the group record. */
     887             : 
     888           0 :             put_short(&v3_group_rcrd->igmp_v3_rpt_num_srcs, source_count);
     889             : 
     890             :             /*
     891             :              * We've either run out of addresses or run out of space.  If
     892             :              * we've run out of space, and the record is of type IS_EX or
     893             :              * TO_EX, we flush the address list, which effectively truncates
     894             :              * the list in the packet.  Otherwise, we leave it alone, and
     895             :              * we'll send more sources in the next packet.
     896             :              */
     897           0 :             if (!gmp_xmit_addr_list_empty(addr_list)) {
     898             : 
     899             :                 /* Not empty.  Flush if the record is IS_EX or TO_EX. */
     900             : 
     901           0 :                 if (group_record->gmp_rpt_type == GMP_RPT_IS_EX ||
     902           0 :                     group_record->gmp_rpt_type == GMP_RPT_TO_EX) {
     903           0 :                     gmp_flush_xmit_list(addr_list); /* Flush it. */
     904             :                 } else {
     905           0 :                     group_done = FALSE; /* More to go! */
     906             :                 }
     907             :             }
     908             :         }
     909             : 
     910             :         /* If we finished that group, notify the authorities. */
     911             : 
     912           0 :         if (group_done) {
     913           0 :             gmpp_group_done(role, GMP_PROTO_IGMP,
     914             :                             group_record->gmp_rpt_group_id);
     915             :         }
     916             : 
     917             :         /* Advance the group record pointer. */
     918             : 
     919           0 :         v3_group_rcrd = (igmp_v3_rpt_rcrd *) byte_ptr;
     920           0 :         groups_remaining--;
     921             :     }
     922             : 
     923             :     /* Set the destination address. */
     924             : 
     925           0 :     memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, igmp_all_v3_routers, IPV4_ADDR_LEN);
     926             : 
     927             :     /* Update the group count in the packet header. */
     928             : 
     929           0 :     put_short(&v3_rpt_pkt->igmp_v3_report_num_rcrds, group_count);
     930             : 
     931           0 :     return formatted_len;
     932             : }
     933             : 
     934             : 
     935             : /*
     936             :  * igmp_format_v3_packet
     937             :  *
     938             :  * Format an IGMP V3 packet.
     939             :  *
     940             :  * Returns the formatted packet length, or 0 if nothing to send.
     941             :  */
     942             : static uint32_t
     943         850 : igmp_format_v3_packet (gmp_role role, gmp_packet *gen_packet,
     944             :                        igmp_packet *packet, uint32_t packet_len)
     945             : {
     946             :     uint32_t formatted_len;
     947             : 
     948             :     /* See what kind of packet we're sending. */
     949             : 
     950         850 :     switch (gen_packet->gmp_packet_type) {
     951             : 
     952         850 :       case GMP_QUERY_PACKET:
     953         850 :         formatted_len = igmp_format_v3_query(role, gen_packet, packet,
     954             :                                              packet_len);
     955             : 
     956         850 :         break;
     957             : 
     958             : 
     959           0 :       case GMP_REPORT_PACKET:
     960           0 :         formatted_len = igmp_format_v3_report(role, gen_packet, packet,
     961             :                                               packet_len);
     962           0 :         break;
     963             : 
     964           0 :       default:
     965           0 :         gmpx_assert(FALSE);
     966             :         formatted_len = 0;              /* Quiet the compiler. */
     967             :     }
     968             : 
     969         850 :     return formatted_len;
     970             : }
     971             : 
     972             : 
     973             : /*
     974             :  * igmp_next_xmit_packet
     975             :  *
     976             :  * Format the next IGMP packet to transmit for a role (host/router).
     977             :  *
     978             :  * Returns the length of the formatted packet, or 0 if there was
     979             :  * nothing to send.
     980             :  *
     981             :  * Also returns a pointer to the destination address to send the packet to.
     982             :  *
     983             :  * The packet is formatted in the supplied buffer, which is assumed to be
     984             :  * large enough to carry at least a minimum packet of any type.
     985             :  *
     986             :  * Source and destination addresses are written through the supplied pointers.
     987             :  */
     988             : uint32_t
     989        3546 : igmp_next_xmit_packet (gmp_role role, gmpx_intf_id intf_id,
     990             :                        void *packet, uint8_t *dest_addr, uint32_t packet_len,
     991             :                        void *trace_context, uint32_t trace_flags)
     992             : {
     993             :     gmp_packet *gen_packet;
     994             :     igmp_packet *igmp_pkt;
     995             :     igmp_hdr *pkt_hdr;
     996             :     uint32_t formatted_len;
     997             :     uint16_t checksum;
     998             : 
     999        3546 :     igmp_pkt = packet;
    1000        3546 :     formatted_len = 0;
    1001             : 
    1002             :     while (TRUE) {
    1003             : 
    1004             :         /* Get the next generic packet. */
    1005             : 
    1006        3546 :         gen_packet = gmpp_next_xmit_packet(role, GMP_PROTO_IGMP, intf_id,
    1007             :                                            packet_len);
    1008             : 
    1009             :         /* Bail if nothing to send. */
    1010             : 
    1011        3546 :         if (!gen_packet)
    1012        2627 :             break;
    1013             : 
    1014         919 :         gmpx_assert(gen_packet->gmp_packet_type < GMP_NUM_PACKET_TYPES);
    1015             : 
    1016             :         /* Got a packet.  Split based on the version number. */
    1017             : 
    1018         919 :         switch (gen_packet->gmp_packet_version) {
    1019             : 
    1020           6 :           case GMP_VERSION_BASIC:
    1021           6 :             formatted_len = igmp_format_v1_packet(role, gen_packet, igmp_pkt,
    1022             :                                                   packet_len);
    1023           6 :             break;
    1024             : 
    1025          63 :           case GMP_VERSION_LEAVES:
    1026          63 :             formatted_len = igmp_format_v2_packet(role, gen_packet, igmp_pkt,
    1027             :                                                   packet_len);
    1028          63 :             break;
    1029             : 
    1030         850 :           case GMP_VERSION_SOURCES:
    1031         850 :             formatted_len = igmp_format_v3_packet(role, gen_packet, igmp_pkt,
    1032             :                                                   packet_len);
    1033         850 :             break;
    1034             : 
    1035           0 :           default:
    1036           0 :             gmpx_assert(FALSE);
    1037             :             break;
    1038             :         }
    1039             : 
    1040             :         /*
    1041             :          * If the formatting routines returned a length of zero, it means
    1042             :          * that the packet was suppressed.  Loop back to try another one
    1043             :          * in that case, or break out if not.
    1044             :          */
    1045         919 :         if (formatted_len)
    1046         919 :             break;
    1047             : 
    1048             :         /* Toss the packet that didn't build. */
    1049             : 
    1050           0 :         gmpp_packet_done(role, GMP_PROTO_IGMP, gen_packet);
    1051             :     }
    1052             : 
    1053             :     /*
    1054             :      * Checksum the packet and fill in the destination address field
    1055             :      * if it's there.
    1056             :      */
    1057        3546 :     if (formatted_len) {
    1058         919 :         pkt_hdr = &igmp_pkt->igmp_pkt_naked.igmp_naked_header_hdr;
    1059         919 :         checksum = gmpx_calculate_cksum(packet, formatted_len);
    1060         919 :         pkt_hdr->igmp_hdr_cksum = checksum;
    1061         919 :         memmove(dest_addr, gen_packet->gmp_packet_dest_addr.gmp_addr, IPV4_ADDR_LEN);
    1062             : 
    1063             :         /* Trace the packet. */
    1064             : 
    1065         919 :         gmp_igmp_trace_pkt(igmp_pkt, formatted_len, dest_addr, intf_id, FALSE,
    1066             :                            trace_context, trace_flags);
    1067             :     }
    1068             : 
    1069             :     /* Notify that we're done with the packet. */
    1070             : 
    1071        3546 :     if (gen_packet)
    1072         919 :         gmpp_packet_done(role, GMP_PROTO_IGMP, gen_packet);
    1073             : 
    1074        3546 :     return formatted_len;
    1075             : }
    1076             : 
    1077             : 
    1078             : /*
    1079             :  * igmp_parse_v1_packet
    1080             :  *
    1081             :  * Parse an IGMP V1 packet.
    1082             :  *
    1083             :  * Returns TRUE if the packet parsed OK, or FALSE if not.
    1084             :  *
    1085             :  * Fills in the generic packet structure as appropriate.
    1086             :  */
    1087             : static boolean
    1088           1 : igmp_parse_v1_packet(igmp_packet *packet, gmp_packet *gen_packet,
    1089             :                      uint32_t packet_len GMPX_UNUSED,
    1090             :                      gmp_message_type msg_type)
    1091             : {
    1092             :     gmp_query_packet *query_pkt;
    1093             :     gmp_report_packet *report_pkt;
    1094             :     gmp_report_group_record *group_rcrd;
    1095             :     igmp_v1v2_pkt *v1v2_pkt;
    1096             : 
    1097             :     /* Switch based on the message type. */
    1098             : 
    1099           1 :     v1v2_pkt = &packet->igmp_pkt_v1v2;
    1100             : 
    1101           1 :     switch (msg_type) {
    1102             : 
    1103           0 :       case GMP_QUERY_PACKET:
    1104             : 
    1105             :         /* Query packet.  Not much to do here except get the max resp value. */
    1106             : 
    1107           0 :         query_pkt = &gen_packet->gmp_packet_contents.gmp_packet_query;
    1108           0 :         query_pkt->gmp_query_max_resp =
    1109           0 :             (igmp_max_resp_value(packet, GMP_VERSION_BASIC) *
    1110             :              IGMP_MAX_RESP_MSEC);
    1111           0 :         break;
    1112             : 
    1113           1 :       case GMP_REPORT_PACKET:
    1114             : 
    1115             :         /*
    1116             :          * Report packet.  Grab a group record and fill it in.  We use
    1117             :          * an IS_EX record with a null address list to signify a Join.
    1118             :          */
    1119           1 :         report_pkt = &gen_packet->gmp_packet_contents.gmp_packet_report;
    1120           1 :         group_rcrd = gmpp_create_group_record(report_pkt, NULL,
    1121           1 :                                               v1v2_pkt->igmp_v1v2_pkt_group,
    1122             :                                               IPV4_ADDR_LEN);
    1123           1 :         if (!group_rcrd)
    1124           0 :             return FALSE;
    1125           1 :         group_rcrd->gmp_rpt_type = GMP_RPT_IS_EX;
    1126             : 
    1127             :         /* Complain if the group address is wrong. */
    1128             : 
    1129           1 :         if (!igmp_addr_is_mcast(group_rcrd->gmp_rpt_group.gmp_v4_addr))
    1130           0 :             return FALSE;
    1131             : 
    1132           1 :         break;
    1133             : 
    1134           0 :       default:
    1135           0 :         gmpx_assert(FALSE);
    1136             :     }
    1137             : 
    1138           1 :     return TRUE;
    1139             : }
    1140             : 
    1141             : 
    1142             : /*
    1143             :  * igmp_parse_v2_packet
    1144             :  *
    1145             :  * Parse an IGMP V2 packet.
    1146             :  *
    1147             :  * Returns TRUE if the packet parsed OK, or FALSE if not.
    1148             :  *
    1149             :  * Fills in the generic packet structure as appropriate.
    1150             :  */
    1151             : static boolean
    1152          79 : igmp_parse_v2_packet(igmp_packet *packet, gmp_packet *gen_packet,
    1153             :                      uint32_t packet_len GMPX_UNUSED,
    1154             :                      gmp_message_type msg_type)
    1155             : {
    1156             :     gmp_query_packet *query_pkt;
    1157             :     gmp_report_packet *report_pkt;
    1158             :     gmp_report_group_record *group_rcrd;
    1159             :     igmp_v1v2_pkt *v1v2_pkt;
    1160             :     uint32_t byte_offset;
    1161             : 
    1162             :     /* Switch based on the message type. */
    1163             : 
    1164          79 :     v1v2_pkt = &packet->igmp_pkt_v1v2;
    1165             : 
    1166          79 :     switch (msg_type) {
    1167             : 
    1168           0 :       case GMP_QUERY_PACKET:
    1169             : 
    1170             :         /* Query packet.  Get the max resp value. */
    1171             : 
    1172           0 :         query_pkt = &gen_packet->gmp_packet_contents.gmp_packet_query;
    1173           0 :         query_pkt->gmp_query_max_resp =
    1174           0 :             (igmp_max_resp_value(packet, GMP_VERSION_LEAVES) *
    1175             :              IGMP_MAX_RESP_MSEC);
    1176             : 
    1177             :         /* Copy the group address. */
    1178             : 
    1179           0 :         memmove(query_pkt->gmp_query_group.gmp_addr, v1v2_pkt->igmp_v1v2_pkt_group, IPV4_ADDR_LEN);
    1180             :         
    1181             :         /*
    1182             :          * If the group address is nonzero, flag that we've got a
    1183             :          * group query.
    1184             :          */
    1185           0 :         for (byte_offset = 0; byte_offset < IPV4_ADDR_LEN; byte_offset ++) {
    1186           0 :             if (v1v2_pkt->igmp_v1v2_pkt_group[byte_offset]) {
    1187           0 :                 query_pkt->gmp_query_group_query = TRUE;
    1188           0 :                 break;
    1189             :             }
    1190             :         }
    1191             : 
    1192           0 :         break;
    1193             : 
    1194          79 :       case GMP_REPORT_PACKET:
    1195             : 
    1196             :         /* Report packet.  Grab a group record and fill it in. */
    1197             : 
    1198          79 :         report_pkt = &gen_packet->gmp_packet_contents.gmp_packet_report;
    1199          79 :         group_rcrd = gmpp_create_group_record(report_pkt, NULL,
    1200          79 :                                               v1v2_pkt->igmp_v1v2_pkt_group,
    1201             :                                               IPV4_ADDR_LEN);
    1202          79 :         if (!group_rcrd)
    1203           0 :             return FALSE;
    1204             : 
    1205             :         /* Complain if the group address is wrong. */
    1206             : 
    1207          79 :         if (!igmp_addr_is_mcast(group_rcrd->gmp_rpt_group.gmp_v4_addr))
    1208           0 :             return FALSE;
    1209             : 
    1210             :         /*
    1211             :          * If this is a Leave, use record type TO_IN with a null
    1212             :          * address list.  Otherwise (it's a Join) use IS_EX with a
    1213             :          * null address list.
    1214             :          */
    1215          79 :         if (v1v2_pkt->igmp_v1v2_pkt_hdr.igmp_hdr_type == IGMP_TYPE_V2_LEAVE) {
    1216          31 :             group_rcrd->gmp_rpt_type = GMP_RPT_TO_IN;
    1217             :         } else {
    1218          48 :             group_rcrd->gmp_rpt_type = GMP_RPT_IS_EX;
    1219             :         }
    1220          79 :         break;
    1221             : 
    1222           0 :       default:
    1223           0 :         gmpx_assert(FALSE);
    1224             :     }
    1225             : 
    1226          79 :     return TRUE;
    1227             : }
    1228             : 
    1229             : 
    1230             : /*
    1231             :  * igmp_parse_v3_report_packet
    1232             :  *
    1233             :  * Parse an IGMP V3 Report packet.
    1234             :  *
    1235             :  * Returns TRUE if the packet parsed OK, or FALSE if not.
    1236             :  *
    1237             :  * Fills in the generic packet structure as appropriate.
    1238             :  */
    1239             : static boolean
    1240          56 : igmp_parse_v3_report_packet(igmp_packet *packet, gmp_packet *gen_packet,
    1241             :                             uint32_t packet_len)
    1242             : {
    1243             :     igmp_v3_report *v3_rpt_pkt;
    1244             :     igmp_v3_rpt_rcrd *v3_rpt_rcrd;
    1245             :     gmp_report_packet *report_pkt;
    1246             :     gmp_report_group_record *group_rcrd;
    1247             :     uint32_t group_count;
    1248             :     uint32_t source_count;
    1249             :     uint32_t record_length;
    1250             :     uint8_t *byte_ptr;
    1251             :     uint8_t *addr_ptr;
    1252             :     gmp_addr_thread *addr_thread;
    1253             : 
    1254             :     /* Bail if the packet is too small. */
    1255             : 
    1256          56 :     if (packet_len < sizeof(igmp_v3_report))
    1257           0 :         return FALSE;
    1258             : 
    1259          56 :     v3_rpt_pkt = &packet->igmp_pkt_v3_rpt;
    1260          56 :     report_pkt = &gen_packet->gmp_packet_contents.gmp_packet_report;
    1261             : 
    1262             :     /* Extract the group count. */
    1263             : 
    1264          56 :     group_count = get_short(&v3_rpt_pkt->igmp_v3_report_num_rcrds);
    1265             : 
    1266             :     /* Loop for each group in the report. */
    1267             : 
    1268          56 :     packet_len -= sizeof(igmp_v3_report);
    1269          56 :     v3_rpt_rcrd = v3_rpt_pkt->igmp_v3_report_rcrd;
    1270          56 :     byte_ptr = (uint8_t *) v3_rpt_rcrd;
    1271         116 :     while (group_count--) {
    1272             : 
    1273             :         /*
    1274             :          * Looks like we have a group record.  First make sure that there's
    1275             :          * enough left in the packet to examine the header.
    1276             :          */
    1277          60 :         if (packet_len < sizeof(igmp_v3_rpt_rcrd))
    1278           0 :             return FALSE;
    1279             : 
    1280             :         /* Validate the record type. */
    1281             : 
    1282          60 :         if (v3_rpt_rcrd->igmp_v3_rpt_rec_type < GMP_RPT_MIN ||
    1283          60 :             v3_rpt_rcrd->igmp_v3_rpt_rec_type > GMP_RPT_MAX)
    1284           0 :             return FALSE;
    1285             : 
    1286             :         /* Extract the source count. */
    1287             : 
    1288          60 :         source_count = get_short(&v3_rpt_rcrd->igmp_v3_rpt_num_srcs);
    1289             : 
    1290             :         /* Make sure there's enough packet left to hold the whole record. */
    1291             : 
    1292          60 :         record_length = sizeof(igmp_v3_rpt_rcrd) +
    1293          60 :             (source_count * IPV4_ADDR_LEN) + v3_rpt_rcrd->igmp_v3_rpt_aux_len;
    1294          60 :         if (packet_len < record_length)
    1295           0 :             return FALSE;
    1296             : 
    1297             :         /* Enough space.  Create a group record. */
    1298             : 
    1299          60 :         group_rcrd = gmpp_create_group_record(report_pkt, NULL,
    1300          60 :                                               v3_rpt_rcrd->igmp_v3_rpt_group,
    1301             :                                               IPV4_ADDR_LEN);
    1302          60 :         if (!group_rcrd)
    1303           0 :             return FALSE;               /* Out of memory */
    1304             : 
    1305             :         /* Complain if the group address is wrong. */
    1306             : 
    1307          60 :         if (!igmp_addr_is_mcast(group_rcrd->gmp_rpt_group.gmp_v4_addr))
    1308           0 :             return FALSE;
    1309             : 
    1310             :         /* Stash the record type. */
    1311             : 
    1312          60 :         group_rcrd->gmp_rpt_type = v3_rpt_rcrd->igmp_v3_rpt_rec_type;
    1313             : 
    1314             :         /* If there are any sources, add them to the record. */
    1315             : 
    1316          60 :         if (source_count) {
    1317          59 :             addr_ptr = get_igmp_v3_rpt_source(v3_rpt_rcrd);
    1318             : 
    1319             :             /* Allocate an address thread. */
    1320             : 
    1321          59 :             addr_thread = gmp_alloc_addr_thread();
    1322          59 :             if (!addr_thread)
    1323           0 :                 return FALSE;           /* Out of memory */
    1324             : 
    1325         193 :             while (source_count--) {
    1326             : 
    1327             :                 /* Enqueue an address thread entry for the next address. */
    1328             : 
    1329         134 :                 if (gmp_enqueue_addr_thread_addr(addr_thread, addr_ptr,
    1330             :                                                  IPV4_ADDR_LEN) < 0) {
    1331           0 :                     free(addr_thread);
    1332           0 :                     return FALSE;       /* Out of memory */
    1333             :                 }
    1334         134 :                 addr_ptr += IPV4_ADDR_LEN;
    1335             :             }
    1336          59 :             group_rcrd->gmp_rpt_rcv_srcs = addr_thread;
    1337             :         }
    1338             : 
    1339             :         /* Advance to the next group. */
    1340             : 
    1341          60 :         packet_len -= record_length;
    1342          60 :         byte_ptr += record_length;
    1343          60 :         v3_rpt_rcrd = (igmp_v3_rpt_rcrd *) byte_ptr;
    1344             :     }
    1345             : 
    1346          56 :     return TRUE;
    1347             : }
    1348             : 
    1349             : 
    1350             : /*
    1351             :  * igmp_parse_v3_query_packet
    1352             :  *
    1353             :  * Parse an IGMP V3 Query packet.
    1354             :  *
    1355             :  * Returns TRUE if the packet parsed OK, or FALSE if not.
    1356             :  *
    1357             :  * Fills in the generic packet structure as appropriate.
    1358             :  */
    1359             : static boolean
    1360           0 : igmp_parse_v3_query_packet(igmp_packet *packet, gmp_packet *gen_packet,
    1361             :                            uint32_t packet_len)
    1362             : {
    1363             :     igmp_v3_query *v3_query_pkt;
    1364             :     gmp_query_packet *query_pkt;
    1365             :     uint32_t byte_offset;
    1366             :     gmp_addr_thread *addr_thread;
    1367             :     uint32_t source_count;
    1368             :     uint8_t *addr_ptr;
    1369             : 
    1370             :     /* Bail if the packet is too small. */
    1371             : 
    1372           0 :     if (packet_len < sizeof(igmp_v3_query))
    1373           0 :         return FALSE;
    1374             : 
    1375           0 :     v3_query_pkt = &packet->igmp_pkt_v3_query;
    1376           0 :     query_pkt = &gen_packet->gmp_packet_contents.gmp_packet_query;
    1377             : 
    1378             :     /* Query packet.  Get the max resp value. */
    1379             : 
    1380           0 :     query_pkt->gmp_query_max_resp =
    1381           0 :         igmp_max_resp_value(packet, GMP_VERSION_SOURCES) * IGMP_MAX_RESP_MSEC;
    1382             : 
    1383             :     /* Copy the group address. */
    1384             : 
    1385           0 :     memmove(query_pkt->gmp_query_group.gmp_addr, v3_query_pkt->igmp_v3_query_group, IPV4_ADDR_LEN);
    1386             : 
    1387             :     /*
    1388             :      * If the group address is nonzero, flag that we've got a
    1389             :      * group query.
    1390             :      */
    1391           0 :     for (byte_offset = 0; byte_offset < IPV4_ADDR_LEN; byte_offset ++) {
    1392           0 :         if (v3_query_pkt->igmp_v3_query_group[byte_offset]) {
    1393           0 :             query_pkt->gmp_query_group_query = TRUE;
    1394           0 :             break;
    1395             :         }
    1396             :     }
    1397             : 
    1398             :     /* Parse the S and QRV fields. */
    1399             : 
    1400           0 :     query_pkt->gmp_query_suppress =
    1401           0 :         ((v3_query_pkt->igmp_v3_query_s_qrv & IGMP_SUPP_RTR_PROC_MASK) != 0);
    1402           0 :     query_pkt->gmp_query_qrv =
    1403           0 :         v3_query_pkt->igmp_v3_query_s_qrv & IGMP_QRV_MASK;
    1404             : 
    1405             :     /* Parse the QQIC field. */
    1406             : 
    1407           0 :     query_pkt->gmp_query_qqi =
    1408           0 :         igmp_decode_fixfloat(v3_query_pkt->igmp_v3_query_qqic) * MSECS_PER_SEC;
    1409             : 
    1410             :     /*
    1411             :      * Parse the source count field.  If nonzero and this is a general query,
    1412             :      * flag a parse error.
    1413             :      */
    1414           0 :     source_count = get_short(&v3_query_pkt->igmp_v3_query_num_srcs);
    1415           0 :     if (source_count && !query_pkt->gmp_query_group_query)
    1416           0 :         return FALSE;
    1417             : 
    1418           0 :     packet_len -= sizeof(igmp_v3_query);
    1419             :     
    1420             :     /*
    1421             :      * If the remaining packet length doesn't match the count of sources, flag
    1422             :      * a parse error.
    1423             :      */
    1424           0 :     if (packet_len != (source_count * IPV4_ADDR_LEN))
    1425           0 :         return FALSE;
    1426             : 
    1427             :     /*
    1428             :      * Looks kosher.  Now pull the list of source addresses into an
    1429             :      * address thread, if they are there.
    1430             :      */
    1431           0 :     if (source_count) {
    1432           0 :         addr_ptr = v3_query_pkt->igmp_v3_query_source;
    1433           0 :         addr_thread = gmp_alloc_addr_thread();
    1434           0 :         query_pkt->gmp_query_rcv_srcs = addr_thread;
    1435           0 :         if (!addr_thread)
    1436           0 :             return FALSE;               /* Out of memory */
    1437           0 :         while (source_count--) {
    1438           0 :             if (gmp_enqueue_addr_thread_addr(addr_thread, addr_ptr,
    1439             :                                              IPV4_ADDR_LEN) < 0) {
    1440           0 :                 return FALSE;           /* Out of memory */
    1441             :             }
    1442           0 :             addr_ptr += IPV4_ADDR_LEN;
    1443             :         }
    1444             :     }
    1445             : 
    1446           0 :     return TRUE;
    1447             : }
    1448             : 
    1449             : 
    1450             : /*
    1451             :  * igmp_parse_v3_packet
    1452             :  *
    1453             :  * Parse an IGMP V3 packet.
    1454             :  *
    1455             :  * Returns TRUE if the packet parsed OK, or FALSE if not.
    1456             :  *
    1457             :  * Fills in the generic packet structure as appropriate.
    1458             :  */
    1459             : static boolean
    1460          56 : igmp_parse_v3_packet(igmp_packet *packet, gmp_packet *gen_packet,
    1461             :                      uint32_t packet_len, gmp_message_type msg_type)
    1462             : {
    1463             :     boolean result;
    1464             : 
    1465             :     /* Switch based on message type. */
    1466             : 
    1467          56 :     switch (msg_type) {
    1468             : 
    1469           0 :       case GMP_QUERY_PACKET:
    1470           0 :         result = igmp_parse_v3_query_packet(packet, gen_packet, packet_len);
    1471           0 :         break;
    1472             : 
    1473             : 
    1474          56 :       case GMP_REPORT_PACKET:
    1475          56 :         result = igmp_parse_v3_report_packet(packet, gen_packet, packet_len);
    1476          56 :         break;
    1477             : 
    1478           0 :       default:
    1479           0 :         gmpx_assert(FALSE);
    1480             :         result = FALSE;                 /* Quiet the compiler. */
    1481             :         break;
    1482             : 
    1483             :     }
    1484             : 
    1485          56 :     return result;
    1486             : }
    1487             : 
    1488             : 
    1489             : /*
    1490             :  * igmp_process_pkt
    1491             :  *
    1492             :  * Process a received IGMP packet.  We parse it into generic form and pass it
    1493             :  * to the clients.
    1494             :  *
    1495             :  * Returns TRUE if the packet parsed OK, or FALSE if there was a problem.
    1496             :  */
    1497             : boolean
    1498         136 : igmp_process_pkt (void *rcv_pkt, const uint8_t *src_addr,
    1499             :                   const uint8_t *dest_addr, uint32_t packet_len,
    1500             :                   gmpx_intf_id intf_id, gmpx_packet_attr attrib,
    1501             :                   void *trace_context, uint32_t trace_flags)
    1502             : {
    1503             :     igmp_packet *packet;
    1504             :     gmp_packet *gen_packet;
    1505             :     gmp_version version;
    1506             :     gmp_message_type msg_type;
    1507             :     boolean parse_ok;
    1508             : 
    1509         136 :     packet = rcv_pkt;
    1510         136 :     parse_ok = TRUE;
    1511             : 
    1512             :     /* Bail if the packet is too small. */
    1513             : 
    1514         136 :     if (packet_len < sizeof(igmp_naked_header)) {
    1515           0 :         parse_ok = FALSE;
    1516             :     }
    1517             : 
    1518             :     /*
    1519             :      * Bail if the version is bogus or the packet is otherwise
    1520             :      * unrecognizable.
    1521             :      */
    1522         136 :     version = igmp_generic_version(packet, packet_len, &msg_type);
    1523         136 :     if (version == GMP_VERSION_INVALID) {
    1524           0 :         parse_ok = FALSE;
    1525             :     }
    1526             : 
    1527             :     /*
    1528             :      * Looks like we have a recognizable header, message type, and version.
    1529             :      * Create a basic generic packet of the appropriate type.
    1530             :      */
    1531         136 :     gen_packet = gmpp_create_packet_header(version, msg_type, GMP_PROTO_IGMP);
    1532         136 :     if (!gen_packet)
    1533           0 :         return FALSE;                   /* Out of memory */
    1534             : 
    1535             :     /* Set up the address fields. */
    1536             : 
    1537         136 :     if (src_addr) {
    1538         136 :         memmove(gen_packet->gmp_packet_src_addr.gmp_addr, src_addr, IPV4_ADDR_LEN);
    1539             :     }
    1540             : 
    1541         136 :     if (dest_addr) {
    1542         136 :         memmove(gen_packet->gmp_packet_dest_addr.gmp_addr, dest_addr, IPV4_ADDR_LEN);
    1543             :     }
    1544             : 
    1545             :     /* If the packet looks OK so far, parse it further. */
    1546             : 
    1547         136 :     if (parse_ok) {
    1548             : 
    1549             :         /* Save the attribute. */
    1550             : 
    1551         136 :         gen_packet->gmp_packet_attr = attrib;
    1552             : 
    1553             :         /* Split off by version and by packet type for further parsing. */
    1554             : 
    1555         136 :         switch (version) {
    1556           1 :           case GMP_VERSION_BASIC:
    1557             : 
    1558             :             /* IGMPv1.  Parse it. */
    1559             : 
    1560           1 :             parse_ok = igmp_parse_v1_packet(packet, gen_packet, packet_len,
    1561             :                                             msg_type);
    1562           1 :             break;
    1563             : 
    1564          79 :           case GMP_VERSION_LEAVES:
    1565             : 
    1566             :             /* IGMPv2.  Parse it. */
    1567             : 
    1568          79 :             parse_ok = igmp_parse_v2_packet(packet, gen_packet, packet_len,
    1569             :                                             msg_type);
    1570          79 :             break;
    1571             : 
    1572          56 :           case GMP_VERSION_SOURCES:
    1573             : 
    1574             :             /* IGMPv3.  Parse it. */
    1575             : 
    1576          56 :             parse_ok = igmp_parse_v3_packet(packet, gen_packet, packet_len,
    1577             :                                             msg_type);
    1578          56 :             break;
    1579             : 
    1580           0 :           default:
    1581           0 :             gmpx_assert(FALSE);
    1582             :             break;
    1583             :         }
    1584             :     }
    1585             : 
    1586             :     /*
    1587             :      * If the packet parsed OK, trace it and then pass it along.
    1588             :      * Otherwise, complain.
    1589             :      */
    1590         136 :     if (parse_ok) {
    1591         136 :         gmp_igmp_trace_pkt(packet, packet_len, src_addr, intf_id, TRUE,
    1592             :                            trace_context, trace_flags);
    1593         136 :         gmpp_process_rcv_packet(gen_packet, intf_id);
    1594             :     } else {
    1595           0 :         gmp_igmp_trace_bad_pkt(packet_len, src_addr, intf_id, trace_context,
    1596             :                                trace_flags);
    1597             :     }
    1598             : 
    1599             :     /* Toss the parsed packet. */
    1600             : 
    1601         136 :     gmpp_destroy_packet(gen_packet);
    1602             : 
    1603         136 :     return parse_ok;
    1604             : }

Generated by: LCOV version 1.14