Théophile Bastian
7b5ffa4d46
Still lacks an outbound bandwidth limiter and outbound actual emission bitrate, to avoid increasing the available bandwidth when the bandwidth is not saturated.
289 lines
11 KiB
C++
289 lines
11 KiB
C++
#pragma once
|
|
|
|
/** A packet to be transmitted or received over the VPN socket */
|
|
|
|
#include <cstdlib>
|
|
#include <memory>
|
|
|
|
#include "ip_header.hpp"
|
|
|
|
/** VPN packet layout:
|
|
*
|
|
* 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] |
|
|
* +---------------------------------------------------------------+
|
|
* |C| Sending timestamp (μs) [4B-1b] |
|
|
* +---------------+-----------------------------------------------+
|
|
* | ... |
|
|
* +---------------------------------------------------------------+
|
|
*
|
|
* Where C is a single bit. If set, the packet is a control packet, containing
|
|
* TLVs. If unset, the packet is a tunnelled packet bearing data to be
|
|
* reinjected.
|
|
*
|
|
* If C is set, the remaining of the packet has the following structure:
|
|
*
|
|
* +---------------+-----------------------------------------------+
|
|
* | 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;
|
|
* - Sender ID is an arbitrary value, recommended to be randomly chosen
|
|
* - Payload size is the size of the payload (excluding headers), in bytes;
|
|
* - Payload is an arbitrary value, defined by Type
|
|
*/
|
|
|
|
class VpnPeer;
|
|
class VpnPacketTLV;
|
|
|
|
class VpnPacket {
|
|
public:
|
|
static const size_t VPN_HEADER_BYTES;
|
|
class PeerNotSet: public std::exception {};
|
|
|
|
VpnPacket(size_t mtu, bool inbound);
|
|
~VpnPacket();
|
|
|
|
// Remove copy constructor and operator=
|
|
VpnPacket(const VpnPacket& copy) = delete;
|
|
VpnPacket& operator=(const VpnPacket& copy) = delete;
|
|
|
|
/// Move constructor
|
|
VpnPacket(VpnPacket&& move_from);
|
|
VpnPacket& operator=(VpnPacket&& move_from) = delete;
|
|
|
|
/// Get the maximal payload space for a given tunnel MTU
|
|
static size_t get_tunnelled_mtu(size_t udp_mtu);
|
|
|
|
/// Set packet peer -- used for sequence numbers and loss rate
|
|
void set_peer(VpnPeer* peer);
|
|
|
|
/// Checks whether the packet currently bears a payload
|
|
bool is_empty() const { return get_payload_size() == 0; }
|
|
|
|
/// Get a pointer to the packet payload (const version)
|
|
const char* get_payload() const {
|
|
return _data.get() + VPN_HEADER_BYTES; }
|
|
/// Get a pointer to the packet payload
|
|
char* get_payload() { return _data.get() + 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(); }
|
|
/// Get a pointer to the full packet data
|
|
char* get_data() { return _data.get(); }
|
|
/// 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; }
|
|
/// Check whether this packet is control or data
|
|
bool is_control() const;
|
|
|
|
/** Fill the headers of the packet. This method must be called as close
|
|
* to the time the packet is actually sent as possible, as it handles
|
|
* timestamps. */
|
|
void prepare_for_sending();
|
|
|
|
/** Do what must be done at reception, eg. keep the reception
|
|
* timestamp. This method must be called as close to the time the
|
|
* packet is actually received as possible, as it handles timestamps.
|
|
*/
|
|
void upon_reception();
|
|
|
|
protected:
|
|
void set_control(bool is_control);
|
|
|
|
private: // methods
|
|
inline uint32_t next_seqno();
|
|
|
|
private:
|
|
VpnPeer* _peer; // raw pointer: we do not own the peer in any way
|
|
bool _inbound; ///< is the packet received or sent?
|
|
|
|
std::unique_ptr<char[]> _data;
|
|
size_t _data_space, _data_size;
|
|
|
|
uint32_t _reception_timestamp;
|
|
};
|
|
|
|
class VpnControlPacket: public VpnPacket {
|
|
public:
|
|
static const size_t TLV_HEADER_BYTES;
|
|
|
|
VpnControlPacket(size_t mtu, bool inbound);
|
|
VpnControlPacket(VpnPacket&& move_from);
|
|
|
|
VpnPacketTLV first_tlv();
|
|
};
|
|
|
|
/** A packet sent through the VPN tunnel. */
|
|
class VpnDataPacket: public VpnPacket {
|
|
public:
|
|
VpnDataPacket(size_t mtu, bool inbound);
|
|
VpnDataPacket(VpnPacket&& move_from);
|
|
|
|
static VpnDataPacket create(VpnPacket& packet);
|
|
|
|
/** Try to parse the packet as IPv6, return `false` upon failure.
|
|
* Does not reparse if _ipv6_parsed, unless reparse is true
|
|
*/
|
|
bool parse_as_ipv6(bool reparse=false);
|
|
bool ipv6_parsed() const { return _ipv6_parsed; }
|
|
const IPv6Header& get_ipv6_header() const { return _ipv6_header; }
|
|
|
|
private:
|
|
bool _ipv6_parsed;
|
|
IPv6Header _ipv6_header;
|
|
};
|
|
|
|
/** Base class for a TLV contained in a VpnPacket */
|
|
class VpnPacketTLV {
|
|
public:
|
|
VpnPacketTLV(VpnControlPacket& packet, size_t payload_offset);
|
|
VpnPacketTLV(const VpnPacketTLV& other);
|
|
|
|
enum PayloadType {
|
|
PAYLOAD_TYPE_UNDEF, ///< Undefined packet type
|
|
PAYLOAD_TYPE_LOSS_REPORT, ///< Loss report
|
|
PAYLOAD_TYPE_REMB, ///< Receiver Estimated Maximum Bitrate
|
|
PAYLOAD_TYPE_RTTQ, ///< RTT update query
|
|
PAYLOAD_TYPE_RTTA, ///< RTT update answer
|
|
};
|
|
|
|
|
|
static VpnPacketTLV create(
|
|
VpnControlPacket& packet,
|
|
PayloadType type=PAYLOAD_TYPE_UNDEF);
|
|
|
|
const VpnControlPacket& get_packet() const { return _packet; }
|
|
VpnControlPacket& get_packet() { return _packet; }
|
|
|
|
/// Get the next TLV in this packet.
|
|
VpnPacketTLV next_tlv();
|
|
/// Point this object to the next TLV (ie. `next_tlv` in place)
|
|
void seek_next_tlv();
|
|
|
|
/// Check whether the current TLV is past the packet's end
|
|
bool past_the_end() const {
|
|
return _tlv_pos >= _packet.get_payload_size(); }
|
|
|
|
/// 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
|
|
+ VpnControlPacket::TLV_HEADER_BYTES; }
|
|
/// Get a pointer to the payload
|
|
char* get_payload() {
|
|
return _packet.get_payload() + _tlv_pos
|
|
+ VpnControlPacket::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 the total available raw data space
|
|
uint16_t get_payload_space() const;
|
|
|
|
/// 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() + VpnControlPacket::TLV_HEADER_BYTES; }
|
|
/// Set the current raw data size
|
|
void set_data_size(uint16_t size) {
|
|
set_payload_size(size + VpnControlPacket::TLV_HEADER_BYTES); }
|
|
|
|
/// Get this TLV's type
|
|
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(PayloadType type);
|
|
|
|
protected:
|
|
VpnControlPacket& _packet;
|
|
size_t _tlv_pos;
|
|
};
|
|
|
|
class VpnTlvLossReport: public VpnPacketTLV {
|
|
public:
|
|
VpnTlvLossReport(VpnControlPacket& packet, size_t payload_offset);
|
|
VpnTlvLossReport(const VpnPacketTLV& other);
|
|
|
|
static VpnTlvLossReport create(VpnControlPacket& packet);
|
|
|
|
uint32_t get_report_seqno() const;
|
|
void set_report_seqno(uint32_t seqno);
|
|
|
|
uint32_t get_losses() const;
|
|
void set_losses(uint32_t losses);
|
|
|
|
private:
|
|
static const uint32_t REP_SEQNO_POS, REP_LOSS_POS;
|
|
};
|
|
|
|
class VpnTlvRTTQ: public VpnPacketTLV {
|
|
public:
|
|
VpnTlvRTTQ(VpnControlPacket& packet, size_t payload_offset);
|
|
VpnTlvRTTQ(const VpnPacketTLV& other);
|
|
|
|
static VpnTlvRTTQ create(VpnControlPacket& packet);
|
|
};
|
|
|
|
class VpnTlvRTTA: public VpnPacketTLV {
|
|
public:
|
|
VpnTlvRTTA(VpnControlPacket& packet, size_t payload_offset);
|
|
VpnTlvRTTA(const VpnPacketTLV& other);
|
|
|
|
static VpnTlvRTTA create(VpnControlPacket& packet);
|
|
|
|
uint32_t get_exp_ts() const;
|
|
void set_exp_ts(uint32_t ts);
|
|
|
|
uint32_t get_recv_ts() const;
|
|
void set_recv_ts(uint32_t ts);
|
|
|
|
private:
|
|
static const uint32_t EXP_TS_POS, RECV_TS_POS;
|
|
};
|