Coverage Report

Created: 2026-09-28 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/IgmpLayer.cpp
Line
Count
Source
1
0
#define LOG_MODULE PacketLogModuleIgmpLayer
2
3
#include "IgmpLayer.h"
4
#include "PacketUtils.h"
5
#include "Logger.h"
6
#include "EndianPortable.h"
7
8
namespace pcpp
9
{
10
  // -------- Class IgmpLayer -----------------
11
12
  IgmpLayer::IgmpLayer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime, ProtocolType igmpVer)
13
0
  {
14
0
    allocData(getHeaderSizeByVerAndType(igmpVer, type));
15
0
    m_Protocol = igmpVer;
16
17
0
    setType(type);
18
0
    setGroupAddress(groupAddr);
19
20
0
    getIgmpHeader()->maxResponseTime = maxResponseTime;
21
0
  }
22
23
  void IgmpLayer::setGroupAddress(const IPv4Address& groupAddr)
24
0
  {
25
0
    igmp_header* hdr = getIgmpHeader();
26
0
    hdr->groupAddress = groupAddr.toInt();
27
0
  }
28
29
  IgmpType IgmpLayer::getType() const
30
5.92k
  {
31
5.92k
    uint8_t type = getIgmpHeader()->type;
32
5.92k
    if (type < (uint8_t)IgmpType_MembershipQuery ||
33
5.92k
        (type > (uint8_t)IgmpType_LeaveGroup && type < (uint8_t)IgmpType_MulticastTracerouteResponse) ||
34
5.92k
        (type > (uint8_t)IgmpType_MulticastTraceroute && type < (uint8_t)IgmpType_MembershipReportV3) ||
35
5.92k
        (type > (uint8_t)IgmpType_MembershipReportV3 && type < (uint8_t)IgmpType_MulticastRouterAdvertisement) ||
36
5.92k
        type > IgmpType_MulticastRouterTermination)
37
0
    {
38
0
      return IgmpType_Unknown;
39
0
    }
40
41
5.92k
    return (IgmpType)type;
42
5.92k
  }
43
44
  void IgmpLayer::setType(IgmpType type)
45
0
  {
46
0
    if (type == IgmpType_Unknown)
47
0
      return;
48
49
0
    igmp_header* hdr = getIgmpHeader();
50
0
    hdr->type = type;
51
0
  }
52
53
  ProtocolType IgmpLayer::getIGMPVerFromData(uint8_t* data, size_t dataLen, bool& isQuery)
54
14.8k
  {
55
14.8k
    isQuery = false;
56
57
14.8k
    if (dataLen < 8 || data == nullptr)
58
135
      return UnknownProtocol;
59
60
14.6k
    switch ((int)data[0])
61
14.6k
    {
62
1.46k
    case IgmpType_MembershipReportV2:
63
4.08k
    case IgmpType_LeaveGroup:
64
4.08k
      return IGMPv2;
65
1.35k
    case IgmpType_MembershipReportV1:
66
1.35k
      return IGMPv1;
67
2.02k
    case IgmpType_MembershipReportV3:
68
2.02k
      return IGMPv3;
69
6.82k
    case IgmpType_MembershipQuery:
70
6.82k
    {
71
6.82k
      isQuery = true;
72
73
6.82k
      if (dataLen >= sizeof(igmpv3_query_header))
74
1.85k
        return IGMPv3;
75
76
4.96k
      if (data[1] == 0)
77
1.75k
        return IGMPv1;
78
3.21k
      else
79
3.21k
        return IGMPv2;
80
4.96k
    }
81
379
    default:
82
379
      return UnknownProtocol;
83
14.6k
    }
84
14.6k
  }
85
86
  uint16_t IgmpLayer::calculateChecksum()
87
2.96k
  {
88
2.96k
    ScalarBuffer<uint16_t> buffer;
89
2.96k
    buffer.buffer = reinterpret_cast<uint16_t*>(getIgmpHeader());
90
2.96k
    buffer.len = getHeaderLen();
91
2.96k
    return computeChecksum(&buffer, 1);
92
2.96k
  }
93
94
  size_t IgmpLayer::getHeaderSizeByVerAndType(ProtocolType igmpVer, IgmpType igmpType) const
95
0
  {
96
0
    if (igmpVer == IGMPv1 || igmpVer == IGMPv2)
97
0
      return sizeof(igmp_header);
98
99
0
    if (igmpVer == IGMPv3)
100
0
    {
101
0
      if (igmpType == IgmpType_MembershipQuery)
102
0
        return sizeof(igmpv3_query_header);
103
0
      else if (igmpType == IgmpType_MembershipReportV3)
104
0
        return sizeof(igmpv3_report_header);
105
0
    }
106
107
0
    return 0;
108
0
  }
109
110
  std::string IgmpLayer::toString() const
111
5.92k
  {
112
5.92k
    std::string igmpVer = "";
113
5.92k
    switch (getProtocol())
114
5.92k
    {
115
1.26k
    case IGMPv1:
116
1.26k
      igmpVer = "1";
117
1.26k
      break;
118
2.93k
    case IGMPv2:
119
2.93k
      igmpVer = "2";
120
2.93k
      break;
121
1.73k
    default:
122
1.73k
      igmpVer = "3";
123
5.92k
    }
124
125
5.92k
    std::string msgType;
126
127
5.92k
    switch (getType())
128
5.92k
    {
129
2.91k
    case IgmpType_MembershipQuery:
130
2.91k
      msgType = "Membership Query";
131
2.91k
      break;
132
560
    case IgmpType_MembershipReportV1:
133
560
      msgType = "Membership Report";
134
560
      break;
135
0
    case IgmpType_DVMRP:
136
0
      msgType = "DVMRP";
137
0
      break;
138
0
    case IgmpType_P1Mv1:
139
0
      msgType = "PIMv1";
140
0
      break;
141
0
    case IgmpType_CiscoTrace:
142
0
      msgType = "Cisco Trace";
143
0
      break;
144
592
    case IgmpType_MembershipReportV2:
145
592
      msgType = "Membership Report";
146
592
      break;
147
1.05k
    case IgmpType_LeaveGroup:
148
1.05k
      msgType = "Leave Group";
149
1.05k
      break;
150
0
    case IgmpType_MulticastTracerouteResponse:
151
0
      msgType = "Multicast Traceroute Response";
152
0
      break;
153
0
    case IgmpType_MulticastTraceroute:
154
0
      msgType = "Multicast Traceroute";
155
0
      break;
156
812
    case IgmpType_MembershipReportV3:
157
812
      msgType = "Membership Report";
158
812
      break;
159
0
    case IgmpType_MulticastRouterAdvertisement:
160
0
      msgType = "Multicast Router Advertisement";
161
0
      break;
162
0
    case IgmpType_MulticastRouterSolicitation:
163
0
      msgType = "Multicast Router Solicitation";
164
0
      break;
165
0
    case IgmpType_MulticastRouterTermination:
166
0
      msgType = "Multicast Router Termination";
167
0
      break;
168
0
    default:
169
0
      msgType = "Unknown";
170
0
      break;
171
5.92k
    }
172
173
5.92k
    std::string result = "IGMPv" + igmpVer + " Layer, " + msgType + " message";
174
5.92k
    return result;
175
5.92k
  }
176
177
  // -------- Class IgmpV1Layer -----------------
178
179
  void IgmpV1Layer::computeCalculateFields()
180
630
  {
181
630
    igmp_header* hdr = getIgmpHeader();
182
630
    hdr->checksum = 0;
183
630
    hdr->checksum = htobe16(calculateChecksum());
184
630
    hdr->maxResponseTime = 0;
185
630
  }
186
187
  // -------- Class IgmpV2Layer -----------------
188
189
  void IgmpV2Layer::computeCalculateFields()
190
1.46k
  {
191
1.46k
    igmp_header* hdr = getIgmpHeader();
192
1.46k
    hdr->checksum = 0;
193
1.46k
    hdr->checksum = htobe16(calculateChecksum());
194
1.46k
  }
195
196
  // -------- Class IgmpV3QueryLayer -----------------
197
198
  IgmpV3QueryLayer::IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
199
1.85k
      : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3)
200
1.85k
  {}
201
202
  IgmpV3QueryLayer::IgmpV3QueryLayer(const IPv4Address& multicastAddr, uint8_t maxResponseTime, uint8_t s_qrv)
203
0
      : IgmpLayer(IgmpType_MembershipQuery, multicastAddr, maxResponseTime, IGMPv3)
204
0
  {
205
0
    getIgmpV3QueryHeader()->s_qrv = s_qrv;
206
0
  }
207
208
  uint16_t IgmpV3QueryLayer::getSourceAddressCount() const
209
926
  {
210
926
    return be16toh(getIgmpV3QueryHeader()->numOfSources);
211
926
  }
212
213
  IPv4Address IgmpV3QueryLayer::getSourceAddressAtIndex(int index) const
214
0
  {
215
0
    uint16_t numOfSources = getSourceAddressCount();
216
0
    if (index < 0 || index >= numOfSources)
217
0
      return IPv4Address();
218
219
    // verify numOfRecords is a reasonable number that points to data within the packet
220
0
    int ptrOffset = index * sizeof(uint32_t) + sizeof(igmpv3_query_header);
221
0
    if (ptrOffset + sizeof(uint32_t) > getDataLen())
222
0
      return IPv4Address();
223
224
0
    uint8_t* ptr = m_Data + ptrOffset;
225
0
    return IPv4Address(*reinterpret_cast<uint32_t*>(ptr));
226
0
  }
227
228
  size_t IgmpV3QueryLayer::getHeaderLen() const
229
926
  {
230
926
    uint16_t numOfSources = getSourceAddressCount();
231
232
926
    int headerLen = numOfSources * sizeof(uint32_t) + sizeof(igmpv3_query_header);
233
234
    // verify numOfRecords is a reasonable number that points to data within the packet
235
926
    if ((size_t)headerLen > getDataLen())
236
150
      return getDataLen();
237
238
776
    return (size_t)headerLen;
239
926
  }
240
241
  void IgmpV3QueryLayer::computeCalculateFields()
242
463
  {
243
463
    igmpv3_query_header* hdr = getIgmpV3QueryHeader();
244
463
    hdr->checksum = 0;
245
463
    hdr->checksum = htobe16(calculateChecksum());
246
463
  }
247
248
  bool IgmpV3QueryLayer::addSourceAddress(const IPv4Address& addr)
249
0
  {
250
0
    return addSourceAddressAtIndex(addr, getSourceAddressCount());
251
0
  }
252
253
  bool IgmpV3QueryLayer::addSourceAddressAtIndex(const IPv4Address& addr, int index)
254
0
  {
255
0
    uint16_t sourceAddrCount = getSourceAddressCount();
256
257
0
    if (index < 0 || index > static_cast<int>(sourceAddrCount))
258
0
    {
259
0
      PCPP_LOG_ERROR("Cannot add source address at index " << index << ", index is out of bounds");
260
0
      return false;
261
0
    }
262
263
0
    size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t);
264
0
    if (offset > getHeaderLen())
265
0
    {
266
0
      PCPP_LOG_ERROR("Cannot add source address at index " << index << ", index is out of packet bounds");
267
0
      return false;
268
0
    }
269
270
0
    if (!extendLayer(offset, sizeof(uint32_t)))
271
0
    {
272
0
      PCPP_LOG_ERROR("Cannot add source address at index " << index << ", didn't manage to extend layer");
273
0
      return false;
274
0
    }
275
276
0
    memcpy(m_Data + offset, addr.toBytes(), sizeof(uint32_t));
277
278
0
    getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount + 1);
279
280
0
    return true;
281
0
  }
282
283
  bool IgmpV3QueryLayer::removeSourceAddressAtIndex(int index)
284
0
  {
285
0
    uint16_t sourceAddrCount = getSourceAddressCount();
286
287
0
    if (index < 0 || index > static_cast<int>(sourceAddrCount) - 1)
288
0
    {
289
0
      PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", index is out of bounds");
290
0
      return false;
291
0
    }
292
293
0
    size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t);
294
0
    if (offset >= getHeaderLen())
295
0
    {
296
0
      PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", index is out of packet bounds");
297
0
      return false;
298
0
    }
299
300
0
    if (!shortenLayer(offset, sizeof(uint32_t)))
301
0
    {
302
0
      PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", didn't manage to shorten layer");
303
0
      return false;
304
0
    }
305
306
0
    getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount - 1);
307
308
0
    return true;
309
0
  }
310
311
  bool IgmpV3QueryLayer::removeAllSourceAddresses()
312
0
  {
313
0
    size_t offset = sizeof(igmpv3_query_header);
314
0
    size_t numOfBytesToShorted = getHeaderLen() - offset;
315
316
0
    if (!shortenLayer(offset, numOfBytesToShorted))
317
0
    {
318
0
      PCPP_LOG_ERROR("Cannot remove all source addresses, didn't manage to shorten layer");
319
0
      return false;
320
0
    }
321
322
0
    getIgmpV3QueryHeader()->numOfSources = 0;
323
324
0
    return true;
325
0
  }
326
327
  // -------- Class IgmpV3ReportLayer -----------------
328
329
  uint16_t IgmpV3ReportLayer::getGroupRecordCount() const
330
0
  {
331
0
    return be16toh(getReportHeader()->numOfGroupRecords);
332
0
  }
333
334
  igmpv3_group_record* IgmpV3ReportLayer::getFirstGroupRecord() const
335
0
  {
336
    // a group record is only usable if its fixed part fits inside the layer, otherwise reading
337
    // the record type, the source count or the multicast address runs past the end of the data
338
0
    if (getHeaderLen() < sizeof(igmpv3_report_header) + sizeof(igmpv3_group_record))
339
0
      return nullptr;
340
341
0
    uint8_t* curGroupPtr = m_Data + sizeof(igmpv3_report_header);
342
0
    return reinterpret_cast<igmpv3_group_record*>(curGroupPtr);
343
0
  }
344
345
  igmpv3_group_record* IgmpV3ReportLayer::getNextGroupRecord(igmpv3_group_record* groupRecord) const
346
0
  {
347
0
    if (groupRecord == nullptr)
348
0
      return nullptr;
349
350
0
    size_t recordOffset = static_cast<size_t>(reinterpret_cast<uint8_t*>(groupRecord) - m_Data);
351
0
    size_t nextOffset = recordOffset + groupRecord->getRecordLen();
352
353
    // checking only that the next record starts inside the layer leaves a trailing partial
354
    // record reachable, which the caller then reads in full
355
0
    if (nextOffset + sizeof(igmpv3_group_record) > getHeaderLen())
356
0
    {
357
0
      return nullptr;
358
0
    }
359
360
0
    return reinterpret_cast<igmpv3_group_record*>(m_Data + nextOffset);
361
0
  }
362
363
  void IgmpV3ReportLayer::computeCalculateFields()
364
406
  {
365
406
    igmpv3_report_header* hdr = getReportHeader();
366
406
    hdr->checksum = 0;
367
406
    hdr->checksum = htobe16(calculateChecksum());
368
406
  }
369
370
  igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress,
371
                                                           const std::vector<IPv4Address>& sourceAddresses,
372
                                                           int offset)
373
0
  {
374
0
    if (offset > static_cast<int>(getHeaderLen()))
375
0
    {
376
0
      PCPP_LOG_ERROR("Cannot add group record, offset is out of layer bounds");
377
0
      return nullptr;
378
0
    }
379
380
0
    size_t groupRecordSize = sizeof(igmpv3_group_record) + sizeof(uint32_t) * sourceAddresses.size();
381
382
0
    if (!extendLayer(offset, groupRecordSize))
383
0
    {
384
0
      PCPP_LOG_ERROR("Cannot add group record, cannot extend layer");
385
0
      return nullptr;
386
0
    }
387
388
0
    uint8_t* groupRecordBuffer = new uint8_t[groupRecordSize];
389
0
    memset(groupRecordBuffer, 0, groupRecordSize);
390
0
    igmpv3_group_record* newGroupRecord = reinterpret_cast<igmpv3_group_record*>(groupRecordBuffer);
391
0
    newGroupRecord->multicastAddress = multicastAddress.toInt();
392
0
    newGroupRecord->recordType = recordType;
393
0
    newGroupRecord->auxDataLen = 0;
394
0
    newGroupRecord->numOfSources = htobe16(sourceAddresses.size());
395
396
0
    int srcAddrOffset = 0;
397
0
    for (const auto& address : sourceAddresses)
398
0
    {
399
0
      memcpy(newGroupRecord->sourceAddresses + srcAddrOffset, address.toBytes(), sizeof(uint32_t));
400
0
      srcAddrOffset += sizeof(uint32_t);
401
0
    }
402
403
0
    memcpy(m_Data + offset, groupRecordBuffer, groupRecordSize);
404
405
0
    delete[] groupRecordBuffer;
406
407
0
    getReportHeader()->numOfGroupRecords = htobe16(getGroupRecordCount() + 1);
408
409
0
    return reinterpret_cast<igmpv3_group_record*>(m_Data + offset);
410
0
  }
411
412
  igmpv3_group_record* IgmpV3ReportLayer::addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress,
413
                                                         const std::vector<IPv4Address>& sourceAddresses)
414
0
  {
415
0
    return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, static_cast<int>(getHeaderLen()));
416
0
  }
417
418
  igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAtIndex(uint8_t recordType,
419
                                                                const IPv4Address& multicastAddress,
420
                                                                const std::vector<IPv4Address>& sourceAddresses,
421
                                                                int index)
422
0
  {
423
0
    int groupCnt = (int)getGroupRecordCount();
424
425
0
    if (index < 0 || index > groupCnt)
426
0
    {
427
0
      PCPP_LOG_ERROR("Cannot add group record, index " << index << " out of bounds");
428
0
      return nullptr;
429
0
    }
430
431
0
    size_t offset = sizeof(igmpv3_report_header);
432
433
0
    igmpv3_group_record* curRecord = getFirstGroupRecord();
434
0
    for (int i = 0; i < index; i++)
435
0
    {
436
0
      if (curRecord == nullptr)
437
0
      {
438
0
        PCPP_LOG_ERROR("Cannot add group record, cannot find group record at index " << i);
439
0
        return nullptr;
440
0
      }
441
442
0
      offset += curRecord->getRecordLen();
443
0
      curRecord = getNextGroupRecord(curRecord);
444
0
    }
445
446
0
    return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, (int)offset);
447
0
  }
448
449
  bool IgmpV3ReportLayer::removeGroupRecordAtIndex(int index)
450
0
  {
451
0
    int groupCnt = (int)getGroupRecordCount();
452
453
0
    if (index < 0 || index >= groupCnt)
454
0
    {
455
0
      PCPP_LOG_ERROR("Cannot remove group record, index " << index << " is out of bounds");
456
0
      return false;
457
0
    }
458
459
0
    size_t offset = sizeof(igmpv3_report_header);
460
461
0
    igmpv3_group_record* curRecord = getFirstGroupRecord();
462
0
    for (int i = 0; i < index; i++)
463
0
    {
464
0
      if (curRecord == nullptr)
465
0
      {
466
0
        PCPP_LOG_ERROR("Cannot remove group record at index " << index << ", cannot find group record at index "
467
0
                                                              << i);
468
0
        return false;
469
0
      }
470
471
0
      offset += curRecord->getRecordLen();
472
0
      curRecord = getNextGroupRecord(curRecord);
473
0
    }
474
475
0
    if (!shortenLayer((int)offset, curRecord->getRecordLen()))
476
0
    {
477
0
      PCPP_LOG_ERROR("Cannot remove group record at index " << index << ", cannot shorted layer");
478
0
      return false;
479
0
    }
480
481
0
    getReportHeader()->numOfGroupRecords = htobe16(groupCnt - 1);
482
483
0
    return true;
484
0
  }
485
486
  bool IgmpV3ReportLayer::removeAllGroupRecords()
487
0
  {
488
0
    int offset = (int)sizeof(igmpv3_report_header);
489
490
0
    if (!shortenLayer(offset, getHeaderLen() - offset))
491
0
    {
492
0
      PCPP_LOG_ERROR("Cannot remove all group records, cannot shorted layer");
493
0
      return false;
494
0
    }
495
496
0
    getReportHeader()->numOfGroupRecords = 0;
497
0
    return true;
498
0
  }
499
500
  // -------- Struct igmpv3_group_record -----------------
501
502
  uint16_t igmpv3_group_record::getSourceAddressCount() const
503
0
  {
504
0
    return be16toh(numOfSources);
505
0
  }
506
507
  IPv4Address igmpv3_group_record::getSourceAddressAtIndex(int index) const
508
0
  {
509
0
    uint16_t numOfRecords = getSourceAddressCount();
510
0
    if (index < 0 || index >= numOfRecords)
511
0
      return IPv4Address();
512
513
0
    int offset = index * sizeof(uint32_t);
514
0
    const uint8_t* ptr = sourceAddresses + offset;
515
0
    return IPv4Address(*reinterpret_cast<const uint32_t*>(ptr));
516
0
  }
517
518
  size_t igmpv3_group_record::getRecordLen() const
519
0
  {
520
0
    uint16_t numOfRecords = getSourceAddressCount();
521
522
0
    int headerLen = numOfRecords * sizeof(uint32_t) + sizeof(igmpv3_group_record);
523
0
    return (size_t)headerLen;
524
0
  }
525
}  // namespace pcpp