#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include "utp_types.h"
#include "utp_packedsockaddr.h"
#include "utp_internal.h"
#include "utp_hash.h"
#define TIMEOUT_CHECK_INTERVAL 500
#define MAX_CWND_INCREASE_BYTES_PER_RTT 3000
#define CUR_DELAY_SIZE 3
#define DELAY_BASE_HISTORY 13
#define MAX_WINDOW_DECAY 100
#define REORDER_BUFFER_SIZE 32
#define REORDER_BUFFER_MAX_SIZE 1024
#define OUTGOING_BUFFER_MAX_SIZE 1024
#define PACKET_SIZE 1435
#define MIN_WINDOW_SIZE 10
#define DUPLICATE_ACKS_BEFORE_RESEND 3
#define ACK_NR_ALLOWED_WINDOW DUPLICATE_ACKS_BEFORE_RESEND
#define RST_INFO_TIMEOUT 10000
#define RST_INFO_LIMIT 1000
#define KEEPALIVE_INTERVAL 29000
#define SEQ_NR_MASK 0xFFFF
#define ACK_NR_MASK 0xFFFF
#define TIMESTAMP_MASK 0xFFFFFFFF
#define DIV_ROUND_UP(num, denom) ((num + denom - 1) / denom)
char addrbuf[65];
#define addrfmt(x, s) x.fmt(s, sizeof(s))
#if (defined(__SVR4) && defined(__sun))
#pragma pack(1)
#else
#pragma pack(push,1)
#endif
#define PACKET_SIZE_EMPTY_BUCKET 0
#define PACKET_SIZE_EMPTY 23
#define PACKET_SIZE_SMALL_BUCKET 1
#define PACKET_SIZE_SMALL 373
#define PACKET_SIZE_MID_BUCKET 2
#define PACKET_SIZE_MID 723
#define PACKET_SIZE_BIG_BUCKET 3
#define PACKET_SIZE_BIG 1400
#define PACKET_SIZE_HUGE_BUCKET 4
struct PACKED_ATTRIBUTE PacketFormatV1 {
byte ver_type;
byte version() const { return ver_type & 0xf; }
byte type() const { return ver_type >> 4; }
void set_version(byte v) { ver_type = (ver_type & 0xf0) | (v & 0xf); }
void set_type(byte t) { ver_type = (ver_type & 0xf) | (t << 4); }
byte ext;
uint16_big connid;
uint32_big tv_usec;
uint32_big reply_micro;
uint32_big windowsize;
uint16_big seq_nr;
uint16_big ack_nr;
};
struct PACKED_ATTRIBUTE PacketFormatAckV1 {
PacketFormatV1 pf;
byte ext_next;
byte ext_len;
byte acks[4];
};
#if (defined(__SVR4) && defined(__sun))
#pragma pack(0)
#else
#pragma pack(pop)
#endif
enum {
ST_DATA = 0, ST_FIN = 1, ST_STATE = 2, ST_RESET = 3, ST_SYN = 4, ST_NUM_STATES, };
static const cstr flagnames[] = {
"ST_DATA","ST_FIN","ST_STATE","ST_RESET","ST_SYN"
};
enum CONN_STATE {
CS_UNINITIALIZED = 0,
CS_IDLE,
CS_SYN_SENT,
CS_SYN_RECV,
CS_CONNECTED,
CS_CONNECTED_FULL,
CS_RESET,
CS_DESTROY
};
static const cstr statenames[] = {
"UNINITIALIZED", "IDLE","SYN_SENT", "SYN_RECV", "CONNECTED","CONNECTED_FULL","DESTROY_DELAY","RESET","DESTROY"
};
struct OutgoingPacket {
size_t length;
size_t payload;
uint64 time_sent; uint transmissions:31;
bool need_resend:1;
byte data[1];
};
struct SizableCircularBuffer {
size_t mask;
void **elements;
void *get(size_t i) const { assert(elements); return elements ? elements[i & mask] : NULL; }
void put(size_t i, void *data) { assert(elements); elements[i&mask] = data; }
void grow(size_t item, size_t index);
void ensure_size(size_t item, size_t index) { if (index > mask) grow(item, index); }
size_t size() { return mask + 1; }
};
void SizableCircularBuffer::grow(size_t item, size_t index)
{
size_t size = mask + 1;
do size *= 2; while (index >= size);
void **buf = (void**)calloc(size, sizeof(void*));
size--;
for (size_t i = 0; i <= mask; i++) {
buf[(item - index + i) & size] = get(item - index + i);
}
mask = size;
free(elements);
elements = buf;
}
bool wrapping_compare_less(uint32 lhs, uint32 rhs, uint32 mask)
{
const uint32 dist_down = (lhs - rhs) & mask;
const uint32 dist_up = (rhs - lhs) & mask;
return dist_up < dist_down;
}
struct DelayHist {
uint32 delay_base;
uint32 cur_delay_hist[CUR_DELAY_SIZE];
size_t cur_delay_idx;
uint32 delay_base_hist[DELAY_BASE_HISTORY];
size_t delay_base_idx;
uint64 delay_base_time;
bool delay_base_initialized;
void clear(uint64 current_ms)
{
delay_base_initialized = false;
delay_base = 0;
cur_delay_idx = 0;
delay_base_idx = 0;
delay_base_time = current_ms;
for (size_t i = 0; i < CUR_DELAY_SIZE; i++) {
cur_delay_hist[i] = 0;
}
for (size_t i = 0; i < DELAY_BASE_HISTORY; i++) {
delay_base_hist[i] = 0;
}
}
void shift(const uint32 offset)
{
for (size_t i = 0; i < DELAY_BASE_HISTORY; i++) {
delay_base_hist[i] += offset;
}
delay_base += offset;
}
void add_sample(const uint32 sample, uint64 current_ms)
{
if (!delay_base_initialized) {
for (size_t i = 0; i < DELAY_BASE_HISTORY; i++) {
delay_base_hist[i] = sample;
continue;
}
delay_base = sample;
delay_base_initialized = true;
}
if (wrapping_compare_less(sample, delay_base_hist[delay_base_idx], TIMESTAMP_MASK)) {
delay_base_hist[delay_base_idx] = sample;
}
if (wrapping_compare_less(sample, delay_base, TIMESTAMP_MASK)) {
delay_base = sample;
}
const uint32 delay = sample - delay_base;
cur_delay_hist[cur_delay_idx] = delay;
cur_delay_idx = (cur_delay_idx + 1) % CUR_DELAY_SIZE;
if (current_ms - delay_base_time > 60 * 1000) {
delay_base_time = current_ms;
delay_base_idx = (delay_base_idx + 1) % DELAY_BASE_HISTORY;
delay_base_hist[delay_base_idx] = sample;
delay_base = delay_base_hist[0];
for (size_t i = 0; i < DELAY_BASE_HISTORY; i++) {
if (wrapping_compare_less(delay_base_hist[i], delay_base, TIMESTAMP_MASK))
delay_base = delay_base_hist[i];
}
}
}
uint32 get_value()
{
uint32 value = UINT_MAX;
for (size_t i = 0; i < CUR_DELAY_SIZE; i++) {
value = min<uint32>(cur_delay_hist[i], value);
}
return value;
}
};
struct UTPSocket {
~UTPSocket();
PackedSockAddr addr;
utp_context *ctx;
int ida;
uint16 retransmit_count;
uint16 reorder_count;
byte duplicate_ack;
uint16 cur_window_packets;
size_t cur_window;
size_t max_window;
size_t opt_sndbuf;
size_t opt_rcvbuf;
size_t target_delay;
bool got_fin:1;
bool got_fin_reached:1;
bool fin_sent:1;
bool fin_sent_acked:1;
bool read_shutdown:1;
bool close_requested:1;
bool fast_timeout:1;
size_t max_window_user;
CONN_STATE state;
int64 last_rwin_decay;
uint16 eof_pkt;
uint16 ack_nr;
uint16 seq_nr;
uint16 timeout_seq_nr;
uint16 fast_resend_seq_nr;
uint32 reply_micro;
uint64 last_got_packet;
uint64 last_sent_packet;
uint64 last_measured_delay;
mutable uint64 last_maxed_out_window;
void *userdata;
uint rtt;
uint rtt_var;
uint rto;
DelayHist rtt_hist;
uint retransmit_timeout;
uint64 rto_timeout;
uint64 zerowindow_time;
uint32 conn_seed;
uint32 conn_id_recv;
uint32 conn_id_send;
size_t last_rcv_win;
DelayHist our_hist;
DelayHist their_hist;
byte extensions[8];
uint64 mtu_discover_time;
uint32 mtu_ceiling, mtu_floor, mtu_last;
uint32 mtu_probe_seq, mtu_probe_size;
int32 average_delay;
int64 current_delay_sum;
int current_delay_samples;
uint32 average_delay_base;
uint64 average_sample_time;
int32 clock_drift;
int32 clock_drift_raw;
SizableCircularBuffer inbuf, outbuf;
#ifdef _DEBUG
utp_socket_stats _stats;
#endif
bool slow_start;
size_t ssthresh;
void log(int level, char const *fmt, ...)
{
va_list va;
char buf[4096], buf2[4096];
if (!ctx->would_log(level)) {
return;
}
va_start(va, fmt);
vsnprintf(buf, 4096, fmt, va);
va_end(va);
buf[4095] = '\0';
snprintf(buf2, 4096, "%p %s %06u %s", this, addrfmt(addr, addrbuf), conn_id_recv, buf);
buf2[4095] = '\0';
ctx->log_unchecked(this, buf2);
}
void schedule_ack();
void mtu_search_update();
void mtu_reset();
size_t get_rcv_window()
{
const size_t numbuf = utp_call_get_read_buffer_size(this->ctx, this);
assert((int)numbuf >= 0);
return opt_rcvbuf > numbuf ? opt_rcvbuf - numbuf : 0;
}
bool can_decay_win(int64 msec) const
{
return (msec - last_rwin_decay) >= MAX_WINDOW_DECAY;
}
void maybe_decay_win(uint64 current_ms)
{
if (can_decay_win(current_ms)) {
max_window = (size_t)(max_window * .5);
last_rwin_decay = current_ms;
if (max_window < MIN_WINDOW_SIZE)
max_window = MIN_WINDOW_SIZE;
slow_start = false;
ssthresh = max_window;
}
}
size_t get_header_size() const
{
return sizeof(PacketFormatV1);
}
size_t get_udp_mtu()
{
socklen_t len;
SOCKADDR_STORAGE sa = addr.get_sockaddr_storage(&len);
return utp_call_get_udp_mtu(this->ctx, this, (const struct sockaddr *)&sa, len);
}
size_t get_udp_overhead()
{
socklen_t len;
SOCKADDR_STORAGE sa = addr.get_sockaddr_storage(&len);
return utp_call_get_udp_overhead(this->ctx, this, (const struct sockaddr *)&sa, len);
}
size_t get_overhead()
{
return get_udp_overhead() + get_header_size();
}
void send_data(byte* b, size_t length, bandwidth_type_t type, uint32 flags = 0);
void send_ack(bool synack = false);
void send_keep_alive();
static void send_rst(utp_context *ctx,
const PackedSockAddr &addr, uint32 conn_id_send,
uint16 ack_nr, uint16 seq_nr);
void send_packet(OutgoingPacket *pkt);
bool is_full(int bytes = -1);
bool flush_packets();
void write_outgoing_packet(size_t payload, uint flags, struct utp_iovec *iovec, size_t num_iovecs);
#ifdef _DEBUG
void check_invariant();
#endif
void check_timeouts();
int ack_packet(uint16 seq);
size_t selective_ack_bytes(uint base, const byte* mask, byte len, int64& min_rtt);
void selective_ack(uint base, const byte *mask, byte len);
void apply_ccontrol(size_t bytes_acked, uint32 actual_delay, int64 min_rtt);
size_t get_packet_size() const;
};
void removeSocketFromAckList(UTPSocket *conn)
{
if (conn->ida >= 0)
{
UTPSocket *last = conn->ctx->ack_sockets[conn->ctx->ack_sockets.GetCount() - 1];
assert(last->ida < (int)(conn->ctx->ack_sockets.GetCount()));
assert(conn->ctx->ack_sockets[last->ida] == last);
last->ida = conn->ida;
conn->ctx->ack_sockets[conn->ida] = last;
conn->ida = -1;
conn->ctx->ack_sockets.SetCount(conn->ctx->ack_sockets.GetCount() - 1);
}
}
static void utp_register_sent_packet(utp_context *ctx, size_t length)
{
if (length <= PACKET_SIZE_MID) {
if (length <= PACKET_SIZE_EMPTY) {
ctx->context_stats._nraw_send[PACKET_SIZE_EMPTY_BUCKET]++;
} else if (length <= PACKET_SIZE_SMALL) {
ctx->context_stats._nraw_send[PACKET_SIZE_SMALL_BUCKET]++;
} else
ctx->context_stats._nraw_send[PACKET_SIZE_MID_BUCKET]++;
} else {
if (length <= PACKET_SIZE_BIG) {
ctx->context_stats._nraw_send[PACKET_SIZE_BIG_BUCKET]++;
} else
ctx->context_stats._nraw_send[PACKET_SIZE_HUGE_BUCKET]++;
}
}
void send_to_addr(utp_context *ctx, const byte *p, size_t len, const PackedSockAddr &addr, int flags = 0)
{
socklen_t tolen;
SOCKADDR_STORAGE to = addr.get_sockaddr_storage(&tolen);
utp_register_sent_packet(ctx, len);
utp_call_sendto(ctx, NULL, p, len, (const struct sockaddr *)&to, tolen, flags);
}
void UTPSocket::schedule_ack()
{
if (ida == -1){
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "schedule_ack");
#endif
ida = ctx->ack_sockets.Append(this);
} else {
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "schedule_ack: already in list");
#endif
}
}
void UTPSocket::send_data(byte* b, size_t length, bandwidth_type_t type, uint32 flags)
{
uint64 time = utp_call_get_microseconds(ctx, this);
PacketFormatV1* b1 = (PacketFormatV1*)b;
b1->tv_usec = (uint32)time;
b1->reply_micro = reply_micro;
last_sent_packet = ctx->current_ms;
#ifdef _DEBUG
_stats.nbytes_xmit += length;
++_stats.nxmit;
#endif
if (ctx->callbacks[UTP_ON_OVERHEAD_STATISTICS]) {
size_t n;
if (type == payload_bandwidth) {
type = header_overhead;
n = get_overhead();
} else {
n = length + get_udp_overhead();
}
utp_call_on_overhead_statistics(ctx, this, true, n, type);
}
#if UTP_DEBUG_LOGGING
int flags2 = b1->type();
uint16 seq_nr = b1->seq_nr;
uint16 ack_nr = b1->ack_nr;
log(UTP_LOG_DEBUG, "send %s len:%u id:%u timestamp:" I64u " reply_micro:%u flags:%s seq_nr:%u ack_nr:%u",
addrfmt(addr, addrbuf), (uint)length, conn_id_send, time, reply_micro, flagnames[flags2],
seq_nr, ack_nr);
#endif
send_to_addr(ctx, b, length, addr, flags);
removeSocketFromAckList(this);
}
void UTPSocket::send_ack(bool synack)
{
PacketFormatAckV1 pfa;
zeromem(&pfa);
size_t len;
last_rcv_win = get_rcv_window();
pfa.pf.set_version(1);
pfa.pf.set_type(ST_STATE);
pfa.pf.ext = 0;
pfa.pf.connid = conn_id_send;
pfa.pf.ack_nr = ack_nr;
pfa.pf.seq_nr = seq_nr;
pfa.pf.windowsize = (uint32)last_rcv_win;
len = sizeof(PacketFormatV1);
if (reorder_count != 0 && !got_fin_reached) {
assert(!synack);
pfa.pf.ext = 1;
pfa.ext_next = 0;
pfa.ext_len = 4;
uint m = 0;
assert(inbuf.get(ack_nr + 1) == NULL);
size_t window = min<size_t>(14+16, inbuf.size());
for (size_t i = 0; i < window; i++) {
if (inbuf.get(ack_nr + i + 2) != NULL) {
m |= 1 << i;
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "EACK packet [%u]", ack_nr + i + 2);
#endif
}
}
pfa.acks[0] = (byte)m;
pfa.acks[1] = (byte)(m >> 8);
pfa.acks[2] = (byte)(m >> 16);
pfa.acks[3] = (byte)(m >> 24);
len += 4 + 2;
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "Sending EACK %u [%u] bits:[%032b]", ack_nr, conn_id_send, m);
#endif
} else {
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "Sending ACK %u [%u]", ack_nr, conn_id_send);
#endif
}
send_data((byte*)&pfa, len, ack_overhead);
removeSocketFromAckList(this);
}
void UTPSocket::send_keep_alive()
{
ack_nr--;
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "Sending KeepAlive ACK %u [%u]", ack_nr, conn_id_send);
#endif
send_ack();
ack_nr++;
}
void UTPSocket::send_rst(utp_context *ctx,
const PackedSockAddr &addr, uint32 conn_id_send, uint16 ack_nr, uint16 seq_nr)
{
PacketFormatV1 pf1;
zeromem(&pf1);
size_t len;
pf1.set_version(1);
pf1.set_type(ST_RESET);
pf1.ext = 0;
pf1.connid = conn_id_send;
pf1.ack_nr = ack_nr;
pf1.seq_nr = seq_nr;
pf1.windowsize = 0;
len = sizeof(PacketFormatV1);
send_to_addr(ctx, (const byte*)&pf1, len, addr);
}
void UTPSocket::send_packet(OutgoingPacket *pkt)
{
time_t cur_time = utp_call_get_milliseconds(this->ctx, this);
if (pkt->transmissions == 0 || pkt->need_resend) {
cur_window += pkt->payload;
}
pkt->need_resend = false;
PacketFormatV1* p1 = (PacketFormatV1*)pkt->data;
p1->ack_nr = ack_nr;
pkt->time_sent = utp_call_get_microseconds(this->ctx, this);
bool use_as_mtu_probe = false;
if (mtu_discover_time < (uint64)cur_time) {
mtu_reset();
}
if (mtu_floor < mtu_ceiling
&& pkt->length > mtu_floor
&& pkt->length <= mtu_ceiling
&& mtu_probe_seq == 0
&& seq_nr != 1
&& pkt->transmissions == 0) {
mtu_probe_seq = (seq_nr - 1) & ACK_NR_MASK;
mtu_probe_size = pkt->length;
assert(pkt->length >= mtu_floor);
assert(pkt->length <= mtu_ceiling);
use_as_mtu_probe = true;
log(UTP_LOG_MTU, "MTU [PROBE] floor:%d ceiling:%d current:%d"
, mtu_floor, mtu_ceiling, mtu_probe_size);
}
pkt->transmissions++;
send_data((byte*)pkt->data, pkt->length,
(state == CS_SYN_SENT) ? connect_overhead
: (pkt->transmissions == 1) ? payload_bandwidth
: retransmit_overhead, use_as_mtu_probe ? UTP_UDP_DONTFRAG : 0);
}
bool UTPSocket::is_full(int bytes)
{
size_t packet_size = get_packet_size();
if (bytes < 0) bytes = packet_size;
else if (bytes > (int)packet_size) bytes = (int)packet_size;
size_t max_send = min(max_window, opt_sndbuf, max_window_user);
if (cur_window_packets >= OUTGOING_BUFFER_MAX_SIZE - 1) {
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "is_full:false cur_window_packets:%d MAX:%d", cur_window_packets, OUTGOING_BUFFER_MAX_SIZE - 1);
#endif
last_maxed_out_window = ctx->current_ms;
return true;
}
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "is_full:%s. cur_window:%u pkt:%u max:%u cur_window_packets:%u max_window:%u"
, (cur_window + bytes > max_send) ? "true" : "false"
, cur_window, bytes, max_send, cur_window_packets
, max_window);
#endif
if (cur_window + bytes > max_send) {
last_maxed_out_window = ctx->current_ms;
return true;
}
return false;
}
bool UTPSocket::flush_packets()
{
size_t packet_size = get_packet_size();
for (uint16 i = seq_nr - cur_window_packets; i != seq_nr; ++i) {
OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(i);
if (pkt == 0 || (pkt->transmissions > 0 && pkt->need_resend == false)) continue;
if (is_full()) return true;
if (i != ((seq_nr - 1) & ACK_NR_MASK) ||
cur_window_packets == 1 ||
pkt->payload >= packet_size) {
send_packet(pkt);
}
}
return false;
}
void UTPSocket::write_outgoing_packet(size_t payload, uint flags, struct utp_iovec *iovec, size_t num_iovecs)
{
if (cur_window_packets == 0) {
retransmit_timeout = rto;
rto_timeout = ctx->current_ms + retransmit_timeout;
assert(cur_window == 0);
}
size_t packet_size = get_packet_size();
do {
assert(cur_window_packets < OUTGOING_BUFFER_MAX_SIZE);
assert(flags == ST_DATA || flags == ST_FIN);
size_t added = 0;
OutgoingPacket *pkt = NULL;
if (cur_window_packets > 0) {
pkt = (OutgoingPacket*)outbuf.get(seq_nr - 1);
}
const size_t header_size = get_header_size();
bool append = true;
if (payload && pkt && !pkt->transmissions && pkt->payload < packet_size) {
added = min(payload + pkt->payload, max<size_t>(packet_size, pkt->payload)) - pkt->payload;
pkt = (OutgoingPacket*)realloc(pkt,
(sizeof(OutgoingPacket) - 1) +
header_size +
pkt->payload + added);
outbuf.put(seq_nr - 1, pkt);
append = false;
assert(!pkt->need_resend);
} else {
added = payload;
pkt = (OutgoingPacket*)malloc((sizeof(OutgoingPacket) - 1) +
header_size +
added);
pkt->payload = 0;
pkt->transmissions = 0;
pkt->need_resend = false;
}
if (added) {
assert(flags == ST_DATA);
unsigned char *p = pkt->data + header_size + pkt->payload;
size_t needed = added;
for (size_t i = 0; i < num_iovecs && needed; i++) {
if (iovec[i].iov_len == 0)
continue;
size_t num = min<size_t>(needed, iovec[i].iov_len);
memcpy(p, iovec[i].iov_base, num);
p += num;
iovec[i].iov_len -= num;
iovec[i].iov_base = (byte*)iovec[i].iov_base + num; needed -= num;
}
assert(needed == 0);
}
pkt->payload += added;
pkt->length = header_size + pkt->payload;
last_rcv_win = get_rcv_window();
PacketFormatV1* p1 = (PacketFormatV1*)pkt->data;
p1->set_version(1);
p1->set_type(flags);
p1->ext = 0;
p1->connid = conn_id_send;
p1->windowsize = (uint32)last_rcv_win;
p1->ack_nr = ack_nr;
if (append) {
outbuf.ensure_size(seq_nr, cur_window_packets);
outbuf.put(seq_nr, pkt);
p1->seq_nr = seq_nr;
seq_nr++;
cur_window_packets++;
}
payload -= added;
} while (payload);
flush_packets();
}
#ifdef _DEBUG
void UTPSocket::check_invariant()
{
if (reorder_count > 0) {
assert(inbuf.get(ack_nr + 1) == NULL);
}
size_t outstanding_bytes = 0;
for (int i = 0; i < cur_window_packets; ++i) {
OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(seq_nr - i - 1);
if (pkt == 0 || pkt->transmissions == 0 || pkt->need_resend) continue;
outstanding_bytes += pkt->payload;
}
assert(outstanding_bytes == cur_window);
}
#endif
void UTPSocket::check_timeouts()
{
#ifdef _DEBUG
check_invariant();
#endif
assert(cur_window_packets == 0 || outbuf.get(seq_nr - cur_window_packets));
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "CheckTimeouts timeout:%d max_window:%u cur_window:%u "
"state:%s cur_window_packets:%u",
(int)(rto_timeout - ctx->current_ms), (uint)max_window, (uint)cur_window,
statenames[state], cur_window_packets);
#endif
if (state != CS_DESTROY) flush_packets();
switch (state) {
case CS_SYN_SENT:
case CS_SYN_RECV:
case CS_CONNECTED_FULL:
case CS_CONNECTED: {
if ((int)(ctx->current_ms - zerowindow_time) >= 0 && max_window_user == 0) {
max_window_user = PACKET_SIZE;
}
if ((int)(ctx->current_ms - rto_timeout) >= 0
&& rto_timeout > 0) {
bool ignore_loss = false;
if (cur_window_packets == 1
&& ((seq_nr - 1) & ACK_NR_MASK) == mtu_probe_seq
&& mtu_probe_seq != 0) {
mtu_ceiling = mtu_probe_size - 1;
mtu_search_update();
ignore_loss = true;
log(UTP_LOG_MTU, "MTU [PROBE-TIMEOUT] floor:%d ceiling:%d current:%d"
, mtu_floor, mtu_ceiling, mtu_last);
}
mtu_probe_seq = mtu_probe_size = 0;
log(UTP_LOG_MTU, "MTU [TIMEOUT]");
const uint new_timeout = ignore_loss ? retransmit_timeout : retransmit_timeout * 2;
if (state == CS_SYN_RECV) {
state = CS_DESTROY;
utp_call_on_error(ctx, this, UTP_ETIMEDOUT);
return;
}
if (retransmit_count >= 4 || (state == CS_SYN_SENT && retransmit_count >= 2)) {
if (close_requested)
state = CS_DESTROY;
else
state = CS_RESET;
utp_call_on_error(ctx, this, UTP_ETIMEDOUT);
return;
}
retransmit_timeout = new_timeout;
rto_timeout = ctx->current_ms + new_timeout;
if (!ignore_loss) {
duplicate_ack = 0;
int packet_size = get_packet_size();
if ((cur_window_packets == 0) && ((int)max_window > packet_size)) {
max_window = max(max_window * 2 / 3, size_t(packet_size));
} else {
max_window = packet_size;
slow_start = true;
}
}
for (int i = 0; i < cur_window_packets; ++i) {
OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(seq_nr - i - 1);
if (pkt == 0 || pkt->transmissions == 0 || pkt->need_resend) continue;
pkt->need_resend = true;
assert(cur_window >= pkt->payload);
cur_window -= pkt->payload;
}
if (cur_window_packets > 0) {
retransmit_count++;
log(UTP_LOG_NORMAL, "Packet timeout. Resend. seq_nr:%u. timeout:%u "
"max_window:%u cur_window_packets:%d"
, seq_nr - cur_window_packets, retransmit_timeout
, (uint)max_window, int(cur_window_packets));
fast_timeout = true;
timeout_seq_nr = seq_nr;
OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(seq_nr - cur_window_packets);
assert(pkt);
send_packet(pkt);
}
}
if (state == CS_CONNECTED_FULL && !is_full()) {
state = CS_CONNECTED;
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "Socket writable. max_window:%u cur_window:%u packet_size:%u",
(uint)max_window, (uint)cur_window, (uint)get_packet_size());
#endif
utp_call_on_state_change(this->ctx, this, UTP_STATE_WRITABLE);
}
if (state >= CS_CONNECTED && !fin_sent) {
if ((int)(ctx->current_ms - last_sent_packet) >= KEEPALIVE_INTERVAL) {
send_keep_alive();
}
}
break;
}
case CS_UNINITIALIZED:
case CS_IDLE:
case CS_RESET:
case CS_DESTROY:
break;
}
}
void UTPSocket::mtu_search_update()
{
assert(mtu_floor <= mtu_ceiling);
mtu_last = (mtu_floor + mtu_ceiling) / 2;
mtu_probe_seq = mtu_probe_size = 0;
if (mtu_ceiling - mtu_floor <= 16) {
mtu_last = mtu_floor;
log(UTP_LOG_MTU, "MTU [DONE] floor:%d ceiling:%d current:%d"
, mtu_floor, mtu_ceiling, mtu_last);
mtu_ceiling = mtu_floor;
assert(mtu_floor <= mtu_ceiling);
mtu_discover_time = utp_call_get_milliseconds(this->ctx, this) + 30 * 60 * 1000;
}
}
void UTPSocket::mtu_reset()
{
mtu_ceiling = get_udp_mtu();
mtu_floor = 576;
log(UTP_LOG_MTU, "MTU [RESET] floor:%d ceiling:%d current:%d"
, mtu_floor, mtu_ceiling, mtu_last);
assert(mtu_floor <= mtu_ceiling);
mtu_discover_time = utp_call_get_milliseconds(this->ctx, this) + 30 * 60 * 1000;
}
int UTPSocket::ack_packet(uint16 seq)
{
OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(seq);
if (pkt == NULL) {
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "got ack for:%u (already acked, or never sent)", seq);
#endif
return 1;
}
if (pkt->transmissions == 0) {
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "got ack for:%u (never sent, pkt_size:%u need_resend:%u)",
seq, (uint)pkt->payload, pkt->need_resend);
#endif
return 2;
}
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "got ack for:%u (pkt_size:%u need_resend:%u)",
seq, (uint)pkt->payload, pkt->need_resend);
#endif
outbuf.put(seq, NULL);
if (pkt->transmissions == 1) {
const uint32 ertt = (uint32)((utp_call_get_microseconds(this->ctx, this) - pkt->time_sent) / 1000);
if (rtt == 0) {
rtt = ertt;
rtt_var = ertt / 2;
} else {
const int delta = (int)rtt - ertt;
rtt_var = rtt_var + (int)(abs(delta) - rtt_var) / 4;
rtt = rtt - rtt/8 + ertt/8;
rtt_hist.add_sample(ertt, ctx->current_ms);
}
rto = max<uint>(rtt + rtt_var * 4, 1000);
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "rtt:%u avg:%u var:%u rto:%u",
ertt, rtt, rtt_var, rto);
#endif
}
retransmit_timeout = rto;
rto_timeout = ctx->current_ms + rto;
if (!pkt->need_resend) {
assert(cur_window >= pkt->payload);
cur_window -= pkt->payload;
}
free(pkt);
retransmit_count = 0;
return 0;
}
size_t UTPSocket::selective_ack_bytes(uint base, const byte* mask, byte len, int64& min_rtt)
{
if (cur_window_packets == 0) return 0;
size_t acked_bytes = 0;
int bits = len * 8;
uint64 now = utp_call_get_microseconds(this->ctx, this);
do {
uint v = base + bits;
if (((seq_nr - v - 1) & ACK_NR_MASK) >= (uint16)(cur_window_packets - 1))
continue;
OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(v);
if (!pkt || pkt->transmissions == 0)
continue;
if (bits >= 0 && mask[bits>>3] & (1 << (bits & 7))) {
assert((int)(pkt->payload) >= 0);
acked_bytes += pkt->payload;
if (pkt->time_sent < now)
min_rtt = min<int64>(min_rtt, now - pkt->time_sent);
else
min_rtt = min<int64>(min_rtt, 50000);
continue;
}
} while (--bits >= -1);
return acked_bytes;
}
enum { MAX_EACK = 128 };
void UTPSocket::selective_ack(uint base, const byte *mask, byte len)
{
if (cur_window_packets == 0) return;
int bits = len * 8 - 1;
int count = 0;
int resends[MAX_EACK];
int nr = 0;
#if UTP_DEBUG_LOGGING
char bitmask[1024] = {0};
int counter = bits;
for (int i = 0; i <= bits; ++i) {
bool bit_set = counter >= 0 && mask[counter>>3] & (1 << (counter & 7));
bitmask[i] = bit_set ? '1' : '0';
--counter;
}
log(UTP_LOG_DEBUG, "Got EACK [%s] base:%u", bitmask, base);
#endif
do {
uint v = base + bits;
if (((seq_nr - v - 1) & ACK_NR_MASK) >= (uint16)(cur_window_packets - 1))
continue;
bool bit_set = bits >= 0 && mask[bits>>3] & (1 << (bits & 7));
if (bit_set) count++;
OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(v);
if (!pkt || pkt->transmissions == 0) {
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "skipping %u. pkt:%08x transmissions:%u %s",
v, pkt, pkt?pkt->transmissions:0, pkt?"(not sent yet?)":"(already acked?)");
#endif
continue;
}
if (bit_set) {
assert((v & outbuf.mask) != ((seq_nr - cur_window_packets) & outbuf.mask));
ack_packet(v);
continue;
}
if (((v - fast_resend_seq_nr) & ACK_NR_MASK) <= OUTGOING_BUFFER_MAX_SIZE &&
count >= DUPLICATE_ACKS_BEFORE_RESEND) {
if (nr >= MAX_EACK - 2) {
memmove(resends, &resends[MAX_EACK/2], MAX_EACK/2 * sizeof(resends[0]));
nr -= MAX_EACK / 2;
}
resends[nr++] = v;
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "no ack for %u", v);
#endif
} else {
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "not resending %u count:%d dup_ack:%u fast_resend_seq_nr:%u",
v, count, duplicate_ack, fast_resend_seq_nr);
#endif
}
} while (--bits >= -1);
if (((base - 1 - fast_resend_seq_nr) & ACK_NR_MASK) <= OUTGOING_BUFFER_MAX_SIZE &&
count >= DUPLICATE_ACKS_BEFORE_RESEND) {
resends[nr++] = (base - 1) & ACK_NR_MASK;
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "no ack for %u", (base - 1) & ACK_NR_MASK);
#endif
} else {
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "not resending %u count:%d dup_ack:%u fast_resend_seq_nr:%u",
base - 1, count, duplicate_ack, fast_resend_seq_nr);
#endif
}
bool back_off = false;
int i = 0;
while (nr > 0) {
uint v = resends[--nr];
OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(v);
if (!pkt) continue;
log(UTP_LOG_NORMAL, "Packet %u lost. Resending", v);
back_off = true;
#ifdef _DEBUG
++_stats.rexmit;
#endif
send_packet(pkt);
fast_resend_seq_nr = (v + 1) & ACK_NR_MASK;
if (++i >= 4) break;
}
if (back_off)
maybe_decay_win(ctx->current_ms);
duplicate_ack = count;
}
void UTPSocket::apply_ccontrol(size_t bytes_acked, uint32 actual_delay, int64 min_rtt)
{
assert(min_rtt >= 0);
int32 our_delay = min<uint32>(our_hist.get_value(), uint32(min_rtt));
assert(our_delay != INT_MAX);
assert(our_delay >= 0);
utp_call_on_delay_sample(this->ctx, this, our_delay / 1000);
int target = target_delay;
if (target <= 0) target = 100000;
int32 penalty = 0;
if (clock_drift < -200000) {
penalty = (-clock_drift - 200000) / 7;
our_delay += penalty;
}
double off_target = target - our_delay;
assert(bytes_acked > 0);
double window_factor = (double)min(bytes_acked, max_window) / (double)max(max_window, bytes_acked);
double delay_factor = off_target / target;
double scaled_gain = MAX_CWND_INCREASE_BYTES_PER_RTT * window_factor * delay_factor;
assert(scaled_gain <= 1. + MAX_CWND_INCREASE_BYTES_PER_RTT * (double)min(bytes_acked, max_window) / (double)max(max_window, bytes_acked));
if (scaled_gain > 0 && ctx->current_ms - last_maxed_out_window > 1000) {
scaled_gain = 0;
}
size_t ledbat_cwnd = (max_window + scaled_gain < MIN_WINDOW_SIZE) ? MIN_WINDOW_SIZE : (size_t)(max_window + scaled_gain);
if (slow_start) {
size_t ss_cwnd = (size_t)(max_window + window_factor*get_packet_size());
if (ss_cwnd > ssthresh) {
slow_start = false;
} else if (our_delay > target*0.9) {
slow_start = false;
ssthresh = max_window;
} else {
max_window = max(ss_cwnd, ledbat_cwnd);
}
} else {
max_window = ledbat_cwnd;
}
max_window = clamp<size_t>(max_window, MIN_WINDOW_SIZE, opt_sndbuf);
log(UTP_LOG_NORMAL, "actual_delay:%u our_delay:%d their_delay:%u off_target:%d max_window:%u "
"delay_base:%u delay_sum:%d target_delay:%d acked_bytes:%u cur_window:%u "
"scaled_gain:%f rtt:%u rate:%u wnduser:%u rto:%u timeout:%d get_microseconds:" I64u " "
"cur_window_packets:%u packet_size:%u their_delay_base:%u their_actual_delay:%u "
"average_delay:%d clock_drift:%d clock_drift_raw:%d delay_penalty:%d current_delay_sum:" I64u
"current_delay_samples:%d average_delay_base:%d last_maxed_out_window:" I64u " opt_sndbuf:%d "
"current_ms:" I64u "",
actual_delay, our_delay / 1000, their_hist.get_value() / 1000,
int(off_target / 1000), uint(max_window), uint32(our_hist.delay_base),
int((our_delay + their_hist.get_value()) / 1000), int(target / 1000), uint(bytes_acked),
(uint)(cur_window - bytes_acked), (float)(scaled_gain), rtt,
(uint)(max_window * 1000 / (rtt_hist.delay_base?rtt_hist.delay_base:50)),
(uint)max_window_user, rto, (int)(rto_timeout - ctx->current_ms),
utp_call_get_microseconds(this->ctx, this), cur_window_packets, (uint)get_packet_size(),
their_hist.delay_base, their_hist.delay_base + their_hist.get_value(),
average_delay, clock_drift, clock_drift_raw, penalty / 1000,
current_delay_sum, current_delay_samples, average_delay_base,
uint64(last_maxed_out_window), int(opt_sndbuf), uint64(ctx->current_ms));
}
static void utp_register_recv_packet(UTPSocket *conn, size_t len)
{
#ifdef _DEBUG
++conn->_stats.nrecv;
conn->_stats.nbytes_recv += len;
#endif
if (len <= PACKET_SIZE_MID) {
if (len <= PACKET_SIZE_EMPTY) {
conn->ctx->context_stats._nraw_recv[PACKET_SIZE_EMPTY_BUCKET]++;
} else if (len <= PACKET_SIZE_SMALL) {
conn->ctx->context_stats._nraw_recv[PACKET_SIZE_SMALL_BUCKET]++;
} else
conn->ctx->context_stats._nraw_recv[PACKET_SIZE_MID_BUCKET]++;
} else {
if (len <= PACKET_SIZE_BIG) {
conn->ctx->context_stats._nraw_recv[PACKET_SIZE_BIG_BUCKET]++;
} else
conn->ctx->context_stats._nraw_recv[PACKET_SIZE_HUGE_BUCKET]++;
}
}
size_t UTPSocket::get_packet_size() const
{
int header_size = sizeof(PacketFormatV1);
size_t mtu = mtu_last ? mtu_last : mtu_ceiling;
return mtu - header_size;
}
size_t utp_process_incoming(UTPSocket *conn, const byte *packet, size_t len, bool syn = false)
{
utp_register_recv_packet(conn, len);
conn->ctx->current_ms = utp_call_get_milliseconds(conn->ctx, conn);
const PacketFormatV1 *pf1 = (PacketFormatV1*)packet;
const byte *packet_end = packet + len;
uint16 pk_seq_nr = pf1->seq_nr;
uint16 pk_ack_nr = pf1->ack_nr;
uint8 pk_flags = pf1->type();
if (pk_flags >= ST_NUM_STATES) return 0;
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Got %s. seq_nr:%u ack_nr:%u state:%s timestamp:" I64u " reply_micro:%u"
, flagnames[pk_flags], pk_seq_nr, pk_ack_nr, statenames[conn->state]
, uint64(pf1->tv_usec), (uint32)(pf1->reply_micro));
#endif
uint64 time = utp_call_get_microseconds(conn->ctx, conn);
const uint16 curr_window = max<uint16>(conn->cur_window_packets + ACK_NR_ALLOWED_WINDOW, ACK_NR_ALLOWED_WINDOW);
if ((pk_flags != ST_SYN || conn->state != CS_SYN_RECV) &&
(wrapping_compare_less(conn->seq_nr - 1, pk_ack_nr, ACK_NR_MASK)
|| wrapping_compare_less(pk_ack_nr, conn->seq_nr - 1 - curr_window, ACK_NR_MASK))) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Invalid ack_nr: %u. our seq_nr: %u last unacked: %u"
, pk_ack_nr, conn->seq_nr, (conn->seq_nr - conn->cur_window_packets) & ACK_NR_MASK);
#endif
return 0;
}
assert(pk_flags != ST_RESET);
const byte *selack_ptr = NULL;
const byte *data = (const byte*)pf1 + conn->get_header_size();
if (conn->get_header_size() > len) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Invalid packet size (less than header size)");
#endif
return 0;
}
uint extension = pf1->ext;
if (extension != 0) {
do {
data += 2;
if ((int)(packet_end - data) < 0 || (int)(packet_end - data) < data[-1]) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Invalid len of extensions");
#endif
return 0;
}
switch(extension) {
case 1: selack_ptr = data;
break;
case 2: if (data[-1] != 8) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Invalid len of extension bits header");
#endif
return 0;
}
memcpy(conn->extensions, data, 8);
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "got extension bits:%02x%02x%02x%02x%02x%02x%02x%02x",
conn->extensions[0], conn->extensions[1], conn->extensions[2], conn->extensions[3],
conn->extensions[4], conn->extensions[5], conn->extensions[6], conn->extensions[7]);
#endif
}
extension = data[-2];
data += data[-1];
} while (extension);
}
if (conn->state == CS_SYN_SENT) {
conn->ack_nr = (pk_seq_nr - 1) & SEQ_NR_MASK;
}
conn->last_got_packet = conn->ctx->current_ms;
if (syn) {
return 0;
}
const uint seqnr = (pk_seq_nr - conn->ack_nr - 1) & SEQ_NR_MASK;
if (seqnr >= REORDER_BUFFER_MAX_SIZE) {
if (seqnr >= (SEQ_NR_MASK + 1) - REORDER_BUFFER_MAX_SIZE && pk_flags != ST_STATE) {
conn->schedule_ack();
}
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, " Got old Packet/Ack (%u/%u)=%u"
, pk_seq_nr, conn->ack_nr, seqnr);
#endif
return 0;
}
int acks = (pk_ack_nr - (conn->seq_nr - 1 - conn->cur_window_packets)) & ACK_NR_MASK;
if (acks > conn->cur_window_packets) acks = 0;
if (conn->cur_window_packets > 0) {
if (pk_ack_nr == ((conn->seq_nr - conn->cur_window_packets - 1) & ACK_NR_MASK)
&& conn->cur_window_packets > 0
&& pk_flags == ST_STATE) {
++conn->duplicate_ack;
if (conn->duplicate_ack == DUPLICATE_ACKS_BEFORE_RESEND && conn->mtu_probe_seq) {
if (pk_ack_nr == ((conn->mtu_probe_seq - 1) & ACK_NR_MASK)) {
conn->mtu_ceiling = conn->mtu_probe_size - 1;
conn->mtu_search_update();
conn->log(UTP_LOG_MTU, "MTU [DUPACK] floor:%d ceiling:%d current:%d"
, conn->mtu_floor, conn->mtu_ceiling, conn->mtu_last);
} else {
conn->mtu_probe_seq = conn->mtu_probe_size = 0;
}
}
} else {
conn->duplicate_ack = 0;
}
}
size_t acked_bytes = 0;
int64 min_rtt = INT64_MAX;
uint64 now = utp_call_get_microseconds(conn->ctx, conn);
for (int i = 0; i < acks; ++i) {
int seq = (conn->seq_nr - conn->cur_window_packets + i) & ACK_NR_MASK;
OutgoingPacket *pkt = (OutgoingPacket*)conn->outbuf.get(seq);
if (pkt == 0 || pkt->transmissions == 0) continue;
assert((int)(pkt->payload) >= 0);
acked_bytes += pkt->payload;
if (conn->mtu_probe_seq && seq == conn->mtu_probe_seq) {
conn->mtu_floor = conn->mtu_probe_size;
conn->mtu_search_update();
conn->log(UTP_LOG_MTU, "MTU [ACK] floor:%d ceiling:%d current:%d"
, conn->mtu_floor, conn->mtu_ceiling, conn->mtu_last);
}
if (pkt->time_sent < now)
min_rtt = min<int64>(min_rtt, now - pkt->time_sent);
else
min_rtt = min<int64>(min_rtt, 50000);
}
if (selack_ptr != NULL) {
acked_bytes += conn->selective_ack_bytes((pk_ack_nr + 2) & ACK_NR_MASK,
selack_ptr, selack_ptr[-1], min_rtt);
}
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "acks:%d acked_bytes:%u seq_nr:%d cur_window:%u cur_window_packets:%u relative_seqnr:%u max_window:%u min_rtt:%u rtt:%u",
acks, (uint)acked_bytes, conn->seq_nr, (uint)conn->cur_window, conn->cur_window_packets,
seqnr, (uint)conn->max_window, (uint)(min_rtt / 1000), conn->rtt);
#endif
uint64 p = pf1->tv_usec;
conn->last_measured_delay = conn->ctx->current_ms;
const uint32 their_delay = (uint32)(p == 0 ? 0 : time - p);
conn->reply_micro = their_delay;
uint32 prev_delay_base = conn->their_hist.delay_base;
if (their_delay != 0) conn->their_hist.add_sample(their_delay, conn->ctx->current_ms);
if (prev_delay_base != 0 &&
wrapping_compare_less(conn->their_hist.delay_base, prev_delay_base, TIMESTAMP_MASK)) {
if (prev_delay_base - conn->their_hist.delay_base <= 10000) {
conn->our_hist.shift(prev_delay_base - conn->their_hist.delay_base);
}
}
const uint32 actual_delay = (uint32(pf1->reply_micro)==INT_MAX?0:uint32(pf1->reply_micro));
if (actual_delay != 0) {
conn->our_hist.add_sample(actual_delay, conn->ctx->current_ms);
if (conn->average_delay_base == 0) conn->average_delay_base = actual_delay;
int64 average_delay_sample = 0;
const uint32 dist_down = conn->average_delay_base - actual_delay;
const uint32 dist_up = actual_delay - conn->average_delay_base;
if (dist_down > dist_up) {
average_delay_sample = dist_up;
} else {
average_delay_sample = -int64(dist_down);
}
conn->current_delay_sum += average_delay_sample;
++conn->current_delay_samples;
if (conn->ctx->current_ms > conn->average_sample_time) {
int32 prev_average_delay = conn->average_delay;
assert(conn->current_delay_sum / conn->current_delay_samples < INT_MAX);
assert(conn->current_delay_sum / conn->current_delay_samples > -INT_MAX);
conn->average_delay = (int32)(conn->current_delay_sum / conn->current_delay_samples);
conn->average_sample_time += 5000;
conn->current_delay_sum = 0;
conn->current_delay_samples = 0;
int min_sample = min(prev_average_delay, conn->average_delay);
int max_sample = max(prev_average_delay, conn->average_delay);
int adjust = 0;
if (min_sample > 0) {
adjust = -min_sample;
} else if (max_sample < 0) {
adjust = -max_sample;
}
if (adjust) {
conn->average_delay_base -= adjust;
conn->average_delay += adjust;
prev_average_delay += adjust;
}
int32 drift = conn->average_delay - prev_average_delay;
conn->clock_drift = (int64(conn->clock_drift) * 7 + drift) / 8;
conn->clock_drift_raw = drift;
}
}
assert(min_rtt >= 0);
if (int64(conn->our_hist.get_value()) > min_rtt) {
conn->our_hist.shift((uint32)(conn->our_hist.get_value() - min_rtt));
}
if (actual_delay != 0 && acked_bytes >= 1)
conn->apply_ccontrol(acked_bytes, actual_delay, min_rtt);
if (acks <= conn->cur_window_packets) {
conn->max_window_user = pf1->windowsize;
if (conn->max_window_user == 0)
conn->zerowindow_time = conn->ctx->current_ms + 15000;
if (pk_flags == ST_DATA && conn->state == CS_SYN_RECV) {
conn->state = CS_CONNECTED;
}
if (pk_flags == ST_STATE && conn->state == CS_SYN_SENT) {
conn->state = CS_CONNECTED;
if (conn->ctx->callbacks[UTP_ON_CONNECT])
utp_call_on_connect(conn->ctx, conn);
else
utp_call_on_state_change(conn->ctx, conn, UTP_STATE_CONNECT);
} else if (conn->fin_sent && conn->cur_window_packets == acks) {
conn->fin_sent_acked = true;
if (conn->close_requested) {
conn->state = CS_DESTROY;
}
}
if (wrapping_compare_less(conn->fast_resend_seq_nr
, (pk_ack_nr + 1) & ACK_NR_MASK, ACK_NR_MASK))
conn->fast_resend_seq_nr = (pk_ack_nr + 1) & ACK_NR_MASK;
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "fast_resend_seq_nr:%u", conn->fast_resend_seq_nr);
#endif
for (int i = 0; i < acks; ++i) {
int ack_status = conn->ack_packet(conn->seq_nr - conn->cur_window_packets);
if (ack_status == 2) {
#ifdef _DEBUG
OutgoingPacket* pkt = (OutgoingPacket*)conn->outbuf.get(conn->seq_nr - conn->cur_window_packets);
assert(pkt->transmissions == 0);
#endif
break;
}
conn->cur_window_packets--;
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "decementing cur_window_packets:%u", conn->cur_window_packets);
#endif
}
#ifdef _DEBUG
if (conn->cur_window_packets == 0)
assert(conn->cur_window == 0);
#endif
while (conn->cur_window_packets > 0 && !conn->outbuf.get(conn->seq_nr - conn->cur_window_packets)) {
conn->cur_window_packets--;
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "decementing cur_window_packets:%u", conn->cur_window_packets);
#endif
}
#ifdef _DEBUG
if (conn->cur_window_packets == 0)
assert(conn->cur_window == 0);
#endif
assert(conn->cur_window_packets == 0 || conn->outbuf.get(conn->seq_nr - conn->cur_window_packets));
if (conn->cur_window_packets == 1) {
OutgoingPacket *pkt = (OutgoingPacket*)conn->outbuf.get(conn->seq_nr - 1);
if (pkt->transmissions == 0) {
conn->send_packet(pkt);
}
}
if (conn->fast_timeout) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Fast timeout %u,%u,%u?", (uint)conn->cur_window, conn->seq_nr - conn->timeout_seq_nr, conn->timeout_seq_nr);
#endif
if (((conn->seq_nr - conn->cur_window_packets) & ACK_NR_MASK) != conn->fast_resend_seq_nr) {
conn->fast_timeout = false;
} else {
OutgoingPacket *pkt = (OutgoingPacket*)conn->outbuf.get(conn->seq_nr - conn->cur_window_packets);
if (pkt && pkt->transmissions > 0) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Packet %u fast timeout-retry.", conn->seq_nr - conn->cur_window_packets);
#endif
#ifdef _DEBUG
++conn->_stats.fastrexmit;
#endif
conn->fast_resend_seq_nr++;
conn->send_packet(pkt);
}
}
}
}
if (selack_ptr != NULL) {
conn->selective_ack(pk_ack_nr + 2, selack_ptr, selack_ptr[-1]);
}
assert(conn->cur_window_packets == 0 || conn->outbuf.get(conn->seq_nr - conn->cur_window_packets));
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "acks:%d acked_bytes:%u seq_nr:%u cur_window:%u cur_window_packets:%u ",
acks, (uint)acked_bytes, conn->seq_nr, (uint)conn->cur_window, conn->cur_window_packets);
#endif
if (conn->state == CS_CONNECTED_FULL && !conn->is_full()) {
conn->state = CS_CONNECTED;
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Socket writable. max_window:%u cur_window:%u packet_size:%u",
(uint)conn->max_window, (uint)conn->cur_window, (uint)conn->get_packet_size());
#endif
utp_call_on_state_change(conn->ctx, conn, UTP_STATE_WRITABLE);
}
if (pk_flags == ST_STATE) {
return 0;
}
if (conn->state != CS_CONNECTED &&
conn->state != CS_CONNECTED_FULL) {
return 0;
}
if (pk_flags == ST_FIN && !conn->got_fin) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Got FIN eof_pkt:%u", pk_seq_nr);
#endif
conn->got_fin = true;
conn->eof_pkt = pk_seq_nr;
}
if (seqnr == 0) {
size_t count = packet_end - data;
if (count > 0 && !conn->read_shutdown) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Got Data len:%u (rb:%u)", (uint)count, (uint)utp_call_get_read_buffer_size(conn->ctx, conn));
#endif
utp_call_on_read(conn->ctx, conn, data, count);
}
conn->ack_nr++;
for (;;) {
if (!conn->got_fin_reached && conn->got_fin && conn->eof_pkt == conn->ack_nr) {
conn->got_fin_reached = true;
conn->rto_timeout = conn->ctx->current_ms + min<uint>(conn->rto * 3, 60);
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Posting EOF");
#endif
utp_call_on_state_change(conn->ctx, conn, UTP_STATE_EOF);
conn->send_ack();
conn->reorder_count = 0;
}
if (conn->reorder_count == 0)
break;
byte *p = (byte*)conn->inbuf.get(conn->ack_nr+1);
if (p == NULL)
break;
conn->inbuf.put(conn->ack_nr+1, NULL);
count = *(uint*)p;
if (count > 0 && !conn->read_shutdown) {
utp_call_on_read(conn->ctx, conn, p + sizeof(uint), count);
}
conn->ack_nr++;
free(p);
assert(conn->reorder_count > 0);
conn->reorder_count--;
}
conn->schedule_ack();
} else {
if (conn->got_fin && pk_seq_nr > conn->eof_pkt) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Got an invalid packet sequence number, past EOF "
"reorder_count:%u len:%u (rb:%u)",
conn->reorder_count, (uint)(packet_end - data), (uint)utp_call_get_read_buffer_size(conn->ctx, conn));
#endif
return 0;
}
if (seqnr > 0x3ff) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "0x%08x: Got an invalid packet sequence number, too far off "
"reorder_count:%u len:%u (rb:%u)",
conn->reorder_count, (uint)(packet_end - data), (uint)utp_call_get_read_buffer_size(conn->ctx, conn));
#endif
return 0;
}
conn->inbuf.ensure_size(pk_seq_nr + 1, seqnr + 1);
if (conn->inbuf.get(pk_seq_nr) != NULL) {
#ifdef _DEBUG
++conn->_stats.nduprecv;
#endif
return 0;
}
byte *mem = (byte*)malloc((packet_end - data) + sizeof(uint));
*(uint*)mem = (uint)(packet_end - data);
memcpy(mem + sizeof(uint), data, packet_end - data);
assert(conn->inbuf.get(pk_seq_nr) == NULL);
assert((pk_seq_nr & conn->inbuf.mask) != ((conn->ack_nr+1) & conn->inbuf.mask));
conn->inbuf.put(pk_seq_nr, mem);
conn->reorder_count++;
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "0x%08x: Got out of order data reorder_count:%u len:%u (rb:%u)",
conn->reorder_count, (uint)(packet_end - data), (uint)utp_call_get_read_buffer_size(conn->ctx, conn));
#endif
conn->schedule_ack();
}
return (size_t)(packet_end - data);
}
inline byte UTP_Version(PacketFormatV1 const* pf)
{
return (pf->type() < ST_NUM_STATES && pf->ext < 3 ? pf->version() : 0);
}
UTPSocket::~UTPSocket()
{
#if UTP_DEBUG_LOGGING
log(UTP_LOG_DEBUG, "Killing socket");
#endif
utp_call_on_state_change(ctx, this, UTP_STATE_DESTROYING);
if (ctx->last_utp_socket == this) {
ctx->last_utp_socket = NULL;
}
UTPSocketKeyData* kd = ctx->utp_sockets->Delete(UTPSocketKey(addr, conn_id_recv));
assert(kd);
removeSocketFromAckList(this);
for (size_t i = 0; i <= inbuf.mask; i++) {
free(inbuf.elements[i]);
}
for (size_t i = 0; i <= outbuf.mask; i++) {
free(outbuf.elements[i]);
}
free(inbuf.elements);
free(outbuf.elements);
}
void UTP_FreeAll(struct UTPSocketHT *utp_sockets) {
utp_hash_iterator_t it;
UTPSocketKeyData* keyData;
while ((keyData = utp_sockets->Iterate(it))) {
delete keyData->socket;
}
}
void utp_initialize_socket( utp_socket *conn,
const struct sockaddr *addr,
socklen_t addrlen,
bool need_seed_gen,
uint32 conn_seed,
uint32 conn_id_recv,
uint32 conn_id_send)
{
PackedSockAddr psaddr = PackedSockAddr((const SOCKADDR_STORAGE*)addr, addrlen);
if (need_seed_gen) {
do {
conn_seed = utp_call_get_random(conn->ctx, conn);
conn_seed &= 0xffff;
} while (conn->ctx->utp_sockets->Lookup(UTPSocketKey(psaddr, conn_seed)));
conn_id_recv += conn_seed;
conn_id_send += conn_seed;
}
conn->state = CS_IDLE;
conn->conn_seed = conn_seed;
conn->conn_id_recv = conn_id_recv;
conn->conn_id_send = conn_id_send;
conn->addr = psaddr;
conn->ctx->current_ms = utp_call_get_milliseconds(conn->ctx, NULL);
conn->last_got_packet = conn->ctx->current_ms;
conn->last_sent_packet = conn->ctx->current_ms;
conn->last_measured_delay = conn->ctx->current_ms + 0x70000000;
conn->average_sample_time = conn->ctx->current_ms + 5000;
conn->last_rwin_decay = conn->ctx->current_ms - MAX_WINDOW_DECAY;
conn->our_hist.clear(conn->ctx->current_ms);
conn->their_hist.clear(conn->ctx->current_ms);
conn->rtt_hist.clear(conn->ctx->current_ms);
conn->mtu_reset();
conn->mtu_last = conn->mtu_ceiling;
conn->ctx->utp_sockets->Add(UTPSocketKey(conn->addr, conn->conn_id_recv))->socket = conn;
conn->max_window = conn->get_packet_size();
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "UTP socket initialized");
#endif
}
utp_socket* utp_create_socket(utp_context *ctx)
{
assert(ctx);
if (!ctx) return NULL;
UTPSocket *conn = new UTPSocket;
conn->state = CS_UNINITIALIZED;
conn->ctx = ctx;
conn->userdata = NULL;
conn->reorder_count = 0;
conn->duplicate_ack = 0;
conn->timeout_seq_nr = 0;
conn->last_rcv_win = 0;
conn->got_fin = false;
conn->got_fin_reached = false;
conn->fin_sent = false;
conn->fin_sent_acked = false;
conn->read_shutdown = false;
conn->close_requested = false;
conn->fast_timeout = false;
conn->rtt = 0;
conn->retransmit_timeout = 0;
conn->rto_timeout = 0;
conn->zerowindow_time = 0;
conn->average_delay = 0;
conn->current_delay_samples = 0;
conn->cur_window = 0;
conn->eof_pkt = 0;
conn->last_maxed_out_window = 0;
conn->mtu_probe_seq = 0;
conn->mtu_probe_size = 0;
conn->current_delay_sum = 0;
conn->average_delay_base = 0;
conn->retransmit_count = 0;
conn->rto = 3000;
conn->rtt_var = 800;
conn->seq_nr = 1;
conn->ack_nr = 0;
conn->max_window_user = 255 * PACKET_SIZE;
conn->cur_window_packets = 0;
conn->fast_resend_seq_nr = conn->seq_nr;
conn->target_delay = ctx->target_delay;
conn->reply_micro = 0;
conn->opt_sndbuf = ctx->opt_sndbuf;
conn->opt_rcvbuf = ctx->opt_rcvbuf;
conn->slow_start = true;
conn->ssthresh = conn->opt_sndbuf;
conn->clock_drift = 0;
conn->clock_drift_raw = 0;
conn->outbuf.mask = 15;
conn->inbuf.mask = 15;
conn->outbuf.elements = (void**)calloc(16, sizeof(void*));
conn->inbuf.elements = (void**)calloc(16, sizeof(void*));
conn->ida = -1;
memset(conn->extensions, 0, sizeof(conn->extensions));
#ifdef _DEBUG
memset(&conn->_stats, 0, sizeof(utp_socket_stats));
#endif
return conn;
}
int utp_context_set_option(utp_context *ctx, int opt, int val)
{
assert(ctx);
if (!ctx) return -1;
switch (opt) {
case UTP_LOG_NORMAL:
ctx->log_normal = val ? true : false;
return 0;
case UTP_LOG_MTU:
ctx->log_mtu = val ? true : false;
return 0;
case UTP_LOG_DEBUG:
ctx->log_debug = val ? true : false;
return 0;
case UTP_TARGET_DELAY:
ctx->target_delay = val;
return 0;
case UTP_SNDBUF:
assert(val >= 1);
ctx->opt_sndbuf = val;
return 0;
case UTP_RCVBUF:
assert(val >= 1);
ctx->opt_rcvbuf = val;
return 0;
}
return -1;
}
int utp_context_get_option(utp_context *ctx, int opt)
{
assert(ctx);
if (!ctx) return -1;
switch (opt) {
case UTP_LOG_NORMAL: return ctx->log_normal ? 1 : 0;
case UTP_LOG_MTU: return ctx->log_mtu ? 1 : 0;
case UTP_LOG_DEBUG: return ctx->log_debug ? 1 : 0;
case UTP_TARGET_DELAY: return ctx->target_delay;
case UTP_SNDBUF: return ctx->opt_sndbuf;
case UTP_RCVBUF: return ctx->opt_rcvbuf;
}
return -1;
}
int utp_setsockopt(UTPSocket* conn, int opt, int val)
{
assert(conn);
if (!conn) return -1;
switch (opt) {
case UTP_SNDBUF:
assert(val >= 1);
conn->opt_sndbuf = val;
return 0;
case UTP_RCVBUF:
assert(val >= 1);
conn->opt_rcvbuf = val;
return 0;
case UTP_TARGET_DELAY:
conn->target_delay = val;
return 0;
}
return -1;
}
int utp_getsockopt(UTPSocket* conn, int opt)
{
assert(conn);
if (!conn) return -1;
switch (opt) {
case UTP_SNDBUF: return conn->opt_sndbuf;
case UTP_RCVBUF: return conn->opt_rcvbuf;
case UTP_TARGET_DELAY: return conn->target_delay;
}
return -1;
}
int utp_connect(utp_socket *conn, const struct sockaddr *to, socklen_t tolen)
{
assert(conn);
if (!conn) return -1;
assert(conn->state == CS_UNINITIALIZED);
if (conn->state != CS_UNINITIALIZED) {
conn->state = CS_DESTROY;
return -1;
}
utp_initialize_socket(conn, to, tolen, true, 0, 0, 1);
assert(conn->cur_window_packets == 0);
assert(conn->outbuf.get(conn->seq_nr) == NULL);
assert(sizeof(PacketFormatV1) == 20);
conn->state = CS_SYN_SENT;
conn->ctx->current_ms = utp_call_get_milliseconds(conn->ctx, conn);
conn->log(UTP_LOG_NORMAL, "UTP_Connect conn_seed:%u packet_size:%u (B) "
"target_delay:%u (ms) delay_history:%u "
"delay_base_history:%u (minutes)",
conn->conn_seed, PACKET_SIZE, conn->target_delay / 1000,
CUR_DELAY_SIZE, DELAY_BASE_HISTORY);
conn->retransmit_timeout = 3000;
conn->rto_timeout = conn->ctx->current_ms + conn->retransmit_timeout;
conn->last_rcv_win = conn->get_rcv_window();
conn->seq_nr = utp_call_get_random(conn->ctx, conn);
const size_t header_size = sizeof(PacketFormatV1);
OutgoingPacket *pkt = (OutgoingPacket*)malloc(sizeof(OutgoingPacket) - 1 + header_size);
PacketFormatV1* p1 = (PacketFormatV1*)pkt->data;
memset(p1, 0, header_size);
p1->set_version(1);
p1->set_type(ST_SYN);
p1->ext = 0;
p1->connid = conn->conn_id_recv;
p1->windowsize = (uint32)conn->last_rcv_win;
p1->seq_nr = conn->seq_nr;
pkt->transmissions = 0;
pkt->length = header_size;
pkt->payload = 0;
conn->outbuf.ensure_size(conn->seq_nr, conn->cur_window_packets);
conn->outbuf.put(conn->seq_nr, pkt);
conn->seq_nr++;
conn->cur_window_packets++;
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "incrementing cur_window_packets:%u", conn->cur_window_packets);
#endif
conn->send_packet(pkt);
return 0;
}
int utp_process_udp(utp_context *ctx, const byte *buffer, size_t len, const struct sockaddr *to, socklen_t tolen)
{
assert(ctx);
if (!ctx) return 0;
assert(buffer);
if (!buffer) return 0;
assert(to);
if (!to) return 0;
const PackedSockAddr addr((const SOCKADDR_STORAGE*)to, tolen);
if (len < sizeof(PacketFormatV1)) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv %s len:%u too small", addrfmt(addr, addrbuf), (uint)len);
#endif
return 0;
}
const PacketFormatV1 *pf1 = (PacketFormatV1*)buffer;
const byte version = UTP_Version(pf1);
const uint32 id = uint32(pf1->connid);
if (version != 1) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv %s len:%u version:%u unsupported version", addrfmt(addr, addrbuf), (uint)len, version);
#endif
return 0;
}
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv %s len:%u id:%u", addrfmt(addr, addrbuf), (uint)len, id);
ctx->log(UTP_LOG_DEBUG, NULL, "recv id:%u seq_nr:%u ack_nr:%u", id, (uint)pf1->seq_nr, (uint)pf1->ack_nr);
#endif
const byte flags = pf1->type();
if (flags == ST_RESET) {
UTPSocketKeyData* keyData;
if ( (keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id))) ||
((keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id + 1))) && keyData->socket->conn_id_send == id) ||
((keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id - 1))) && keyData->socket->conn_id_send == id))
{
UTPSocket* conn = keyData->socket;
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv RST for existing connection");
#endif
if (conn->close_requested)
conn->state = CS_DESTROY;
else
conn->state = CS_RESET;
utp_call_on_overhead_statistics(conn->ctx, conn, false, len + conn->get_udp_overhead(), close_overhead);
const int err = (conn->state == CS_SYN_SENT) ? UTP_ECONNREFUSED : UTP_ECONNRESET;
utp_call_on_error(conn->ctx, conn, err);
}
else {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv RST for unknown connection");
#endif
}
return 1;
}
else if (flags != ST_SYN) {
UTPSocket* conn = NULL;
if (ctx->last_utp_socket && ctx->last_utp_socket->addr == addr && ctx->last_utp_socket->conn_id_recv == id) {
conn = ctx->last_utp_socket;
} else {
UTPSocketKeyData* keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id));
if (keyData) {
conn = keyData->socket;
ctx->last_utp_socket = conn;
}
}
if (conn) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv processing");
#endif
const size_t read = utp_process_incoming(conn, buffer, len);
utp_call_on_overhead_statistics(conn->ctx, conn, false, (len - read) + conn->get_udp_overhead(), header_overhead);
return 1;
}
}
const uint32 seq_nr = pf1->seq_nr;
if (flags != ST_SYN) {
ctx->current_ms = utp_call_get_milliseconds(ctx, NULL);
for (size_t i = 0; i < ctx->rst_info.GetCount(); i++) {
if ((ctx->rst_info[i].connid == id) &&
(ctx->rst_info[i].addr == addr) &&
(ctx->rst_info[i].ack_nr == seq_nr))
{
ctx->rst_info[i].timestamp = ctx->current_ms;
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv not sending RST to non-SYN (stored)");
#endif
return 1;
}
}
if (ctx->rst_info.GetCount() > RST_INFO_LIMIT) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv not sending RST to non-SYN (limit at %u stored)", (uint)ctx->rst_info.GetCount());
#endif
return 1;
}
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv send RST to non-SYN (%u stored)", (uint)ctx->rst_info.GetCount());
#endif
RST_Info &r = ctx->rst_info.Append();
r.addr = addr;
r.connid = id;
r.ack_nr = seq_nr;
r.timestamp = ctx->current_ms;
UTPSocket::send_rst(ctx, addr, id, seq_nr, utp_call_get_random(ctx, NULL));
return 1;
}
if (ctx->callbacks[UTP_ON_ACCEPT]) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "Incoming connection from %s", addrfmt(addr, addrbuf));
#endif
UTPSocketKeyData* keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id + 1));
if (keyData) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "rejected incoming connection, connection already exists");
#endif
return 1;
}
if (ctx->utp_sockets->GetCount() > 3000) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "rejected incoming connection, too many uTP sockets %d", ctx->utp_sockets->GetCount());
#endif
return 1;
}
if (utp_call_on_firewall(ctx, to, tolen)) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "rejected incoming connection, firewall callback returned true");
#endif
return 1;
}
UTPSocket *conn = utp_create_socket(ctx);
utp_initialize_socket(conn, to, tolen, false, id, id+1, id);
conn->ack_nr = seq_nr;
conn->seq_nr = utp_call_get_random(ctx, NULL);
conn->fast_resend_seq_nr = conn->seq_nr;
conn->state = CS_SYN_RECV;
const size_t read = utp_process_incoming(conn, buffer, len, true);
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "recv send connect ACK");
#endif
conn->send_ack(true);
utp_call_on_accept(ctx, conn, to, tolen);
utp_call_on_overhead_statistics(conn->ctx, conn, false, (len - read) + conn->get_udp_overhead(), header_overhead); utp_call_on_overhead_statistics(conn->ctx, conn, true, conn->get_overhead(), ack_overhead); }
else {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "rejected incoming connection, UTP_ON_ACCEPT callback not set");
#endif
}
return 1;
}
static UTPSocket* parse_icmp_payload(utp_context *ctx, const byte *buffer, size_t len, const struct sockaddr *to, socklen_t tolen)
{
assert(ctx);
if (!ctx) return NULL;
assert(buffer);
if (!buffer) return NULL;
assert(to);
if (!to) return NULL;
const PackedSockAddr addr((const SOCKADDR_STORAGE*)to, tolen);
if (len < sizeof(PacketFormatV1)) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "Ignoring ICMP from %s: runt length %d", addrfmt(addr, addrbuf), len);
#endif
return NULL;
}
const PacketFormatV1 *pf = (PacketFormatV1*)buffer;
const byte version = UTP_Version(pf);
const uint32 id = uint32(pf->connid);
if (version != 1) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "Ignoring ICMP from %s: not UTP version 1", addrfmt(addr, addrbuf));
#endif
return NULL;
}
UTPSocketKeyData* keyData;
if ( (keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id))) ||
((keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id + 1))) && keyData->socket->conn_id_send == id) ||
((keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id - 1))) && keyData->socket->conn_id_send == id))
{
return keyData->socket;
}
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "Ignoring ICMP from %s: No matching connection found for id %u", addrfmt(addr, addrbuf), id);
#endif
return NULL;
}
int utp_process_icmp_fragmentation(utp_context *ctx, const byte* buffer, size_t len, const struct sockaddr *to, socklen_t tolen, uint16 next_hop_mtu)
{
UTPSocket* conn = parse_icmp_payload(ctx, buffer, len, to, tolen);
if (!conn) return 0;
if (next_hop_mtu >= 576 && next_hop_mtu < 0x2000) {
conn->mtu_ceiling = min<uint32>(next_hop_mtu, conn->mtu_ceiling);
conn->mtu_search_update();
conn->mtu_last = conn->mtu_ceiling;
} else {
conn->mtu_ceiling = (conn->mtu_floor + conn->mtu_ceiling) / 2;
conn->mtu_search_update();
}
conn->log(UTP_LOG_MTU, "MTU [ICMP] floor:%d ceiling:%d current:%d", conn->mtu_floor, conn->mtu_ceiling, conn->mtu_last);
return 1;
}
int utp_process_icmp_error(utp_context *ctx, const byte *buffer, size_t len, const struct sockaddr *to, socklen_t tolen)
{
UTPSocket* conn = parse_icmp_payload(ctx, buffer, len, to, tolen);
if (!conn) return 0;
const int err = (conn->state == CS_SYN_SENT) ? UTP_ECONNREFUSED : UTP_ECONNRESET;
const PackedSockAddr addr((const SOCKADDR_STORAGE*)to, tolen);
switch(conn->state) {
case CS_IDLE:
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "ICMP from %s in state CS_IDLE, ignoring", addrfmt(addr, addrbuf));
#endif
return 1;
default:
if (conn->close_requested) {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "ICMP from %s after close, setting state to CS_DESTROY and causing error %d", addrfmt(addr, addrbuf), err);
#endif
conn->state = CS_DESTROY;
} else {
#if UTP_DEBUG_LOGGING
ctx->log(UTP_LOG_DEBUG, NULL, "ICMP from %s, setting state to CS_RESET and causing error %d", addrfmt(addr, addrbuf), err);
#endif
conn->state = CS_RESET;
}
break;
}
utp_call_on_error(conn->ctx, conn, err);
return 1;
}
ssize_t utp_writev(utp_socket *conn, struct utp_iovec *iovec_input, size_t num_iovecs)
{
static utp_iovec iovec[UTP_IOV_MAX];
assert(conn);
if (!conn) return -1;
assert(iovec_input);
if (!iovec_input) return -1;
assert(num_iovecs);
if (!num_iovecs) return -1;
if (num_iovecs > UTP_IOV_MAX)
num_iovecs = UTP_IOV_MAX;
memcpy(iovec, iovec_input, sizeof(struct utp_iovec)*num_iovecs);
size_t bytes = 0;
size_t sent = 0;
for (size_t i = 0; i < num_iovecs; i++)
bytes += iovec[i].iov_len;
#if UTP_DEBUG_LOGGING
size_t param = bytes;
#endif
if (conn->state != CS_CONNECTED) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "UTP_Write %u bytes = false (not CS_CONNECTED)", (uint)bytes);
#endif
return 0;
}
if (conn->fin_sent) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "UTP_Write %u bytes = false (fin_sent already)", (uint)bytes);
#endif
return 0;
}
conn->ctx->current_ms = utp_call_get_milliseconds(conn->ctx, conn);
size_t packet_size = conn->get_packet_size();
size_t num_to_send = min<size_t>(bytes, packet_size);
while (!conn->is_full(num_to_send)) {
bytes -= num_to_send;
sent += num_to_send;
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Sending packet. seq_nr:%u ack_nr:%u wnd:%u/%u/%u rcv_win:%u size:%u cur_window_packets:%u",
conn->seq_nr, conn->ack_nr,
(uint)(conn->cur_window + num_to_send),
(uint)conn->max_window, (uint)conn->max_window_user,
(uint)conn->last_rcv_win, num_to_send,
conn->cur_window_packets);
#endif
conn->write_outgoing_packet(num_to_send, ST_DATA, iovec, num_iovecs);
num_to_send = min<size_t>(bytes, packet_size);
if (num_to_send == 0) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "UTP_Write %u bytes = true", (uint)param);
#endif
return sent;
}
}
bool full = conn->is_full();
if (full) {
conn->state = CS_CONNECTED_FULL;
}
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "UTP_Write %u bytes = %s", (uint)bytes, full ? "false" : "true");
#endif
return sent;
}
void utp_read_drained(utp_socket *conn)
{
assert(conn);
if (!conn) return;
assert(conn->state != CS_UNINITIALIZED);
if (conn->state == CS_UNINITIALIZED) return;
const size_t rcvwin = conn->get_rcv_window();
if (rcvwin > conn->last_rcv_win) {
if (conn->last_rcv_win == 0) {
conn->send_ack();
} else {
conn->ctx->current_ms = utp_call_get_milliseconds(conn->ctx, conn);
conn->schedule_ack();
}
}
}
void utp_issue_deferred_acks(utp_context *ctx)
{
assert(ctx);
if (!ctx) return;
for (size_t i = 0; i < ctx->ack_sockets.GetCount(); i++) {
UTPSocket *conn = ctx->ack_sockets[i];
conn->send_ack();
i--;
}
}
void utp_check_timeouts(utp_context *ctx)
{
assert(ctx);
if (!ctx) return;
ctx->current_ms = utp_call_get_milliseconds(ctx, NULL);
if (ctx->current_ms - ctx->last_check < TIMEOUT_CHECK_INTERVAL)
return;
ctx->last_check = ctx->current_ms;
for (size_t i = 0; i < ctx->rst_info.GetCount(); i++) {
if ((int)(ctx->current_ms - ctx->rst_info[i].timestamp) >= RST_INFO_TIMEOUT) {
ctx->rst_info.MoveUpLast(i);
i--;
}
}
if (ctx->rst_info.GetCount() != ctx->rst_info.GetAlloc()) {
ctx->rst_info.Compact();
}
utp_hash_iterator_t it;
UTPSocketKeyData* keyData;
while ((keyData = ctx->utp_sockets->Iterate(it))) {
UTPSocket *conn = keyData->socket;
conn->check_timeouts();
if (conn->state == CS_DESTROY) {
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "Destroying");
#endif
delete conn;
}
}
}
int utp_getpeername(utp_socket *conn, struct sockaddr *addr, socklen_t *addrlen)
{
assert(addr);
if (!addr) return -1;
assert(addrlen);
if (!addrlen) return -1;
assert(conn);
if (!conn) return -1;
assert(conn->state != CS_UNINITIALIZED);
if (conn->state == CS_UNINITIALIZED) return -1;
socklen_t len;
const SOCKADDR_STORAGE sa = conn->addr.get_sockaddr_storage(&len);
*addrlen = min(len, *addrlen);
memcpy(addr, &sa, *addrlen);
return 0;
}
int utp_get_delays(UTPSocket *conn, uint32 *ours, uint32 *theirs, uint32 *age)
{
assert(conn);
if (!conn) return -1;
assert(conn->state != CS_UNINITIALIZED);
if (conn->state == CS_UNINITIALIZED) {
if (ours) *ours = 0;
if (theirs) *theirs = 0;
if (age) *age = 0;
return -1;
}
if (ours) *ours = conn->our_hist.get_value();
if (theirs) *theirs = conn->their_hist.get_value();
if (age) *age = (uint32)(conn->ctx->current_ms - conn->last_measured_delay);
return 0;
}
void utp_close(UTPSocket *conn)
{
assert(conn);
if (!conn) return;
assert(conn->state != CS_UNINITIALIZED
&& conn->state != CS_DESTROY);
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "UTP_Close in state:%s", statenames[conn->state]);
#endif
switch(conn->state) {
case CS_CONNECTED:
case CS_CONNECTED_FULL:
conn->read_shutdown = true;
conn->close_requested = true;
if (!conn->fin_sent) {
conn->fin_sent = true;
conn->write_outgoing_packet(0, ST_FIN, NULL, 0);
} else if (conn->fin_sent_acked) {
conn->state = CS_DESTROY;
}
break;
case CS_SYN_SENT:
conn->rto_timeout = utp_call_get_milliseconds(conn->ctx, conn) + min<uint>(conn->rto * 2, 60);
case CS_SYN_RECV:
default:
conn->state = CS_DESTROY;
break;
}
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "UTP_Close end in state:%s", statenames[conn->state]);
#endif
}
void utp_shutdown(UTPSocket *conn, int how)
{
assert(conn);
if (!conn) return;
assert(conn->state != CS_UNINITIALIZED
&& conn->state != CS_DESTROY);
#if UTP_DEBUG_LOGGING
conn->log(UTP_LOG_DEBUG, "UTP_shutdown(%d) in state:%s", how, statenames[conn->state]);
#endif
if (how != SHUT_WR) {
conn->read_shutdown = true;
}
if (how != SHUT_RD) {
switch(conn->state) {
case CS_CONNECTED:
case CS_CONNECTED_FULL:
if (!conn->fin_sent) {
conn->fin_sent = true;
conn->write_outgoing_packet(0, ST_FIN, NULL, 0);
}
break;
case CS_SYN_SENT:
conn->rto_timeout = utp_call_get_milliseconds(conn->ctx, conn) + min<uint>(conn->rto * 2, 60);
default:
break;
}
}
}
utp_context* utp_get_context(utp_socket *socket) {
assert(socket);
return socket ? socket->ctx : NULL;
}
void* utp_set_userdata(utp_socket *socket, void *userdata) {
assert(socket);
if (socket) socket->userdata = userdata;
return socket ? socket->userdata : NULL;
}
void* utp_get_userdata(utp_socket *socket) {
assert(socket);
return socket ? socket->userdata : NULL;
}
void struct_utp_context::log(int level, utp_socket *socket, char const *fmt, ...)
{
if (!would_log(level)) {
return;
}
va_list va;
va_start(va, fmt);
log_unchecked(socket, fmt, va);
va_end(va);
}
void struct_utp_context::log_unchecked(utp_socket *socket, char const *fmt, ...)
{
va_list va;
char buf[4096];
va_start(va, fmt);
vsnprintf(buf, 4096, fmt, va);
buf[4095] = '\0';
va_end(va);
utp_call_log(this, socket, (const byte *)buf);
}
inline bool struct_utp_context::would_log(int level)
{
if (level == UTP_LOG_NORMAL) return log_normal;
if (level == UTP_LOG_MTU) return log_mtu;
if (level == UTP_LOG_DEBUG) return log_debug;
return true;
}
utp_socket_stats* utp_get_stats(utp_socket *socket)
{
#ifdef _DEBUG
assert(socket);
if (!socket) return NULL;
socket->_stats.mtu_guess = socket->mtu_last ? socket->mtu_last : socket->mtu_ceiling;
return &socket->_stats;
#else
return NULL;
#endif
}