diff --git a/UdpVpn.cpp b/UdpVpn.cpp index 0363f31..69a9ec8 100644 --- a/UdpVpn.cpp +++ b/UdpVpn.cpp @@ -90,7 +90,7 @@ size_t UdpVpn::read_from_udp(char* buffer, size_t len, { ssize_t nread; socklen_t peer_addr_len = sizeof(peer_addr); - nread = recvfrom(_socket, buffer, len, MSG_WAITALL, + nread = recvfrom(_socket, buffer, len, 0, (struct sockaddr*) &peer_addr, &peer_addr_len); if(nread < 0) @@ -114,6 +114,7 @@ size_t UdpVpn::read_from_udp(char* buffer, size_t len, } size_t UdpVpn::read_from_udp(VpnPacket& packet, sockaddr_in6& peer_addr) { + packet.upon_reception(); // The packet is not read yet, but it has arrived size_t nread = read_from_udp(packet.get_data(), packet.get_data_space(), peer_addr); packet.set_data_size(nread); diff --git a/UdpVpnClient.cpp b/UdpVpnClient.cpp index fe73715..63a5bed 100644 --- a/UdpVpnClient.cpp +++ b/UdpVpnClient.cpp @@ -26,6 +26,7 @@ void UdpVpnClient::receive_from_tun() { format_address(packet.get_ipv6_header().dest.s6_addr), nread); + packet.prepare_for_sending(); write_to_server(packet); } @@ -37,7 +38,8 @@ void UdpVpnClient::receive_from_udp() { return; // Reinject into tun - kdebugf("Receiving packet of size %d from %s\n", + kdebugf("Receiving packet #%u of size %d from %s\n", + packet.get_seqno(), nread, format_address(packet.get_ipv6_header().source.s6_addr)); _tun_dev.write(packet.get_payload(), packet.get_payload_size()); diff --git a/UdpVpnServer.cpp b/UdpVpnServer.cpp index ffc4488..bb0468b 100644 --- a/UdpVpnServer.cpp +++ b/UdpVpnServer.cpp @@ -55,11 +55,13 @@ void UdpVpnServer::receive_from_tun() { format_address(peer_inner_addr.s6_addr)); return; } + packet.set_peer(peer); kdebugf("Transmitting %s -> %s, size %d\n", format_address(packet.get_ipv6_header().source.s6_addr), format_address(packet.get_ipv6_header().dest.s6_addr), nread); + packet.prepare_for_sending(); peer->write(packet); } @@ -82,11 +84,11 @@ void UdpVpnServer::receive_from_udp() { htons(peer_addr.sin6_port), format_address(peer_inner_addr.s6_addr)); } - // VpnPeer* peer = (peer_iter->second); - // TODO -- pass the packet to `peer` for a cleaner flow + packet.set_peer(peer); // Reinject into tun - kdebugf("Receiving packet of size %d from %s\n", + kdebugf("Receiving packet #%u of size %d from %s\n", + packet.get_seqno(), nread, format_address(packet.get_ipv6_header().source.s6_addr)); _tun_dev.write(packet.get_payload(), packet.get_payload_size()); diff --git a/VpnPacket.cpp b/VpnPacket.cpp index 4b4f9d6..be34717 100644 --- a/VpnPacket.cpp +++ b/VpnPacket.cpp @@ -1,15 +1,24 @@ #include "VpnPacket.hpp" +#include "VpnPeer.hpp" -const size_t VpnPacket::VPN_HEADER_SIZE = 0; +#include -static const size_t OUTER_HEADERS_SIZE = +const size_t VpnPacket::VPN_HEADER_BYTES = 8; +uint32_t VpnPacket::_next_general_seqno = 0; + +static const size_t OUTER_HEADERS_BYTES = 40 /* IPv6 header */ + 8 /* UDP header */; // We use a TUN device, hence we don't have a layer 2 header. +static const int + DATA_SEQNO_POS = 0, + DATA_TIMESTAMP_POS = 4; + VpnPacket::VpnPacket(size_t mtu) - : _data_space(mtu-OUTER_HEADERS_SIZE), _data_size(0) + : _peer(nullptr), _data_space(mtu-OUTER_HEADERS_BYTES), _data_size(0), + _reception_timestamp(0) { - _data = new char[mtu - OUTER_HEADERS_SIZE]; + _data = new char[mtu - OUTER_HEADERS_BYTES]; } VpnPacket::~VpnPacket() { @@ -19,3 +28,30 @@ VpnPacket::~VpnPacket() { bool VpnPacket::parse_as_ipv6() { return parse_ipv6_header(get_payload(), get_payload_size(), _ipv6_header); } + +uint32_t VpnPacket::get_seqno() const { + return *(uint32_t*)(_data + DATA_SEQNO_POS); +} + +uint32_t VpnPacket::get_sending_timestamp() const { + return *(uint32_t*)(_data + DATA_TIMESTAMP_POS); +} + +void VpnPacket::prepare_for_sending() { + *(uint32_t*)(_data + DATA_SEQNO_POS) = next_seqno(); + *(uint32_t*)(_data + DATA_TIMESTAMP_POS) = + std::chrono::time_point_cast( + std::chrono::steady_clock::now()).time_since_epoch().count(); +} + +void VpnPacket::upon_reception() { + _reception_timestamp = + std::chrono::time_point_cast( + std::chrono::steady_clock::now()).time_since_epoch().count(); +} + +uint32_t VpnPacket::next_seqno() { + if(_peer) + return _peer->next_seqno(); + return _next_general_seqno++; +} diff --git a/VpnPacket.hpp b/VpnPacket.hpp index 0a97567..e820c89 100644 --- a/VpnPacket.hpp +++ b/VpnPacket.hpp @@ -3,29 +3,46 @@ /** A packet to be transmitted or received over the VPN socket */ #include +#include #include "ip_header.hpp" +/** VPN packet layout: + * + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | 1B | 1B | 1B | 1B | 1B | 1B | 1B | 1B | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Sequence number [4B] | Sending timestamp (μs) [4B] | + * +---------------------------------------------------------------+ + * | Nested packet (payload) | + * +---------------------------------------------------------------+ + */ + +class VpnPeer; + class VpnPacket { public: - static const size_t VPN_HEADER_SIZE; + static const size_t VPN_HEADER_BYTES; VpnPacket(size_t mtu); ~VpnPacket(); + /// Set packet peer -- used for sequence numbers + void set_peer(std::shared_ptr peer) { _peer = peer; } + /// Try to parse the packet as IPv6, return `false` upon failure. bool parse_as_ipv6(); bool ipv6_parsed() const { return _ipv6_parsed; } const IPv6Header& get_ipv6_header() const { return _ipv6_header; } - const char* get_payload() const { return _data + VPN_HEADER_SIZE; } - char* get_payload() { return _data + VPN_HEADER_SIZE; } + const char* get_payload() const { return _data + VPN_HEADER_BYTES; } + char* get_payload() { return _data + VPN_HEADER_BYTES; } size_t get_payload_space() const { - return _data_space - VPN_HEADER_SIZE; } + return _data_space - VPN_HEADER_BYTES; } size_t get_payload_size() const { - return _data_size - VPN_HEADER_SIZE; } + return _data_size - VPN_HEADER_BYTES; } void set_payload_size(size_t payload_size) { - _data_size = payload_size + VPN_HEADER_SIZE; } + _data_size = payload_size + VPN_HEADER_BYTES; } const char* get_data() const { return _data; } char* get_data() { return _data; } @@ -33,10 +50,34 @@ class VpnPacket { size_t get_data_size() const { return _data_size; } void set_data_size(size_t data_size) { _data_size = data_size; } + uint32_t get_seqno() const; + uint32_t get_sending_timestamp() const; + uint32_t get_reception_timestamp() const { return _reception_timestamp; } + + /** 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(); + + private: // methods + inline uint32_t next_seqno(); + private: + std::shared_ptr _peer; + char* _data; size_t _data_space, _data_size; bool _ipv6_parsed; IPv6Header _ipv6_header; + + uint32_t _reception_timestamp; + + static uint32_t _next_general_seqno; }; diff --git a/VpnPeer.cpp b/VpnPeer.cpp index 12218ff..8dc7470 100644 --- a/VpnPeer.cpp +++ b/VpnPeer.cpp @@ -7,7 +7,7 @@ VpnPeer::VpnPeer(UdpVpn* vpn, const sockaddr_in6& ext_addr, const in6_addr int_addr) - : _vpn(vpn), _ext_addr(ext_addr), _int_addr(int_addr) + : _vpn(vpn), _ext_addr(ext_addr), _int_addr(int_addr), _next_seqno(0) {} diff --git a/VpnPeer.hpp b/VpnPeer.hpp index aa72c75..1e65db6 100644 --- a/VpnPeer.hpp +++ b/VpnPeer.hpp @@ -28,8 +28,12 @@ class VpnPeer { size_t write(const char* data, size_t len); size_t write(const VpnPacket& packet); + uint32_t peek_next_seqno() const { return _next_seqno; } + uint32_t next_seqno() { return _next_seqno++; } + private: UdpVpn* _vpn; sockaddr_in6 _ext_addr; in6_addr _int_addr; + uint32_t _next_seqno; };