1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-23 02:35:57 +01:00

Add VXLAN support (#501)

This patch adds a new PDU class to support VXLAN. Several VXLAN-related
tests have also been added.

Signed-off-by: James Raphael Tiovalen <jamestiotio@gmail.com>
This commit is contained in:
James R T
2023-05-04 09:21:59 +08:00
committed by GitHub
parent b7e61f4c76
commit 850bb9b642
7 changed files with 228 additions and 1 deletions

View File

@@ -180,6 +180,7 @@ public:
PKTAP, PKTAP,
MPLS, MPLS,
DOT11_CONTROL_TA, DOT11_CONTROL_TA,
VXLAN,
UNKNOWN = 999, UNKNOWN = 999,
USER_DEFINED_PDU = 1000 USER_DEFINED_PDU = 1000
}; };

View File

@@ -78,7 +78,7 @@
#include <tins/pdu_allocator.h> #include <tins/pdu_allocator.h>
#include <tins/ipsec.h> #include <tins/ipsec.h>
#include <tins/ip_reassembler.h> #include <tins/ip_reassembler.h>
#include <tins/pdu_iterator.h> #include <tins/pdu_iterator.h>
#include <tins/vxlan.h>
#endif // TINS_TINS_H #endif // TINS_TINS_H

98
include/tins/vxlan.h Normal file
View File

@@ -0,0 +1,98 @@
#ifndef TINS_VXLAN_H
#define TINS_VXLAN_H
#include <tins/pdu.h>
#include <tins/small_uint.h>
namespace Tins {
/**
* \class VXLAN
* \brief Represents a VXLAN PDU.
*
* This class represents a VXLAN PDU.
*
* \sa RawPDU
*/
class TINS_API VXLAN : public PDU {
public:
/**
* \brief This PDU's flag.
*/
static const PDU::PDUType pdu_flag = PDU::VXLAN;
/**
* \brief Constructs a VXLAN PDU.
*
* \param vni VXLAN Network Identifier.
*/
VXLAN(const small_uint<24> vni = 0);
/**
* \brief Constructs a VXLAN object from a buffer and adds
* the Ethernet II PDU.
*
* \param buffer The buffer from which this PDU will be constructed.
* \param total_sz The total size of the buffer.
*/
VXLAN(const uint8_t* buffer, uint32_t total_sz);
/**
* \brief Getter for the flags.
*/
uint8_t get_flags() const { return Endian::be_to_host(header_.flags) >> 24; }
/**
* \brief Getter for the VNI.
*/
small_uint<24> get_vni() const { return Endian::be_to_host(header_.vni) >> 8; }
/**
* \brief Setter for the flags.
* \param new_flags The new flags.
*/
void set_flags(uint8_t new_flags) { header_.flags = Endian::host_to_be(new_flags << 24); }
/**
* \brief Setter for the VNI.
* \param new_vni The new VNI.
*/
void set_vni(small_uint<24> new_vni) { header_.vni = Endian::host_to_be(new_vni << 8); }
/**
* \brief Returns the VXLAN frame's header length.
*
* This method overrides PDU::header_size. This size includes the
* payload and options size.
*
* \return An uint32_t with the header's size.
* \sa PDU::header_size
*/
uint32_t header_size() const { return sizeof(header_); }
/**
* \brief Getter for the PDU's type.
* \sa PDU::pdu_type
*/
PDUType pdu_type() const { return pdu_flag; }
/**
* \sa PDU::clone
*/
VXLAN *clone() const { return new VXLAN(*this); }
private:
TINS_BEGIN_PACK
struct vxlan_header {
uint32_t flags;
uint32_t vni;
} TINS_END_PACK;
void write_serialization(uint8_t* buffer, uint32_t total_sz);
vxlan_header header_;
};
} // Tins
#endif // TINS_VXLAN_H

View File

@@ -73,6 +73,7 @@ set(SOURCES
utils/routing_utils.cpp utils/routing_utils.cpp
utils/resolve_utils.cpp utils/resolve_utils.cpp
utils/pdu_utils.cpp utils/pdu_utils.cpp
vxlan.cpp
) )
set(HEADERS set(HEADERS

36
src/vxlan.cpp Normal file
View File

@@ -0,0 +1,36 @@
#include <tins/internals.h>
#include <tins/memory_helpers.h>
#include <tins/small_uint.h>
#include <tins/vxlan.h>
using Tins::Memory::InputMemoryStream;
using Tins::Memory::OutputMemoryStream;
namespace Tins {
VXLAN::VXLAN(const small_uint<24> vni) {
set_flags(8);
set_vni(vni);
}
VXLAN::VXLAN(const uint8_t* buffer, uint32_t total_sz) {
InputMemoryStream stream(buffer, total_sz);
stream.read(header_);
// If there is any size left
if (stream) {
inner_pdu(
Internals::pdu_from_flag(
PDU::ETHERNET_II,
stream.pointer(),
stream.size()
)
);
}
}
void VXLAN::write_serialization(uint8_t* buffer, uint32_t total_sz) {
OutputMemoryStream stream(buffer, total_sz);
stream.write(header_);
}
} // Tins

View File

@@ -70,6 +70,7 @@ CREATE_TEST(tcp)
CREATE_TEST(tcp_ip) CREATE_TEST(tcp_ip)
CREATE_TEST(udp) CREATE_TEST(udp)
CREATE_TEST(utils) CREATE_TEST(utils)
CREATE_TEST(vxlan)
IF(LIBTINS_ENABLE_PCAP) IF(LIBTINS_ENABLE_PCAP)
CREATE_TEST(offline_packet_filter) CREATE_TEST(offline_packet_filter)

90
tests/src/vxlan_test.cpp Normal file
View File

@@ -0,0 +1,90 @@
#include <gtest/gtest.h>
#include <string>
#include <tins/ethernetII.h>
#include <tins/ip.h>
#include <tins/tcp.h>
#include <tins/udp.h>
#include <tins/pdu.h>
#include <tins/small_uint.h>
#include <tins/vxlan.h>
#define PACKET_SIZE 68ul
using namespace std;
using namespace Tins;
class VXLANTest : public testing::Test {
public:
static const uint8_t expected_packet[PACKET_SIZE];
static const uint8_t flags;
static const uint16_t dport, sport, p_type;
static const small_uint<24> vni;
static const IP::address_type dst_ip, src_ip;
static const EthernetII::address_type dst_addr, src_addr;
};
const uint8_t VXLANTest::expected_packet[PACKET_SIZE] = {
0x08, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x00,
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const uint8_t VXLANTest::flags = 8;
const uint16_t VXLANTest::dport = 19627;
const uint16_t VXLANTest::sport = 4789;
const uint16_t VXLANTest::p_type = 0xd0ab;
const small_uint<24> VXLANTest::vni = 0xffffff;
const IP::address_type VXLANTest::dst_ip = IP::address_type{"2.2.2.2"};
const IP::address_type VXLANTest::src_ip = IP::address_type{"1.1.1.1"};
const EthernetII::address_type VXLANTest::dst_addr = EthernetII::address_type{"aa:bb:cc:dd:ee:ff"};
const EthernetII::address_type VXLANTest::src_addr = EthernetII::address_type{"8a:8b:8c:8d:8e:8f"};
TEST_F(VXLANTest, Flags) {
auto const vxlan = VXLAN{};
EXPECT_EQ(vxlan.get_flags(), flags);
}
TEST_F(VXLANTest, VNI) {
auto const vxlan = VXLAN{vni};
EXPECT_EQ(vxlan.get_vni(), vni);
}
TEST_F(VXLANTest, Find) {
auto const pdu = VXLAN{} / EthernetII{dst_addr, src_addr};
auto const eth = pdu.find_pdu<EthernetII>();
ASSERT_TRUE(eth != nullptr);
EXPECT_EQ(eth->dst_addr(), dst_addr);
EXPECT_EQ(eth->src_addr(), src_addr);
}
TEST_F(VXLANTest, Serialize) {
auto eth = EthernetII{dst_addr, src_addr};
eth.payload_type(p_type);
auto vxlan = VXLAN{vni};
vxlan.inner_pdu(eth);
auto serialized = vxlan.serialize();
ASSERT_EQ(serialized.size(), PACKET_SIZE);
EXPECT_TRUE(std::equal(serialized.begin(), serialized.end(), expected_packet));
}
TEST_F(VXLANTest, ConstructorFromBuffer) {
auto vxlan = VXLAN{expected_packet, PACKET_SIZE};
EXPECT_EQ(vxlan.get_vni(), vni);
EXPECT_EQ(vxlan.get_flags(), flags);
auto const eth = vxlan.find_pdu<EthernetII>();
ASSERT_TRUE(eth != nullptr);
EXPECT_EQ(eth->dst_addr(), dst_addr);
EXPECT_EQ(eth->src_addr(), src_addr);
}
TEST_F(VXLANTest, OuterUDP) {
auto pkt = IP{dst_ip, src_ip} / UDP{dport, sport} / VXLAN{expected_packet, PACKET_SIZE};
auto const vxlan = pkt.find_pdu<VXLAN>();
ASSERT_TRUE(vxlan != nullptr);
EXPECT_EQ(vxlan->get_flags(), flags);
EXPECT_EQ(vxlan->get_vni(), vni);
}