Skip to content

Commit d50ef12

Browse files
committed
Refine GENEVE API accessors and validation
1 parent 130d50a commit d50ef12

3 files changed

Lines changed: 146 additions & 58 deletions

File tree

Packet++/header/GeneveLayer.h

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ namespace pcpp
2020
#pragma pack(push, 1)
2121
struct geneve_header
2222
{
23+
static constexpr size_t OptionsLengthUnit = 4;
24+
static constexpr size_t MaxOptionsLength = ((1 << 6) - 1) * OptionsLengthUnit;
25+
2326
#if (BYTE_ORDER == LITTLE_ENDIAN)
2427
/// Options length in 4-byte units
2528
uint8_t optionsLength : 6;
@@ -49,6 +52,33 @@ namespace pcpp
4952
uint8_t vni[3];
5053
/// Reserved byte
5154
uint8_t reserved2;
55+
56+
/// @return Options length in bytes
57+
size_t getOptionsLength() const
58+
{
59+
return static_cast<size_t>(optionsLength) * OptionsLengthUnit;
60+
}
61+
62+
/// @param[in] value Options length in bytes
63+
/// @pre value must be divisible by 4 and no greater than MaxOptionsLength
64+
void setOptionsLength(size_t value)
65+
{
66+
optionsLength = static_cast<uint8_t>(value / OptionsLengthUnit);
67+
}
68+
69+
/// @return The 24-bit virtual network identifier
70+
uint32_t getVNI() const
71+
{
72+
return (static_cast<uint32_t>(vni[0]) << 16) | (static_cast<uint32_t>(vni[1]) << 8) | vni[2];
73+
}
74+
75+
/// @param[in] value The 24-bit virtual network identifier
76+
void setVNI(uint32_t value)
77+
{
78+
vni[0] = static_cast<uint8_t>((value >> 16) & 0xff);
79+
vni[1] = static_cast<uint8_t>((value >> 8) & 0xff);
80+
vni[2] = static_cast<uint8_t>(value & 0xff);
81+
}
5282
};
5383
#pragma pack(pop)
5484
static_assert(sizeof(geneve_header) == 8, "geneve_header size is not 8 bytes");
@@ -58,8 +88,19 @@ namespace pcpp
5888
#pragma pack(push, 1)
5989
struct geneve_option_header
6090
{
91+
static constexpr size_t DataLengthUnit = 4;
92+
static constexpr size_t MaxDataLength = ((1 << 5) - 1) * DataLengthUnit;
93+
static constexpr uint8_t TypeMask = 0x7f;
94+
static constexpr uint8_t CriticalBitMask = 0x80;
95+
6196
/// Option namespace assigned by IANA
6297
uint16_t optionClass;
98+
/// @return Option class in host byte order
99+
uint16_t getOptionClass() const;
100+
101+
/// @param[in] value Option class in host byte order
102+
void setOptionClass(uint16_t value);
103+
63104
/// Option type. The most significant bit is the critical bit
64105
uint8_t type;
65106
#if (BYTE_ORDER == LITTLE_ENDIAN)
@@ -73,6 +114,60 @@ namespace pcpp
73114
/// Option data length in 4-byte units
74115
uint8_t length : 5;
75116
#endif
117+
118+
/// @return The 7-bit option type without the critical bit
119+
uint8_t getType() const
120+
{
121+
return extractType(type);
122+
}
123+
124+
/// @param[in] value A raw option type, optionally including the critical bit
125+
/// @return The 7-bit option type without the critical bit
126+
static uint8_t extractType(uint8_t value)
127+
{
128+
return static_cast<uint8_t>(value & TypeMask);
129+
}
130+
131+
/// Round an option data length up to the next 4-byte boundary
132+
/// @param[in] value Unpadded option data length in bytes
133+
/// @return Option data length rounded up to a multiple of 4
134+
static size_t alignDataSize(size_t value)
135+
{
136+
constexpr size_t AlignmentMask = DataLengthUnit - 1;
137+
return (value + AlignmentMask) & ~AlignmentMask;
138+
}
139+
140+
/// @return True if the critical bit is set
141+
bool isCritical() const
142+
{
143+
return (type & CriticalBitMask) != 0;
144+
}
145+
146+
/// @return Option data length in bytes
147+
size_t getDataSize() const
148+
{
149+
return static_cast<size_t>(length) * DataLengthUnit;
150+
}
151+
152+
/// @param[in] value Option data length in bytes
153+
/// @pre value must be divisible by 4 and no greater than MaxDataLength
154+
void setDataSize(size_t value)
155+
{
156+
length = static_cast<uint8_t>(value / DataLengthUnit);
157+
}
158+
159+
/// @return Total option size including its 4-byte header
160+
size_t getTotalSize() const
161+
{
162+
return sizeof(geneve_option_header) + getDataSize();
163+
}
164+
165+
/// @param[in] value The 7-bit option type
166+
/// @param[in] critical Whether to set the critical bit
167+
void setType(uint8_t value, bool critical)
168+
{
169+
type = static_cast<uint8_t>(extractType(value) | (critical ? CriticalBitMask : 0));
170+
}
76171
};
77172
#pragma pack(pop)
78173
static_assert(sizeof(geneve_option_header) == 4, "geneve_option_header size is not 4 bytes");
@@ -207,6 +302,7 @@ namespace pcpp
207302
GeneveOptionIterator find(uint16_t optionClass, uint8_t optionType) const;
208303

209304
/// @return The number of structurally valid options in this range
305+
/// @note This operation has O(n) time complexity, where n is the number of options.
210306
size_t size() const;
211307

212308
/// @return True if this range contains no options
@@ -254,6 +350,8 @@ namespace pcpp
254350
/// @param[in] dataLen Size of the data in bytes
255351
/// @param[in] prevLayer A pointer to the previous layer
256352
/// @param[in] packet A pointer to the Packet instance where the layer is stored
353+
/// @note This constructor does not validate the input. Use isDataValid() before constructing a standalone
354+
/// parsed layer.
257355
GeneveLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
258356
: Layer(data, dataLen, prevLayer, packet, Geneve)
259357
{}
@@ -281,32 +379,38 @@ namespace pcpp
281379
}
282380

283381
/// @return A pointer to the fixed GENEVE header
382+
/// @pre The layer must contain a complete fixed GENEVE header
284383
geneve_header* getGeneveHeader() const
285384
{
286385
return reinterpret_cast<geneve_header*>(m_Data);
287386
}
288387

289388
/// @return The VNI in host byte order
389+
/// @pre The layer must contain a complete fixed GENEVE header
290390
uint32_t getVNI() const;
291391

292392
/// Set the VNI. Only the least significant 24 bits are used
293393
/// @param[in] vni The VNI to set
394+
/// @pre The layer must contain a complete fixed GENEVE header
294395
void setVNI(uint32_t vni);
295396

296397
/// @return Encapsulated protocol EtherType in host byte order
398+
/// @pre The layer must contain a complete fixed GENEVE header
297399
uint16_t getProtocolType() const;
298400

299401
/// Set the encapsulated protocol EtherType
300402
/// @param[in] protocolType EtherType in host byte order
403+
/// @pre The layer must contain a complete fixed GENEVE header
301404
void setProtocolType(uint16_t protocolType);
302405

303-
/// @return Total options length in bytes
406+
/// @return Total options length in bytes, or zero if the fixed GENEVE header is unavailable or truncated
304407
size_t getOptionsLength() const;
305408

306-
/// @return Number of structurally valid options in this layer
409+
/// @return Number of structurally valid options in this layer, or zero if no valid option range is available
307410
size_t getOptionCount() const;
308411

309-
/// @return A non-owning range over the options in this layer
412+
/// @return A non-owning range over the options in this layer, or an empty range if the fixed GENEVE header is
413+
/// unavailable or truncated
310414
GeneveOptionRange getOptions() const;
311415

312416
/// Add an option after all existing options
@@ -327,7 +431,8 @@ namespace pcpp
327431
/// Parse the encapsulated protocol according to the Protocol Type field
328432
void parseNextLayer() override;
329433

330-
/// @return Fixed header plus the declared options length
434+
/// @return Zero if no data is available; the available data length if the fixed header is truncated; otherwise,
435+
/// the fixed header plus the declared options length, capped at the available data length
331436
size_t getHeaderLen() const override;
332437

333438
/// Update the Protocol Type and Critical flag from the following layer and options

Packet++/src/GeneveLayer.cpp

Lines changed: 33 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -18,59 +18,47 @@
1818

1919
namespace pcpp
2020
{
21-
namespace
22-
{
23-
constexpr uint8_t GeneveSupportedVersion = 0;
24-
constexpr size_t GeneveOptionAlignment = 4;
25-
constexpr uint8_t GeneveOptionLengthMask = 0x1f;
26-
constexpr uint8_t GeneveOptionTypeMask = 0x7f;
27-
constexpr uint8_t GeneveOptionCriticalBitMask = 0x80;
28-
constexpr uint8_t GeneveOptionsLengthMask = 0x3f;
29-
constexpr size_t GeneveMaxOptionDataLength = GeneveOptionLengthMask * GeneveOptionAlignment;
30-
constexpr size_t GeneveMaxOptionsLength = GeneveOptionsLengthMask * GeneveOptionAlignment;
31-
constexpr uint8_t BitsPerByte = 8;
32-
constexpr uint32_t ByteMask = 0xff;
33-
34-
size_t getGeneveOptionTotalSize(const uint8_t* optionData)
35-
{
36-
constexpr size_t OptionLengthFieldOffset = sizeof(geneve_option_header) - 1;
37-
auto optionDataLength = static_cast<size_t>(optionData[OptionLengthFieldOffset] & GeneveOptionLengthMask) *
38-
GeneveOptionAlignment;
39-
return sizeof(geneve_option_header) + optionDataLength;
40-
}
41-
} // namespace
21+
uint16_t geneve_option_header::getOptionClass() const
22+
{
23+
return be16toh(optionClass);
24+
}
25+
26+
void geneve_option_header::setOptionClass(uint16_t value)
27+
{
28+
optionClass = htobe16(value);
29+
}
4230

4331
bool GeneveOption::canAssign(const uint8_t* optionRawData, size_t optionDataLen)
4432
{
4533
if (optionRawData == nullptr || optionDataLen < sizeof(geneve_option_header))
4634
return false;
4735

48-
return getGeneveOptionTotalSize(optionRawData) <= optionDataLen;
36+
return reinterpret_cast<const geneve_option_header*>(optionRawData)->getTotalSize() <= optionDataLen;
4937
}
5038

5139
uint16_t GeneveOption::getOptionClass() const
5240
{
53-
return be16toh(m_Data->optionClass);
41+
return m_Data->getOptionClass();
5442
}
5543

5644
uint8_t GeneveOption::getType() const
5745
{
58-
return static_cast<uint8_t>(m_Data->type & GeneveOptionTypeMask);
46+
return m_Data->getType();
5947
}
6048

6149
bool GeneveOption::isCritical() const
6250
{
63-
return (m_Data->type & GeneveOptionCriticalBitMask) != 0;
51+
return m_Data->isCritical();
6452
}
6553

6654
size_t GeneveOption::getDataSize() const
6755
{
68-
return static_cast<size_t>(m_Data->length) * GeneveOptionAlignment;
56+
return m_Data->getDataSize();
6957
}
7058

7159
size_t GeneveOption::getTotalSize() const
7260
{
73-
return sizeof(geneve_option_header) + getDataSize();
61+
return m_Data->getTotalSize();
7462
}
7563

7664
uint8_t* GeneveOption::getData() const
@@ -125,7 +113,8 @@ namespace pcpp
125113
for (auto iterator = begin(); iterator != end(); ++iterator)
126114
{
127115
GeneveOption option = *iterator;
128-
if (option.getOptionClass() == optionClass && option.getType() == (optionType & GeneveOptionTypeMask))
116+
if (option.getOptionClass() == optionClass &&
117+
option.getType() == geneve_option_header::extractType(optionType))
129118
return iterator;
130119
}
131120

@@ -142,21 +131,18 @@ namespace pcpp
142131

143132
std::vector<uint8_t> GeneveOptionBuilder::build() const
144133
{
145-
if (m_RecValueLen > GeneveMaxOptionDataLength)
134+
if (m_RecValueLen > geneve_option_header::MaxDataLength)
146135
return {};
147136

148-
constexpr size_t AlignmentMask = GeneveOptionAlignment - 1;
149-
size_t paddedDataLength = (m_RecValueLen + AlignmentMask) & ~AlignmentMask;
137+
size_t paddedDataLength = geneve_option_header::alignDataSize(m_RecValueLen);
150138

151139
size_t totalLength = sizeof(geneve_option_header) + paddedDataLength;
152140
std::vector<uint8_t> optionData(totalLength, 0);
153141

154142
geneve_option_header header = {};
155-
header.optionClass = htobe16(m_OptionClass);
156-
header.type = static_cast<uint8_t>(m_RecType & GeneveOptionTypeMask);
157-
if (m_Critical)
158-
header.type |= GeneveOptionCriticalBitMask;
159-
header.length = static_cast<uint8_t>(paddedDataLength / GeneveOptionAlignment);
143+
header.setOptionClass(m_OptionClass);
144+
header.setType(m_RecType, m_Critical);
145+
header.setDataSize(paddedDataLength);
160146
memcpy(optionData.data(), &header, sizeof(header));
161147
if (m_RecValueLen > 0)
162148
memcpy(optionData.data() + sizeof(geneve_option_header), m_RecValue, m_RecValueLen);
@@ -179,14 +165,15 @@ namespace pcpp
179165
return false;
180166

181167
auto* header = reinterpret_cast<const geneve_header*>(data);
182-
if (header->version != GeneveSupportedVersion)
168+
// RFC 8926 defines GENEVE version 0; this implementation supports that version only.
169+
if (header->version != 0)
183170
return false;
184171
// RFC 8926 Section 3.4 requires Protocol Type to follow the EtherType convention,
185172
// whose valid encodings start at 0x0600.
186173
if (be16toh(header->protocolType) < 0x0600)
187174
return false;
188175

189-
auto optionsLength = static_cast<size_t>(header->optionsLength) * GeneveOptionAlignment;
176+
auto optionsLength = header->getOptionsLength();
190177
if (optionsLength > dataLen - sizeof(geneve_header))
191178
return false;
192179

@@ -196,11 +183,10 @@ namespace pcpp
196183
{
197184
if (!GeneveOption::canAssign(option, remaining))
198185
return false;
199-
if ((reinterpret_cast<const geneve_option_header*>(option)->type & GeneveOptionCriticalBitMask) != 0 &&
200-
header->criticalFlag == 0)
186+
if (reinterpret_cast<const geneve_option_header*>(option)->isCritical() && header->criticalFlag == 0)
201187
return false;
202188

203-
size_t optionLength = getGeneveOptionTotalSize(option);
189+
size_t optionLength = reinterpret_cast<const geneve_option_header*>(option)->getTotalSize();
204190
option += optionLength;
205191
remaining -= optionLength;
206192
}
@@ -210,17 +196,12 @@ namespace pcpp
210196

211197
uint32_t GeneveLayer::getVNI() const
212198
{
213-
const uint8_t* vni = getGeneveHeader()->vni;
214-
return (static_cast<uint32_t>(vni[0]) << (2 * BitsPerByte)) | (static_cast<uint32_t>(vni[1]) << BitsPerByte) |
215-
vni[2];
199+
return getGeneveHeader()->getVNI();
216200
}
217201

218202
void GeneveLayer::setVNI(uint32_t vni)
219203
{
220-
uint8_t* vniData = getGeneveHeader()->vni;
221-
vniData[0] = static_cast<uint8_t>((vni >> (2 * BitsPerByte)) & ByteMask);
222-
vniData[1] = static_cast<uint8_t>((vni >> BitsPerByte) & ByteMask);
223-
vniData[2] = static_cast<uint8_t>(vni & ByteMask);
204+
getGeneveHeader()->setVNI(vni);
224205
}
225206

226207
uint16_t GeneveLayer::getProtocolType() const
@@ -237,7 +218,7 @@ namespace pcpp
237218
{
238219
if (m_Data == nullptr || m_DataLen < sizeof(geneve_header))
239220
return 0;
240-
return static_cast<size_t>(getGeneveHeader()->optionsLength) * GeneveOptionAlignment;
221+
return getGeneveHeader()->getOptionsLength();
241222
}
242223

243224
size_t GeneveLayer::getHeaderLen() const
@@ -275,7 +256,7 @@ namespace pcpp
275256
}
276257

277258
size_t oldOptionsLength = getOptionsLength();
278-
if (oldOptionsLength + optionData.size() > GeneveMaxOptionsLength)
259+
if (oldOptionsLength + optionData.size() > geneve_header::MaxOptionsLength)
279260
{
280261
PCPP_LOG_ERROR("GENEVE options exceed the maximum length of 252 bytes");
281262
return false;
@@ -290,8 +271,7 @@ namespace pcpp
290271
}
291272

292273
memcpy(m_Data + offset, optionData.data(), optionSize);
293-
getGeneveHeader()->optionsLength =
294-
static_cast<uint8_t>((oldOptionsLength + optionSize) / GeneveOptionAlignment);
274+
getGeneveHeader()->setOptionsLength(oldOptionsLength + optionSize);
295275
updateCriticalFlag();
296276
return true;
297277
}
@@ -310,8 +290,7 @@ namespace pcpp
310290
if (!shortenLayer(offset, optionSize))
311291
return false;
312292

313-
getGeneveHeader()->optionsLength =
314-
static_cast<uint8_t>((oldOptionsLength - optionSize) / GeneveOptionAlignment);
293+
getGeneveHeader()->setOptionsLength(oldOptionsLength - optionSize);
315294
updateCriticalFlag();
316295
return true;
317296
}

0 commit comments

Comments
 (0)