From 324d156cf3380de7e94458aed44ed681adddbb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 12 Jun 2020 17:36:55 +0200 Subject: [PATCH] Redefine VpnPackets as TLV containers --- VpnPacket.cpp | 60 +++++++++++++++++++- VpnPacket.hpp | 153 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 206 insertions(+), 7 deletions(-) diff --git a/VpnPacket.cpp b/VpnPacket.cpp index be34717..715ecc7 100644 --- a/VpnPacket.cpp +++ b/VpnPacket.cpp @@ -2,8 +2,10 @@ #include "VpnPeer.hpp" #include +#include const size_t VpnPacket::VPN_HEADER_BYTES = 8; +const size_t VpnPacket::TLV_HEADER_BYTES = 3; uint32_t VpnPacket::_next_general_seqno = 0; static const size_t OUTER_HEADERS_BYTES = @@ -15,8 +17,8 @@ static const int DATA_TIMESTAMP_POS = 4; VpnPacket::VpnPacket(size_t mtu) - : _peer(nullptr), _data_space(mtu-OUTER_HEADERS_BYTES), _data_size(0), - _reception_timestamp(0) + : _peer(nullptr), _data_space(mtu-OUTER_HEADERS_BYTES), + _data_size(VPN_HEADER_BYTES), _reception_timestamp(0) { _data = new char[mtu - OUTER_HEADERS_BYTES]; } @@ -25,6 +27,13 @@ VpnPacket::~VpnPacket() { delete[] _data; } +VpnPacket::iterator VpnPacket::begin() { + return iterator(VpnPacketTLV(*this, 0)); +} +VpnPacket::iterator VpnPacket::end() { + return iterator(VpnPacketTLV(*this, get_payload_size())); +} + bool VpnPacket::parse_as_ipv6() { return parse_ipv6_header(get_payload(), get_payload_size(), _ipv6_header); } @@ -55,3 +64,50 @@ uint32_t VpnPacket::next_seqno() { return _peer->next_seqno(); return _next_general_seqno++; } + + +VpnPacketTLV::VpnPacketTLV(VpnPacket& packet, size_t payload_offset) + : _packet(packet), _tlv_pos(payload_offset) +{} + +VpnPacketTLV VpnPacketTLV::create( + VpnPacket& packet, VpnPacket::PayloadType type) +{ + VpnPacketTLV tlv = VpnPacketTLV(packet, packet.get_payload_size()); + packet.increase_payload_size(VpnPacket::TLV_HEADER_BYTES); + + char* data = tlv.get_data(); + data[0] = type; + *(uint16_t*)(data+1) = 0; // Set len to 0 + + return tlv; +} + +uint16_t VpnPacketTLV::get_payload_size() const { + return *(uint16_t*)(get_data() + 1); +} +void VpnPacketTLV::set_payload_size(uint16_t size) { + uint16_t* data_size_ptr = (uint16_t*)(get_data() + 1); + uint16_t old_size = *data_size_ptr; + *data_size_ptr = size; + _packet.increase_payload_size(size - old_size); +} + +VpnPacket::PayloadType VpnPacketTLV::get_type() const { + return (VpnPacket::PayloadType)(*(uint8_t*)(get_data())); +} +void VpnPacketTLV::set_type(VpnPacket::PayloadType type) { + *(uint8_t*)(get_data()) = (uint8_t) type; +} + +TunnelledPacket::TunnelledPacket(VpnPacket& packet, size_t payload_offset) + : VpnPacketTLV(packet, payload_offset) +{} + +TunnelledPacket::TunnelledPacket(const VpnPacketTLV& copy) + : VpnPacketTLV(copy._packet, copy._tlv_pos) +{} + +TunnelledPacket TunnelledPacket::create(VpnPacket& packet) { + return VpnPacketTLV::create(packet, VpnPacket::PAYLOAD_TYPE_TUNNELLED); +} diff --git a/VpnPacket.hpp b/VpnPacket.hpp index e820c89..7114c9e 100644 --- a/VpnPacket.hpp +++ b/VpnPacket.hpp @@ -9,24 +9,53 @@ /** VPN packet layout: * - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | 1B | 1B | 1B | 1B | 1B | 1B | 1B | 1B | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Sequence number [4B] | Sending timestamp (μs) [4B] | + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +---------------+---------------+---------------+---------------+ + * | Sequence number [4B] | * +---------------------------------------------------------------+ - * | Nested packet (payload) | + * | Sending timestamp (μs) [4B] | + * +---------------+-----------------------------------------------+ + * | Type [1B] | First payload size (B) [2B] | Payload | + * +---------------+-----------------------------------------------+ + * | Payload (cont.) | + * +---------------+-----------------------------------------------+ + * | Type [1B] | Second payload size (B) [2B] | Payload | + * +---------------+-----------------------------------------------+ + * | Payload (cont.) | * +---------------------------------------------------------------+ + * | ... | + * +---------------------------------------------------------------+ + * + * Where + * - Type is one of the values from PayloadType below; + * - Payload size is the size of the payload (excluding headers), in bytes; + * - Payload is an arbitrary value, defined by Type */ class VpnPeer; +class VpnPacketTLVIterator; class VpnPacket { public: static const size_t VPN_HEADER_BYTES; + static const size_t TLV_HEADER_BYTES; + + typedef VpnPacketTLVIterator iterator; + + enum PayloadType { + PAYLOAD_TYPE_UNDEF, ///< Undefined packet type + PAYLOAD_TYPE_TUNNELLED, ///< A tunnelled packet + PAYLOAD_TYPE_RR, ///< Receiver report + PAYLOAD_TYPE_REMB, ///< Receiver Estimated Maximum Bitrate + }; VpnPacket(size_t mtu); ~VpnPacket(); + iterator begin(); + iterator end(); + /// Set packet peer -- used for sequence numbers void set_peer(std::shared_ptr peer) { _peer = peer; } @@ -35,23 +64,42 @@ class VpnPacket { bool ipv6_parsed() const { return _ipv6_parsed; } const IPv6Header& get_ipv6_header() const { return _ipv6_header; } + /// Get a pointer to the packet payload (const version) const char* get_payload() const { return _data + VPN_HEADER_BYTES; } + /// Get a pointer to the packet payload char* get_payload() { return _data + VPN_HEADER_BYTES; } + /// Get a pointer to the first free byte of the packet payload + char* get_next_payload() { return get_payload() + get_payload_size(); } + /// Get the space allocated for the packet payload size_t get_payload_space() const { return _data_space - VPN_HEADER_BYTES; } + /// Get the current size filled by the payload size_t get_payload_size() const { return _data_size - VPN_HEADER_BYTES; } + /// Set current size filled by the packet payload void set_payload_size(size_t payload_size) { _data_size = payload_size + VPN_HEADER_BYTES; } + /// Increase the current size filled by the packet payload (size may be + /// negative). + void increase_payload_size(ssize_t payload_size_increment) { + _data_size += payload_size_increment; } + /// Get a pointer to the full packet data (const version) const char* get_data() const { return _data; } + /// Get a pointer to the full packet data char* get_data() { return _data; } + /// Get the space allocated for the packet size_t get_data_space() const { return _data_space; } + /// Get the total current size of the packet size_t get_data_size() const { return _data_size; } + /// Set the total current size of the packet void set_data_size(size_t data_size) { _data_size = data_size; } + /// Get this packet's seqno uint32_t get_seqno() const; + /// Get this packet's sending timestamp uint32_t get_sending_timestamp() const; + /// Get this packet's reception timestamp uint32_t get_reception_timestamp() const { return _reception_timestamp; } /** Fill the headers of the packet. This method must be called as close @@ -81,3 +129,98 @@ class VpnPacket { static uint32_t _next_general_seqno; }; + +/** Base class for a TLV contained in a VpnPacket */ +class VpnPacketTLV { + public: + VpnPacketTLV(VpnPacket& packet, size_t payload_offset); + + static VpnPacketTLV create( + VpnPacket& packet, + VpnPacket::PayloadType type=VpnPacket::PAYLOAD_TYPE_UNDEF); + + const VpnPacket& get_packet() const { return _packet; } + VpnPacket& get_packet() { return _packet; } + + /// Get the offset in the packet + size_t get_offset() const { return _tlv_pos; } + + /// Get a pointer to the payload (const version) + const char* get_payload() const { + return _packet.get_payload() + _tlv_pos + VpnPacket::TLV_HEADER_BYTES; } + /// Get a pointer to the payload + char* get_payload() { + return _packet.get_payload() + _tlv_pos + VpnPacket::TLV_HEADER_BYTES; } + /// Get the current payload size + uint16_t get_payload_size() const; + /// Set the current payload size + void set_payload_size(uint16_t size); + + /// Get a pointer to the raw data (const version) + const char* get_data() const { + return _packet.get_payload() + _tlv_pos; } + /// Get a pointer to the raw data + char* get_data() { return _packet.get_payload() + _tlv_pos; } + /// Get the current raw data size + uint16_t get_data_size() const { + return get_payload_size() + VpnPacket::TLV_HEADER_BYTES; } + /// Set the current raw data size + void set_data_size(uint16_t size) { + set_payload_size(size + VpnPacket::TLV_HEADER_BYTES); } + + /// Get this TLV's type + VpnPacket::PayloadType get_type() const; + + bool operator==(const VpnPacketTLV& other) const { + return &_packet == &other._packet && _tlv_pos == other._tlv_pos; } + + protected: // meth + /// Set this TLV's type + void set_type(VpnPacket::PayloadType type); + + protected: + VpnPacket& _packet; + size_t _tlv_pos; + + friend class TunnelledPacket; +}; + +/** An iterator over the VpnPacketTLVs of a VpnPacket */ +class VpnPacketTLVIterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = VpnPacketTLV; + using difference_type = ptrdiff_t; + using pointer = VpnPacketTLV*; + using reference = VpnPacketTLV&; + + VpnPacketTLVIterator(const VpnPacketTLV& tlv) : _tlv(tlv) {} + + VpnPacketTLVIterator& operator++(); + VpnPacketTLVIterator operator++(int) { + VpnPacketTLVIterator tmp(*this); + operator++(); + return tmp; + } + + bool operator==(const VpnPacketTLVIterator& other) { + return _tlv == other._tlv; } + bool operator!=(const VpnPacketTLVIterator& other) { + return !(operator==(other)); } + bool past_the_end() const { + return _tlv.get_offset() >= _tlv.get_packet().get_payload_size(); } + reference operator*() { return _tlv; } + + private: + VpnPacketTLV _tlv; +}; + +/** A packet sent through the VPN tunnel. + * + * This must instantiated just before filling it with data. */ +class TunnelledPacket: public VpnPacketTLV { + public: + TunnelledPacket(VpnPacket& packet, size_t payload_offset); + TunnelledPacket(const VpnPacketTLV& copy); + static TunnelledPacket create(VpnPacket& packet); +};