Measure real-time outbound throughput
This commit is contained in:
parent
7b5ffa4d46
commit
c23741bea3
8 changed files with 67 additions and 7 deletions
15
UdpVpn.cpp
15
UdpVpn.cpp
|
@ -19,7 +19,7 @@ UdpVpn::UdpVpn()
|
||||||
: _stopped(false), _dump_requested(false), _vpn_mtu(VPN_MTU),
|
: _stopped(false), _dump_requested(false), _vpn_mtu(VPN_MTU),
|
||||||
_tun_dev("cvpn%d"), _peer(nullptr)
|
_tun_dev("cvpn%d"), _peer(nullptr)
|
||||||
{
|
{
|
||||||
_last_control_sent =
|
_last_tick = _last_control_sent =
|
||||||
std::chrono::steady_clock::now() - std::chrono::seconds(1);
|
std::chrono::steady_clock::now() - std::chrono::seconds(1);
|
||||||
_tun_dev.set_mtu(VpnPacket::get_tunnelled_mtu(_vpn_mtu));
|
_tun_dev.set_mtu(VpnPacket::get_tunnelled_mtu(_vpn_mtu));
|
||||||
_socket = socket(AF_INET6, SOCK_DGRAM, 0);
|
_socket = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||||
|
@ -52,7 +52,7 @@ void UdpVpn::run() {
|
||||||
_dump_requested = false;
|
_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(rc < 0) {
|
||||||
if(errno == EINTR) // Interrupt.
|
if(errno == EINTR) // Interrupt.
|
||||||
|
@ -70,6 +70,13 @@ void UdpVpn::run() {
|
||||||
_last_control_sent = std::chrono::steady_clock::now();
|
_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
|
if(rc == 0) // Nothing to read -- timeout
|
||||||
continue;
|
continue;
|
||||||
|
@ -257,6 +264,10 @@ void UdpVpn::dump_state() const {
|
||||||
std::chrono::steady_clock::now()
|
std::chrono::steady_clock::now()
|
||||||
- _peer->get_rtt().get_last_update()).count()
|
- _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",
|
printf("Available bandwidth:\n\t%s [loss based controller]\n",
|
||||||
human_readable_unit(congest_ctrl.get_lossbased_bandwidth(), "B")
|
human_readable_unit(congest_ctrl.get_lossbased_bandwidth(), "B")
|
||||||
);
|
);
|
||||||
|
|
|
@ -73,5 +73,8 @@ class UdpVpn {
|
||||||
TunDevice _tun_dev;
|
TunDevice _tun_dev;
|
||||||
std::unique_ptr<VpnPeer> _peer;
|
std::unique_ptr<VpnPeer> _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 */;
|
||||||
};
|
};
|
||||||
|
|
18
VpnPeer.cpp
18
VpnPeer.cpp
|
@ -11,8 +11,10 @@ const unsigned int RTTLogger::BASE_UPDATE_DELAY = 1000; // ms
|
||||||
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), _next_send_seqno(0),
|
: _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)
|
_congestion_controller(*this)
|
||||||
{
|
{
|
||||||
|
_prev_tick_time = std::chrono::steady_clock::now();
|
||||||
cycle_next_control();
|
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));
|
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<std::chrono::microseconds>(
|
||||||
|
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) {
|
void VpnPeer::log_loss_report(const VpnTlvLossReport& loss_rep) {
|
||||||
_loss_reports.prev_seqno = _loss_reports.last_seqno;
|
_loss_reports.prev_seqno = _loss_reports.last_seqno;
|
||||||
_loss_reports.prev_losses = _loss_reports.last_losses;
|
_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)
|
if(nsent < 0)
|
||||||
throw NetError("Could not send UDP packet", errno, true);
|
throw NetError("Could not send UDP packet", errno, true);
|
||||||
|
|
||||||
|
_tot_bytes_sent += nsent;
|
||||||
|
|
||||||
return (size_t) nsent;
|
return (size_t) nsent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
VpnPeer.hpp
10
VpnPeer.hpp
|
@ -103,6 +103,8 @@ class VpnPeer {
|
||||||
const CongestionController& get_congestion_controller() const {
|
const CongestionController& get_congestion_controller() const {
|
||||||
return _congestion_controller; }
|
return _congestion_controller; }
|
||||||
|
|
||||||
|
void tick(); ///< periodic actions, triggered every UdpVpn tick
|
||||||
|
|
||||||
void log_rtta(const VpnTlvRTTA& rtta) { _rtt.log(rtta); }
|
void log_rtta(const VpnTlvRTTA& rtta) { _rtt.log(rtta); }
|
||||||
void log_loss_report(const VpnTlvLossReport& loss_rep);
|
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 peek_next_seqno() const { return _next_send_seqno; }
|
||||||
uint32_t next_seqno() { 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);
|
void got_inbound_packet(const VpnPacket& packet);
|
||||||
|
|
||||||
/* === Control protocol === */
|
/* === Control protocol === */
|
||||||
|
@ -131,6 +136,11 @@ class VpnPeer {
|
||||||
in6_addr _int_addr;
|
in6_addr _int_addr;
|
||||||
uint32_t _next_send_seqno;
|
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;
|
PacketLossLogger _packet_loss;
|
||||||
LossReports _loss_reports;
|
LossReports _loss_reports;
|
||||||
RTTLogger _rtt;
|
RTTLogger _rtt;
|
||||||
|
|
|
@ -6,6 +6,8 @@ CongestionController::CongestionController(const VpnPeer& peer):
|
||||||
{
|
{
|
||||||
_last_seqno = _peer.get_loss_logger().get_cur_seqno();
|
_last_seqno = _peer.get_loss_logger().get_cur_seqno();
|
||||||
_loss_based.bandwidth = 3e5; // 300kBps seems a good value to start with
|
_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() {
|
void CongestionController::update_lossbased() {
|
||||||
|
@ -16,8 +18,21 @@ void CongestionController::update_lossbased() {
|
||||||
|
|
||||||
double loss_rate = (double)delta_losses / (double)delta_seqno;
|
double loss_rate = (double)delta_losses / (double)delta_seqno;
|
||||||
|
|
||||||
if(loss_rate < 0.02) // FIXME only if the bandwidth is used
|
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<std::chrono::microseconds>(
|
||||||
|
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;
|
_loss_based.bandwidth *= 1.05;
|
||||||
|
}
|
||||||
else if(loss_rate >= 0.1)
|
else if(loss_rate >= 0.1)
|
||||||
_loss_based.bandwidth *= (1 - 0.5 * loss_rate);
|
_loss_based.bandwidth *= (1 - 0.5 * loss_rate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
class VpnPeer;
|
class VpnPeer;
|
||||||
|
|
||||||
|
@ -7,6 +8,8 @@ class CongestionController {
|
||||||
public:
|
public:
|
||||||
struct LossBased {
|
struct LossBased {
|
||||||
uint64_t bandwidth; // bytes per second
|
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);
|
CongestionController(const VpnPeer& peer);
|
||||||
|
|
2
util.cpp
2
util.cpp
|
@ -34,7 +34,7 @@ format_address(const unsigned char *address)
|
||||||
}
|
}
|
||||||
|
|
||||||
const char*
|
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 char buf[4][24];
|
||||||
static int cur_buf = 0;
|
static int cur_buf = 0;
|
||||||
static const char MULTIPLIERS[] = {' ', 'k', 'M', 'G', 'T'};
|
static const char MULTIPLIERS[] = {' ', 'k', 'M', 'G', 'T'};
|
||||||
|
|
2
util.hpp
2
util.hpp
|
@ -48,7 +48,7 @@ void do_debugf(int level, const char *format, ...);
|
||||||
const char* format_address(const unsigned char* address);
|
const char* format_address(const unsigned char* address);
|
||||||
|
|
||||||
/** turns a value into a human-readable one, eg. "21.2 kB" */
|
/** 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
|
/** remove the upper bit from a microsecond timestamp, to conform with the
|
||||||
|
|
Loading…
Reference in a new issue