Send packets with timestamp and seqno
This commit is contained in:
parent
5a7b108602
commit
20d0d3fc59
7 changed files with 102 additions and 16 deletions
|
@ -90,7 +90,7 @@ size_t UdpVpn::read_from_udp(char* buffer, size_t len,
|
||||||
{
|
{
|
||||||
ssize_t nread;
|
ssize_t nread;
|
||||||
socklen_t peer_addr_len = sizeof(peer_addr);
|
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);
|
(struct sockaddr*) &peer_addr, &peer_addr_len);
|
||||||
|
|
||||||
if(nread < 0)
|
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) {
|
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 =
|
size_t nread =
|
||||||
read_from_udp(packet.get_data(), packet.get_data_space(), peer_addr);
|
read_from_udp(packet.get_data(), packet.get_data_space(), peer_addr);
|
||||||
packet.set_data_size(nread);
|
packet.set_data_size(nread);
|
||||||
|
|
|
@ -26,6 +26,7 @@ void UdpVpnClient::receive_from_tun() {
|
||||||
format_address(packet.get_ipv6_header().dest.s6_addr),
|
format_address(packet.get_ipv6_header().dest.s6_addr),
|
||||||
nread);
|
nread);
|
||||||
|
|
||||||
|
packet.prepare_for_sending();
|
||||||
write_to_server(packet);
|
write_to_server(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +38,8 @@ void UdpVpnClient::receive_from_udp() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Reinject into tun
|
// 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,
|
nread,
|
||||||
format_address(packet.get_ipv6_header().source.s6_addr));
|
format_address(packet.get_ipv6_header().source.s6_addr));
|
||||||
_tun_dev.write(packet.get_payload(), packet.get_payload_size());
|
_tun_dev.write(packet.get_payload(), packet.get_payload_size());
|
||||||
|
|
|
@ -55,11 +55,13 @@ void UdpVpnServer::receive_from_tun() {
|
||||||
format_address(peer_inner_addr.s6_addr));
|
format_address(peer_inner_addr.s6_addr));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
packet.set_peer(peer);
|
||||||
|
|
||||||
kdebugf("Transmitting %s -> %s, size %d\n",
|
kdebugf("Transmitting %s -> %s, size %d\n",
|
||||||
format_address(packet.get_ipv6_header().source.s6_addr),
|
format_address(packet.get_ipv6_header().source.s6_addr),
|
||||||
format_address(packet.get_ipv6_header().dest.s6_addr),
|
format_address(packet.get_ipv6_header().dest.s6_addr),
|
||||||
nread);
|
nread);
|
||||||
|
packet.prepare_for_sending();
|
||||||
peer->write(packet);
|
peer->write(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,11 +84,11 @@ void UdpVpnServer::receive_from_udp() {
|
||||||
htons(peer_addr.sin6_port),
|
htons(peer_addr.sin6_port),
|
||||||
format_address(peer_inner_addr.s6_addr));
|
format_address(peer_inner_addr.s6_addr));
|
||||||
}
|
}
|
||||||
// VpnPeer* peer = (peer_iter->second);
|
packet.set_peer(peer);
|
||||||
// TODO -- pass the packet to `peer` for a cleaner flow
|
|
||||||
|
|
||||||
// Reinject into tun
|
// 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,
|
nread,
|
||||||
format_address(packet.get_ipv6_header().source.s6_addr));
|
format_address(packet.get_ipv6_header().source.s6_addr));
|
||||||
_tun_dev.write(packet.get_payload(), packet.get_payload_size());
|
_tun_dev.write(packet.get_payload(), packet.get_payload_size());
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
#include "VpnPacket.hpp"
|
#include "VpnPacket.hpp"
|
||||||
|
#include "VpnPeer.hpp"
|
||||||
|
|
||||||
const size_t VpnPacket::VPN_HEADER_SIZE = 0;
|
#include <chrono>
|
||||||
|
|
||||||
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 */;
|
40 /* IPv6 header */ + 8 /* UDP header */;
|
||||||
// We use a TUN device, hence we don't have a layer 2 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)
|
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() {
|
VpnPacket::~VpnPacket() {
|
||||||
|
@ -19,3 +28,30 @@ VpnPacket::~VpnPacket() {
|
||||||
bool VpnPacket::parse_as_ipv6() {
|
bool VpnPacket::parse_as_ipv6() {
|
||||||
return parse_ipv6_header(get_payload(), get_payload_size(), _ipv6_header);
|
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::microseconds>(
|
||||||
|
std::chrono::steady_clock::now()).time_since_epoch().count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VpnPacket::upon_reception() {
|
||||||
|
_reception_timestamp =
|
||||||
|
std::chrono::time_point_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now()).time_since_epoch().count();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t VpnPacket::next_seqno() {
|
||||||
|
if(_peer)
|
||||||
|
return _peer->next_seqno();
|
||||||
|
return _next_general_seqno++;
|
||||||
|
}
|
||||||
|
|
|
@ -3,29 +3,46 @@
|
||||||
/** A packet to be transmitted or received over the VPN socket */
|
/** A packet to be transmitted or received over the VPN socket */
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "ip_header.hpp"
|
#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 {
|
class VpnPacket {
|
||||||
public:
|
public:
|
||||||
static const size_t VPN_HEADER_SIZE;
|
static const size_t VPN_HEADER_BYTES;
|
||||||
|
|
||||||
VpnPacket(size_t mtu);
|
VpnPacket(size_t mtu);
|
||||||
~VpnPacket();
|
~VpnPacket();
|
||||||
|
|
||||||
|
/// Set packet peer -- used for sequence numbers
|
||||||
|
void set_peer(std::shared_ptr<VpnPeer> peer) { _peer = peer; }
|
||||||
|
|
||||||
/// Try to parse the packet as IPv6, return `false` upon failure.
|
/// Try to parse the packet as IPv6, return `false` upon failure.
|
||||||
bool parse_as_ipv6();
|
bool parse_as_ipv6();
|
||||||
bool ipv6_parsed() const { return _ipv6_parsed; }
|
bool ipv6_parsed() const { return _ipv6_parsed; }
|
||||||
const IPv6Header& get_ipv6_header() const { return _ipv6_header; }
|
const IPv6Header& get_ipv6_header() const { return _ipv6_header; }
|
||||||
|
|
||||||
const char* get_payload() const { return _data + VPN_HEADER_SIZE; }
|
const char* get_payload() const { return _data + VPN_HEADER_BYTES; }
|
||||||
char* get_payload() { return _data + VPN_HEADER_SIZE; }
|
char* get_payload() { return _data + VPN_HEADER_BYTES; }
|
||||||
size_t get_payload_space() const {
|
size_t get_payload_space() const {
|
||||||
return _data_space - VPN_HEADER_SIZE; }
|
return _data_space - VPN_HEADER_BYTES; }
|
||||||
size_t get_payload_size() const {
|
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) {
|
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; }
|
const char* get_data() const { return _data; }
|
||||||
char* get_data() { return _data; }
|
char* get_data() { return _data; }
|
||||||
|
@ -33,10 +50,34 @@ class VpnPacket {
|
||||||
size_t get_data_size() const { return _data_size; }
|
size_t get_data_size() const { return _data_size; }
|
||||||
void set_data_size(size_t data_size) { _data_size = 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:
|
private:
|
||||||
|
std::shared_ptr<VpnPeer> _peer;
|
||||||
|
|
||||||
char* _data;
|
char* _data;
|
||||||
size_t _data_space, _data_size;
|
size_t _data_space, _data_size;
|
||||||
|
|
||||||
bool _ipv6_parsed;
|
bool _ipv6_parsed;
|
||||||
IPv6Header _ipv6_header;
|
IPv6Header _ipv6_header;
|
||||||
|
|
||||||
|
uint32_t _reception_timestamp;
|
||||||
|
|
||||||
|
static uint32_t _next_general_seqno;
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
VpnPeer::VpnPeer(UdpVpn* vpn, const sockaddr_in6& ext_addr,
|
VpnPeer::VpnPeer(UdpVpn* vpn, const sockaddr_in6& ext_addr,
|
||||||
const in6_addr int_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)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,12 @@ class VpnPeer {
|
||||||
size_t write(const char* data, size_t len);
|
size_t write(const char* data, size_t len);
|
||||||
size_t write(const VpnPacket& packet);
|
size_t write(const VpnPacket& packet);
|
||||||
|
|
||||||
|
uint32_t peek_next_seqno() const { return _next_seqno; }
|
||||||
|
uint32_t next_seqno() { return _next_seqno++; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UdpVpn* _vpn;
|
UdpVpn* _vpn;
|
||||||
sockaddr_in6 _ext_addr;
|
sockaddr_in6 _ext_addr;
|
||||||
in6_addr _int_addr;
|
in6_addr _int_addr;
|
||||||
|
uint32_t _next_seqno;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue