Added logic to strip 1, 2 or 3 nested vlans

This commit is contained in:
Pavel Odintsov 2024-02-01 13:36:54 +00:00
parent ed7ae8ee98
commit 78bdeae324
2 changed files with 97 additions and 87 deletions

View File

@ -130,7 +130,7 @@ class __attribute__((__packed__)) mpls_label_t {
public:
uint32_t label : 20 = 0, qos : 3 = 0, bottom_of_stack : 1 = 0, ttl : 8 = 0;
std::string print() {
std::string print() const {
std::stringstream buffer;
buffer << "label: " << uint32_t(label) << " "
@ -162,7 +162,6 @@ class __attribute__((__packed__)) ethernet_vlan_header_t {
// We can access data in packet only using special methods which can do all required format conversions
public:
// Returns ethertype in host byte order
uint16_t get_ethertype_host_byte_order() const {
return fast_ntoh(ethertype);
@ -230,7 +229,7 @@ class __attribute__((__packed__)) ethernet_header_t {
public:
// Returns ethertype in host byte order
uint16_t get_ethertype_host_byte_order() {
uint16_t get_ethertype_host_byte_order() const {
return fast_ntoh(ethertype);
}
@ -270,7 +269,6 @@ class __attribute__((__packed__)) arp_header_t {
uint32_t target_protocol_address = 0;
public:
uint8_t get_hardware_address_length() const {
return hardware_address_length;
}
@ -310,9 +308,11 @@ class __attribute__((__packed__)) arp_header_t {
<< "protocol_address_length: " << uint32_t(get_protocol_address_length()) << " "
<< "operation: " << get_operation_host_byte_order() << " "
<< "sender_hardware_address: " << convert_mac_to_string(sender_hardware_address) << " "
<< "sender_protocol_address: " << convert_ip_as_big_endian_to_string(get_sender_protocol_address_network_byte_order()) << " "
<< "sender_protocol_address: "
<< convert_ip_as_big_endian_to_string(get_sender_protocol_address_network_byte_order()) << " "
<< "target_hardware_address: " << convert_mac_to_string(target_hardware_address) << " "
<< "target_protocol_address: " << convert_ip_as_big_endian_to_string(get_target_protocol_address_network_byte_order());
<< "target_protocol_address: "
<< convert_ip_as_big_endian_to_string(get_target_protocol_address_network_byte_order());
return buffer.str();
}
@ -401,7 +401,6 @@ class __attribute__((__packed__)) gre_header_t {
uint16_t protocol_type = 0;
public:
uint16_t get_protocol_type_host_byte_order() const {
return fast_ntoh(protocol_type);
}
@ -472,7 +471,6 @@ class __attribute__((__packed__)) cropped_tcp_header_only_ports_t {
uint16_t destination_port = 0;
public:
uint16_t get_source_port_host_byte_order() const {
return fast_ntoh(source_port);
}
@ -496,13 +494,11 @@ class __attribute__((__packed__)) tcp_header_t {
uint16_t data_offset_and_flags_as_integer = 0;
private:
uint16_t window_size = 0;
uint16_t checksum = 0;
uint16_t urgent = 0;
public:
uint16_t get_source_port_host_byte_order() const {
return fast_ntoh(source_port);
}
@ -717,7 +713,6 @@ class __attribute__((__packed__)) ipv6_extension_header_fragment_t {
uint32_t identification = 0;
public:
uint16_t get_more_fragments() const {
uint16_t flags_little_endian = fast_ntoh(fragmentation_and_flags_as_integer);
@ -876,7 +871,6 @@ static_assert(sizeof(ipv4_header_fragmentation_flags_t) == 2, "Bad size for ipv4
class __attribute__((__packed__)) ipv4_header_t {
// We must not access these fields directly as they need conversion
private:
uint8_t ihl : 4 = 0, version : 4 = 0;
uint8_t ecn : 2 = 0, dscp : 6 = 0;
@ -896,7 +890,6 @@ class __attribute__((__packed__)) ipv4_header_t {
uint32_t destination_ip = 0;
public:
ipv4_header_t()
: ihl(0), version(0), ecn(0), dscp(0), total_length(0), identification(0), fragmentation_details_as_integer(0),
ttl(0), protocol(0), checksum(0), source_ip(0), destination_ip(0) {
@ -910,7 +903,7 @@ class __attribute__((__packed__)) ipv4_header_t {
return fast_ntoh(total_length);
}
bool is_fragmented() {
bool is_fragmented() const {
if (this->get_more_fragments_flag()) {
return true;
}
@ -1082,7 +1075,8 @@ enum class parser_code_t {
no_ipv6_support,
no_ipv6_options_support,
unknown_ethertype,
arp
arp,
too_many_nested_vlans,
};
std::string parser_code_to_string(parser_code_t code);

View File

@ -5,8 +5,8 @@
#include "fast_library.hpp"
#include <algorithm>
#include <iterator>
#include <cstring>
#include <iterator>
using namespace network_data_stuctures;
@ -14,6 +14,9 @@ using namespace network_data_stuctures;
// TODO: it's not working code yet
bool decode_mpls = false;
// We can strip only 3 nested vlans
const uint32_t maximum_vlans_to_strip = 3;
// Our own native function to convert wire packet into simple_packet_t
parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
int length_before_sampling,
@ -32,30 +35,44 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
return parser_code_t::memory_violation;
}
ethernet_header_t* ethernet_header = (ethernet_header_t*)local_pointer;
const ethernet_header_t* ethernet_header = (const ethernet_header_t*)local_pointer;
// Copy Ethernet MAC addresses to packet structure using native C++ approach to avoid touching memory with memcpy
std::copy(std::begin(ethernet_header->source_mac), std::end(ethernet_header->source_mac), std::begin(packet.source_mac));
std::copy(std::begin(ethernet_header->destination_mac), std::end(ethernet_header->destination_mac), std::begin(packet.destination_mac));
std::copy(std::begin(ethernet_header->destination_mac), std::end(ethernet_header->destination_mac),
std::begin(packet.destination_mac));
local_pointer += sizeof(ethernet_header_t);
// Copy ethertype as we may need to change it below
uint16_t ethertype = ethernet_header->get_ethertype_host_byte_order();
if (ethertype == IanaEthertypeVLAN) {
// Return error if it shorter then vlan header
uint32_t number_of_stripped_vlans = 0;
// This loop will not start if ethertype is not VLAN
while (ethertype == IanaEthertypeVLAN) {
// Return error if it's shorter than vlan header
if (local_pointer + sizeof(ethernet_vlan_header_t) > end_pointer) {
return parser_code_t::memory_violation;
}
ethernet_vlan_header_t* ethernet_vlan_header = (ethernet_vlan_header_t*)local_pointer;
const ethernet_vlan_header_t* ethernet_vlan_header = (const ethernet_vlan_header_t*)local_pointer;
// We've agreed that this field keeps only outermost vlan
if (number_of_stripped_vlans == 0) {
packet.vlan = ethernet_vlan_header->get_vlan_id_host_byte_order();
}
local_pointer += sizeof(ethernet_vlan_header_t);
number_of_stripped_vlans++;
// We need to limit it to avoid possibility of attack which uses too many vlans tags to overload our parser
if (number_of_stripped_vlans > maximum_vlans_to_strip) {
return parser_code_t::too_many_nested_vlans;
}
// Change ethertype to vlan's ethertype
ethertype = ethernet_vlan_header->get_ethertype_host_byte_order();
}
@ -67,7 +84,7 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
return parser_code_t::memory_violation;
}
mpls_label_t* mpls_label_header = (mpls_label_t*)local_pointer;
const mpls_label_t* mpls_label_header = (const mpls_label_t*)local_pointer;
std::cout << "MPLS header: " << mpls_label_header->print() << std::endl;
@ -90,7 +107,7 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
return parser_code_t::memory_violation;
}
ipv4_header_t* ipv4_header = (ipv4_header_t*)local_pointer;
const ipv4_header_t* ipv4_header = (const ipv4_header_t*)local_pointer;
// Use network representation of addresses
packet.src_ip = ipv4_header->get_source_ip_network_byte_order();
@ -145,7 +162,7 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
return parser_code_t::memory_violation;
}
ipv6_header_t* ipv6_header = (ipv6_header_t*)local_pointer;
const ipv6_header_t* ipv6_header = (const ipv6_header_t*)local_pointer;
// TODO: we may use std::copy for it to avoid touching memory with memcpy
memcpy(&packet.src_ipv6, ipv6_header->source_address, sizeof(packet.src_ipv6));
@ -196,7 +213,7 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
// We decided to parse only fragmentation header option as only this field may be found in the Wild
if (protocol == IpProtocolNumberIPV6_FRAG) {
ipv6_extension_header_fragment_t* ipv6_extension_header_fragment = (ipv6_extension_header_fragment_t*)local_pointer;
const ipv6_extension_header_fragment_t* ipv6_extension_header_fragment = (const ipv6_extension_header_fragment_t*)local_pointer;
// If we received this header then we assume that packet was fragmented
packet.ip_fragmented = true;
@ -224,8 +241,8 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
if (protocol == IpProtocolNumberTCP) {
if (local_pointer + sizeof(tcp_header_t) > end_pointer) {
// We observed that Huawei routers may send only first 52 bytes of header in sFlow mode
// and it's not enough to accommodate whole TCP header which has length of 20 bytes and we can observe only first 14 bytes
// and it happened in case of vlan presence
// and it's not enough to accommodate whole TCP header which has length of 20 bytes and we can observe only
// first 14 bytes and it happened in case of vlan presence
// To offer better experience we will try retrieving only ports from TCP header as they're located in the beginning of packet
if (local_pointer + sizeof(cropped_tcp_header_only_ports_t) > end_pointer) {
@ -235,7 +252,7 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
}
// Use short TCP header which has only access to source and destination ports
cropped_tcp_header_only_ports_t* tcp_header = (cropped_tcp_header_only_ports_t*)local_pointer;
const cropped_tcp_header_only_ports_t* tcp_header = (const cropped_tcp_header_only_ports_t*)local_pointer;
packet.source_port = tcp_header->get_source_port_host_byte_order();
packet.destination_port = tcp_header->get_destination_port_host_byte_order();
@ -243,7 +260,7 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
return parser_code_t::success;
}
tcp_header_t* tcp_header = (tcp_header_t*)local_pointer;
const tcp_header_t* tcp_header = (const tcp_header_t*)local_pointer;
packet.source_port = tcp_header->get_source_port_host_byte_order();
packet.destination_port = tcp_header->get_destination_port_host_byte_order();
@ -257,7 +274,7 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
return parser_code_t::memory_violation;
}
udp_header_t* udp_header = (udp_header_t*)local_pointer;
const udp_header_t* udp_header = (const udp_header_t*)local_pointer;
packet.source_port = udp_header->get_source_port_host_byte_order();
packet.destination_port = udp_header->get_destination_port_host_byte_order();
@ -271,7 +288,7 @@ parser_code_t parse_raw_packet_to_simple_packet_full_ng(const uint8_t* pointer,
return parser_code_t::memory_violation;
}
gre_header_t* gre_header = (gre_header_t*)local_pointer;
const gre_header_t* gre_header = (const gre_header_t*)local_pointer;
// Current version of parser does not handle these special codes and we just fail parsing process
// These flags may extend length of GRE header and current logic is not ready to decode any of them
@ -409,4 +426,3 @@ parser_code_t parse_raw_ipv4_packet_to_simple_packet_full_ng(const uint8_t* poin
return parser_code_t::success;
}