Skip to content

Commit fa79582

Browse files
laudrupmfontanini
authored andcommitted
Add parsing of well known IPv6 extension headers (#287)
* Add parsing of well known IPv6 extension headers Add classes for IPv6 extension headers defined in the IPv6 protocol specification (RFC 2460) as well as functions for creating them from the IPv6 class' ext_header type. The currently known extension headers are Hop-By-Hop Option, Destination Routing, Routing and Fragment. * Cleanup after PR #287 comments Pull in stuff from the std namespace with "using" instead of qualifying with std::. Keep starting braces on the same line. Avoid potential copy when appending to vector.
1 parent 342e2c7 commit fa79582

File tree

4 files changed

+180
-0
lines changed

4 files changed

+180
-0
lines changed

include/tins/exceptions.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ class option_payload_too_large : public exception_base {
204204
option_payload_too_large() : exception_base("Option payload too large") { }
205205
};
206206

207+
/**
208+
* \brief Exception thrown when an IPv6 extension header is being
209+
* created from invalid data
210+
*/
211+
class invalid_ipv6_extension_header : public exception_base {
212+
public:
213+
invalid_ipv6_extension_header() : exception_base("Invalid IPv6 extension header") { }
214+
};
215+
207216
/**
208217
* \brief Generic pcap error
209218
*/

include/tins/ipv6.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ class TINS_API IPv6 : public PDU {
7373
*/
7474
typedef std::vector<ext_header> headers_type;
7575

76+
/**
77+
* The type used to store an extension header option.
78+
*/
79+
typedef std::pair<uint8_t, std::vector<uint8_t> > header_option_type;
80+
7681
/**
7782
* The values used to identify extension headers.
7883
*/
@@ -105,6 +110,46 @@ class TINS_API IPv6 : public PDU {
105110
*/
106111
static metadata extract_metadata(const uint8_t *buffer, uint32_t total_sz);
107112

113+
/*
114+
* \brief The type used to store Hop-By-Hop Extension Headers
115+
*/
116+
struct hop_by_hop_header {
117+
std::vector<header_option_type> options;
118+
119+
static hop_by_hop_header from_extension_header(const ext_header& hdr);
120+
};
121+
122+
/*
123+
* \brief The type used to store Destination Routing Extension Headers
124+
*/
125+
struct destination_routing_header {
126+
std::vector<header_option_type> options;
127+
128+
static destination_routing_header from_extension_header(const ext_header& hdr);
129+
};
130+
131+
/**
132+
* \brief The type used to store Routing Extension headers
133+
*/
134+
struct routing_header {
135+
uint8_t routing_type;
136+
uint8_t segments_left;
137+
std::vector<uint8_t> data;
138+
139+
static routing_header from_extension_header(const ext_header& hdr);
140+
};
141+
142+
/**
143+
* \brief The type used to store Fragment Extension headers
144+
*/
145+
struct fragment_header {
146+
uint16_t fragment_offset;
147+
bool more_fragments;
148+
uint32_t identification;
149+
150+
static fragment_header from_extension_header(const ext_header& hdr);
151+
};
152+
108153
/**
109154
* \brief Constructs an IPv6 object.
110155
*
@@ -365,6 +410,7 @@ class TINS_API IPv6 : public PDU {
365410
static void write_header(const ext_header& header, Memory::OutputMemoryStream& stream);
366411
static bool is_extension_header(uint8_t header_id);
367412
static uint32_t get_padding_size(const ext_header& header);
413+
static std::vector<header_option_type> parse_header_options(const uint8_t* data, size_t size);
368414

369415
TINS_BEGIN_PACK
370416
struct ipv6_header {

src/ipv6.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <tins/memory_helpers.h>
4444
#include <tins/detail/pdu_helpers.h>
4545

46+
using std::make_pair;
4647
using std::vector;
4748

4849
using Tins::Memory::InputMemoryStream;
@@ -69,6 +70,49 @@ PDU::metadata IPv6::extract_metadata(const uint8_t *buffer, uint32_t total_sz) {
6970
return metadata(header_size, pdu_flag, PDU::UNKNOWN);
7071
}
7172

73+
IPv6::hop_by_hop_header IPv6::hop_by_hop_header::from_extension_header(const ext_header& hdr) {
74+
if (TINS_UNLIKELY(hdr.option() != HOP_BY_HOP)) {
75+
throw invalid_ipv6_extension_header();
76+
}
77+
hop_by_hop_header header;
78+
header.options = parse_header_options(hdr.data_ptr(), hdr.data_size());
79+
return header;
80+
}
81+
82+
IPv6::destination_routing_header IPv6::destination_routing_header::from_extension_header(const ext_header& hdr) {
83+
if (TINS_UNLIKELY(hdr.option() != DESTINATION_ROUTING_OPTIONS)) {
84+
throw invalid_ipv6_extension_header();
85+
}
86+
destination_routing_header header;
87+
header.options = parse_header_options(hdr.data_ptr(), hdr.data_size());
88+
return header;
89+
}
90+
91+
IPv6::routing_header IPv6::routing_header::from_extension_header(const ext_header& hdr) {
92+
if (TINS_UNLIKELY(hdr.option() != ROUTING)) {
93+
throw invalid_ipv6_extension_header();
94+
}
95+
Memory::InputMemoryStream stream(hdr.data_ptr(), hdr.data_size());
96+
routing_header header;
97+
header.routing_type = stream.read<uint8_t>();
98+
header.segments_left = stream.read<uint8_t>();
99+
header.data.assign(stream.pointer(), stream.pointer() + stream.size());
100+
return header;
101+
}
102+
103+
IPv6::fragment_header IPv6::fragment_header::from_extension_header(const ext_header& hdr) {
104+
if (TINS_UNLIKELY(hdr.option() != FRAGMENT)) {
105+
throw invalid_ipv6_extension_header();
106+
}
107+
Memory::InputMemoryStream stream(hdr.data_ptr(), hdr.data_size());
108+
fragment_header header;
109+
uint16_t field = stream.read_be<uint16_t>();
110+
header.fragment_offset = field >> 3;
111+
header.more_fragments = field & 1;
112+
header.identification = stream.read_be<uint32_t>();
113+
return header;
114+
}
115+
72116
IPv6::IPv6(address_type ip_dst, address_type ip_src, PDU* /*child*/)
73117
: header_(), next_header_() {
74118
version(6);
@@ -169,6 +213,33 @@ uint32_t IPv6::get_padding_size(const ext_header& header) {
169213
return padding == 0 ? 0 : (8 - padding);
170214
}
171215

216+
vector<IPv6::header_option_type> IPv6::parse_header_options(const uint8_t* data, size_t size) {
217+
Memory::InputMemoryStream stream(data, size);
218+
vector<header_option_type> options;
219+
220+
while (stream.size() > 0) {
221+
try {
222+
uint8_t option = stream.read<uint8_t>();
223+
if (option == PAD_1) {
224+
continue;
225+
}
226+
uint8_t size = stream.read<uint8_t>();
227+
if (size > stream.size()) {
228+
throw invalid_ipv6_extension_header();
229+
}
230+
if (option != PAD_N) {
231+
options.push_back(make_pair(option, vector<uint8_t>(stream.pointer(),
232+
stream.pointer() +
233+
size)));
234+
}
235+
stream.skip(size);
236+
} catch (const malformed_packet&) {
237+
throw invalid_ipv6_extension_header();
238+
}
239+
}
240+
return options;
241+
}
242+
172243
void IPv6::version(small_uint<4> new_version) {
173244
header_.version = new_version;
174245
}

tests/src/ipv6_test.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,57 @@ TEST_F(IPv6Test, HopByHopPadding) {
366366
ipv6_header.add_header(IPv6::ExtensionHeader::HOP_BY_HOP);
367367
EXPECT_EQ(48UL, ipv6_header.serialize().size());
368368
}
369+
370+
TEST_F(IPv6Test, HopByHopParsing) {
371+
EthernetII pkt(hop_by_hop_options, sizeof(hop_by_hop_options));
372+
IPv6& ipv6 = pkt.rfind_pdu<IPv6>();
373+
374+
const IPv6::headers_type& headers = ipv6.headers();
375+
EXPECT_EQ(1UL, headers.size());
376+
377+
const IPv6::ext_header* ext_header = ipv6.search_header(IPv6::ExtensionHeader::HOP_BY_HOP);
378+
EXPECT_TRUE(ext_header != NULL);
379+
380+
const IPv6::hop_by_hop_header hbh_header = IPv6::hop_by_hop_header::from_extension_header(*ext_header);
381+
EXPECT_EQ(1UL, hbh_header.options.size());
382+
EXPECT_EQ(5, hbh_header.options[0].first);
383+
}
384+
385+
TEST_F(IPv6Test, HopByHopExtensionHeader) {
386+
const uint8_t options[] = {42, 3, 0, 0, 0, 86, 0, 17, 2, 0, 0, 1, 2, 0, 0};
387+
IPv6::ext_header hdr(IPv6::HOP_BY_HOP, options, options + sizeof(options));
388+
389+
IPv6::hop_by_hop_header header = IPv6::hop_by_hop_header::from_extension_header(hdr);
390+
EXPECT_EQ(3UL, header.options.size());
391+
EXPECT_EQ(42, header.options[0].first);
392+
EXPECT_EQ(86, header.options[1].first);
393+
EXPECT_EQ(17, header.options[2].first);
394+
}
395+
396+
TEST_F(IPv6Test, DestinationRoutingExtensionHeader) {
397+
EXPECT_THROW(IPv6::destination_routing_header::from_extension_header(IPv6::HOP_BY_HOP),
398+
invalid_ipv6_extension_header);
399+
400+
IPv6::destination_routing_header header = IPv6::destination_routing_header::from_extension_header(IPv6::DESTINATION_ROUTING_OPTIONS);
401+
EXPECT_EQ(0UL, header.options.size());
402+
}
403+
404+
TEST_F(IPv6Test, RoutingExtensionHeader) {
405+
const uint8_t header_data[] = {42, 17, 0, 0, 0, 0, 0};
406+
IPv6::ext_header hdr(IPv6::ROUTING, header_data, header_data + sizeof(header_data));
407+
408+
IPv6::routing_header header = IPv6::routing_header::from_extension_header(hdr);
409+
EXPECT_EQ(42, header.routing_type);
410+
EXPECT_EQ(17, header.segments_left);
411+
EXPECT_EQ(5UL, header.data.size());
412+
}
413+
414+
TEST_F(IPv6Test, FragmentExtensionHeader) {
415+
const uint8_t header_data[] = {128, 1, 0, 0, 0, 42};
416+
IPv6::ext_header hdr(IPv6::FRAGMENT, header_data, header_data + sizeof(header_data));
417+
418+
IPv6::fragment_header header = IPv6::fragment_header::from_extension_header(hdr);
419+
EXPECT_EQ(4096, header.fragment_offset);
420+
EXPECT_TRUE(header.more_fragments);
421+
EXPECT_EQ(42UL, header.identification);
422+
}

0 commit comments

Comments
 (0)