Add GENEVE protocol layer - #2239
alacrity-aya wants to merge 6 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #2239 +/- ##
==========================================
+ Coverage 82.61% 82.66% +0.05%
==========================================
Files 339 342 +3
Lines 61875 62425 +550
Branches 13036 13114 +78
==========================================
+ Hits 51115 51602 +487
- Misses 9877 9948 +71
+ Partials 883 875 -8
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
d50ef12 to
78e3b7c
Compare
| /// GENEVE protocol | ||
| const ProtocolType Geneve = 65; |
There was a problem hiding this comment.
Can you move it below QUICv1 to keep the numerical order?
There was a problem hiding this comment.
Can you please update the README.md file and all of its translations?
| size_t getOptionsLength() const | ||
| { | ||
| return static_cast<size_t>(optionsLength) * OptionsLengthUnit; | ||
| } | ||
|
|
||
| /// @param[in] value Options length in bytes | ||
| /// @pre value must be divisible by 4 and no greater than MaxOptionsLength | ||
| void setOptionsLength(size_t value) | ||
| { | ||
| optionsLength = static_cast<uint8_t>(value / OptionsLengthUnit); | ||
| } | ||
|
|
||
| /// @return The 24-bit virtual network identifier | ||
| uint32_t getVNI() const | ||
| { | ||
| return (static_cast<uint32_t>(vni[0]) << 16) | (static_cast<uint32_t>(vni[1]) << 8) | vni[2]; | ||
| } | ||
|
|
||
| /// @param[in] value The 24-bit virtual network identifier | ||
| void setVNI(uint32_t value) | ||
| { | ||
| vni[0] = static_cast<uint8_t>((value >> 16) & 0xff); | ||
| vni[1] = static_cast<uint8_t>((value >> 8) & 0xff); | ||
| vni[2] = static_cast<uint8_t>(value & 0xff); | ||
| } |
There was a problem hiding this comment.
We usually keep the struct simple and keep these methods only on the layer class which is the main interface for the protocol. Moreover, we can keep the struct private inside GeneveLayer.
Same goes for geneve_option_header that we can keep as private inside GeneveOption and move the struct's methods to the class.
|
|
||
| uint16_t GeneveLayer::getProtocolType() const | ||
| { | ||
| return be16toh(getGeneveHeader()->protocolType); |
There was a problem hiding this comment.
We have wrapper methods in SystemUtils.h so we don't need to include "EndianPortable.h" directly
| if (m_Data == nullptr || m_DataLen < sizeof(geneve_header)) | ||
| return 0; |
There was a problem hiding this comment.
This check is probably not needed because PcapPlusPlus or users shouldn't be able to generate a malformed layer.
Ditto in getHeaderLen() and in getOptions()
| /// @class GeneveOptionRange | ||
| /// A non-owning range of GENEVE options. The range and all iterators obtained from it are invalidated when the | ||
| /// containing GeneveLayer is modified or destroyed | ||
| class GeneveOptionRange |
There was a problem hiding this comment.
The pattern we usually use in PcapPlusPlus is getFirstOption() and getNextOption(option) directly on the layer. We usually also expose getOption(type) on the layer. This approach requires much less code (which makes it simpler) and achieves the same goals
| GeneveOptionBuilder(uint16_t optionClass, uint8_t optionType, const uint8_t* optionData, uint8_t optionDataLen, | ||
| bool critical = false) | ||
| : TLVRecordBuilder(optionType, optionData, optionDataLen), m_OptionClass(optionClass), m_Critical(critical) |
There was a problem hiding this comment.
I'd vote for a "builder" class only if we have multiple c'tors with different types of data. Currently there's only one c'tor that accepts a byte array. If we plan to keep it like this we can probably move these parameters to addOption() and build it there directly
|
|
||
| const uint8_t* option = data + sizeof(geneve_header); | ||
| size_t remaining = optionsLength; | ||
| while (remaining > 0) |
There was a problem hiding this comment.
I don't think we need to parse all the options inside of isDataValid() which is part of the fast path. Instead, the methods to parse and return the options should take into account that the data might be malformed
| /// @return True if all options were removed | ||
| bool removeAllOptions(); | ||
|
|
||
| /// Parse the encapsulated protocol according to the Protocol Type field |
There was a problem hiding this comment.
I'd mention which layers we support parsing as the next layer
There was a problem hiding this comment.
This tests is written in an older style that we don't use anymore. I'd propose a few changes:
- Separate the parsing, creation and update to different tests
- For the parsing test, get a sample pcap with real packet(s), create
.datfiles for each packet using Wireshark's "Copy as a hex stream" (see the screenshot), read them inside the test and make sure the packet is parsed correctly. You can see other tests as references, try to look at newer tests that were written more recently - We use a "sub-test" convention to separate different things in the same test. You can look at
QuicTests.cppas an example - Try to make sure you cover as much as the code as possible. You can look at the Codecov report to see which lines and use-cases are covered
|
@seladb Thanks for the review! I'm a little tied up at the moment, but I'll update the PR as soon as I have a free slot over the next few days. |
Summary
Add parsing, validation, crafting, and editing support for GENEVE packets.
Features
GeneveLayerparsing and packet creation support.API Example
Validation
Malformed packets are rejected when they contain:
Notes
Partially addresses #1712.
TODO
missing translation