Skip to content

Commit

Permalink
tap_user: Handle 0-byte UDP payload, RST on send()
Browse files Browse the repository at this point in the history
- Rewrite networking lib send/recv error handling to be a bit more like POSIX (Negative return values are errors)
- Handle 0-byte net_udp_recv() properly
- Handle send() errors other than NET_ERR_BLOCK as a connection reset
  • Loading branch information
LekKit authored Feb 28, 2024
1 parent 85b0718 commit 59cd381
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 39 deletions.
39 changes: 23 additions & 16 deletions src/devices/tap_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@ static void handle_tcp(tap_dev_t* tap, const uint8_t* buffer, size_t size, net_a
tap_sock_t* ts = tap_tcp_lookup(tap, dst, src);
if (ts) {
tcp_ctx_t* tcp = ts->tcp;
bool reset = !!(flags & TCP_FLAG_RST);
bool resp_ack = seq != tcp->ack; // Respond with ACK on keepalive
bool cleanup = false;
tcp->window = window; // Scale the window
Expand All @@ -591,7 +592,7 @@ static void handle_tcp(tap_dev_t* tap, const uint8_t* buffer, size_t size, net_a
}
if (tcp->win_full && (tcp->state & TCP_STATE_RECV_OPEN) && tcp_window_avail(tcp)) {
// Window became available
if (!tap_tcp_arm_poll(tap, ts)) cleanup = true;
if (!tap_tcp_arm_poll(tap, ts)) reset = true;
tcp->win_full = false;
}
if (tcp->seq == tcp->seq_ack + 1 && ack == tcp->seq) {
Expand All @@ -609,7 +610,7 @@ static void handle_tcp(tap_dev_t* tap, const uint8_t* buffer, size_t size, net_a
if (tap_tcp_arm_poll(tap, ts)) {
tcp->state |= TCP_STATE_ESTABLISHED;
tcp->seq_ack++;
} else cleanup = true;
} else reset = true;
}
if (tcp->state == TCP_STATE_RECV_OPEN && (flags & TCP_FLAG_SYN)) {
// Guest SYN ACKed an inbound connection
Expand All @@ -618,7 +619,7 @@ static void handle_tcp(tap_dev_t* tap, const uint8_t* buffer, size_t size, net_a
tcp->ack = seq + 1;
tcp->seq_ack++;
resp_ack = true;
} else cleanup = true;
} else reset = true;
}
}
}
Expand All @@ -629,7 +630,13 @@ static void handle_tcp(tap_dev_t* tap, const uint8_t* buffer, size_t size, net_a
size_t send_len = size - data_off;
size_t seq_off = tcp->ack - seq;
if (send_len > seq_off) {
tcp->ack += net_tcp_send(ts->sock, buffer + data_off + seq_off, send_len - seq_off);
int32_t result = net_tcp_send(ts->sock, buffer + data_off + seq_off, send_len - seq_off);
if (result >= 0) {
tcp->ack += result;
} else if (result != NET_ERR_BLOCK) {
// Connection is reset
reset = true;
}
}
// Acknowledge the bytes actually sent
// TODO: Reduce amount of response ACKs
Expand All @@ -649,16 +656,15 @@ static void handle_tcp(tap_dev_t* tap, const uint8_t* buffer, size_t size, net_a
}
resp_ack = true;
}
if (flags & TCP_FLAG_RST) {
if (reset) {
// Reset the connection
if ((tcp->state & TCP_STATE_ESTABLISHED) && !(tcp->state & TCP_STATE_RECV_OPEN)) {
if (!(flags & TCP_FLAG_RST)) tap_tcp_segment(tap, ts, TCP_FLAG_RST);
if (!!(tcp->state & TCP_STATE_ESTABLISHED) != !!(tcp->state & TCP_STATE_RECV_OPEN)) {
// Closed completely
cleanup = true;
}
tcp->state = TCP_STATE_CLOSED;
resp_ack = false;
}
if (resp_ack) {
} else if (resp_ack) {
// Handle keepalive, ACKs
tap_tcp_segment(tap, ts, TCP_FLAG_ACK);
}
Expand Down Expand Up @@ -842,8 +848,9 @@ static void tap_udp_recv(tap_dev_t* tap, tap_sock_t* ts)
size_t size = sizeof(buffer) - offset;

if (ts->timeout != BOUND_INF) ts->timeout = 0;
size = net_udp_recv(ts->sock, buffer + offset, size, &addr);
if (size) {
int32_t result = net_udp_recv(ts->sock, buffer + offset, size, &addr);
if (result >= 0) {
size = result;
tap_addr_convert(&addr);
uint8_t* ipv4 = create_eth_frame(tap, buffer, ETH2_IPv4);
uint8_t* udp = create_ipv4_frame(ipv4, size + UDP_HDR_SIZE, IP_PROTO_UDP, ts->addr.ip, addr.ip);
Expand All @@ -864,10 +871,10 @@ static void tap_tcp_recv(tap_dev_t* tap, tap_sock_t* ts)

tcp_segment_t* seg = safe_new_obj(tcp_segment_t);
size_t size = sizeof(seg->buffer) - TCP_WRAP_SIZE;
uint32_t error = 0;
seg->size = net_tcp_recv(ts->sock, seg->buffer + TCP_WRAP_SIZE, size, &error);
if (seg->size) {
int32_t result = net_tcp_recv(ts->sock, seg->buffer + TCP_WRAP_SIZE, size);
if (result > 0) {
// Push a segment and buffer it for retransmit
seg->size = result;
uint8_t* ipv4 = create_eth_frame(tap, seg->buffer, ETH2_IPv4);
uint8_t* tcp = create_ipv4_frame(ipv4, seg->size + TCP_HDR_SIZE, IP_PROTO_TCP, ts->addr.ip, net_sock_addr(ts->sock)->ip);
create_tcp_segment(tcp, TCP_FLAG_PSH | TCP_FLAG_ACK, ts->tcp->seq, ts->tcp->ack, ts->addr.port, net_sock_addr(ts->sock)->port);
Expand All @@ -885,14 +892,14 @@ static void tap_tcp_recv(tap_dev_t* tap, tap_sock_t* ts)
eth_send(tap, seg->buffer, seg->size + TCP_WRAP_SIZE);
} else {
free(seg);
if (error == NET_ERR_DISCONNECT) {
if (result == NET_ERR_DISCONNECT) {
// Receiving side closed
ts->tcp->state &= ~TCP_STATE_RECV_OPEN;
ts->tcp->seq++;
tap_tcp_segment(tap, ts, TCP_FLAG_FIN | TCP_FLAG_ACK);

net_poll_remove(tap->poll, ts->sock);
} else if (error != NET_ERR_BLOCK) {
} else if (result != NET_ERR_BLOCK) {
// Connection reset
tap_tcp_segment(tap, ts, TCP_FLAG_RST);

Expand Down
31 changes: 16 additions & 15 deletions src/networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ static net_sock_t* net_init_localaddr(net_sock_t* sock, const net_addr_t* addr)
return sock;
}

static uint32_t net_last_error()
static int32_t net_last_error()
{
#ifdef _WIN32
int err = WSAGetLastError();
Expand Down Expand Up @@ -519,21 +519,21 @@ bool net_tcp_shutdown(net_sock_t* sock)
return sock && shutdown(sock->fd, 1) == 0;
}

size_t net_tcp_send(net_sock_t* sock, const void* buffer, size_t size)
int32_t net_tcp_send(net_sock_t* sock, const void* buffer, size_t size)
{
int ret = sock ? send(sock->fd, buffer, size, 0) : 0;
return ret > 0 ? ret : 0;
if (sock == NULL) return NET_ERR_RESET;
int ret = send(sock->fd, buffer, size, 0);
if (ret < 0) return net_last_error();
return ret;
}

size_t net_tcp_recv(net_sock_t* sock, void* buffer, size_t size, uint32_t* error)
int32_t net_tcp_recv(net_sock_t* sock, void* buffer, size_t size)
{
int ret = sock ? recv(sock->fd, buffer, size, 0) : 0;
if (error) {
if (ret > 0) *error = NET_ERR_NONE;
if (ret == 0) *error = NET_ERR_DISCONNECT;
if (ret < 0) *error = net_last_error();
}
return ret > 0 ? ret : 0;
if (sock == NULL) return NET_ERR_RESET;
int ret = recv(sock->fd, buffer, size, 0);
if (ret > 0) return ret;
if (ret == 0) return NET_ERR_DISCONNECT;
return net_last_error();
}

net_sock_t* net_udp_bind(const net_addr_t* addr)
Expand Down Expand Up @@ -567,10 +567,10 @@ size_t net_udp_send(net_sock_t* sock, const void* buffer, size_t size, const net
return ret > 0 ? ret : 0;
}

size_t net_udp_recv(net_sock_t* sock, void* buffer, size_t size, net_addr_t* addr)
int32_t net_udp_recv(net_sock_t* sock, void* buffer, size_t size, net_addr_t* addr)
{
int ret = 0;
if (sock == NULL) return 0;
if (sock == NULL) return NET_ERR_RESET;
if (sock->addr.type == NET_TYPE_IPV4) {
struct sockaddr_in sock_addr = {0};
net_addrlen_t addr_len = sizeof(struct sockaddr_in);
Expand All @@ -584,7 +584,8 @@ size_t net_udp_recv(net_sock_t* sock, void* buffer, size_t size, net_addr_t* add
net_addr_from_sockaddr6(addr, &sock_addr);
#endif
}
return ret > 0 ? ret : 0;
if (ret < 0) return net_last_error();
return ret;
}

// Generic socket operations
Expand Down
16 changes: 8 additions & 8 deletions src/networking.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ extern const net_addr_t net_ipv6_local_addr;
#define NET_IPV6_ANY (&net_ipv6_any_addr)
#define NET_IPV6_LOCAL (&net_ipv6_local_addr)

#define NET_ERR_NONE 0x0
#define NET_ERR_BLOCK 0x1
#define NET_ERR_DISCONNECT 0x2
#define NET_ERR_RESET 0x3
#define NET_ERR_UNKNOWN 0xFFFF
#define NET_ERR_NONE 0
#define NET_ERR_UNKNOWN (-1)
#define NET_ERR_BLOCK (-2)
#define NET_ERR_DISCONNECT (-3)
#define NET_ERR_RESET (-4)

// TCP Sockets

Expand All @@ -66,15 +66,15 @@ bool net_tcp_sockpair(net_sock_t* pair[2]);
bool net_tcp_status(net_sock_t* sock); // Connected & not yet closed on both sides
bool net_tcp_shutdown(net_sock_t* sock); // Send EOF (FIN), only recv() works afterwards

size_t net_tcp_send(net_sock_t* sock, const void* buffer, size_t size);
size_t net_tcp_recv(net_sock_t* sock, void* buffer, size_t size, uint32_t* error);
int32_t net_tcp_send(net_sock_t* sock, const void* buffer, size_t size);
int32_t net_tcp_recv(net_sock_t* sock, void* buffer, size_t size);

// UDP Sockets

net_sock_t* net_udp_bind(const net_addr_t* addr);

size_t net_udp_send(net_sock_t* sock, const void* buffer, size_t size, const net_addr_t* addr);
size_t net_udp_recv(net_sock_t* sock, void* buffer, size_t size, net_addr_t* addr);
int32_t net_udp_recv(net_sock_t* sock, void* buffer, size_t size, net_addr_t* addr);

// Generic socket operations

Expand Down

0 comments on commit 59cd381

Please sign in to comment.