diff --git a/UdpVpn.cpp b/UdpVpn.cpp index f9a44db..95d9cf4 100644 --- a/UdpVpn.cpp +++ b/UdpVpn.cpp @@ -19,7 +19,7 @@ UdpVpn::UdpVpn() : _stopped(false), _dump_requested(false), _vpn_mtu(VPN_MTU), _tun_dev("cvpn%d"), _peer(nullptr) { - _last_control_sent = + _last_tick = _last_control_sent = std::chrono::steady_clock::now() - std::chrono::seconds(1); _tun_dev.set_mtu(VpnPacket::get_tunnelled_mtu(_vpn_mtu)); _socket = socket(AF_INET6, SOCK_DGRAM, 0); @@ -52,7 +52,7 @@ void UdpVpn::run() { _dump_requested = false; } - rc = poll(poll_fds, nfds, 100); // timeout every 100ms + rc = poll(poll_fds, nfds, 10); // timeout every 10ms if(rc < 0) { if(errno == EINTR) // Interrupt. @@ -70,6 +70,13 @@ void UdpVpn::run() { _last_control_sent = std::chrono::steady_clock::now(); } } + if(std::chrono::steady_clock::now() - _last_tick + > std::chrono::milliseconds(50)) + { + _last_tick = std::chrono::steady_clock::now(); + if(_peer) + _peer->tick(); + } if(rc == 0) // Nothing to read -- timeout continue; @@ -257,6 +264,10 @@ void UdpVpn::dump_state() const { std::chrono::steady_clock::now() - _peer->get_rtt().get_last_update()).count() ); + printf("Total bytes transmitted: %s\n", + human_readable_unit(_peer->get_tot_bytes_sent(), "B")); + printf("Current outbound byte rate: %s\n", + human_readable_unit(_peer->get_outbound_byte_rate(), "Bps")); printf("Available bandwidth:\n\t%s [loss based controller]\n", human_readable_unit(congest_ctrl.get_lossbased_bandwidth(), "B") ); diff --git a/UdpVpn.hpp b/UdpVpn.hpp index d8a1585..a43bb77 100644 --- a/UdpVpn.hpp +++ b/UdpVpn.hpp @@ -73,5 +73,8 @@ class UdpVpn { TunDevice _tun_dev; std::unique_ptr _peer; - std::chrono::steady_clock::time_point _last_control_sent; + std::chrono::steady_clock::time_point + _last_control_sent, /**< A control is offered to be sent approx. + every 100ms */ + _last_tick /**< A tick occurs approx. each 50ms */; }; diff --git a/VpnPeer.cpp b/VpnPeer.cpp index 7720689..6e65ae6 100644 --- a/VpnPeer.cpp +++ b/VpnPeer.cpp @@ -11,8 +11,10 @@ const unsigned int RTTLogger::BASE_UPDATE_DELAY = 1000; // ms 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_send_seqno(0), + _tot_bytes_sent(0), _prev_tot_bytes_sent(0), _congestion_controller(*this) { + _prev_tick_time = std::chrono::steady_clock::now(); cycle_next_control(); } @@ -32,6 +34,20 @@ void VpnPeer::set_int_addr(const in6_addr& int_addr) { memcpy(&_int_addr, &int_addr, sizeof(_int_addr)); } +void VpnPeer::tick() { + auto cur_time = std::chrono::steady_clock::now(); + + // Compute byte rate + _outbound_byte_rate = /* byte per second */ + (double)(_tot_bytes_sent - _prev_tot_bytes_sent) + / (double)(std::chrono::duration_cast( + cur_time - _prev_tick_time).count()) + * 1e6; + + _prev_tot_bytes_sent = _tot_bytes_sent; + _prev_tick_time = cur_time; +} + void VpnPeer::log_loss_report(const VpnTlvLossReport& loss_rep) { _loss_reports.prev_seqno = _loss_reports.last_seqno; _loss_reports.prev_losses = _loss_reports.last_losses; @@ -50,6 +66,8 @@ size_t VpnPeer::write(const char* data, size_t len) { if(nsent < 0) throw NetError("Could not send UDP packet", errno, true); + _tot_bytes_sent += nsent; + return (size_t) nsent; } diff --git a/VpnPeer.hpp b/VpnPeer.hpp index ddef8e5..bd440d3 100644 --- a/VpnPeer.hpp +++ b/VpnPeer.hpp @@ -103,6 +103,8 @@ class VpnPeer { const CongestionController& get_congestion_controller() const { return _congestion_controller; } + void tick(); ///< periodic actions, triggered every UdpVpn tick + void log_rtta(const VpnTlvRTTA& rtta) { _rtt.log(rtta); } void log_loss_report(const VpnTlvLossReport& loss_rep); @@ -112,6 +114,9 @@ class VpnPeer { uint32_t peek_next_seqno() const { return _next_send_seqno; } uint32_t next_seqno() { return _next_send_seqno++; } + uint64_t get_tot_bytes_sent() const { return _tot_bytes_sent; } + double get_outbound_byte_rate() const { return _outbound_byte_rate; } + void got_inbound_packet(const VpnPacket& packet); /* === Control protocol === */ @@ -131,6 +136,11 @@ class VpnPeer { in6_addr _int_addr; uint32_t _next_send_seqno; + uint64_t _tot_bytes_sent, _prev_tot_bytes_sent; + double _outbound_byte_rate; + + std::chrono::steady_clock::time_point _prev_tick_time; + PacketLossLogger _packet_loss; LossReports _loss_reports; RTTLogger _rtt; diff --git a/congestion_control.cpp b/congestion_control.cpp index dc34bf0..89c70e7 100644 --- a/congestion_control.cpp +++ b/congestion_control.cpp @@ -6,6 +6,8 @@ CongestionController::CongestionController(const VpnPeer& peer): { _last_seqno = _peer.get_loss_logger().get_cur_seqno(); _loss_based.bandwidth = 3e5; // 300kBps seems a good value to start with + _loss_based.prev_tot_sent = _peer.get_tot_bytes_sent(); + _loss_based.prev_time = std::chrono::steady_clock::now(); } void CongestionController::update_lossbased() { @@ -16,8 +18,21 @@ void CongestionController::update_lossbased() { double loss_rate = (double)delta_losses / (double)delta_seqno; - if(loss_rate < 0.02) // FIXME only if the bandwidth is used - _loss_based.bandwidth *= 1.05; + auto cur_time = std::chrono::steady_clock::now(); + uint64_t cur_sent = _peer.get_tot_bytes_sent(); + double instant_bandwidth = /* byte per second */ + (double)(cur_sent - _loss_based.prev_tot_sent) + / (double)(std::chrono::duration_cast( + cur_time - _loss_based.prev_time).count()) + * 1e6; + + _loss_based.prev_time = cur_time; + _loss_based.prev_tot_sent = cur_sent; + + if(loss_rate < 0.02) { + if(instant_bandwidth > 0.75 * _loss_based.bandwidth) + _loss_based.bandwidth *= 1.05; + } else if(loss_rate >= 0.1) _loss_based.bandwidth *= (1 - 0.5 * loss_rate); } diff --git a/congestion_control.hpp b/congestion_control.hpp index 616e510..ca8d9a5 100644 --- a/congestion_control.hpp +++ b/congestion_control.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include class VpnPeer; @@ -7,6 +8,8 @@ class CongestionController { public: struct LossBased { uint64_t bandwidth; // bytes per second + uint64_t prev_tot_sent; // tot bytes sent during last update + std::chrono::steady_clock::time_point prev_time; // last update time }; CongestionController(const VpnPeer& peer); diff --git a/util.cpp b/util.cpp index 88ef3e3..b3f9005 100644 --- a/util.cpp +++ b/util.cpp @@ -34,7 +34,7 @@ format_address(const unsigned char *address) } const char* -human_readable_unit(size_t value, const char* unit) { +human_readable_unit(double value, const char* unit) { static char buf[4][24]; static int cur_buf = 0; static const char MULTIPLIERS[] = {' ', 'k', 'M', 'G', 'T'}; diff --git a/util.hpp b/util.hpp index 7fc7503..1b324c3 100644 --- a/util.hpp +++ b/util.hpp @@ -48,7 +48,7 @@ void do_debugf(int level, const char *format, ...); const char* format_address(const unsigned char* address); /** turns a value into a human-readable one, eg. "21.2 kB" */ -const char* human_readable_unit(size_t value, const char* unit); +const char* human_readable_unit(double value, const char* unit); /** remove the upper bit from a microsecond timestamp, to conform with the