#pragma once /** A packet to be transmitted or received over the VPN socket */ #include #include #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 _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; };