Coverage Report

Created: 2026-10-17 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/header/NdpLayer.h
Line
Count
Source
1
#pragma once
2
3
#include "IcmpV6Layer.h"
4
#include "IpAddress.h"
5
#include "Layer.h"
6
#include "MacAddress.h"
7
#include "TLVData.h"
8
9
#include <vector>
10
11
/// @file
12
13
/// @namespace pcpp
14
/// @brief The main namespace for the PcapPlusPlus lib
15
namespace pcpp
16
{
17
18
  /// An enum representing the available option types for Neighbor Discovery in IPv6 (see RFC 4861)
19
  enum class NDPNeighborOptionTypes : int
20
  {
21
    NDP_OPTION_SOURCE_LINK_LAYER = 1,
22
    NDP_OPTION_TARGET_LINK_LAYER = 2,
23
    NDP_OPTION_PREFIX_INFORMATION = 3,
24
    NDP_OPTION_REDIRECTED_HEADER = 4,
25
    NDP_OPTION_MTU = 5,
26
    NDP_OPTION_UNKNOWN = 255
27
  };
28
29
  /// @class NdpOption
30
  /// A wrapper class for NDP options. This class does not create or modify NDP option records, but rather
31
  /// serves as a wrapper and provides useful methods for retrieving data from them
32
  class NdpOption : public TLVRecord<uint8_t, uint8_t>
33
  {
34
  public:
35
    /// A c'tor for this class that gets a pointer to the option raw data (byte array)
36
    /// @param[in] optionRawData A pointer to the NDP option raw data
37
0
    explicit NdpOption(uint8_t* optionRawData) : TLVRecord(optionRawData)
38
0
    {}
39
40
    /// A d'tor for this class, currently does nothing
41
    ~NdpOption() override = default;
42
43
    /// @return NDP option type casted as pcpp::NDPNeighborOptionTypes enum. If the data is null a value
44
    /// of NDP_OPTION_UNKNOWN is returned
45
    NDPNeighborOptionTypes getNdpOptionType() const
46
0
    {
47
0
      if (m_Data == nullptr)
48
0
        return NDPNeighborOptionTypes::NDP_OPTION_UNKNOWN;
49
50
0
      return static_cast<NDPNeighborOptionTypes>(m_Data->recordType);
51
0
    }
52
53
    // implement abstract methods
54
55
    size_t getTotalSize() const override
56
0
    {
57
0
      if (m_Data == nullptr)
58
0
        return 0;
59
60
0
      return static_cast<size_t>(m_Data->recordLen) * 8;
61
0
    }
62
63
    size_t getDataSize() const override
64
0
    {
65
0
      if (m_Data == nullptr)
66
0
      {
67
0
        return 0;
68
0
      }
69
70
      // length value is stored in units of 8 octets
71
0
      return static_cast<size_t>(m_Data->recordLen) * 8 - (2 * sizeof(uint8_t));
72
0
    }
73
  };
74
75
  /// @class NdpOptionBuilder
76
  /// A class for building NDP option records. This builder receives the NDP option parameters in its c'tor,
77
  /// builds the NDP option raw buffer and provides a build() method to get a NdpOption object out of it
78
  class NdpOptionBuilder : public TLVRecordBuilder
79
  {
80
  public:
81
    /// A c'tor for building NDP options which their value is a byte array. The NdpOption object can be later
82
    /// retrieved by calling build(). Each option is padded to have a 64-bit boundary.
83
    /// @param[in] optionType NDP option type
84
    /// @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in
85
    /// any way.
86
    /// @param[in] optionValueLen Option value length in bytes
87
    NdpOptionBuilder(NDPNeighborOptionTypes optionType, const uint8_t* optionValue, uint8_t optionValueLen)
88
0
        : TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen)
89
0
    {}
90
91
    /// Build the NdpOption object out of the parameters defined in the c'tor. Padding bytes are added to the
92
    /// option for option length with 64-bit boundaries.
93
    /// @return The NdpOption object
94
    NdpOption build() const;
95
  };
96
97
  /// @class NDPLayerBase
98
  /// Represents a base for NDP packet types
99
  class NDPLayerBase : public IcmpV6Layer
100
  {
101
  public:
102
0
    ~NDPLayerBase() override = default;
103
104
    /// @return The number of NDP options in this layer
105
    size_t getNdpOptionCount() const;
106
107
    /// Get a NDP option by type.
108
    /// @param[in] option NDP option type
109
    /// @return An NdpOption object that contains the first option that matches this type, or logical null
110
    /// (NdpOption#isNull() == true) if no such option found
111
    NdpOption getNdpOption(NDPNeighborOptionTypes option) const;
112
113
    /// @return The first NDP option in the packet. If the current layer contains no options the returned value will
114
    /// contain a logical null (NdpOption#isNull() == true)
115
    NdpOption getFirstNdpOption() const;
116
117
    /// Get the NDP option that comes after a given option. If the given option was the last one, the
118
    /// returned value will contain a logical null (IdpOption#isNull() == true)
119
    /// @param[in] option An NDP option object that exists in the current layer
120
    /// @return A NdpOption object that contains the NDP option data that comes next, or logical null if the given
121
    /// NDP option: (1) was the last one; or (2) contains a logical null; or (3) doesn't belong to this packet
122
    NdpOption getNextNdpOption(NdpOption& option) const;
123
124
    /// Add a new NDP option at the end of the layer (after the last NDP option)
125
    /// @param[in] optionBuilder An NdpOptionBuilder object that contains the NDP option data to be added
126
    /// @return A NdpOption object that contains the newly added NDP option data or logical null
127
    /// (NdpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be
128
    /// printed to log
129
    NdpOption addNdpOption(const NdpOptionBuilder& optionBuilder);
130
131
    /// Remove all NDP options from the layer
132
    /// @return True if options removed successfully or false if some error occurred (an appropriate error message
133
    /// will be printed to log)
134
    bool removeAllNdpOptions();
135
136
    /// @struct SerializedFields
137
    /// Fields written by NDPLayerBase's serializeLayer(), in addition to
138
    /// IcmpV6Layer::SerializedFields.
139
    struct SerializedFields : IcmpV6Layer::SerializedFields
140
    {
141
      /// @brief The highest field ID used by NDPLayerBase
142
      static constexpr uint16_t MaxID = IcmpV6Layer::SerializedFields::MaxID + 1;
143
144
      /// @return All field descriptors for NDPLayerBase
145
      static std::vector<FieldDescriptor> all()
146
0
      {
147
0
        auto result = IcmpV6Layer::SerializedFields::all();
148
0
        std::initializer_list<FieldDescriptor> extra{ Options };
149
0
        std::copy(extra.begin(), extra.end(), std::back_inserter(result));
150
0
        return result;
151
0
      }
152
153
      /// @brief Array of NDP options; elements described by Option
154
      static const FieldDescriptor Options;
155
156
      /// @brief One option's type name; an Options element, not included in all()
157
      static const FieldDescriptor Option;
158
    };
159
160
  protected:
161
0
    NDPLayerBase() = default;
162
163
    NDPLayerBase(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
164
0
        : IcmpV6Layer(data, dataLen, prevLayer, packet)
165
0
    {}
166
167
    void serializeOptions(ObjectScope& serializer) const;
168
169
  private:
170
    TLVRecordReader<NdpOption> m_OptionReader;
171
172
    virtual size_t getNdpHeaderLen() const = 0;
173
    virtual uint8_t* getNdpOptionsBasePtr() const
174
0
    {
175
0
      return m_Data + getNdpHeaderLen();
176
0
    };
177
    NdpOption addNdpOptionAt(const NdpOptionBuilder& optionBuilder, int offset);
178
  };
179
180
  /// @class NDPNeighborSolicitationLayer
181
  /// Represents a NDP Neighbor Solicitation protocol layer
182
  class NDPNeighborSolicitationLayer : public NDPLayerBase
183
  {
184
  public:
185
    /// @struct ndpneighborsolicitationhdr
186
    /// Represents neighbor solicitation message format
187
#pragma pack(push, 1)
188
    struct ndpneighborsolicitationhdr : icmpv6hdr
189
    {
190
      /// Reserved
191
      uint32_t reserved;
192
      /// Target address - Target address of solicitation message
193
      uint8_t targetIP[16];
194
    };
195
#pragma pack(pop)
196
197
    /// A constructor that creates the layer from an existing packet raw data
198
    /// @param[in] data A pointer to the raw data
199
    /// @param[in] dataLen Size of the data in bytes
200
    /// @param[in] prevLayer A pointer to the previous layer
201
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
202
    NDPNeighborSolicitationLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
203
0
        : NDPLayerBase(data, dataLen, prevLayer, packet)
204
0
    {}
205
206
    /// Checks whether the data is large enough to be parsed as a neighbor solicitation message
207
    /// @param[in] data A pointer to the raw data
208
    /// @param[in] dataLen Size of the data in bytes
209
    /// @return True if the data is at least the size of the neighbor solicitation header
210
    static bool isDataValid(const uint8_t* data, size_t dataLen)
211
0
    {
212
0
      return data != nullptr && dataLen >= sizeof(ndpneighborsolicitationhdr);
213
0
    }
214
215
    /// A constructor for a new NDPNeighborSolicitationLayer object
216
    /// @param[in] code Code field
217
    /// @param[in] targetIP Target IP address for which the solicitation shall be created
218
    NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address& targetIP);
219
220
    /// A constructor for a new NDPNeighborSolicitationLayer object
221
    /// @param[in] code Code field
222
    /// @param[in] targetIP Target IP address for which the solicitation shall be created
223
    /// @param[in] srcMac Mac address which shall be put in the linklayer option
224
    NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address& targetIP, const MacAddress& srcMac);
225
226
    ~NDPNeighborSolicitationLayer() override = default;
227
228
    /// @return Get the IP address specified as the target IP address in the solicitation message
229
    IPv6Address getTargetIP() const
230
0
    {
231
0
      return IPv6Address(getNdpHeader()->targetIP);
232
0
    };
233
234
    /// Checks if the layer has a link layer address option set
235
    /// @return true if link layer address option is available, false otherwise
236
    bool hasLinkLayerAddress() const;
237
238
    /// Get the Link Layer Address
239
    /// @return Mac address which is specified in the link layer address option
240
    MacAddress getLinkLayerAddress() const;
241
242
    std::string toString() const override;
243
244
    /// @struct SerializedFields
245
    /// Fields written by NDPNeighborSolicitationLayer's serializeLayer(), in addition to
246
    /// NDPLayerBase::SerializedFields.
247
    struct SerializedFields : NDPLayerBase::SerializedFields
248
    {
249
      /// @return All field descriptors for NDPNeighborSolicitationLayer
250
      static std::vector<FieldDescriptor> all()
251
0
      {
252
0
        auto result = NDPLayerBase::SerializedFields::all();
253
0
        std::initializer_list<FieldDescriptor> extra{ TargetIP, LinkLayerAddress };
254
0
        std::copy(extra.begin(), extra.end(), std::back_inserter(result));
255
0
        return result;
256
0
      }
257
258
      /// @brief Target IPv6 address, as a string
259
      static const FieldDescriptor TargetIP;
260
261
      /// @brief Link-layer MAC address option, as a string (if present)
262
      static const FieldDescriptor LinkLayerAddress;
263
    };
264
265
  protected:
266
    void serializeLayer(ObjectScope& serializer) const override;
267
268
  private:
269
    void initLayer(uint8_t code, const IPv6Address& targetIP);
270
    ndpneighborsolicitationhdr* getNdpHeader() const
271
0
    {
272
0
      return reinterpret_cast<ndpneighborsolicitationhdr*>(m_Data);
273
0
    }
274
    size_t getNdpHeaderLen() const override
275
0
    {
276
0
      return sizeof(ndpneighborsolicitationhdr);
277
0
    };
278
  };
279
280
  /// @class NDPNeighborAdvertisementLayer
281
  /// Represents a NDP Neighbor Advertisement protocol layer
282
  class NDPNeighborAdvertisementLayer : public NDPLayerBase
283
  {
284
  public:
285
    /// @struct ndpneighboradvertisementhdr
286
    /// Represents neighbor advertisement message format
287
#pragma pack(push, 1)
288
    struct ndpneighboradvertisementhdr : icmpv6hdr
289
    {
290
#if (BYTE_ORDER == LITTLE_ENDIAN)
291
      uint32_t
292
          /// Unused field
293
          reserved : 5,
294
          /// Flag indicating that this entry should override the old one
295
          override : 1,
296
          /// Flag indicating that the advertisement was sent in response to a Neighbor Solicitation from the
297
          /// Destination address
298
          solicited : 1,
299
          /// Flag indicating that the advertisement is sent by a router
300
          router : 1,
301
          /// Unused field
302
          reserved2 : 24;
303
#else
304
      uint32_t
305
          /// Flag indicating that the advertisement is sent by a router
306
          router : 1,
307
          /// Flag indicating that the advertisement was sent in response to a Neighbor Solicitation from the
308
          /// Destination address
309
          solicited : 1,
310
          /// Flag indicating that this entry should override the old one
311
          override : 1,
312
          /// Unused field
313
          reserved : 29;
314
#endif
315
      /// Target address - Either source address of advertisement or address for requested MAC
316
      uint8_t targetIP[16];
317
    };
318
#pragma pack(pop)
319
320
    /// A constructor that creates the layer from an existing packet raw data
321
    /// @param[in] data A pointer to the raw data
322
    /// @param[in] dataLen Size of the data in bytes
323
    /// @param[in] prevLayer A pointer to the previous layer
324
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
325
    NDPNeighborAdvertisementLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
326
0
        : NDPLayerBase(data, dataLen, prevLayer, packet)
327
0
    {}
328
329
    /// Checks whether the data is large enough to be parsed as a neighbor advertisement message
330
    /// @param[in] data A pointer to the raw data
331
    /// @param[in] dataLen Size of the data in bytes
332
    /// @return True if the data is at least the size of the neighbor advertisement header
333
    static bool isDataValid(const uint8_t* data, size_t dataLen)
334
0
    {
335
0
      return data != nullptr && dataLen >= sizeof(ndpneighboradvertisementhdr);
336
0
    }
337
338
    /// A constructor that allocates a new NDP Advertisement Layer with target link-layer address option
339
    /// @param[in] code Code field
340
    /// @param[in] targetIP The target IP address from the Neighbor Solicitation message (solicited advertisements)
341
    /// or the address whose link-layer address has changed (unsolicited advertisement)
342
    /// @param[in] targetMac Adds the target link-layer address into the option field of the layer
343
    /// @param[in] routerFlag The router flag
344
    /// @param[in] unicastFlag The solicited flag
345
    /// @param[in] overrideFlag The override flag
346
    NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address& targetIP, const MacAddress& targetMac,
347
                                  bool routerFlag, bool unicastFlag, bool overrideFlag);
348
349
    /// A constructor that allocates a new NDP Advertisement Layer
350
    /// @param code Code field
351
    /// @param targetIP The target IP address from the Neighbor Solicitation message (solicited advertisements) or
352
    /// the address whose link-layer address has changed (unsolicited advertisement)
353
    /// @param routerFlag The router flag
354
    /// @param unicastFlag The solicited flag
355
    /// @param overrideFlag The override flag
356
    NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address& targetIP, bool routerFlag, bool unicastFlag,
357
                                  bool overrideFlag);
358
359
    ~NDPNeighborAdvertisementLayer() override = default;
360
361
    /// @return Get the target MAC address
362
    MacAddress getTargetMac() const;
363
364
    /// @return Get the target IP address
365
    IPv6Address getTargetIP() const
366
0
    {
367
0
      return IPv6Address(getNdpHeader()->targetIP);
368
0
    }
369
370
    /// @return Get information if the target link-layer address was added in the option field of the header
371
    bool hasTargetMacInfo() const;
372
373
    /// @return Get the router flag
374
    bool getRouterFlag() const
375
0
    {
376
0
      return getNdpHeader()->router;
377
0
    }
378
379
    /// @return Get the unicast flag
380
    bool getUnicastFlag() const
381
0
    {
382
0
      return getNdpHeader()->solicited;
383
0
    }
384
385
    /// @return Get the override flag
386
    bool getOverrideFlag() const
387
0
    {
388
0
      return getNdpHeader()->override;
389
0
    }
390
391
    std::string toString() const override;
392
393
    /// @struct SerializedFields
394
    /// Fields written by NDPNeighborAdvertisementLayer's serializeLayer(), in addition to
395
    /// NDPLayerBase::SerializedFields.
396
    struct SerializedFields : NDPLayerBase::SerializedFields
397
    {
398
      /// @return All field descriptors for NDPNeighborAdvertisementLayer
399
      static std::vector<FieldDescriptor> all()
400
0
      {
401
0
        auto result = NDPLayerBase::SerializedFields::all();
402
0
        std::initializer_list<FieldDescriptor> extra{ TargetIP, RouterFlag, UnicastFlag, OverrideFlag,
403
0
                                                    TargetMac };
404
0
        std::copy(extra.begin(), extra.end(), std::back_inserter(result));
405
0
        return result;
406
0
      }
407
408
      /// @brief Target IPv6 address, as a string
409
      static const FieldDescriptor TargetIP;
410
411
      /// @brief Router flag
412
      static const FieldDescriptor RouterFlag;
413
414
      /// @brief Solicited (unicast) flag
415
      static const FieldDescriptor UnicastFlag;
416
417
      /// @brief Override flag
418
      static const FieldDescriptor OverrideFlag;
419
420
      /// @brief Target MAC address option, as a string (if present)
421
      static const FieldDescriptor TargetMac;
422
    };
423
424
  protected:
425
    void serializeLayer(ObjectScope& serializer) const override;
426
427
  private:
428
    void initLayer(uint8_t code, const IPv6Address& targetIP, bool routerFlag, bool unicastFlag, bool overrideFlag);
429
    ndpneighboradvertisementhdr* getNdpHeader() const
430
0
    {
431
0
      return reinterpret_cast<ndpneighboradvertisementhdr*>(m_Data);
432
0
    }
433
    size_t getNdpHeaderLen() const override
434
0
    {
435
0
      return sizeof(ndpneighboradvertisementhdr);
436
0
    };
437
  };
438
439
}  // namespace pcpp