From 3489f621405abd781e083fe647fec40a8deaecd2 Mon Sep 17 00:00:00 2001 From: cozis Date: Tue, 29 Aug 2023 14:22:52 +0200 Subject: [PATCH] tcp refactoring --- src/defs.h | 6 +- src/ip.c | 10 ++- src/ip.h | 2 +- src/microtcp.c | 5 +- src/tcp.c | 168 ++++++++++++++++++++++++++++++------------------- src/tcp.h | 2 +- 6 files changed, 114 insertions(+), 79 deletions(-) diff --git a/src/defs.h b/src/defs.h index 18db114..2c6f5a5 100644 --- a/src/defs.h +++ b/src/defs.h @@ -16,12 +16,14 @@ static_assert(sizeof(ip_address_t) == 4); #define MAC_BROADCAST (mac_address_t) {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}} typedef struct { - const void *src; + const void *ptr; size_t len; -} slice_list_t; +} slice_t; #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #define ABS(X) ((X) < 0 ? -(X) : (X)) +#define COUNT(X) ((int) (sizeof(X) / sizeof((X)[0]))) +#define SLICE(X) ((slice_t) {.ptr=&(X), .len=sizeof(X)}) #endif /* MICROTCP_DEFS_H */ \ No newline at end of file diff --git a/src/ip.c b/src/ip.c index 8b4a4d1..5ef8e6e 100644 --- a/src/ip.c +++ b/src/ip.c @@ -134,14 +134,12 @@ int ip_send(ip_state_t *state, ip_protocol_t protocol, ip_address_t dst, bool no_fragm, const void *src, size_t len) { - const slice_list_t slices[] = { - {src, len} - }; - return ip_send_2(state, protocol, dst, no_fragm, slices, 1); + const slice_t slices[] = {{src, len}}; + return ip_send_2(state, protocol, dst, no_fragm, slices, COUNT(slices)); } int ip_send_2(ip_state_t *state, ip_protocol_t protocol, ip_address_t dst, - bool no_fragm, const slice_list_t *slices, size_t num_slices) + bool no_fragm, const slice_t *slices, size_t num_slices) { size_t total_len = 0; for (size_t i = 0; i < num_slices; i++) @@ -197,7 +195,7 @@ int ip_send_2(ip_state_t *state, ip_protocol_t protocol, ip_address_t dst, size_t copied_slices = 0; while (copied_bytes < considered_payload) { size_t copying = MIN(slices[copied_slices].len, considered_payload - copied_bytes); - memcpy(packet->payload + copied_bytes, slices[copied_slices].src, copying); + memcpy(packet->payload + copied_bytes, slices[copied_slices].ptr, copying); copied_bytes += copying; copied_slices++; } diff --git a/src/ip.h b/src/ip.h index 50f9f9e..326738a 100644 --- a/src/ip.h +++ b/src/ip.h @@ -59,6 +59,6 @@ void ip_change_output_buffer(ip_state_t *state, void *ptr, size_t max); void ip_init(ip_state_t *state, ip_address_t ip, void *send_data, void (*send)(void*, ip_address_t, size_t)); void ip_free(ip_state_t *state); int ip_send(ip_state_t *state, ip_protocol_t protocol, ip_address_t dst, bool no_fragm, const void *src, size_t len); -int ip_send_2(ip_state_t *state, ip_protocol_t protocol, ip_address_t dst, bool no_fragm, const slice_list_t *slices, size_t num_slices); +int ip_send_2(ip_state_t *state, ip_protocol_t protocol, ip_address_t dst, bool no_fragm, const slice_t *slices, size_t num_slices); void ip_process_packet(ip_state_t *state, const void *packet, size_t len); bool ip_plug_protocol(ip_state_t *ip_state, uint8_t protocol, void *data, void (*process_packet)(void *data, ip_address_t sender, const void *packet, size_t len)); diff --git a/src/microtcp.c b/src/microtcp.c index 86e845d..bd39b14 100644 --- a/src/microtcp.c +++ b/src/microtcp.c @@ -197,7 +197,7 @@ static void send_arp_packet(void *data, mac_address_t dst) } static int send_tcp_segment(void *data, ip_address_t ip, - const slice_list_t *slices, + const slice_t *slices, size_t num_slices) { microtcp_t *mtcp = data; @@ -416,8 +416,7 @@ void microtcp_step(microtcp_t *mtcp) return; uint64_t current_time_ms = get_time_in_ms(); - fprintf(stderr, "current_time_ms=%lld\n", current_time_ms); - + LOCK_WHEN_THREADED(mtcp); { process_packet(mtcp, packet, size); diff --git a/src/tcp.c b/src/tcp.c index 153daba..619610c 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -128,20 +128,20 @@ typedef struct { uint8_t reserved; uint8_t protocol; uint16_t tcp_length; -} tcp_pseudoheader_t; +} tcp_pseudoheader_t; // Ensure packed? static uint16_t -calculate_checksum(const slice_list_t *slices, size_t num_slices) +calculate_byte_checksum(const slice_t *slices, size_t num_slices) { uint32_t sum = 0xffff; for (size_t slice_idx = 0; slice_idx < num_slices; slice_idx++) { - const uint16_t *src = slices[slice_idx].src; + const uint16_t *ptr = slices[slice_idx].ptr; const size_t len = slices[slice_idx].len; for (size_t i = 0; i < len/2; i++) { - sum += net_to_cpu_u16(src[i]); + sum += net_to_cpu_u16(ptr[i]); if (sum > 0xffff) sum -= 0xffff; } @@ -149,7 +149,7 @@ calculate_checksum(const slice_list_t *slices, size_t num_slices) if (len & 1) { alignas(uint16_t) uint8_t temp[2]; - temp[0] = ((uint8_t*) slices[slice_idx].src)[len-1]; + temp[0] = ((uint8_t*) slices[slice_idx].ptr)[len-1]; temp[1] = 0; uint16_t temp2 = *(uint16_t*) temp; @@ -162,77 +162,116 @@ calculate_checksum(const slice_list_t *slices, size_t num_slices) return cpu_to_net_u16(~sum); } -static void emit_segment(tcp_connection_t *connection, uint8_t flags, size_t payload) +static tcp_segment_t +compile_segment(uint32_t wnd, uint8_t flags, + uint16_t sport, uint16_t dport, + uint32_t seq_no, uint32_t ack_no) { - tcp_listener_t *listener = connection->listener; - tcp_state_t *state = listener->state; + tcp_segment_t header; + //memset(header, 0, sizeof(tcp_segment_t)); + + int offset = 5; // No options + header.src_port = cpu_to_net_u16(sport); + header.dst_port = cpu_to_net_u16(dport); + header.flags = flags; + header.seq_no = cpu_to_net_u32(seq_no); + header.ack_no = cpu_to_net_u32(ack_no); + header.offset1 = cpu_is_little_endian() ? 0 : offset; + header.offset2 = cpu_is_little_endian() ? offset : 0; + header.window = cpu_to_net_u16(wnd); // Why is a 32 bit integer being backed into a 16 bit? + header.checksum = 0; // Will be calculated later + header.urgent_pointer = 0; + + return header; +} + +static uint16_t +calculate_header_checksum(tcp_segment_t header, slice_t data, + ip_address_t self_ip, ip_address_t peer_ip) +{ + tcp_pseudoheader_t pseudo; + + pseudo.src_addr = self_ip; + pseudo.dst_addr = peer_ip; + pseudo.reserved = 0; + pseudo.protocol = 6; // TCP + pseudo.tcp_length = cpu_to_net_u16(sizeof(tcp_segment_t) + data.len); + + slice_t list[] = { SLICE(pseudo), SLICE(header), data }; + return calculate_byte_checksum(list, COUNT(list)); +} + +static void +compile_checksum(tcp_segment_t *header, slice_t data, + ip_address_t self_ip, ip_address_t peer_ip) +{ + header->checksum = calculate_header_checksum(*header, data, self_ip, peer_ip); +} + +static int +emit_segment_bytes(tcp_state_t *tcp, ip_address_t ip, + tcp_segment_t header, slice_t data) +{ + slice_t slices[] = { SLICE(header), data }; + return tcp->callbacks.send(tcp->callbacks.data, ip, slices, COUNT(slices)); +} + +static slice_t +get_pending_output_data(tcp_connection_t *c, size_t max) +{ + slice_t s; // [snd_wnd + snd_una - snd_nxt] is the number of bytes that // are in the output buffer but weren't sent yet - size_t payload_being_sent = MIN(payload, connection->snd_wnd + connection->snd_una - connection->snd_nxt); - size_t total_segment_size = sizeof(tcp_segment_t) + payload_being_sent; + s.ptr = c->out_buffer + c->snd_nxt - c->snd_una; + s.len = MIN(max, c->snd_wnd + c->snd_una - c->snd_nxt); + return s; +} - uint32_t ack_no = 0; - if (flags & TCP_FLAG_ACK) - ack_no = connection->rcv_nxt; - - uint32_t seq_no = connection->snd_nxt; +// Send a tcp segment on the connection +// with a maximum payload of "payload" and +// the specified flags. +static void +emit_segment(tcp_connection_t *c, uint8_t flags, size_t max_payload) +{ + tcp_listener_t *listener = c->listener; + tcp_state_t *state = listener->state; - int offset = 5; // No options - tcp_segment_t header = { - .src_port = cpu_to_net_u16(listener->port), - .dst_port = cpu_to_net_u16(connection->peer_port), - .flags = flags, - .seq_no = cpu_to_net_u32(seq_no), - .ack_no = cpu_to_net_u32(ack_no), - .offset1 = cpu_is_little_endian() ? 0 : offset, - .offset2 = cpu_is_little_endian() ? offset : 0, - .window = cpu_to_net_u16(connection->rcv_wnd), // Why is a 32 bit integer being backed into a 16 bit? - .checksum = 0, // Will be calculated later - .urgent_pointer = 0, - }; + slice_t data = get_pending_output_data(c, max_payload); - tcp_pseudoheader_t pseudo_header = { - .src_addr = state->ip, - .dst_addr = connection->peer_ip, - .reserved = 0, - .protocol = 6, // TCP - .tcp_length = cpu_to_net_u16(total_segment_size), - }; + uint16_t sport = listener->port; + uint16_t dport = c->peer_port; + uint32_t ack_no = (flags & TCP_FLAG_ACK) ? c->rcv_nxt : 0; + uint32_t seq_no = c->snd_nxt; - const void *send_ptr = connection->out_buffer + connection->snd_nxt - connection->snd_una; - size_t send_len = payload_being_sent; + ip_address_t self_ip = state->ip; + ip_address_t peer_ip = c->peer_ip; - header.checksum = calculate_checksum((slice_list_t[]) { - {&pseudo_header, sizeof(tcp_pseudoheader_t)}, - {&header, sizeof(tcp_segment_t)}, - {send_ptr, send_len}, - }, 3); + tcp_segment_t header = compile_segment(c->rcv_wnd, flags, sport, dport, seq_no, ack_no); + compile_checksum(&header, data, self_ip, peer_ip); + int result = emit_segment_bytes(state, peer_ip, header, data); - int result = state->callbacks.send(state->callbacks.data, connection->peer_ip, (slice_list_t[]) { - {&header, sizeof(tcp_segment_t)}, - {send_ptr, send_len}, - }, 2); + if (result < 0) + return; // It wasn't possible to send out bytes. + // We'll try again later! - if (result < 0) { - // It wasn't possible to send out bytes. We'll try again later! - } else { - size_t actually_sent_bytes = (size_t) result; - - if (actually_sent_bytes < sizeof(tcp_segment_t)) { // What about options?? - // Not even the TCP header was sent. I hope this - // doesn't ever happen! - assert(0); - } else { - size_t actually_sent_payload_bytes = actually_sent_bytes - sizeof(tcp_segment_t); - connection->snd_nxt += actually_sent_payload_bytes; - } + if (result < (int) sizeof(tcp_segment_t)) { // What about options?? + // Not even the TCP header was sent. + // I hope this nexer happens! + assert(0); + return; } - if (!connection->calculating_rtt) { - connection->calculating_rtt = true; - connection->rtt_calc_seq = connection->snd_nxt; // -1? - connection->rtt_calc_time = connection->listener->state->timers.current_time_ms; + c->snd_nxt += result - sizeof(tcp_segment_t); + + // Increment for ghost bytes + if (flags & TCP_FLAG_SYN) c->snd_nxt++; + if (flags & TCP_FLAG_FIN) c->snd_nxt++; + + if (c->calculating_rtt == false && seq_no < c->snd_nxt) { + c->calculating_rtt = true; + c->rtt_calc_seq = seq_no; + c->rtt_calc_time = c->listener->state->timers.current_time_ms; } } @@ -546,7 +585,6 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, // Instead of incrementing the snd_una for the SYN we just send, // we'll ignore it and also ignore it when the respective ACK si // received. - connection->snd_nxt++; TCP_DEBUG_LOG("Connection request handled"); @@ -865,14 +903,12 @@ void tcp_connection_destroy(tcp_connection_t *connection) case TCP_STATE_ESTAB: TCP_DEBUG_LOG("ESTAB -> FIN-WAIT-1"); emit_segment(connection, TCP_FLAG_FIN | TCP_FLAG_ACK, 0); - connection->snd_nxt++; // FIN ghost byte connection->state = TCP_STATE_FIN_WAIT_1; break; case TCP_STATE_CLOSE_WAIT: TCP_DEBUG_LOG("ESTAB -> LAST-ACK"); emit_segment(connection, TCP_FLAG_FIN | TCP_FLAG_ACK, 0); - connection->snd_nxt++; // FIN ghost byte connection->state = TCP_STATE_LAST_ACK; break; diff --git a/src/tcp.h b/src/tcp.h index add4f7b..ebccc8e 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -152,7 +152,7 @@ struct tcp_connection_t { typedef struct { void *data; - int (*send)(void *data, ip_address_t ip, const slice_list_t *slices, size_t num_slices); + int (*send)(void *data, ip_address_t ip, const slice_t *slices, size_t num_slices); } tcp_callbacks_t; struct tcp_state_t {