Coverage Report

Created: 2025-07-11 07:47

/src/PcapPlusPlus/Packet++/header/IgmpLayer.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include "Layer.h"
4
#include "IpAddress.h"
5
#include <vector>
6
7
/// @file
8
9
/// @namespace pcpp
10
/// @brief The main namespace for the PcapPlusPlus lib
11
namespace pcpp
12
{
13
  /// @struct igmp_header
14
  /// IGMPv1 and IGMPv2 basic protocol header
15
  struct igmp_header
16
  {
17
    /// Indicates the message type. The enum for message type is pcpp::IgmpType
18
    uint8_t type;
19
    /// Specifies the time limit for the corresponding report. The field has a resolution of 100 milliseconds
20
    uint8_t maxResponseTime;
21
    /// This is the 16-bit one's complement of the one's complement sum of the entire IGMP message
22
    uint16_t checksum;
23
    /// This is the multicast address being queried when sending a Group-Specific or Group-and-Source-Specific Query
24
    uint32_t groupAddress;
25
  };
26
  static_assert(sizeof(igmp_header) == 8, "igmp_header size is not 8 bytes");
27
28
  /// @struct igmpv3_query_header
29
  /// IGMPv3 membership query basic header
30
  struct igmpv3_query_header
31
  {
32
    /// IGMP message type. Should always have value of membership query (::IgmpType_MembershipQuery)
33
    uint8_t type;
34
    /// This field specifies the maximum time (in 1/10 second) allowed before sending a responding report
35
    uint8_t maxResponseTime;
36
    /// This is the 16-bit one's complement of the one's complement sum of the entire IGMP message
37
    uint16_t checksum;
38
    /// This is the multicast address being queried when sending a Group-Specific or Group-and-Source-Specific Query
39
    uint32_t groupAddress;
40
    /// Suppress Router-side Processing Flag + Querier's Robustness Variable
41
    uint8_t s_qrv;
42
    /// Querier's Query Interval Code
43
    uint8_t qqic;
44
    /// This field specifies the number of source addresses present in the Query
45
    uint16_t numOfSources;
46
  };
47
  static_assert(sizeof(igmpv3_query_header) == 12, "igmpv3_query_header size is not 12 bytes");
48
49
  /// @struct igmpv3_report_header
50
  /// IGMPv3 membership report basic header
51
  struct igmpv3_report_header
52
  {
53
    /// IGMP message type. Should always have value of IGMPv3 membership report (::IgmpType_MembershipReportV3)
54
    uint8_t type;
55
    /// Unused byte
56
    uint8_t reserved1;
57
    /// This is the 16-bit one's complement of the one's complement sum of the entire IGMP message
58
    uint16_t checksum;
59
    /// Unused bytes
60
    uint16_t reserved2;
61
    /// This field specifies the number of group records present in the Report
62
    uint16_t numOfGroupRecords;
63
  };
64
  static_assert(sizeof(igmpv3_report_header) == 8, "igmpv3_report_header size is not 8 bytes");
65
66
  /// @struct igmpv3_group_record
67
  /// A block of fields containing information pertaining to the sender's membership in a single multicast group on
68
  /// the interface from which the Report is sent. Relevant only for IGMPv3 membership report messages
69
  struct igmpv3_group_record
70
  {
71
    /// Group record type
72
    uint8_t recordType;
73
    /// Contains the length of the Auxiliary Data field in this Group Record. A value other than 0 isn't supported
74
    uint8_t auxDataLen;
75
    /// Specifies how many source addresses are present in this Group Record
76
    uint16_t numOfSources;
77
    /// Contains the IP multicast address to which this Group Record pertains
78
    uint32_t multicastAddress;
79
    /// A vector of n IP unicast addresses, where n is the value in this record's Number of Sources field
80
    uint8_t sourceAddresses[];
81
82
    /// @return The multicast address in igmpv3_group_record#multicastAddress as IPv4Address instance
83
    IPv4Address getMulticastAddress() const
84
0
    {
85
0
      return multicastAddress;
86
0
    }
87
88
    /// @return The number of source addresses in this group record
89
    uint16_t getSourceAddressCount() const;
90
91
    /// Get the source address at a certain index
92
    /// @param[in] index The index of the source address in the group record
93
    /// @return The source address in the requested index. If index is negative or higher than the number of source
94
    /// addresses in this group record the value if IPv4Address#Zero is returned
95
    IPv4Address getSourceAddressAtIndex(int index) const;
96
97
    /// @return The total size in bytes of the group record
98
    size_t getRecordLen() const;
99
  };
100
101
  /// IGMP message types
102
  enum IgmpType
103
  {
104
    /// Unknown message type
105
    IgmpType_Unknown = 0,
106
    /// IGMP Membership Query
107
    IgmpType_MembershipQuery = 0x11,
108
    /// IGMPv1 Membership Report
109
    IgmpType_MembershipReportV1 = 0x12,
110
    /// DVMRP
111
    IgmpType_DVMRP = 0x13,
112
    /// PIM version 1
113
    IgmpType_P1Mv1 = 0x14,
114
    /// Cisco Trace Messages
115
    IgmpType_CiscoTrace = 0x15,
116
    /// IGMPv2 Membership Report
117
    IgmpType_MembershipReportV2 = 0x16,
118
    /// IGMPv2 Leave Group
119
    IgmpType_LeaveGroup = 0x17,
120
    /// Multicast Traceroute Response
121
    IgmpType_MulticastTracerouteResponse = 0x1e,
122
    /// Multicast Traceroute
123
    IgmpType_MulticastTraceroute = 0x1f,
124
    /// IGMPv3 Membership Report
125
    IgmpType_MembershipReportV3 = 0x22,
126
    /// MRD, Multicast Router Advertisement
127
    IgmpType_MulticastRouterAdvertisement = 0x30,
128
    /// MRD, Multicast Router Solicitation
129
    IgmpType_MulticastRouterSolicitation = 0x31,
130
    /// MRD, Multicast Router Termination
131
    IgmpType_MulticastRouterTermination = 0x32,
132
  };
133
134
  /// @class IgmpLayer
135
  /// A base class for all IGMP (Internet Group Management Protocol) protocol classes. This is an abstract class and
136
  /// cannot be instantiated, only its child classes can be instantiated. The inherited classes represent the
137
  /// different versions of the protocol: IGMPv1, IGMPv2 and IGMPv3
138
  class IgmpLayer : public Layer
139
  {
140
  protected:
141
    IgmpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, ProtocolType igmpVer)
142
7.90k
        : Layer(data, dataLen, prevLayer, packet, igmpVer)
143
7.90k
    {}
144
145
    IgmpLayer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime, ProtocolType igmpVer);
146
147
    uint16_t calculateChecksum();
148
149
    size_t getHeaderSizeByVerAndType(ProtocolType igmpVer, IgmpType igmpType) const;
150
151
  public:
152
    ~IgmpLayer() override = default;
153
154
    /// Get a pointer to the raw IGMPv1/IGMPv2 header. Notice this points directly to the data, so every change will
155
    /// change the actual packet data
156
    /// @return A pointer to the @ref igmp_header
157
    igmp_header* getIgmpHeader() const
158
5.92k
    {
159
5.92k
      return reinterpret_cast<igmp_header*>(m_Data);
160
5.92k
    }
161
162
    /// @return The IPv4 multicast address stored igmp_header#groupAddress
163
    IPv4Address getGroupAddress() const
164
0
    {
165
0
      return getIgmpHeader()->groupAddress;
166
0
    }
167
168
    /// Set the IPv4 multicast address
169
    /// @param[in] groupAddr The IPv4 address to set
170
    void setGroupAddress(const IPv4Address& groupAddr);
171
172
    /// @return IGMP type set in igmp_header#type as ::IgmpType enum. Notice that if igmp_header#type contains a
173
    /// value that doesn't appear in the ::IgmpType enum, ::IgmpType_Unknown will be returned
174
    IgmpType getType() const;
175
176
    /// Set IGMP type (will be written to igmp_header#type field)
177
    /// @param[in] type The type to set
178
    void setType(IgmpType type);
179
180
    /// A static method that gets raw IGMP data (byte stream) and returns the IGMP version of this IGMP message
181
    /// @param[in] data The IGMP raw data (byte stream)
182
    /// @param[in] dataLen Raw data length
183
    /// @param[out] isQuery Return true if IGMP message type is ::IgmpType_MembershipQuery and false otherwise
184
    /// @return One of the values ::IGMPv1, ::IGMPv2, ::IGMPv3 according to detected IGMP version or
185
    /// ::UnknownProtocol if couldn't detect IGMP version
186
    static ProtocolType getIGMPVerFromData(uint8_t* data, size_t dataLen, bool& isQuery);
187
188
    // implement abstract methods
189
190
    /// Does nothing for this layer (IGMP layer is always last)
191
    void parseNextLayer() override
192
7.90k
    {}
193
194
    /// @return Size of IGMP header = 8B
195
    size_t getHeaderLen() const override
196
1.85k
    {
197
1.85k
      return sizeof(igmp_header);
198
1.85k
    }
199
200
    std::string toString() const override;
201
202
    OsiModelLayer getOsiModelLayer() const override
203
1.66k
    {
204
1.66k
      return OsiModelNetworkLayer;
205
1.66k
    }
206
  };
207
208
  /// @class IgmpV1Layer
209
  /// Represents IGMPv1 (Internet Group Management Protocol ver 1) layer. This class represents all the different
210
  /// messages of IGMPv1
211
  class IgmpV1Layer : public IgmpLayer
212
  {
213
  public:
214
    /// A constructor that creates the layer from an existing packet raw data
215
    /// @param[in] data A pointer to the raw data
216
    /// @param[in] dataLen Size of the data in bytes
217
    /// @param[in] prevLayer A pointer to the previous layer
218
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
219
    IgmpV1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
220
1.58k
        : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv1)
221
1.58k
    {}
222
223
    /// A constructor that allocates a new IGMPv1 header
224
    /// @param[in] type The message type to set
225
    /// @param[in] groupAddr The multicast address to set. This is an optional parameter and has a default value of
226
    /// IPv4Address#Zero if not provided
227
    explicit IgmpV1Layer(IgmpType type, const IPv4Address& groupAddr = IPv4Address())
228
        : IgmpLayer(type, groupAddr, 0, IGMPv1)
229
0
    {}
230
231
    /// A destructor for this layer (does nothing)
232
    ~IgmpV1Layer() override = default;
233
234
    // implement abstract methods
235
236
    /// Calculate the IGMP checksum and set igmp_header#maxResponseTime to 0 (this field is unused in IGMPv1)
237
    void computeCalculateFields() override;
238
239
    /// A static method that validates the input data
240
    /// @param[in] data The pointer to the beginning of a byte stream of an IGMPv1 layer
241
    /// @param[in] dataLen The length of the byte stream
242
    /// @return True if the data is valid and can represent an IGMPv1 layer
243
    static bool isDataValid(uint8_t const* data, size_t dataLen)
244
1.58k
    {
245
1.58k
      return canReinterpretAs<igmp_header>(data, dataLen);
246
1.58k
    }
247
  };
248
249
  /// @class IgmpV2Layer
250
  /// Represents IGMPv2 (Internet Group Management Protocol ver 2) layer. This class represents all the different
251
  /// messages of IGMPv2
252
  class IgmpV2Layer : public IgmpLayer
253
  {
254
  public:
255
    /// A constructor that creates the layer from an existing packet raw data
256
    /// @param[in] data A pointer to the raw data
257
    /// @param[in] dataLen Size of the data in bytes
258
    /// @param[in] prevLayer A pointer to the previous layer
259
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
260
    IgmpV2Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
261
3.03k
        : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv2)
262
3.03k
    {}
263
264
    /// A constructor that allocates a new IGMPv2 header
265
    /// @param[in] type The message type to set
266
    /// @param[in] groupAddr The multicast address to set. This is an optional parameter and has a default value of
267
    /// unspecified/zero IPv4 address
268
    /// @param[in] maxResponseTime The max response time to set. This is an optional parameter and has a default
269
    /// value of 0 if not provided
270
    explicit IgmpV2Layer(IgmpType type, const IPv4Address& groupAddr = IPv4Address(), uint8_t maxResponseTime = 0)
271
        : IgmpLayer(type, groupAddr, maxResponseTime, IGMPv2)
272
0
    {}
273
274
    /// A destructor for this layer (does nothing)
275
    ~IgmpV2Layer() override = default;
276
277
    // implement abstract methods
278
279
    /// Calculate the IGMP checksum
280
    void computeCalculateFields() override;
281
282
    /// A static method that validates the input data
283
    /// @param[in] data The pointer to the beginning of a byte stream of an IGMPv2 layer
284
    /// @param[in] dataLen The length of the byte stream
285
    /// @return True if the data is valid and can represent an IGMPv2 layer
286
    static bool isDataValid(uint8_t const* data, size_t dataLen)
287
3.03k
    {
288
3.03k
      return canReinterpretAs<igmp_header>(data, dataLen);
289
3.03k
    }
290
  };
291
292
  /// @class IgmpV3QueryLayer
293
  /// Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership query message
294
  class IgmpV3QueryLayer : public IgmpLayer
295
  {
296
  public:
297
    /// A constructor that creates the layer from an existing packet raw data
298
    /// @param[in] data A pointer to the raw data
299
    /// @param[in] dataLen Size of the data in bytes
300
    /// @param[in] prevLayer A pointer to the previous layer
301
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
302
    IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
303
304
    /// A constructor that allocates a new IGMPv3 membership query
305
    /// @param[in] multicastAddr The multicast address to set. This is an optional parameter and has a default value
306
    /// of unspecified/zero IPv4 address if not provided
307
    /// @param[in] maxResponseTime The max response time to set. This is an optional parameter and has a default
308
    /// value of 0 if not provided
309
    /// @param[in] s_qrv A 1-byte value representing the value in Suppress Router-side Processing Flag + Querier's
310
    /// Robustness Variable (igmpv3_query_header#s_qrv field). This is an optional parameter and has a default value
311
    /// of 0 if not provided
312
    explicit IgmpV3QueryLayer(const IPv4Address& multicastAddr = IPv4Address(), uint8_t maxResponseTime = 0,
313
                              uint8_t s_qrv = 0);
314
315
    /// Get a pointer to the raw IGMPv3 membership query header. Notice this points directly to the data, so every
316
    /// change will change the actual packet data
317
    /// @return A pointer to the @ref igmpv3_query_header
318
    igmpv3_query_header* getIgmpV3QueryHeader() const
319
1.18k
    {
320
1.18k
      return reinterpret_cast<igmpv3_query_header*>(m_Data);
321
1.18k
    }
322
323
    /// @return The number of source addresses in this message (as extracted from the
324
    /// igmpv3_query_header#numOfSources field)
325
    uint16_t getSourceAddressCount() const;
326
327
    /// Get the IPV4 source address in a certain index
328
    /// @param[in] index The requested index of the source address
329
    /// @return The IPv4 source address, or IPv4Address#Zero if index is out of bounds (of the message or of the
330
    /// layer)
331
    IPv4Address getSourceAddressAtIndex(int index) const;
332
333
    /// Add a new source address at the end of the source address list. The igmpv3_query_header#numOfSources field
334
    /// will be incremented accordingly
335
    /// @param[in] addr The IPv4 source address to add
336
    /// @return True if source address was added successfully or false otherwise. If false is returned an
337
    /// appropriate error message will be printed to log
338
    bool addSourceAddress(const IPv4Address& addr);
339
340
    /// Add a new source address at a certain index of the source address list. The igmpv3_query_header#numOfSources
341
    /// field will be incremented accordingly
342
    /// @param[in] addr The IPv4 source address to add
343
    /// @param[in] index The index to add the new source address at
344
    /// @return True if source address was added successfully or false otherwise. If false is returned an
345
    /// appropriate error message will be printed to log
346
    bool addSourceAddressAtIndex(const IPv4Address& addr, int index);
347
348
    /// Remove a source address at a certain index. The igmpv3_query_header#numOfSources field will be decremented
349
    /// accordingly
350
    /// @param[in] index The index of the source address to be removed
351
    /// @return True if source address was removed successfully or false otherwise. If false is returned an
352
    /// appropriate error message will be printed to log
353
    bool removeSourceAddressAtIndex(int index);
354
355
    /// Remove all source addresses in the message. The igmpv3_query_header#numOfSources field will be set to 0
356
    /// @return True if all source addresses were cleared successfully or false otherwise. If false is returned an
357
    /// appropriate error message will be printed to log
358
    bool removeAllSourceAddresses();
359
360
    // implement abstract methods
361
362
    /// Calculate the IGMP checksum
363
    void computeCalculateFields() override;
364
365
    /// @return The message size in bytes which include the size of the basic header + the size of the source
366
    /// address list
367
    size_t getHeaderLen() const override;
368
369
    /// A static method that validates the input data
370
    /// @param[in] data The pointer to the beginning of a byte stream of an IGMPv3 Query layer
371
    /// @param[in] dataLen The length of the byte stream
372
    /// @return True if the data is valid and can represent an IGMPv3 Query layer
373
    static bool isDataValid(uint8_t const* data, size_t dataLen)
374
1.58k
    {
375
1.58k
      return canReinterpretAs<igmpv3_query_header>(data, dataLen);
376
1.58k
    }
377
  };
378
379
  /// @class IgmpV3ReportLayer
380
  /// Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership report message
381
  class IgmpV3ReportLayer : public IgmpLayer
382
  {
383
  private:
384
    igmpv3_group_record* addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress,
385
                                          const std::vector<IPv4Address>& sourceAddresses, int offset);
386
387
  public:
388
    /// A constructor that creates the layer from an existing packet raw data
389
    /// @param[in] data A pointer to the raw data
390
    /// @param[in] dataLen Size of the data in bytes
391
    /// @param[in] prevLayer A pointer to the previous layer
392
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
393
    IgmpV3ReportLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
394
1.71k
        : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3)
395
1.71k
    {}
396
397
    /// A constructor that allocates a new IGMPv3 membership report with 0 group addresses
398
    IgmpV3ReportLayer() : IgmpLayer(IgmpType_MembershipReportV3, IPv4Address(), 0, IGMPv3)
399
0
    {}
400
401
    /// Get a pointer to the raw IGMPv3 membership report header. Notice this points directly to the data, so every
402
    /// change will change the actual packet data
403
    /// @return A pointer to the @ref igmpv3_report_header
404
    igmpv3_report_header* getReportHeader() const
405
345
    {
406
345
      return reinterpret_cast<igmpv3_report_header*>(m_Data);
407
345
    }
408
409
    /// @return The number of group records in this message (as extracted from the
410
    /// igmpv3_report_header#numOfGroupRecords field)
411
    uint16_t getGroupRecordCount() const;
412
413
    /// @return A pointer to the first group record or nullptr if no group records exist. Notice the return value is
414
    /// a pointer to the real data, so changes in the return value will affect the packet data
415
    igmpv3_group_record* getFirstGroupRecord() const;
416
417
    /// Get the group record that comes next to a given group record. If "groupRecord" is nullptr then nullptr will
418
    /// be returned. If "groupRecord" is the last group record or if it is out of layer bounds nullptr will be
419
    /// returned also. Notice the return value is a pointer to the real data casted to igmpv3_group_record type (as
420
    /// opposed to a copy of the option data). So changes in the return value will affect the packet data
421
    /// @param[in] groupRecord The group record to start searching from
422
    /// @return The next group record or nullptr if "groupRecord" is nullptr, last or out of layer bounds
423
    igmpv3_group_record* getNextGroupRecord(igmpv3_group_record* groupRecord) const;
424
425
    /// Add a new group record at a the end of the group record list. The igmpv3_report_header#numOfGroupRecords
426
    /// field will be incremented accordingly
427
    /// @param[in] recordType The type of the new group record
428
    /// @param[in] multicastAddress The multicast address of the new group record
429
    /// @param[in] sourceAddresses A vector containing all the source addresses of the new group record
430
    /// @return The method constructs a new group record, adds it to the end of the group record list of IGMPv3
431
    /// report message and returns a pointer to the new message. If something went wrong in creating or adding the
432
    /// new group record a nullptr value is returned and an appropriate error message is printed to log
433
    igmpv3_group_record* addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress,
434
                                        const std::vector<IPv4Address>& sourceAddresses);
435
436
    /// Add a new group record at a certain index of the group record list. The
437
    /// igmpv3_report_header#numOfGroupRecords field will be incremented accordingly
438
    /// @param[in] recordType The type of the new group record
439
    /// @param[in] multicastAddress The multicast address of the new group record
440
    /// @param[in] sourceAddresses A vector containing all the source addresses of the new group record
441
    /// @param[in] index The index to add the new group address at
442
    /// @return The method constructs a new group record, adds it to the IGMPv3 report message and returns a pointer
443
    /// to the new message. If something went wrong in creating or adding the new group record a nullptr value is
444
    /// returned and an appropriate error message is printed to log
445
    igmpv3_group_record* addGroupRecordAtIndex(uint8_t recordType, const IPv4Address& multicastAddress,
446
                                               const std::vector<IPv4Address>& sourceAddresses, int index);
447
448
    /// Remove a group record at a certain index. The igmpv3_report_header#numOfGroupRecords field will be
449
    /// decremented accordingly
450
    /// @param[in] index The index of the group record to be removed
451
    /// @return True if group record was removed successfully or false otherwise. If false is returned an
452
    /// appropriate error message will be printed to log
453
    bool removeGroupRecordAtIndex(int index);
454
455
    /// Remove all group records in the message. The igmpv3_report_header#numOfGroupRecords field will be set to 0
456
    /// @return True if all group records were cleared successfully or false otherwise. If false is returned an
457
    /// appropriate error message will be printed to log
458
    bool removeAllGroupRecords();
459
460
    // implement abstract methods
461
462
    /// Calculate the IGMP checksum
463
    void computeCalculateFields() override;
464
465
    /// @return The message size in bytes which include the size of the basic header + the size of the group record
466
    /// list
467
    size_t getHeaderLen() const override
468
690
    {
469
690
      return m_DataLen;
470
690
    }
471
472
    /// A static method that validates the input data
473
    /// @param[in] data The pointer to the beginning of a byte stream of an IGMPv3 Report layer
474
    /// @param[in] dataLen The length of the byte stream
475
    /// @return True if the data is valid and can represent an IGMPv3 Report layer
476
    static bool isDataValid(uint8_t const* data, size_t dataLen)
477
1.71k
    {
478
1.71k
      return canReinterpretAs<igmpv3_report_header>(data, dataLen);
479
1.71k
    }
480
  };
481
482
}  // namespace pcpp