Compare commits
3 commits
94f42d08c0
...
f07f2a853d
Author | SHA1 | Date | |
---|---|---|---|
Théophile Bastian | f07f2a853d | ||
Théophile Bastian | 0b8cf0edc1 | ||
Théophile Bastian | 79dbf23784 |
|
@ -72,6 +72,31 @@ TunDevice::~TunDevice() {
|
|||
close(_fd);
|
||||
}
|
||||
|
||||
uint16_t TunDevice::get_mtu() const {
|
||||
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // Any socket will do
|
||||
|
||||
struct ifreq ifr;
|
||||
ifr.ifr_addr.sa_family = AF_INET6;
|
||||
strncpy(ifr.ifr_name, _dev_name.c_str(), sizeof(ifr.ifr_name)-1);
|
||||
if (ioctl(sockfd, SIOCGIFMTU, (caddr_t)&ifr) < 0)
|
||||
return 0;
|
||||
close(sockfd);
|
||||
return ifr.ifr_mtu;
|
||||
}
|
||||
|
||||
bool TunDevice::set_mtu(uint16_t mtu) {
|
||||
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // Any socket will do
|
||||
|
||||
struct ifreq ifr;
|
||||
ifr.ifr_addr.sa_family = AF_INET6;
|
||||
strncpy(ifr.ifr_name, _dev_name.c_str(), sizeof(ifr.ifr_name)-1);
|
||||
ifr.ifr_mtu = mtu;
|
||||
if (ioctl(sockfd, SIOCSIFMTU, (caddr_t)&ifr) < 0)
|
||||
return false;
|
||||
close(sockfd);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t TunDevice::poll_packet(char* read_buffer, size_t buf_size, int timeout) {
|
||||
int poll_rc = poll(&_poll_fd, 1, timeout);
|
||||
if(poll_rc < 0) {
|
||||
|
|
|
@ -29,6 +29,12 @@ class TunDevice {
|
|||
const std::string& get_dev_name() const { return _dev_name; }
|
||||
int get_fd() const { return _fd; }
|
||||
|
||||
/** Get the interface's MTU */
|
||||
uint16_t get_mtu() const;
|
||||
|
||||
/** Set the interface's MTU */
|
||||
bool set_mtu(uint16_t mtu);
|
||||
|
||||
/* Reads a packet from the device.
|
||||
* Timeouts after `timeout` ms, or never if `timeout < 0`.
|
||||
* Upon timeout, returns 0.
|
||||
|
|
10
UdpVpn.cpp
10
UdpVpn.cpp
|
@ -16,6 +16,7 @@ static const size_t VPN_MTU = 1460; // TODO determine this -- issue #3
|
|||
UdpVpn::UdpVpn()
|
||||
: _stopped(false), _vpn_mtu(VPN_MTU), _tun_dev("cvpn%d"), _peer(nullptr)
|
||||
{
|
||||
_tun_dev.set_mtu(VpnPacket::get_tunnelled_mtu(_vpn_mtu));
|
||||
_socket = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if(_socket < 0)
|
||||
throw UdpVpn::InitializationError("Cannot create socket", errno, true);
|
||||
|
@ -75,13 +76,18 @@ size_t UdpVpn::read_from_tun(char* buffer, size_t len) {
|
|||
}
|
||||
|
||||
size_t UdpVpn::read_from_tun(TunnelledPacket& packet) {
|
||||
size_t nread =
|
||||
read_from_tun(packet.get_payload(), packet.get_payload_space());
|
||||
size_t payload_space = packet.get_payload_space();
|
||||
size_t nread = read_from_tun(packet.get_payload(), payload_space);
|
||||
packet.set_payload_size(nread);
|
||||
if(!packet.parse_as_ipv6()) {
|
||||
debugf("Ignoring packet with invalid header\n");
|
||||
return 0;
|
||||
}
|
||||
if(nread != packet.get_ipv6_header().packet_length()) {
|
||||
debugf("Ignoring packet with bad size (expected %d, got %d, buffer %d)\n",
|
||||
packet.get_ipv6_header().packet_length(), nread, payload_space);
|
||||
return 0;
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@ VpnPacketTLV VpnPacket::first_tlv() {
|
|||
return VpnPacketTLV(*this, 0);
|
||||
}
|
||||
|
||||
size_t VpnPacket::get_tunnelled_mtu(size_t udp_mtu) {
|
||||
return udp_mtu - OUTER_HEADERS_BYTES - VPN_HEADER_BYTES - TLV_HEADER_BYTES;
|
||||
}
|
||||
|
||||
uint32_t VpnPacket::get_seqno() const {
|
||||
return *(uint32_t*)(_data + DATA_SEQNO_POS);
|
||||
}
|
||||
|
@ -98,6 +102,12 @@ void VpnPacketTLV::set_payload_size(uint16_t size) {
|
|||
_packet.increase_payload_size(size - old_size);
|
||||
}
|
||||
|
||||
uint16_t VpnPacketTLV::get_payload_space() const {
|
||||
return _packet.get_payload_space()
|
||||
- _packet.get_payload_size()
|
||||
+ get_payload_size();
|
||||
}
|
||||
|
||||
VpnPacket::PayloadType VpnPacketTLV::get_type() const {
|
||||
return (VpnPacket::PayloadType)(*(uint8_t*)(get_data()));
|
||||
}
|
||||
|
|
|
@ -56,6 +56,9 @@ class VpnPacket {
|
|||
|
||||
VpnPacketTLV first_tlv();
|
||||
|
||||
/// 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
|
||||
void set_peer(VpnPeer* peer) { _peer = peer; }
|
||||
|
||||
|
@ -156,10 +159,7 @@ class VpnPacketTLV {
|
|||
/// 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 {
|
||||
return _packet.get_payload_space()
|
||||
- _packet.get_payload_size()
|
||||
- get_payload_size(); }
|
||||
uint16_t get_payload_space() const;
|
||||
|
||||
/// Get a pointer to the raw data (const version)
|
||||
const char* get_data() const {
|
||||
|
|
42
VpnPeer.cpp
42
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), _next_seqno(0)
|
||||
: _vpn(vpn), _ext_addr(ext_addr), _int_addr(int_addr), _next_send_seqno(0)
|
||||
{}
|
||||
|
||||
void VpnPeer::set_int_addr(const in6_addr& int_addr) {
|
||||
|
@ -28,3 +28,43 @@ size_t VpnPeer::write(const char* data, size_t len) {
|
|||
size_t VpnPeer::write(const VpnPacket& packet) {
|
||||
return write(packet.get_data(), packet.get_data_size());
|
||||
}
|
||||
|
||||
PacketLossLogger::PacketLossLogger() : _cur_seqno(0) {}
|
||||
|
||||
void PacketLossLogger::log_packet(uint32_t seqno) {
|
||||
uint32_t m_seqno = seqno % PACKET_LOSS_HISTSIZE;
|
||||
int64_t diff = (int64_t)seqno - _cur_seqno;
|
||||
|
||||
if(diff == 1) {
|
||||
_cur_seqno++;
|
||||
_packet_loss_hist.reset(m_seqno);
|
||||
while(_received_ahead.test((_cur_seqno + 1) % PACKET_LOST_AFTER)) {
|
||||
_cur_seqno++;
|
||||
_packet_loss_hist.reset(_cur_seqno % PACKET_LOSS_HISTSIZE);
|
||||
_received_ahead.reset(_cur_seqno % PACKET_LOST_AFTER);
|
||||
}
|
||||
} else if(LIKELY(diff > 1)) {
|
||||
if(diff < PACKET_LOST_AFTER)
|
||||
_received_ahead.set(seqno % PACKET_LOST_AFTER);
|
||||
else if(diff < PACKET_LOSS_HISTSIZE) {
|
||||
// Packet too much forwards -- consider _cur_seqno lost
|
||||
for(int offs=1; offs < PACKET_LOST_AFTER; ++offs) {
|
||||
_packet_loss_hist[(_cur_seqno + offs) % PACKET_LOSS_HISTSIZE] =
|
||||
!_received_ahead[(_cur_seqno + offs) % PACKET_LOST_AFTER];
|
||||
}
|
||||
_received_ahead.reset();
|
||||
_cur_seqno = seqno;
|
||||
_packet_loss_hist.reset(m_seqno);
|
||||
} else
|
||||
reboot(); // This is a huge gap -- reboot
|
||||
} else {
|
||||
if(diff < - 2*PACKET_LOSS_HISTSIZE)
|
||||
reboot(); // this is too much backwards -- something's wrong, reboot
|
||||
// else: ignore, we've moved forward and counted the packet as lost
|
||||
}
|
||||
}
|
||||
|
||||
void PacketLossLogger::reboot() {
|
||||
_packet_loss_hist.reset();
|
||||
_received_ahead.reset();
|
||||
}
|
||||
|
|
35
VpnPeer.hpp
35
VpnPeer.hpp
|
@ -3,11 +3,40 @@
|
|||
/** A peer of a bound (server) instance of UdpVpn */
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <bitset>
|
||||
#include "util.hpp"
|
||||
#include "VpnPacket.hpp"
|
||||
|
||||
class UdpVpn;
|
||||
|
||||
const int PACKET_LOSS_HISTSIZE = 128, PACKET_LOST_AFTER = 8;
|
||||
|
||||
class PacketLossLogger {
|
||||
public:
|
||||
PacketLossLogger();
|
||||
void log_packet(uint32_t seqno);
|
||||
double get_loss_rate() const {
|
||||
return (double)_packet_loss_hist.count() / PACKET_LOSS_HISTSIZE;
|
||||
}
|
||||
|
||||
const std::bitset<PACKET_LOSS_HISTSIZE> get_loss_hist() const {
|
||||
return _packet_loss_hist;
|
||||
}
|
||||
|
||||
const std::bitset<PACKET_LOST_AFTER> get_received_ahead() const {
|
||||
return _received_ahead;
|
||||
}
|
||||
|
||||
uint32_t get_cur_seqno() const { return _cur_seqno; }
|
||||
|
||||
private:
|
||||
void reboot(); ///< completely reset the internal state
|
||||
|
||||
std::bitset<PACKET_LOSS_HISTSIZE> _packet_loss_hist;
|
||||
std::bitset<PACKET_LOST_AFTER> _received_ahead;
|
||||
uint32_t _cur_seqno;
|
||||
};
|
||||
|
||||
class VpnPeer {
|
||||
public:
|
||||
class NetError : public MsgException {
|
||||
|
@ -30,12 +59,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++; }
|
||||
uint32_t peek_next_seqno() const { return _next_send_seqno; }
|
||||
uint32_t next_seqno() { return _next_send_seqno++; }
|
||||
|
||||
private:
|
||||
UdpVpn* _vpn;
|
||||
sockaddr_in6 _ext_addr;
|
||||
in6_addr _int_addr;
|
||||
uint32_t _next_seqno;
|
||||
uint32_t _next_send_seqno;
|
||||
};
|
||||
|
|
|
@ -15,5 +15,6 @@ bool parse_ipv6_header(const char* data, size_t len, IPv6Header& header) {
|
|||
|
||||
memcpy(&(header.source), data + 8, 16);
|
||||
memcpy(&(header.dest), data + 24, 16);
|
||||
header.payload_length = ntohs(*(uint16_t*)(data + 4));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
struct IPv6Header {
|
||||
in6_addr source;
|
||||
in6_addr dest;
|
||||
uint16_t payload_length;
|
||||
|
||||
uint16_t packet_length() const { return payload_length + 40; }
|
||||
};
|
||||
|
||||
/** Parse an IPv6 header, filling `header`. Returns `true` on success. */
|
||||
|
|
48
tests/test_packetloss.cpp
Normal file
48
tests/test_packetloss.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include "VpnPeer.hpp"
|
||||
|
||||
template<int len>
|
||||
std::string bitset_to_string(
|
||||
const std::bitset<len>& bs,
|
||||
char c_f, char c_t, size_t mark_pos, char cm_f, char cm_t)
|
||||
{
|
||||
std::string out;
|
||||
for(size_t pos=0; pos < len; ++pos) {
|
||||
if(pos == mark_pos)
|
||||
out += bs[pos] ? cm_t : cm_f;
|
||||
else
|
||||
out += bs[pos] ? c_t : c_f;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void dump(PacketLossLogger& pllog, int received) {
|
||||
printf("%03d, %.03lf ## %s ## %s\n",
|
||||
received,
|
||||
pllog.get_loss_rate(),
|
||||
bitset_to_string<PACKET_LOSS_HISTSIZE>(
|
||||
pllog.get_loss_hist(), '_', 'X',
|
||||
pllog.get_cur_seqno() % PACKET_LOSS_HISTSIZE,
|
||||
'|', '#').c_str(),
|
||||
bitset_to_string<PACKET_LOST_AFTER>(
|
||||
pllog.get_received_ahead(), '_', 'X',
|
||||
pllog.get_cur_seqno() % PACKET_LOST_AFTER,
|
||||
'|', '#').c_str());
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
PacketLossLogger pllog;
|
||||
std::vector<int> sequence({
|
||||
1, 2, 3, 5, 6, 4, 8, 7, 8, 9,
|
||||
12, 13, 14, 15, 16, 17, 18, 19, 20, 10, 21
|
||||
});
|
||||
|
||||
dump(pllog, 0);
|
||||
for(auto val=sequence.begin(); val != sequence.end(); ++val) {
|
||||
pllog.log_packet(*val);
|
||||
dump(pllog, *val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue