Coverage Report

Created: 2025-10-10 07:28

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