Coverage Report

Created: 2026-11-12 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/header/IcmpLayer.h
Line
Count
Source
1
#pragma once
2
3
#include "Layer.h"
4
#include "IPv4Layer.h"
5
#ifdef _MSC_VER
6
# include <winsock2.h>
7
#else
8
# include <sys/time.h>
9
#endif
10
#include <vector>
11
12
/// @file
13
14
/// @namespace pcpp
15
/// @brief The main namespace for the PcapPlusPlus lib
16
namespace pcpp
17
{
18
19
  /// @struct icmphdr
20
  /// Represents ICMP basic protocol header (common for all ICMP message types)
21
#pragma pack(push, 1)
22
  typedef struct icmphdr
23
  {
24
    /// message type
25
    uint8_t type;
26
    /// message code
27
    uint8_t code;
28
    /// message checksum
29
    uint16_t checksum;
30
  } icmphdr;
31
#pragma pack(pop)
32
  static_assert(sizeof(icmphdr) == 4, "icmphdr size is not 4 bytes");
33
34
  /// An enum of all supported ICMP message types
35
  enum IcmpMessageType
36
  {
37
    /// ICMP echo (ping) reply message
38
    ICMP_ECHO_REPLY = 0,
39
    /// ICMP destination unreachable message
40
    ICMP_DEST_UNREACHABLE = 3,
41
    /// ICMP source quench message
42
    ICMP_SOURCE_QUENCH = 4,
43
    /// ICMP redirect message
44
    ICMP_REDIRECT = 5,
45
    /// ICMP echo (ping) request message
46
    ICMP_ECHO_REQUEST = 8,
47
    /// ICMP router advertisement message
48
    ICMP_ROUTER_ADV = 9,
49
    /// ICMP router soliciatation message
50
    ICMP_ROUTER_SOL = 10,
51
    /// ICMP time-to-live excceded message
52
    ICMP_TIME_EXCEEDED = 11,
53
    /// ICMP parameter problem message
54
    ICMP_PARAM_PROBLEM = 12,
55
    /// ICMP timestamp request message
56
    ICMP_TIMESTAMP_REQUEST = 13,
57
    /// ICMP timestamp reply message
58
    ICMP_TIMESTAMP_REPLY = 14,
59
    /// ICMP information request message
60
    ICMP_INFO_REQUEST = 15,
61
    /// ICMP information reply message
62
    ICMP_INFO_REPLY = 16,
63
    /// ICMP address mask request message
64
    ICMP_ADDRESS_MASK_REQUEST = 17,
65
    /// ICMP address mask reply message
66
    ICMP_ADDRESS_MASK_REPLY = 18,
67
    /// ICMP message type unsupported by PcapPlusPlus
68
    ICMP_UNSUPPORTED = 255
69
  };
70
71
  /// An enum for all possible codes for a destination unreachable message type
72
  /// Documentation is taken from Wikipedia: https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol
73
  enum IcmpDestUnreachableCodes
74
  {
75
    /// Network unreachable error
76
    IcmpNetworkUnreachable = 0,
77
    /// Host unreachable error
78
    IcmpHostUnreachable = 1,
79
    /// Protocol unreachable error (the designated transport protocol is not supported)
80
    IcmpProtocolUnreachable = 2,
81
    /// Port unreachable error (the designated protocol is unable to inform the host of the incoming message)
82
    IcmpPortUnreachable = 3,
83
    /// The datagram is too big. Packet fragmentation is required but the 'don't fragment' (DF) flag is on
84
    IcmpDatagramTooBig = 4,
85
    /// Source route failed error
86
    IcmpSourceRouteFailed = 5,
87
    /// Destination network unknown error
88
    IcmpDestinationNetworkUnknown = 6,
89
    /// Destination host unknown error
90
    IcmpDestinationHostUnknown = 7,
91
    /// Source host isolated error
92
    IcmpSourceHostIsolated = 8,
93
    /// The destination network is administratively prohibited
94
    IcmpDestinationNetworkProhibited = 9,
95
    /// The destination host is administratively prohibited
96
    IcmpDestinationHostProhibited = 10,
97
    /// The network is unreachable for Type Of Service
98
    IcmpNetworkUnreachableForTypeOfService = 11,
99
    /// The host is unreachable for Type Of Service
100
    IcmpHostUnreachableForTypeOfService = 12,
101
    /// Communication administratively prohibited (administrative filtering prevents
102
    /// packet from being forwarded)
103
    IcmpCommunicationProhibited = 13,
104
    /// Host precedence violation (indicates the requested precedence is not permitted for
105
    /// the combination of host or network and port)
106
    IcmpHostPrecedenceViolation = 14,
107
    /// Precedence cutoff in effect (precedence of datagram is below the level set by
108
    /// the network administrators)
109
    IcmpPrecedenceCutoff = 15
110
  };
111
112
  /// @struct icmp_echo_hdr
113
  /// ICMP echo (ping) request/reply message structure
114
#pragma pack(push, 1)
115
  typedef struct icmp_echo_hdr : icmphdr
116
  {
117
    /// the echo (ping) request identifier
118
    uint16_t id;
119
    /// the echo (ping) request sequence number
120
    uint16_t sequence;
121
    /// a timestamp of when the message was sent
122
    uint64_t timestamp;
123
  } icmp_echo_hdr;
124
#pragma pack(pop)
125
  static_assert(sizeof(icmp_echo_hdr) == 16, "icmp_echo_hdr size is not 16 bytes");
126
127
  /// @struct icmp_echo_request
128
  /// ICMP echo (ping) request/reply message structure
129
  typedef struct icmp_echo_request
130
  {
131
    /// a pointer to the header data
132
    icmp_echo_hdr* header;
133
    /// most echo requests/replies contain some payload data. This is the data length
134
    size_t dataLength;
135
    /// most echo requests/replies contain some payload data. This is a pointer to this data
136
    uint8_t* data;
137
  } icmp_echo_request;
138
139
  /// @typedef icmp_echo_reply
140
  /// ICMP echo (ping) reply message structure, same as icmp_echo_request
141
  typedef icmp_echo_request icmp_echo_reply;
142
143
  /// @struct icmp_timestamp_request
144
  /// ICMP timestamp request message structure
145
#pragma pack(push, 1)
146
  typedef struct icmp_timestamp_request : icmphdr
147
  {
148
    /// the timestamp request identifier
149
    uint16_t id;
150
    /// the timestamp request sequence number
151
    uint16_t sequence;
152
    /// the time (in milliseconds since midnight) the sender last touched the packet
153
    uint32_t originateTimestamp;
154
    /// relevant for timestamp reply only - the time the echoer first touched it on receipt
155
    uint32_t receiveTimestamp;
156
    /// relevant for timestamp reply only - the time the echoer last touched the message on sending it
157
    uint32_t transmitTimestamp;
158
  } icmp_timestamp_request;
159
#pragma pack(pop)
160
  static_assert(sizeof(icmp_timestamp_request) == 20, "icmp_timestamp_request size is not 20 bytes");
161
162
  /// @typedef icmp_timestamp_reply
163
  /// ICMP timestamp reply message structure, same as icmp_timestamp_request
164
  typedef icmp_timestamp_request icmp_timestamp_reply;
165
  static_assert(sizeof(icmp_timestamp_reply) == 20, "icmp_timestamp_reply size is not 20 bytes");
166
167
  /// @struct icmp_destination_unreachable
168
  /// ICMP destination unreachable message structure
169
#pragma pack(push, 1)
170
  typedef struct icmp_destination_unreachable : icmphdr
171
  {
172
    /// unused 2 bytes
173
    uint16_t unused;
174
    /// contains the MTU of the next-hop network if a code 4 error occurs
175
    uint16_t nextHopMTU;
176
  } icmp_destination_unreachable;
177
#pragma pack(pop)
178
  static_assert(sizeof(icmp_destination_unreachable) == 8, "icmp_destination_unreachable size is not 8 bytes");
179
180
  /// @struct icmp_time_exceeded
181
  /// ICMP time-to-live exceeded message structure
182
#pragma pack(push, 1)
183
  typedef struct icmp_time_exceeded : icmphdr
184
  {
185
    /// unused 4 bytes
186
    uint32_t unused;
187
  } icmp_time_exceeded;
188
#pragma pack(pop)
189
  static_assert(sizeof(icmp_time_exceeded) == 8, "icmp_time_exceeded size is not 8 bytes");
190
191
  /// @typedef icmp_source_quench
192
  /// ICMP source quence message structure, same as icmp_time_exceeded
193
  typedef icmp_time_exceeded icmp_source_quench;
194
  static_assert(sizeof(icmp_source_quench) == 8, "icmp_source_quench size is not 8 bytes");
195
196
  /// @struct icmp_param_problem
197
  /// ICMP parameter problem message structure
198
#pragma pack(push, 1)
199
  typedef struct icmp_param_problem : icmphdr
200
  {
201
    /// in the case of an invalid IP header (Code 0), this field indicates the byte offset of the error in the
202
    /// header
203
    uint8_t pointer;
204
    /// unused 1 byte
205
    uint8_t unused1;
206
    /// unused 2 bytes
207
    uint16_t unused2;
208
  } icmp_param_problem;
209
#pragma pack(pop)
210
  static_assert(sizeof(icmp_param_problem) == 8, "icmp_param_problem size is not 8 bytes");
211
212
  /// @typedef icmp_router_solicitation
213
  /// ICMP router solicitation message structure, same as icmphdr
214
  typedef icmphdr icmp_router_solicitation;
215
  static_assert(sizeof(icmp_router_solicitation) == 4, "icmp_router_solicitation size is not 4 bytes");
216
217
  /// @struct icmp_redirect
218
  /// ICMP redirect message structure
219
#pragma pack(push, 1)
220
  typedef struct icmp_redirect : icmphdr
221
  {
222
    /// an IPv4 address of the gateway to which the redirection should be sent
223
    uint32_t gatewayAddress;
224
  } icmp_redirect;
225
#pragma pack(pop)
226
  static_assert(sizeof(icmp_redirect) == 8, "icmp_redirect size is not 8 bytes");
227
228
  /// @struct icmp_router_address_structure
229
  /// Router address structure, relevant for ICMP router advertisement message type (icmp_router_advertisement)
230
#pragma pack(push, 1)
231
  struct icmp_router_address_structure
232
  {
233
    /// the IPv4 address of the advertised router
234
    uint32_t routerAddress;
235
    /// The preferability of the router address as a default router address, relative to other router addresses
236
    /// on the same subnet. This is a twos-complement value where higher values indicate that the route is
237
    /// more preferable
238
    uint32_t preferenceLevel;
239
240
    /// Set router address structure from a given IPv4 address and preference level
241
    /// @param[in] addr IPv4 address to set
242
    /// @param[in] preference Preference level to set
243
    void setRouterAddress(IPv4Address addr, uint32_t preference);
244
245
    /// @return The IPv4 address extracted from icmp_router_address_structure#routerAddress field
246
    IPv4Address getAddress() const
247
0
    {
248
0
      return routerAddress;
249
0
    }
250
  };
251
#pragma pack(pop)
252
  static_assert(sizeof(icmp_router_address_structure) == 8, "icmp_router_address_structure size is not 8 bytes");
253
254
  /// @struct icmp_router_advertisement_hdr
255
  /// ICMP router advertisement message structure
256
#pragma pack(push, 1)
257
  typedef struct icmp_router_advertisement_hdr : icmphdr
258
  {
259
    /// the number of router advertisements in this message. Each advertisement contains one router
260
    /// address/preference level pair
261
    uint8_t advertisementCount;
262
    /// the number of 32-bit words of information for each router address entry in the list. The value is normally
263
    /// set to 2 (router address + preference level)
264
    uint8_t addressEntrySize;
265
    /// the maximum number of seconds that the router addresses in this list may be considered valid
266
    uint16_t lifetime;
267
  } icmp_router_advertisement_hdr;
268
#pragma pack(pop)
269
  static_assert(sizeof(icmp_router_advertisement_hdr) == 8, "icmp_router_advertisement_hdr size is not 8 bytes");
270
271
  /// @struct icmp_router_advertisement
272
  /// ICMP router advertisement message structure
273
  struct icmp_router_advertisement
274
  {
275
    /// a pointer to the header data on the packet
276
    icmp_router_advertisement_hdr* header = nullptr;
277
278
    /// The number of bytes available for this message, meaning the fixed header and the router address entries.
279
    /// This value is set by IcmpLayer and is used to verify that a requested entry actually exists in the layer
280
    size_t dataLen = 0;
281
282
    /// Extract router advertisement at a given index
283
    /// @param[in] index The index of the router advertisement
284
    /// @return A pointer to the router advertisement on the packet or null if index is out of range: less than
285
    /// zero, greater than or equal to the number of router advertisement records on this message (determined by
286
    /// advertisementCount field), or pointing to an entry that is not present in the layer data
287
    icmp_router_address_structure* getRouterAddress(int index) const;
288
  };
289
290
  /// @struct icmp_address_mask_request
291
  /// ICMP address mask request message structure
292
#pragma pack(push, 1)
293
  typedef struct icmp_address_mask_request : icmphdr
294
  {
295
    /// the address mask request identifier
296
    uint16_t id;
297
    /// the address mask request sequence
298
    uint16_t sequence;
299
    /// the subnet mask of the requesting host
300
    uint32_t addressMask;
301
  } icmp_address_mask_request;
302
#pragma pack(pop)
303
  static_assert(sizeof(icmp_address_mask_request) == 12, "icmp_address_mask_request size is not 12 bytes");
304
305
  /// @typedef icmp_address_mask_reply
306
  /// ICMP address mask reply message structure, same as icmp_address_mask_request
307
  typedef icmp_address_mask_request icmp_address_mask_reply;
308
309
  /// @struct icmp_info_request
310
  /// ICMP information request message structure
311
#pragma pack(push, 1)
312
  typedef struct icmp_info_request : icmphdr
313
  {
314
    /// the information request identifier
315
    uint16_t id;
316
    /// the information request sequence
317
    uint16_t sequence;
318
  } icmp_info_request;
319
#pragma pack(pop)
320
  static_assert(sizeof(icmp_info_request) == 8, "icmp_info_request size is not 8 bytes");
321
322
  /// @typedef icmp_info_reply
323
  /// ICMP information reply message structure, same as icmp_info_request
324
  typedef icmp_info_request icmp_info_reply;
325
  static_assert(sizeof(icmp_info_reply) == 8, "icmp_info_reply size is not 8 bytes");
326
327
  /// @class IcmpLayer
328
  /// Represents an ICMP protocol layer (for IPv4 only)
329
  class IcmpLayer : public Layer
330
  {
331
  private:
332
    icmp_echo_request m_EchoData;
333
    mutable icmp_router_advertisement m_RouterAdvData;
334
335
    bool cleanIcmpLayer();
336
337
    bool setEchoData(IcmpMessageType echoType, uint16_t id, uint16_t sequence, uint64_t timestamp,
338
                     const uint8_t* data, size_t dataLen);
339
340
    bool setIpAndL4Layers(IPv4Layer* ipLayer, Layer* l4Layer);
341
342
  public:
343
    /// A constructor that creates the layer from an existing packet raw data
344
    /// @param[in] data A pointer to the raw data (will be casted to @ref arphdr)
345
    /// @param[in] dataLen Size of the data in bytes
346
    /// @param[in] prevLayer A pointer to the previous layer
347
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
348
    // cppcheck-suppress uninitMemberVar
349
    IcmpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
350
0
        : Layer(data, dataLen, prevLayer, packet, ICMP)
351
0
    {}
352
353
    /// An empty constructor that creates a new layer with an empty ICMP header without setting the ICMP type or
354
    /// ICMP data. Call the set*Data() methods to set ICMP type and data
355
    IcmpLayer();
356
357
    ~IcmpLayer() override = default;
358
359
    /// Get a pointer to the basic ICMP header. Notice this points directly to the data, so every change will change
360
    /// the actual packet data
361
    /// @return A pointer to the @ref icmphdr
362
    icmphdr* getIcmpHeader() const
363
0
    {
364
0
      return reinterpret_cast<icmphdr*>(m_Data);
365
0
    }
366
367
    /// @return The ICMP message type
368
    IcmpMessageType getMessageType() const;
369
370
    /// @param[in] type Type to check
371
    /// @return True if the layer if of the given type, false otherwise
372
    bool isMessageOfType(IcmpMessageType type) const
373
0
    {
374
0
      return getMessageType() == type;
375
0
    }
376
377
    /// @return ICMP echo (ping) request data. If the layer isn't of type ICMP echo request nullptr is returned
378
    icmp_echo_request* getEchoRequestData();
379
380
    /// Set echo (ping) request message data
381
    /// @param[in] id Echo (ping) request identifier
382
    /// @param[in] sequence Echo (ping) request sequence
383
    /// @param[in] timestamp Echo (ping) request timestamp
384
    /// @param[in] data A pointer to echo (ping) request payload to set
385
    /// @param[in] dataLen The length of the echo (ping) request payload
386
    /// @return A pointer to the echo (ping) request data that have been set or nullptr if something went wrong
387
    /// (an appropriate error log is printed in such cases)
388
    icmp_echo_request* setEchoRequestData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data,
389
                                          size_t dataLen);
390
391
    /// @return ICMP echo reply data. If the layer isn't of type ICMP echo reply nullptr is returned
392
    icmp_echo_reply* getEchoReplyData();
393
394
    /// Set echo (ping) reply message data
395
    /// @param[in] id Echo (ping) reply identifier
396
    /// @param[in] sequence Echo (ping) reply sequence
397
    /// @param[in] timestamp Echo (ping) reply timestamp
398
    /// @param[in] data A pointer to echo (ping) reply payload to set
399
    /// @param[in] dataLen The length of the echo (ping) reply payload
400
    /// @return A pointer to the echo (ping) reply data that have been set or nullptr if something went wrong
401
    /// (an appropriate error log is printed in such cases)
402
    icmp_echo_reply* setEchoReplyData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data,
403
                                      size_t dataLen);
404
405
    /// @return ICMP timestamp request data. If the layer isn't of type ICMP timestamp request nullptr is returned
406
    icmp_timestamp_request* getTimestampRequestData();
407
408
    /// Set timestamp request message data
409
    /// @param[in] id Timestamp request identifier
410
    /// @param[in] sequence Timestamp request sequence
411
    /// @param[in] originateTimestamp Time (in milliseconds since midnight) the sender last touched the packet
412
    /// @return A pointer to the timestamp request data that have been set or nullptr if something went wrong
413
    /// (an appropriate error log is printed in such cases)
414
    icmp_timestamp_request* setTimestampRequestData(uint16_t id, uint16_t sequence, timeval originateTimestamp);
415
416
    /// @return ICMP timestamp reply data. If the layer isn't of type ICMP timestamp reply nullptr is returned
417
    icmp_timestamp_reply* getTimestampReplyData();
418
419
    /// Set timestamp reply message data
420
    /// @param[in] id Timestamp reply identifier
421
    /// @param[in] sequence Timestamp reply sequence
422
    /// @param[in] originateTimestamp Time (in milliseconds since midnight) the sender last touched the packet
423
    /// @param[in] receiveTimestamp The time the echoer first touched it on receipt
424
    /// @param[in] transmitTimestamp The time the echoer last touched the message on sending it
425
    /// @return A pointer to the timestamp reply data that have been set or nullptr if something went wrong
426
    /// (an appropriate error log is printed in such cases)
427
    icmp_timestamp_reply* setTimestampReplyData(uint16_t id, uint16_t sequence, timeval originateTimestamp,
428
                                                timeval receiveTimestamp, timeval transmitTimestamp);
429
430
    /// @return ICMP destination unreachable data. If the layer isn't of type ICMP destination unreachable nullptr
431
    /// is returned. The IP and L4 (ICMP/TCP/UDP) headers of the destination unreachable data are parsed as separate
432
    /// layers and can be retrieved via this->getNextLayer()
433
    icmp_destination_unreachable* getDestUnreachableData();
434
435
    /// Set destination unreachable message data. This method only works if IcmpLayer is already part of a packet
436
    /// (not a standalone layer). The reason is the Internet and L4 headers given as parameters are added as
437
    /// separate layers and need a packet to be added to
438
    /// @param[in] code Destination unreachable code
439
    /// @param[in] nextHopMTU The MTU of the next-hop network if a code 4 error occurs
440
    /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the
441
    /// packet
442
    /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the
443
    /// packet
444
    /// @return A pointer to the destination unreachable data that have been set or nullptr if something went wrong
445
    /// (an appropriate error log is printed in such cases)
446
    icmp_destination_unreachable* setDestUnreachableData(IcmpDestUnreachableCodes code, uint16_t nextHopMTU,
447
                                                         IPv4Layer* ipHeader, Layer* l4Header);
448
449
    /// @return ICMP source quench data. If the layer isn't of type ICMP source quench nullptr is returned.
450
    /// The IP and L4 (ICMP/TCP/UDP) headers of the source quench data are parsed as separate layers and can be
451
    /// retrieved via this->getNextLayer()
452
    icmp_source_quench* getSourceQuenchdata();
453
454
    /// Set source quench message data. This method only works if IcmpLayer is already part of a packet (not
455
    /// a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate
456
    /// layers and need a packet to be added to
457
    /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the
458
    /// packet
459
    /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the
460
    /// packet
461
    /// @return A pointer to the source quench data that have been set or nullptr if something went wrong
462
    /// (an appropriate error log is printed in such cases)
463
    icmp_source_quench* setSourceQuenchdata(IPv4Layer* ipHeader, Layer* l4Header);
464
465
    /// @return ICMP redirect data. If the layer isn't of type ICMP redirect nullptr is returned.
466
    /// The IP and L4 (ICMP/TCP/UDP) headers of the redirect data are parsed as separate layers and can be
467
    /// retrieved via this->getNextLayer()
468
    icmp_redirect* getRedirectData();
469
470
    /// Set redirect message data. This method only works if IcmpLayer is already part of a packet (not
471
    /// a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate
472
    /// layers and need a packet to be added to
473
    /// @param[in] code The redirect message code. Only values between 0 and 3 are legal, the rest will cause the
474
    /// method to fail
475
    /// @param[in] gatewayAddress An IPv4 address of the gateway to which the redirection should be sent
476
    /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the
477
    /// packet
478
    /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the
479
    /// packet
480
    /// @return A pointer to the redirect data that have been set or nullptr if something went wrong
481
    /// (an appropriate error log is printed in such cases)
482
    icmp_redirect* setRedirectData(uint8_t code, IPv4Address gatewayAddress, IPv4Layer* ipHeader, Layer* l4Header);
483
484
    /// @return ICMP router advertisement data. If the layer isn't of type ICMP router advertisement nullptr is
485
    /// returned
486
    icmp_router_advertisement* getRouterAdvertisementData() const;
487
488
    /// Set router advertisement message data
489
    /// @param[in] code The router advertisement message code. Only codes 0 or 16 are legal, the rest will fail the
490
    /// method
491
    /// @param[in] lifetimeInSeconds The maximum number of seconds that the router addresses in this list may be
492
    /// considered valid
493
    /// @param[in] routerAddresses A vector of router advertisements to set
494
    /// @return A pointer to the router advertisement data that have been set or nullptr if something went wrong
495
    /// (an appropriate error log is printed in such cases)
496
    icmp_router_advertisement* setRouterAdvertisementData(
497
        uint8_t code, uint16_t lifetimeInSeconds,
498
        const std::vector<icmp_router_address_structure>& routerAddresses);
499
500
    /// @return ICMP router solicitation data. If the layer isn't of type ICMP router solicitation nullptr is
501
    /// returned
502
    icmp_router_solicitation* getRouterSolicitationData();
503
504
    /// Set router solicitation message data. This message accepts no parameters as there are no parameters to this
505
    /// type of message (code is always zero)
506
    /// @return A pointer to the router solicitation data that have been set or nullptr if something went wrong
507
    /// (an appropriate error log is printed in such cases)
508
    icmp_router_solicitation* setRouterSolicitationData();
509
510
    /// @return ICMP time-to-live exceeded data. If the layer isn't of type ICMP time-to-live exceeded nullptr is
511
    /// returned. The IP and L4 (ICMP/TCP/UDP) headers of the time exceeded data are parsed as separate layers and
512
    /// can be retrieved via this->getNextLayer()
513
    icmp_time_exceeded* getTimeExceededData();
514
515
    /// Set time-to-live exceeded message data. This method only works if IcmpLayer is already part of a packet (not
516
    /// a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate
517
    /// layers and need a packet to be added to
518
    /// @param[in] code Time-to-live exceeded message code. Only codes 0 or 1 are legal, the rest will fail the
519
    /// method
520
    /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the
521
    /// packet
522
    /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the
523
    /// packet
524
    /// @return A pointer to the time-to-live exceeded data that have been set or nullptr if something went wrong
525
    /// (an appropriate error log is printed in such cases)
526
    icmp_time_exceeded* setTimeExceededData(uint8_t code, IPv4Layer* ipHeader, Layer* l4Header);
527
528
    /// @return ICMP parameter problem data. If the layer isn't of type ICMP parameter problem nullptr is returned
529
    icmp_param_problem* getParamProblemData();
530
531
    /// Set parameter problem message data. This method only works if IcmpLayer is already part of a packet (not
532
    /// a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate
533
    /// layers and need a packet to be added to
534
    /// @param[in] code Parameter problem message code. Only code between 0 and 2 are legal, the rest will fail the
535
    /// method
536
    /// @param[in] errorOctetPointer In the case of an invalid IP header (Code 0), indicate the byte offset of the
537
    /// error in the header
538
    /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the
539
    /// packet
540
    /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the
541
    /// packet
542
    /// @return A pointer to the parameter problem data that have been set or nullptr if something went wrong
543
    /// (an appropriate error log is printed in such cases)
544
    icmp_param_problem* setParamProblemData(uint8_t code, uint8_t errorOctetPointer, IPv4Layer* ipHeader,
545
                                            Layer* l4Header);
546
547
    /// @return ICMP address mask request data. If the layer isn't of type ICMP address mask request nullptr is
548
    /// returned
549
    icmp_address_mask_request* getAddressMaskRequestData();
550
551
    /// Set address mask request message data
552
    /// @param[in] id Address mask request identifier
553
    /// @param[in] sequence Address mask request sequence
554
    /// @param[in] mask The subnet mask of the requesting host
555
    /// @return A pointer to the address mask request data that have been set or nullptr if something went wrong
556
    /// (an appropriate error log is printed in such cases)
557
    icmp_address_mask_request* setAddressMaskRequestData(uint16_t id, uint16_t sequence, IPv4Address mask);
558
559
    /// @return ICMP address mask reply data. If the layer isn't of type ICMP address mask reply nullptr is returned
560
    icmp_address_mask_reply* getAddressMaskReplyData();
561
562
    /// Set address mask reply message data
563
    /// @param[in] id Address mask reply identifier
564
    /// @param[in] sequence Address mask reply sequence
565
    /// @param[in] mask The subnet mask of the requesting host
566
    /// @return A pointer to the address mask reply data that have been set or nullptr if something went wrong
567
    /// (an appropriate error log is printed in such cases)
568
    icmp_address_mask_reply* setAddressMaskReplyData(uint16_t id, uint16_t sequence, IPv4Address mask);
569
570
    /// @return ICMP address information request data. If the layer isn't of type ICMP information request nullptr
571
    /// is returned
572
    icmp_info_request* getInfoRequestData();
573
574
    /// Set information request message data
575
    /// @param[in] id Information request identifier
576
    /// @param[in] sequence Information request sequence
577
    /// @return A pointer to the information request data that have been set or nullptr if something went wrong
578
    /// (an appropriate error log is printed in such cases)
579
    icmp_info_request* setInfoRequestData(uint16_t id, uint16_t sequence);
580
581
    /// @return ICMP address information reply data. If the layer isn't of type ICMP information reply nullptr is
582
    /// returned
583
    icmp_info_reply* getInfoReplyData();
584
585
    /// Set information reply message data
586
    /// @param[in] id Information reply identifier
587
    /// @param[in] sequence Information reply sequence
588
    /// @return A pointer to the information reply data that have been set or nullptr if something went wrong
589
    /// (an appropriate error log is printed in such cases)
590
    icmp_info_reply* setInfoReplyData(uint16_t id, uint16_t sequence);
591
592
    /// The static method makes validation of input data
593
    /// @param[in] data The pointer to the beginning of byte stream of ICMP packet
594
    /// @param[in] dataLen The length of byte stream
595
    /// @return True if the data is valid and can represent an ICMP packet
596
    static inline bool isDataValid(const uint8_t* data, size_t dataLen);
597
598
    // implement abstract methods
599
600
    /// ICMP messages of types: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, ICMP_TIME_EXCEEDED, ICMP_REDIRECT,
601
    /// ICMP_PARAM_PROBLEM have data that contains IPv4 header and some L4 header (TCP/UDP/ICMP). This method parses
602
    /// these headers as separate layers on top of the ICMP layer
603
    void parseNextLayer() override;
604
605
    /// @return The ICMP header length. This length varies according to the ICMP message type. This length doesn't
606
    /// include IPv4 and L4 headers in case ICMP message type are: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH,
607
    /// ICMP_TIME_EXCEEDED, ICMP_REDIRECT, ICMP_PARAM_PROBLEM
608
    size_t getHeaderLen() const override;
609
610
    /// Calculate ICMP checksum field
611
    void computeCalculateFields() override;
612
613
    std::string toString() const override;
614
615
    OsiModelLayer getOsiModelLayer() const override
616
0
    {
617
0
      return OsiModelNetworkLayer;
618
0
    }
619
620
    /// @struct SerializedFields
621
    /// Fields written by IcmpLayer's serializeLayer(), in addition to
622
    /// Layer::SerializedFields.
623
    struct SerializedFields : Layer::SerializedFields
624
    {
625
      /// @return All field descriptors for IcmpLayer
626
      static std::vector<FieldDescriptor> all()
627
0
      {
628
0
        auto result = Layer::SerializedFields::all();
629
0
        std::initializer_list<FieldDescriptor> extra{ Type,
630
0
                                                    TypeName,
631
0
                                                    Code,
632
0
                                                    Checksum,
633
0
                                                    Id,
634
0
                                                    Sequence,
635
0
                                                    Timestamp,
636
0
                                                    OriginateTimestamp,
637
0
                                                    ReceiveTimestamp,
638
0
                                                    TransmitTimestamp,
639
0
                                                    NextHopMTU,
640
0
                                                    GatewayAddress,
641
0
                                                    AddressMask,
642
0
                                                    Pointer,
643
0
                                                    AddressEntrySize,
644
0
                                                    Lifetime,
645
0
                                                    RouterAddresses };
646
0
        std::copy(extra.begin(), extra.end(), std::back_inserter(result));
647
0
        return result;
648
0
      }
649
650
      /// @brief ICMP message type, as an integer
651
      static const FieldDescriptor Type;
652
653
      /// @brief ICMP message type, as a string
654
      static const FieldDescriptor TypeName;
655
656
      /// @brief ICMP message code, as an integer
657
      static const FieldDescriptor Code;
658
659
      /// @brief Checksum, in hexadecimal notation
660
      static const FieldDescriptor Checksum;
661
662
      /// @brief Identifier for echo, timestamp, information, and address mask request/reply messages, in host
663
      /// byte order
664
      static const FieldDescriptor Id;
665
666
      /// @brief Sequence number for echo, timestamp, information, and address mask request/reply messages, in
667
      /// host byte order
668
      static const FieldDescriptor Sequence;
669
670
      /// @brief Timestamp for echo request/reply messages
671
      static const FieldDescriptor Timestamp;
672
673
      /// @brief Originate timestamp for timestamp request/reply messages (milliseconds since midnight UTC), in
674
      /// host byte order
675
      static const FieldDescriptor OriginateTimestamp;
676
677
      /// @brief Receive timestamp for timestamp request/reply messages (milliseconds since midnight UTC), in host
678
      /// byte order
679
      static const FieldDescriptor ReceiveTimestamp;
680
681
      /// @brief Transmit timestamp for timestamp request/reply messages (milliseconds since midnight UTC), in
682
      /// host byte order
683
      static const FieldDescriptor TransmitTimestamp;
684
685
      /// @brief Next-hop MTU for destination unreachable messages, in host byte order
686
      static const FieldDescriptor NextHopMTU;
687
688
      /// @brief Gateway IPv4 address for redirect messages, as a string
689
      static const FieldDescriptor GatewayAddress;
690
691
      /// @brief Subnet address mask for address mask request/reply messages, as a string
692
      static const FieldDescriptor AddressMask;
693
694
      /// @brief Pointer byte offset for parameter problem messages
695
      static const FieldDescriptor Pointer;
696
697
      /// @brief Number of 32-bit words per router address entry in router advertisement messages (typically 2)
698
      static const FieldDescriptor AddressEntrySize;
699
700
      /// @brief Router advertisement lifetime in seconds, in host byte order
701
      static const FieldDescriptor Lifetime;
702
703
      /// @brief Array of router addresses in router advertisement messages; elements described by
704
      /// RouterAddressEntry
705
      static const FieldDescriptor RouterAddresses;
706
707
      /// @brief Router address entry descriptor; not included in all()
708
      static const FieldDescriptor RouterAddressEntry;
709
710
      /// @brief Router IPv4 address, as a string; element of RouterAddressEntry, not included in all()
711
      static const FieldDescriptor RouterAddress;
712
713
      /// @brief Preference level for the router address; element of RouterAddressEntry, not included in all()
714
      static const FieldDescriptor PreferenceLevel;
715
    };
716
717
  protected:
718
    void serializeLayer(ObjectScope& serializer) const override;
719
  };
720
721
  // implementation of inline methods
722
723
  bool IcmpLayer::isDataValid(const uint8_t* data, size_t dataLen)
724
0
  {
725
0
    if (dataLen < sizeof(icmphdr))
726
0
      return false;
727
728
0
    uint8_t type = data[0];
729
730
    // ICMP_ECHO_REQUEST, ICMP_ECHO_REPLY, ICMP_ROUTER_SOL
731
0
    if (type == 8 || type == 0 || type == 10)
732
0
      return true;
733
734
    // ICMP_INFO_REQUEST, ICMP_INFO_REPLY
735
0
    if (type == 15 || type == 16)
736
0
      return dataLen >= sizeof(icmp_info_request);
737
738
    // ICMP_TIMESTAMP_REQUEST, ICMP_TIMESTAMP_REPLY
739
0
    if (type == 13 || type == 14)
740
0
      return dataLen >= sizeof(icmp_timestamp_request);
741
742
    // ICMP_ADDRESS_MASK_REPLY, ICMP_ADDRESS_MASK_REQUEST
743
0
    if (type == 17 || type == 18)
744
0
      return dataLen >= sizeof(icmp_address_mask_request);
745
746
    // ICMP_DEST_UNREACHABLE
747
0
    if (type == 3)
748
0
      return dataLen >= sizeof(icmp_destination_unreachable);
749
750
    // ICMP_REDIRECT
751
0
    if (type == 5)
752
0
      return dataLen >= sizeof(icmp_redirect);
753
754
    // ICMP_TIME_EXCEEDED, ICMP_SOURCE_QUENCH
755
0
    if (type == 4 || type == 11)
756
0
      return dataLen >= sizeof(icmp_time_exceeded);
757
758
    // ICMP_PARAM_PROBLEM
759
0
    if (type == 12)
760
0
      return dataLen >= sizeof(icmp_param_problem);
761
762
    // ICMP_ROUTER_ADV
763
0
    if (type == 9)
764
0
    {
765
0
      if (dataLen < sizeof(icmp_router_advertisement_hdr))
766
0
        return false;
767
768
0
      const auto* routerAdv = reinterpret_cast<const icmp_router_advertisement_hdr*>(data);
769
0
      return routerAdv->advertisementCount <=
770
0
             (dataLen - sizeof(icmp_router_advertisement_hdr)) / sizeof(icmp_router_address_structure);
771
0
    }
772
773
0
    return false;
774
0
  }
775
776
}  // namespace pcpp