From 5f3f7dc66dc78950c8ee63485b06ba696e8b9aab Mon Sep 17 00:00:00 2001 From: cozis Date: Thu, 31 Aug 2023 13:29:56 +0200 Subject: [PATCH] refactoring of tcp --- .gitignore | 4 +- examples/echo_tcp.c | 2 +- examples/microhttp/xhttp.c | 2 + src/arp.c | 24 +- src/defs.h | 2 +- src/microtcp.c | 16 +- src/microtcp.h | 10 +- src/tcp.c | 651 +++++++++++++++++++++++++------------ src/tcp.h | 19 +- src/tcp_timer.c | 11 +- src/tcp_timer.h | 5 + tests/test_tcp_timer.c | 18 +- 12 files changed, 510 insertions(+), 254 deletions(-) diff --git a/.gitignore b/.gitignore index 6a587c2..2027f10 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ loop2.exe 3p/src/* 3p/include/* build -*.gcov \ No newline at end of file +*.gcov +*.gcno +*.gcda \ No newline at end of file diff --git a/examples/echo_tcp.c b/examples/echo_tcp.c index 18f809a..efa79a0 100644 --- a/examples/echo_tcp.c +++ b/examples/echo_tcp.c @@ -11,7 +11,7 @@ int main(void) return -1; } - uint16_t port = 80; + uint16_t port = 8081; microtcp_socket_t *server = microtcp_open(mtcp, port, &errcode); if (errcode) { fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode)); diff --git a/examples/microhttp/xhttp.c b/examples/microhttp/xhttp.c index a3f0cf1..0711341 100644 --- a/examples/microhttp/xhttp.c +++ b/examples/microhttp/xhttp.c @@ -1283,6 +1283,8 @@ static void when_data_is_ready_to_be_read(context_t *ctx, conn_t *conn) downloaded = b->used - before; } + fprintf(stderr, "XHTTP :: Downloaded %d bytes\n", downloaded); + int served_during_this_while_loop = 0; while(1) diff --git a/src/arp.c b/src/arp.c index 3cc9b6e..c8ad9a3 100644 --- a/src/arp.c +++ b/src/arp.c @@ -718,19 +718,21 @@ arp_process_result_t arp_process_packet(arp_state_t *state, const void *packet, // Generate the ARP REPLY arp_packet_t *response = state->output; - response->hardware_type = packet2->hardware_type; - response->protocol_type = packet2->protocol_type; - response->hardware_len = packet2->hardware_len; - response->protocol_len = packet2->protocol_len; - response->operation_type = cpu_to_net_u16(ARP_OPERATION_REPLY); - response->sender_hardware_address = state->self_mac; - response->sender_protocol_address = state->self_ip; - response->target_hardware_address = packet2->sender_hardware_address; - response->target_protocol_address = packet2->sender_protocol_address; + if (state->output) { + response->hardware_type = packet2->hardware_type; + response->protocol_type = packet2->protocol_type; + response->hardware_len = packet2->hardware_len; + response->protocol_len = packet2->protocol_len; + response->operation_type = cpu_to_net_u16(ARP_OPERATION_REPLY); + response->sender_hardware_address = state->self_mac; + response->sender_protocol_address = state->self_ip; + response->target_hardware_address = packet2->sender_hardware_address; + response->target_protocol_address = packet2->sender_protocol_address; - ARP_DEBUG_LOG("Sending reply"); + ARP_DEBUG_LOG("Sending reply"); - state->send(state->send_data, packet2->sender_hardware_address); + state->send(state->send_data, packet2->sender_hardware_address); + } } } else { //ARP_DEBUG_LOG("Request not for me"); diff --git a/src/defs.h b/src/defs.h index 2c6f5a5..f063bd7 100644 --- a/src/defs.h +++ b/src/defs.h @@ -16,7 +16,7 @@ static_assert(sizeof(ip_address_t) == 4); #define MAC_BROADCAST (mac_address_t) {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}} typedef struct { - const void *ptr; + void *ptr; size_t len; } slice_t; diff --git a/src/microtcp.c b/src/microtcp.c index bd39b14..7b1cb50 100644 --- a/src/microtcp.c +++ b/src/microtcp.c @@ -135,13 +135,13 @@ struct microtcp_t { const char *microtcp_strerror(microtcp_errcode_t errcode) { switch (errcode) { - case MICROTCP_ERRCODE_NONE: return "No error occurred"; - case MICROTCP_ERRCODE_SOCKETLIMIT: return "Can't create a socket because the socket limit per microtcp instance was reached"; - case MICROTCP_ERRCODE_TCPERROR: return "An error occurred at the TCP layer"; - case MICROTCP_ERRCODE_BADCONDVAR: return "Condition variable error"; - case MICROTCP_ERRCODE_NOTLISTENER: return "Invalid operation on a non-listener socket"; - case MICROTCP_ERRCODE_CANTBLOCK: return "Can't execute a blocking call for this function"; - case MICROTCP_ERRCODE_WOULDBLOCK: return "Can't executa e non-blocking call for this function"; + case MICROTCP_ERRCODE_NONE: return "No error occurred"; + case MICROTCP_ERRCODE_SOCKETLIMIT: return "Can't create a socket because the socket limit per microtcp instance was reached"; + case MICROTCP_ERRCODE_TCPERROR: return "An error occurred at the TCP layer"; + case MICROTCP_ERRCODE_BADCONDVAR: return "Condition variable error"; + case MICROTCP_ERRCODE_NOTLISTENER: return "Invalid operation on a non-listener socket"; + case MICROTCP_ERRCODE_CANTBLOCK: return "Can't execute a blocking call for this function"; + case MICROTCP_ERRCODE_WOULDBLOCK: return "Can't executa e non-blocking call for this function"; case MICROTCP_ERRCODE_NOTCONNECTION: return "Invalid operation on a non-connection socket"; } return "???"; @@ -416,7 +416,7 @@ void microtcp_step(microtcp_t *mtcp) return; uint64_t current_time_ms = get_time_in_ms(); - + LOCK_WHEN_THREADED(mtcp); { process_packet(mtcp, packet, size); diff --git a/src/microtcp.h b/src/microtcp.h index e52daff..7f745cb 100644 --- a/src/microtcp.h +++ b/src/microtcp.h @@ -71,9 +71,9 @@ typedef struct { } microtcp_muxevent_t; typedef struct microtcp_mux_t microtcp_mux_t; -microtcp_mux_t *microtcp_mux_create(microtcp_t *mtcp); -void microtcp_mux_destroy(microtcp_mux_t *mux); -bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int events, void *userp); -bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int events); -bool microtcp_mux_wait(microtcp_mux_t *mux, microtcp_muxevent_t *ev); +microtcp_mux_t *microtcp_mux_create(microtcp_t *mtcp); +void microtcp_mux_destroy(microtcp_mux_t *mux); +bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int events, void *userp); +bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int events); +bool microtcp_mux_wait(microtcp_mux_t *mux, microtcp_muxevent_t *ev); #endif \ No newline at end of file diff --git a/src/tcp.c b/src/tcp.c index 619610c..4d4d42e 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -48,9 +48,8 @@ void tcp_ms_passed(tcp_state_t *state, size_t ms) } static tcp_connection_t* -connection_create(tcp_listener_t *listener, - uint32_t seq_no, uint32_t ack_no, - ip_address_t peer_ip, uint16_t peer_port) +connection_create(tcp_listener_t *listener, uint32_t seq_no, + uint32_t ack_no, ip_address_t peer_ip, uint16_t peer_port) { tcp_state_t *state = listener->state; @@ -74,13 +73,15 @@ connection_create(tcp_listener_t *listener, connection->state = TCP_STATE_CLOSED; - connection->rtt = 0; - connection->dev_rtt = 0; + connection->estimated_rtt = 0; + connection->estimated_dev = 0; connection->calculating_rtt = false; connection->rtt_calc_seq = 0; connection->rtt_calc_time = 0; + connection->retr_timer = NULL; + connection->peer_port = peer_port; connection->peer_ip = peer_ip; @@ -92,6 +93,9 @@ connection_create(tcp_listener_t *listener, connection->snd_wnd = 0; connection->snd_nxt = seq_no; + connection->in_buffer_syn = false; + connection->in_buffer_fin = false; + connection->prev = NULL; connection->next = NULL; } @@ -105,8 +109,29 @@ connection_create(tcp_listener_t *listener, return connection; } +static void +signal_ready_to_send(tcp_connection_t *c) +{ + if (c->callback_ready_to_send) + c->callback_ready_to_send(c->callback_data); +} + +static void +signal_ready_to_recv(tcp_connection_t *c) +{ + if (c->callback_ready_to_recv) + c->callback_ready_to_recv(c->callback_data); +} + +static void +signal_ready_to_accept(tcp_listener_t *l) +{ + if (l->callback_ready_to_accept) + l->callback_ready_to_accept(l->callback_data); +} + static tcp_listener_t* -find_listener_with_port(tcp_state_t *state, uint16_t port) +find_listener(tcp_state_t *state, uint16_t port) { tcp_listener_t *cursor = state->used_listener_list; while (cursor) { @@ -219,25 +244,143 @@ emit_segment_bytes(tcp_state_t *tcp, ip_address_t ip, 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 available; + if (c->snd_nxt <= c->snd_wnd + c->snd_una) + available = c->snd_wnd + c->snd_una - c->snd_nxt; + else + available = 0; + + slice_t s; s.ptr = c->out_buffer + c->snd_nxt - c->snd_una; - s.len = MIN(max, c->snd_wnd + c->snd_una - c->snd_nxt); + s.len = MIN(max, available); + return s; +} + +#ifdef TCP_DEBUG +static void +emit_segment_inner(tcp_connection_t *c, uint8_t flags, + size_t max_payload, bool retransmit, + const char *file, int line); +#define emit_segment(c, flags, max_payload, retransmit) \ + emit_segment_inner(c, flags, max_payload, retransmit, __FILE__, __LINE__) +#else +static void +emit_segment(tcp_connection_t *c, uint8_t flags, + size_t max_payload, bool retransmit); +#endif + +static void +timeout_callback_retransmit(void *p) +{ + tcp_connection_t *c = p; + TCP_DEBUG_LOG("Retransmitting"); + emit_segment(c, TCP_FLAG_ACK, SIZE_MAX, true); + c->retr_timer = NULL; +} + +static uint64_t +calc_retr_timeout(tcp_connection_t *c) +{ + #define MIN_RETRANSMISSION_TIMEOUT 1000 + #define MAX_RETRANSMISSION_TIMEOUT 60000 + + uint64_t timeout = c->estimated_rtt + 4 * c->estimated_dev; + timeout = MIN(timeout, MAX_RETRANSMISSION_TIMEOUT); + timeout = MAX(timeout, MIN_RETRANSMISSION_TIMEOUT); + return timeout; +} + +static void +start_retr_timer(tcp_connection_t *c) +{ + tcp_state_t *state = c->listener->state; + + uint64_t timeout = calc_retr_timeout(c); + tcp_timer_t *timer = tcp_timer_create(&state->timers, timeout, "retr", + timeout_callback_retransmit, c); + if (timer == NULL) + TCP_DEBUG_LOG("Failed to start retransmission timer"); + else + TCP_DEBUG_LOG("Retransmission timer started (%d ms)", (int) timeout); + c->retr_timer = timer; +} + +static void +stop_retr_timer(tcp_connection_t *c) +{ + if (c->retr_timer) { + TCP_DEBUG_LOG("Retransmission timer disabled"); + tcp_timer_disable(c->retr_timer); + c->retr_timer = NULL; + } +} + +static slice_t +get_output_data(tcp_connection_t *c) +{ + slice_t s; + s.ptr = c->out_buffer; + s.len = c->snd_wnd; return s; } // Send a tcp segment on the connection // with a maximum payload of "payload" and // the specified flags. + +#ifdef TCP_DEBUG static void -emit_segment(tcp_connection_t *c, uint8_t flags, size_t max_payload) +emit_segment_inner(tcp_connection_t *c, uint8_t flags, + size_t max_payload, bool retransmit, + const char *file, int line) { + { + char peer_ip[16]; + snprintf(peer_ip, sizeof(peer_ip), + "%d.%d.%d.%d", + c->peer_ip >> 24 & 0xff, + c->peer_ip >> 16 & 0xff, + c->peer_ip >> 8 & 0xff, + c->peer_ip >> 0 & 0xff); + + const char *flags_str; + switch (flags) { + case TCP_FLAG_FIN: flags_str = "FUN"; break; + case TCP_FLAG_SYN: flags_str = "SYN"; break; + case TCP_FLAG_RST: flags_str = "RST"; break; + case TCP_FLAG_PUSH: flags_str = "PUSH"; break; + case TCP_FLAG_ACK: flags_str = "ACK"; break; + case TCP_FLAG_URG: flags_str = "URG"; break; + case TCP_FLAG_SYN + | TCP_FLAG_ACK: flags_str = "SYN|ACK"; break; + case TCP_FLAG_FIN + | TCP_FLAG_ACK: flags_str = "FIN|ACK"; break; + default: flags_str="??"; break; + } + + TCP_DEBUG_LOG("emit_segment(peer=%s:%d, flags=%s, retransmit=%s) in %s:%d", + peer_ip, c->peer_port, flags_str, retransmit ? "true" : "false", + file, line); + } +#else +static void +emit_segment(tcp_connection_t *c, uint8_t flags, + size_t max_payload, bool retransmit) +{ +#endif tcp_listener_t *listener = c->listener; tcp_state_t *state = listener->state; - slice_t data = get_pending_output_data(c, max_payload); + slice_t data; + if (retransmit) { + data = get_output_data(c); + if (c->in_buffer_syn) flags |= TCP_FLAG_SYN; + if (c->in_buffer_fin) flags |= TCP_FLAG_FIN; + } else + data = get_pending_output_data(c, max_payload); uint16_t sport = listener->port; uint16_t dport = c->peer_port; @@ -247,8 +390,12 @@ emit_segment(tcp_connection_t *c, uint8_t flags, size_t max_payload) ip_address_t self_ip = state->ip; ip_address_t peer_ip = c->peer_ip; - tcp_segment_t header = compile_segment(c->rcv_wnd, flags, sport, dport, seq_no, ack_no); - compile_checksum(&header, data, self_ip, peer_ip); + 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); if (result < 0) @@ -262,48 +409,81 @@ emit_segment(tcp_connection_t *c, uint8_t flags, size_t max_payload) return; } - c->snd_nxt += result - sizeof(tcp_segment_t); + size_t sent_data = result - sizeof(tcp_segment_t); + if (flags & TCP_FLAG_SYN) sent_data++; + if (flags & TCP_FLAG_FIN) sent_data++; - // Increment for ghost bytes - if (flags & TCP_FLAG_SYN) c->snd_nxt++; - if (flags & TCP_FLAG_FIN) c->snd_nxt++; + if (!retransmit) { + c->snd_nxt += sent_data; + if (flags & TCP_FLAG_SYN) c->in_buffer_syn = true; + if (flags & TCP_FLAG_FIN) c->in_buffer_fin = true; + } - if (c->calculating_rtt == false && seq_no < c->snd_nxt) { + bool expect_ack = (sent_data > 0); + + if (expect_ack) + // Something that needs to be ACKed by peer was sent. + // Start the retransmission timer. + start_retr_timer(c); + + if (c->calculating_rtt == false && expect_ack) { c->calculating_rtt = true; c->rtt_calc_seq = seq_no; c->rtt_calc_time = c->listener->state->timers.current_time_ms; } } -static void handle_received_data(tcp_connection_t *connection, - const void *data, size_t size) +static slice_t +get_input_data(tcp_connection_t *c) { - size_t considered = MIN(size, connection->rcv_wnd); + slice_t s; + s.ptr = c->in_buffer; + s.len = TCP_INPUT_BUFFER_SIZE - c->rcv_wnd; + return s; +} - if (considered > 0) { - size_t input_buffer_usage = TCP_INPUT_BUFFER_SIZE - connection->rcv_wnd; - memcpy(connection->in_buffer + input_buffer_usage, data, considered); - connection->rcv_wnd -= considered; - connection->rcv_nxt += considered; - - emit_segment(connection, TCP_FLAG_ACK, SIZE_MAX); - - // Data is ready to be received by the parent application - if (connection->callback_ready_to_recv) - connection->callback_ready_to_recv(connection->callback_data); +static size_t +move_data_from_input_buffer(tcp_connection_t *c, slice_t dst) +{ + slice_t buf = get_input_data(c); + size_t unread = (c->rcv_nxt - c->rcv_unread); + size_t moving = MIN(dst.len, unread); + + if (moving > 0) { + memcpy(dst.ptr, buf.ptr, moving); + memmove(buf.ptr, buf.ptr + moving, buf.len - moving); + c->rcv_wnd += moving; + c->rcv_unread += moving; } + + return moving; +} + +static size_t +move_data_into_input_buffer(tcp_connection_t *c, slice_t src) +{ + slice_t buf = get_input_data(c); + size_t moving = MIN(src.len, c->rcv_wnd); + + if (moving > 0) { + memcpy(buf.ptr + buf.len, src.ptr, moving); + c->rcv_wnd -= moving; + c->rcv_nxt += moving; + } + + return moving; } static tcp_connection_t* -find_connection_associated_to_listener(tcp_listener_t *listener, - ip_address_t peer_ip, uint16_t peer_port) +find_connection(tcp_listener_t *listener, ip_address_t peer_ip, uint16_t peer_port) { tcp_connection_t *connection; // Check in the accepted list connection = listener->accepted_list; while (connection) { - if (connection->peer_port == peer_port && connection->peer_ip == peer_ip) + if (connection->peer_port == peer_port && + connection->peer_ip == peer_ip) break; connection = connection->next; } @@ -313,7 +493,8 @@ find_connection_associated_to_listener(tcp_listener_t *listener, // accepted=false connection = listener->non_accepted_queue_head; while (connection) { - if (connection->peer_port == peer_port && connection->peer_ip == peer_ip) + if (connection->peer_port == peer_port && + connection->peer_ip == peer_ip) break; connection = connection->next; } @@ -324,7 +505,8 @@ find_connection_associated_to_listener(tcp_listener_t *listener, // established=false connection = listener->non_established_list; while (connection) { - if (connection->peer_port == peer_port && connection->peer_ip == peer_ip) + if (connection->peer_port == peer_port && + connection->peer_ip == peer_ip) break; connection = connection->next; } @@ -358,7 +540,25 @@ move_from_non_established_list_to_non_accepted_queue(tcp_connection_t *connectio listener->non_accepted_queue_head = connection; } -static bool ack_until(tcp_connection_t *connection, uint32_t ack_no) +static void +update_rtt_estimation(tcp_connection_t *c, uint64_t sample_rtt) +{ + uint64_t estimated_rtt = c->estimated_rtt; + uint64_t estimated_dev = c->estimated_dev; + + uint64_t sample_dev = ABS((int) sample_rtt - (int) estimated_rtt); + + float a = 0.9; + float b = 0.125; + + estimated_rtt = a * sample_rtt + (1 - a) * estimated_rtt; + estimated_dev = b * sample_dev + (1 - b) * estimated_dev; + + c->estimated_rtt = estimated_rtt; + c->estimated_dev = estimated_dev; +} + +static bool ack_until(tcp_connection_t *c, uint32_t ack_no) { // Set sent but unacknowledged bytes with sequence // numbers up to [ack_no] as acknowledged. This [ack_no] @@ -367,42 +567,49 @@ static bool ack_until(tcp_connection_t *connection, uint32_t ack_no) // If at least one byte was marked as acknowledged, then // true is returned, else false. - if (ack_no <= connection->snd_una) { + if (ack_no <= c->snd_una) { TCP_DEBUG_LOG("Received segment acknowledged again %d", ack_no); return false; } - if (ack_no > connection->snd_nxt) { + if (ack_no > c->snd_nxt) { // Peer ACKed unsent data. The right course of action - // is probably to drop the connection. + // is probably to drop the c. TCP_DEBUG_LOG("Received segment acknowledged unsent data " "with sequence number %d, but %d still wasn't sent", - ack_no, connection->snd_nxt); + ack_no, c->snd_nxt); return false; // For now we'll just ignore the segment. } - size_t newly_acked_bytes = ack_no - connection->snd_una; + size_t newly_acked_bytes = ack_no - c->snd_una; + + if (ack_no > 1) { + + // Only remove data from the output buffer when + // the acked data wasn't a ghost byte + + memmove(c->out_buffer, + c->out_buffer + newly_acked_bytes, + c->snd_wnd - newly_acked_bytes); - if (ack_no > 1) { // Only remove data from the output buffer when - // the acked data wasn't a ghost byte - memmove(connection->out_buffer, connection->out_buffer + newly_acked_bytes, connection->snd_wnd - newly_acked_bytes); - connection->snd_wnd -= newly_acked_bytes; + c->snd_wnd -= newly_acked_bytes; } - connection->snd_una = ack_no; + c->snd_una = ack_no; + c->in_buffer_syn = false; - TCP_DEBUG_LOG("calculating_rtt=%s, ack_no=%d, calc_seq=%d", connection->calculating_rtt ? "true" : "false", ack_no, connection->rtt_calc_seq); - if (connection->calculating_rtt) { - if (ack_no >= connection->rtt_calc_seq) { + if (ack_no >= c->snd_nxt) { // snd_nxt-1? + // Peer acked everything there was to ack + stop_retr_timer(c); + c->in_buffer_fin = false; + } - uint64_t rtt = connection->listener->state->timers.current_time_ms - connection->rtt_calc_time; - TCP_DEBUG_LOG("Estimated RTT=%lld", rtt); - - float w1 = 0.9; - float w2 = 0.125; - connection->rtt = w1 * rtt + (1-w1) * connection->rtt; - connection->dev_rtt = w2 * ABS((int) rtt - (int) connection->rtt) + (1-w2) * connection->dev_rtt; - - connection->calculating_rtt = false; + if (c->calculating_rtt) { + if (ack_no >= c->rtt_calc_seq) { + uint64_t time_beg = c->rtt_calc_time; + uint64_t time_end = c->listener->state->timers.current_time_ms; + uint64_t sample_rtt = (time_end - time_beg); + update_rtt_estimation(c, sample_rtt); + c->calculating_rtt = false; } } @@ -486,47 +693,107 @@ static void timeout_callback_time_wait(void *data) really_close_connection(connection); } +static void +change_state_to_time_wait(tcp_connection_t *c) +{ + tcp_state_t *state = c->listener->state; + c->state = TCP_STATE_TIME_WAIT; + + stop_retr_timer(c); + + // Don't close the connection just now but + // wait for a given amount of time first. + if (!tcp_timer_create(&state->timers, TCP_TIMEOUT_TIME_WAIT, "wait", + timeout_callback_time_wait, c)) { + assert(0); + } +} + +static slice_t +segment_payload(tcp_segment_t *segment, size_t len) +{ + assert(len >= sizeof(tcp_segment_t)); + + // Length (in bytes) of the TCP header, + // comprehensive of options. + int off = SEGMENT_OFFSET(segment) * sizeof(uint32_t); + + // The number of bytes of the options is + // the size of the whole header minus the + // size of the header without options. + size_t options_len = off - sizeof(tcp_segment_t); + (void) options_len; + + // The segment->payload doesn't refer to the + // first byte of the payload but to the first + // byte of the options!! Use this variable to + // get the payload. + uint8_t *seg = (uint8_t*) segment; + slice_t payload = { + .len = len - off, + .ptr = seg + off, + }; + + return payload; +} + +static bool +set(tcp_segment_t *segment, int flags) +{ + return segment->flags & flags != 0; +} + +static bool +issyn(tcp_segment_t *segment) +{ + return set(segment, TCP_FLAG_SYN); +} + +static bool +isack(tcp_segment_t *segment) +{ + return set(segment, TCP_FLAG_ACK); +} + +static bool +isfin(tcp_segment_t *segment) +{ + return set(segment, TCP_FLAG_FIN); +} + void tcp_process_segment(tcp_state_t *state, ip_address_t sender, tcp_segment_t *segment, size_t len) { // TCP_DEBUG_LOG("Received TCP segment"); - assert(len >= sizeof(tcp_segment_t)); - size_t data_offset = SEGMENT_OFFSET(segment) * sizeof(uint32_t); // Length (in bytes) of the TCP header, - // comprehensive of options. - - size_t options_len = data_offset - sizeof(tcp_segment_t); // The number of bytes of the options is - // the size of the whole header minus the - // size of the header without options. - (void) options_len; - - size_t payload_size = len - data_offset; - void *payload_addr = (uint8_t*) segment + data_offset; // The segment->payload doesn't refer to the - // first byte of the payload but to the first - // byte of the options!! Use this variable to - // get the payload. - - uint16_t reordered_dst_port = net_to_cpu_u16(segment->dst_port); - uint16_t reordered_src_port = net_to_cpu_u16(segment->src_port); - - tcp_listener_t *listener = find_listener_with_port(state, reordered_dst_port); - if (listener == NULL) { - // No connection is listening on this port. Silently drop the segment - TCP_DEBUG_LOG("Segment sent to port %d, which is closed", reordered_dst_port); - return; + { + tcp_timer_t *timer = state->timers.used_list; + TCP_DEBUG_LOG("Timers: [ "); + while (timer) { + TCP_DEBUG_LOG(" %s - %2.2fs", timer->name, (float) (timer->deadline - state->timers.current_time_ms) / 1000); + timer = timer->next; + } + TCP_DEBUG_LOG("]"); } - tcp_connection_t *connection = find_connection_associated_to_listener(listener, sender, reordered_src_port); + slice_t payload = segment_payload(segment, len); + + uint16_t dport = net_to_cpu_u16(segment->dst_port); + uint16_t sport = net_to_cpu_u16(segment->src_port); + + tcp_listener_t *listener = find_listener(state, dport); + if (listener == NULL) + return; // No connection is listening on this port. + // Silently drop the segment + + tcp_connection_t *connection = find_connection(listener, sender, sport); if (!connection) { - if (listener->closed) { + if (listener->closed) // Listener was closed by the user, so already // open connections are ok but new ones are not // allowed. return; - } - - TCP_DEBUG_LOG("Connection request from port %d", reordered_src_port); // Something sent to an open listener. @@ -554,16 +821,16 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, // // (https://www.ietf.org/rfc/rfc793.txt) - if (!(segment->flags & TCP_FLAG_SYN)) { + if (!issyn(segment)) { // Received message isn't SYN. Ignore the segment. TCP_DEBUG_LOG("Connection request is missing the SYN flag"); return; } - if (segment->flags & ~TCP_FLAG_SYN) + if (set(segment, ~TCP_FLAG_SYN)) TCP_DEBUG_LOG("Connection request segment has flags other than SYN set"); - if (payload_size > 0) { + if (payload.len > 0) { TCP_DEBUG_LOG("Connection request segment has some payload. " "This is a valid behaviour but we don't handle " "that case yet. Droppong the connection"); @@ -573,7 +840,7 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, uint32_t seq_no = choose_sequence_no(); uint32_t ack_no = net_to_cpu_u32(segment->seq_no)+1; - connection = connection_create(listener, seq_no, ack_no, sender, reordered_src_port); + connection = connection_create(listener, seq_no, ack_no, sender, sport); if (connection == NULL) { TCP_DEBUG_LOG("Connection limit reached"); // Should we let the peer know what happened? @@ -581,17 +848,13 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, } connection->state = TCP_STATE_SYN_RCVD; - emit_segment(connection, TCP_FLAG_SYN | TCP_FLAG_ACK, 0); + emit_segment(connection, TCP_FLAG_SYN | TCP_FLAG_ACK, 0, false); // 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. - TCP_DEBUG_LOG("Connection request handled"); - } else { - TCP_DEBUG_LOG("RTT=%lld, DevRTT=%lld", connection->rtt, connection->dev_rtt); - // Something sent to an already instanciated // connection. Since there is an instance, it // means that at least the first SYN was received @@ -615,56 +878,50 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, break; case TCP_STATE_SYN_RCVD: - // At this point a SYN was received and a SYN|ACK sent. // We expect an ACK to establish the connection. - if (!(segment->flags & TCP_FLAG_ACK)) - // This isn't what we expected. Ignore it (this is - // probably not the best action) - return; - - if (segment->flags & ~TCP_FLAG_ACK) - TCP_DEBUG_LOG("Incoming segment has flags other than ACK set when just an ACK was expected"); - if (payload_size > 0) - TCP_DEBUG_LOG("Incoming segment has a payload alongside the ACK for " - "the SYN we sent. This is valid TCP but we don't support " - "this case yet. We'll just ignore the data. Hopefully " - "the peer will retransmit it"); + if (isack(segment)) { - // The connection is now established. - uint32_t ack_no = net_to_cpu_u32(segment->ack_no); - ack_until(connection, ack_no); - connection->state = TCP_STATE_ESTAB; - - move_from_non_established_list_to_non_accepted_queue(connection); - - if (listener->callback_ready_to_accept) { - assert(listener->callback_data); - listener->callback_ready_to_accept(listener->callback_data); + if (payload.len > 0) + TCP_DEBUG_LOG("Incoming segment has a payload alongside the ACK for " + "the SYN we sent. This is valid TCP but we don't support " + "this case yet. We'll just ignore the data. Hopefully " + "the peer will retransmit it"); + + ack_until(connection, net_to_cpu_u32(segment->ack_no)); + move_from_non_established_list_to_non_accepted_queue(connection); + signal_ready_to_accept(listener); + + /* The connection is now established */ + connection->state = TCP_STATE_ESTAB; } break; case TCP_STATE_ESTAB: - if (segment->flags & TCP_FLAG_ACK) { + if (isack(segment)) { uint32_t ack_no = net_to_cpu_u32(segment->ack_no); if (ack_until(connection, ack_no)) // At least one byte was ACKed, so now there's space // available in the output buffer - if (connection->callback_ready_to_send) - connection->callback_ready_to_send(connection->callback_data); + signal_ready_to_send(connection); } - handle_received_data(connection, payload_addr, payload_size); + size_t moved = move_data_into_input_buffer(connection, payload); + if (moved > 0) { + // Data is ready to be received by the parent application + signal_ready_to_recv(connection); + emit_segment(connection, TCP_FLAG_ACK, SIZE_MAX, false); + } - if (segment->flags & TCP_FLAG_FIN) { + if (isfin(segment)) { // An unsolicited FIN was received. We ACK the FIN and // set the connection state as waiting to be closed from // this end. TCP_DEBUG_LOG("ESTAB -> CLOSE_WAIT"); connection->rcv_nxt++; // FIN ghost byte - emit_segment(connection, TCP_FLAG_ACK, 0); + emit_segment(connection, TCP_FLAG_ACK, 0, false); connection->state = TCP_STATE_CLOSE_WAIT; } break; @@ -674,35 +931,31 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, // In this state we're expecting the ACK flag for the fin. // The same message could also contain the peer's FIN. - if (segment->flags & (TCP_FLAG_FIN | TCP_FLAG_ACK)) { + if (isack(segment) && isfin(segment)) { TCP_DEBUG_LOG("FIN-WAIT-1 -> TIME-WAIT"); connection->rcv_nxt++; // FIN ghost byte - emit_segment(connection, TCP_FLAG_ACK, 0); - - connection->state = TCP_STATE_TIME_WAIT; - - // Don't close the connection just now but - // wait for a given amount of time first. - if (!tcp_timer_create(&state->timers, TCP_TIMEOUT_TIME_WAIT, timeout_callback_time_wait, connection)) - assert(0); - - } else if (segment->flags & TCP_FLAG_ACK) { + emit_segment(connection, TCP_FLAG_ACK, 0, false); + change_state_to_time_wait(connection); + break; + } + if (isack(segment)) { TCP_DEBUG_LOG("FIN-WAIT-1 -> FIN-WAIT-2"); connection->state = TCP_STATE_FIN_WAIT_2; + break; + } - } else if (segment->flags & TCP_FLAG_FIN) { - + if (isfin(segment)) { TCP_DEBUG_LOG("FIN-WAIT-1 -> CLOSING"); connection->rcv_nxt++; // FIN ghost byte - emit_segment(connection, TCP_FLAG_ACK, 0); + emit_segment(connection, TCP_FLAG_ACK, 0, false); connection->state = TCP_STATE_CLOSING; - - } else { - // Expected FIN and/or ACK but didn't receive them + break; } + + // Expected FIN and/or ACK but didn't receive them break; case TCP_STATE_FIN_WAIT_2: @@ -710,18 +963,12 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, // The FIN was ACKed and now we're waiting for their // FIN to ACK. - if (!(segment->flags & TCP_FLAG_FIN)) - break; // Not a FIN segment so we ignore it. + if (isfin(segment)) { + connection->rcv_nxt++; // FIN ghost byte + emit_segment(connection, TCP_FLAG_ACK, 0, false); + change_state_to_time_wait(connection); + } - connection->rcv_nxt++; // FIN ghost byte - emit_segment(connection, TCP_FLAG_ACK, 0); - - connection->state = TCP_STATE_TIME_WAIT; - - // Don't close the connection just now but - // wait for a given amount of time first. - if (!tcp_timer_create(&state->timers, TCP_TIMEOUT_TIME_WAIT, timeout_callback_time_wait, connection)) - assert(0); break; case TCP_STATE_CLOSE_WAIT: @@ -733,12 +980,10 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, break; case TCP_STATE_LAST_ACK: - { - // FIN was received, ACKed and a FIN was sent. - // Now we expect the final ACK for our FIN. - if (!(segment->flags & TCP_FLAG_ACK)) - break; + // FIN was received, ACKed and a FIN was sent. + // Now we expect the final ACK for our FIN. + if (isack(segment)) { uint32_t ack_no = net_to_cpu_u32(segment->ack_no); if (ack_until(connection, ack_no)) { // FIXME! @@ -758,6 +1003,8 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, // FIN was sent and a FIN received. Now we expect // the ACK for our FIN. // ..TODO.. + + change_state_to_time_wait(connection); break; } } @@ -765,11 +1012,11 @@ void tcp_process_segment(tcp_state_t *state, ip_address_t sender, tcp_listener_t* tcp_listener_create(tcp_state_t *state, uint16_t port, bool reuse, - void *callback_data, void (*callback_ready_to_accept)(void*)) + void *cb_data, void (*cb_ready_accept)(void*)) { tcp_listener_t *listener; - listener = find_listener_with_port(state, port); + listener = find_listener(state, port); if (listener && (!listener->closed || !reuse)) { // ERROR: A connection is already listening on this port TCP_DEBUG_LOG("Faile to create listener on port %d because there already exists one", port); @@ -778,8 +1025,8 @@ tcp_listener_create(tcp_state_t *state, uint16_t port, bool reuse, if (listener) { listener->closed = false; - listener->callback_data = callback_data; - listener->callback_ready_to_accept = callback_ready_to_accept; + listener->cb_data = cb_data; + listener->cb_ready_accept = cb_ready_accept; } else { // Pop a listener connection structure from the free list @@ -799,8 +1046,8 @@ tcp_listener_create(tcp_state_t *state, uint16_t port, bool reuse, listener->non_established_list = NULL; listener->non_accepted_queue_head = NULL; listener->non_accepted_queue_tail = NULL; - listener->callback_data = callback_data; - listener->callback_ready_to_accept = callback_ready_to_accept; + listener->cb_data = cb_data; + listener->cb_ready_accept = cb_ready_accept; // Push listener connection structure to the used list listener->prev = NULL; @@ -817,65 +1064,65 @@ void tcp_listener_destroy(tcp_listener_t *listener) listener->closed = true; } -tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *callback_data, - void (*callback_ready_to_recv)(void*), - void (*callback_ready_to_send)(void*)) +tcp_connection_t *tcp_listener_accept(tcp_listener_t *listener, void *cb_data, + void (*cb_ready_recv)(void*), + void (*cb_ready_send)(void*)) { if (!listener->non_accepted_queue_head) // Nothing to accept return NULL; - tcp_connection_t *connection; + tcp_connection_t *c; // Pop connection from the tail of the accept queue { - connection = listener->non_accepted_queue_tail; - if (connection->prev) + c = listener->non_accepted_queue_tail; + if (c->prev) // ->prev isn't NULL so this isn't the first element of // the queue. Therefore we need to update the ->next pointer // of the previous node. - connection->prev->next = NULL; + c->prev->next = NULL; else // ->prev is NULL so this is the first element of the queue. // We need to update the reference to the head of the list listener->non_accepted_queue_head = NULL; - assert(connection->next == NULL); // This is the last element of the queue so + assert(c->next == NULL); // This is the last element of the queue so // it has no following node. listener->non_accepted_queue_tail = NULL; } // Push it to the head of the accepted list { - connection->prev = NULL; - connection->next = listener->accepted_list; + c->prev = NULL; + c->next = listener->accepted_list; if (listener->accepted_list) - listener->accepted_list->prev = connection; - listener->accepted_list = connection; + listener->accepted_list->prev = c; + listener->accepted_list = c; } - connection->callback_data = callback_data; - connection->callback_ready_to_recv = callback_ready_to_recv; - connection->callback_ready_to_send = callback_ready_to_send; + c->cb_data = cb_data; + c->cb_ready_recv = cb_ready_recv; + c->cb_ready_send = cb_ready_send; - return connection; + return c; } #ifdef TCP_DEBUG -static bool connection_was_accepted(tcp_connection_t *connection) +static bool connection_was_accepted(tcp_connection_t *c) { // Get the listener that's associated to // the connection and iterate over it's // accepted connections list to make sure // that is contains the connection. - tcp_listener_t *listener = connection->listener; + tcp_listener_t *listener = c->listener; tcp_connection_t *cursor = listener->accepted_list; while (cursor) { - if (cursor == connection) + if (cursor == c) return true; cursor = cursor->next; } @@ -883,33 +1130,33 @@ static bool connection_was_accepted(tcp_connection_t *connection) } #endif -void tcp_connection_destroy(tcp_connection_t *connection) +void tcp_connection_destroy(tcp_connection_t *c) { // NOTE: This can only be called when the // connection was accepted, so it must // be a node of the accepted_list. #ifdef TCP_DEBUG - assert(connection_was_accepted(connection)); + assert(connection_was_accepted(c)); #endif // You can only call destroy on connections // that were at least established - assert(connection->state == TCP_STATE_ESTAB || - connection->state == TCP_STATE_CLOSE_WAIT); + assert(c->state == TCP_STATE_ESTAB || + c->state == TCP_STATE_CLOSE_WAIT); - switch (connection->state) { + switch (c->state) { case TCP_STATE_ESTAB: TCP_DEBUG_LOG("ESTAB -> FIN-WAIT-1"); - emit_segment(connection, TCP_FLAG_FIN | TCP_FLAG_ACK, 0); - connection->state = TCP_STATE_FIN_WAIT_1; + emit_segment(c, TCP_FLAG_FIN | TCP_FLAG_ACK, 0, false); + c->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->state = TCP_STATE_LAST_ACK; + emit_segment(c, TCP_FLAG_FIN | TCP_FLAG_ACK, 0, false); + c->state = TCP_STATE_LAST_ACK; break; default: @@ -924,20 +1171,8 @@ void tcp_connection_destroy(tcp_connection_t *connection) size_t tcp_connection_recv(tcp_connection_t *connection, void *dst, size_t len) { - size_t unread = connection->rcv_nxt - connection->rcv_unread; - size_t num = MIN(len, unread); - memcpy(dst, connection->in_buffer, num); - - - size_t input_buffer_usage = TCP_INPUT_BUFFER_SIZE - connection->rcv_wnd; - - memmove(connection->in_buffer, connection->in_buffer + num, input_buffer_usage - num); - connection->rcv_unread += num; - connection->rcv_wnd += num; - - assert(connection->rcv_wnd <= TCP_INPUT_BUFFER_SIZE); - - return num; + slice_t s = {.ptr=dst, .len=len}; + return move_data_from_input_buffer(connection, s); } static size_t @@ -956,6 +1191,6 @@ append_to_output_buffer(tcp_connection_t *connection, size_t tcp_connection_send(tcp_connection_t *connection, const void *src, size_t len) { size_t num = append_to_output_buffer(connection, src, len); - emit_segment(connection, TCP_FLAG_ACK, SIZE_MAX); + emit_segment(connection, TCP_FLAG_ACK, SIZE_MAX, false); return num; } \ No newline at end of file diff --git a/src/tcp.h b/src/tcp.h index ebccc8e..280cb53 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -6,7 +6,8 @@ #include "tcp_timer.h" #endif -#define TCP_TIMEOUT_TIME_WAIT 240000 +#define TCP_TIMEOUT_TIME_WAIT 1000 +//240000 #define TCP_MAX_TIMEOUTS 1024 #define TCP_MAX_LISTENERS 32 @@ -28,13 +29,13 @@ typedef struct { uint16_t dst_port; uint32_t seq_no; uint32_t ack_no; - uint8_t offset1: 4; // When CPU is big endian - uint8_t offset2: 4; // When CPU is little endian + uint8_t offset1: 4; // When CPU is big endian + uint8_t offset2: 4; // When CPU is little endian uint8_t flags; uint16_t window; uint16_t checksum; uint16_t urgent_pointer; - char payload[]; + char payload[]; } tcp_segment_t; static_assert(sizeof(tcp_segment_t) == 20); @@ -88,13 +89,15 @@ struct tcp_connection_t { ip_address_t peer_ip; // Network byte order uint16_t peer_port; // CPU byte order - uint64_t rtt; - uint64_t dev_rtt; + uint64_t estimated_rtt; + uint64_t estimated_dev; bool calculating_rtt; uint32_t rtt_calc_seq; uint64_t rtt_calc_time; + tcp_timer_t *retr_timer; + // Send Sequence Space // // 1 2 3 4 @@ -146,8 +149,10 @@ struct tcp_connection_t { // It's the sequence number of the last // byte sent and acknowledged by the peer. - char out_buffer[TCP_OUTPUT_BUFFER_SIZE]; + bool in_buffer_syn; + bool in_buffer_fin; char in_buffer[TCP_INPUT_BUFFER_SIZE]; + char out_buffer[TCP_OUTPUT_BUFFER_SIZE]; }; typedef struct { diff --git a/src/tcp_timer.c b/src/tcp_timer.c index 5f16d8e..0087402 100644 --- a/src/tcp_timer.c +++ b/src/tcp_timer.c @@ -1,3 +1,4 @@ +#include #include #ifndef MICROTCP_AMALGAMATION @@ -16,6 +17,7 @@ void tcp_timerset_init(tcp_timerset_t *set) set->free_list = set->pool; } set->used_list = NULL; + set->current_time_ms = 0; } void tcp_timerset_free(tcp_timerset_t *set) @@ -42,6 +44,7 @@ void tcp_timer_disable(tcp_timer_t *timer) } tcp_timer_t *tcp_timer_create(tcp_timerset_t *set, size_t ms, + const char *name, void (*callback)(void*), void *data) { assert(callback); @@ -61,7 +64,9 @@ tcp_timer_t *tcp_timer_create(tcp_timerset_t *set, size_t ms, timer->deadline = set->current_time_ms + ms; timer->data = data; timer->callback = callback; - + strncpy(timer->name, name, sizeof(timer->name)); + timer->name[TCP_MAX_TIMER_NAME] = '\0'; + // Insert the timer structure into the timer list // in an orderly fashon if (set->used_list == NULL) { @@ -114,14 +119,14 @@ void tcp_timerset_step(tcp_timerset_t *set, size_t ms) if (set->used_list == NULL || set->used_list->deadline > set->current_time_ms) // No timeouts triggered return; - + tcp_timer_t *timedout_head = set->used_list; // We know that at least one timeout triggered tcp_timer_t *timedout_tail; // This has to be determined by the following loop // Scan through all of the timeouts that just triggered tcp_timer_t *timeout = set->used_list; while (timeout) { - + if (timeout->deadline > set->current_time_ms) // This timeout didn't trigger, so the last // timed out timeout was the previous one. diff --git a/src/tcp_timer.h b/src/tcp_timer.h index c5064f2..7b4741b 100644 --- a/src/tcp_timer.h +++ b/src/tcp_timer.h @@ -1,9 +1,12 @@ +#include #include #ifndef TCP_MAX_TIMERS #define TCP_MAX_TIMERS 1024 #endif +#define TCP_MAX_TIMER_NAME 15 + typedef struct tcp_timerset_t tcp_timerset_t; typedef struct tcp_timer_t tcp_timer_t; @@ -14,6 +17,7 @@ struct tcp_timer_t { uint64_t deadline; void (*callback)(void *data); void *data; + char name[TCP_MAX_TIMER_NAME+1]; }; struct tcp_timerset_t { @@ -28,4 +32,5 @@ void tcp_timerset_init(tcp_timerset_t *set); void tcp_timerset_free(tcp_timerset_t *set); void tcp_timer_disable(tcp_timer_t *timer); tcp_timer_t *tcp_timer_create(tcp_timerset_t *set, size_t ms, + const char *name, void (*callback)(void*), void *data); diff --git a/tests/test_tcp_timer.c b/tests/test_tcp_timer.c index 8908967..9fc72b5 100644 --- a/tests/test_tcp_timer.c +++ b/tests/test_tcp_timer.c @@ -15,7 +15,7 @@ static void test_timer(void) tcp_timerset_init(&set); int resolved = 0; - tcp_timer_t *t1 = tcp_timer_create(&set, 5, set_int_variable_to_1, &resolved); + tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved); assert(t1); assert(resolved == 0); @@ -37,7 +37,7 @@ static void test_disabled_timer(void) tcp_timerset_init(&set); int resolved = 0; - tcp_timer_t *t1 = tcp_timer_create(&set, 5, set_int_variable_to_1, &resolved); + tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved); assert(t1); assert(resolved == 0); @@ -61,7 +61,7 @@ static void test_disabled_timer_2(void) tcp_timerset_init(&set); int resolved = 0; - tcp_timer_t *t1 = tcp_timer_create(&set, 5, set_int_variable_to_1, &resolved); + tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved); assert(t1); assert(resolved == 0); @@ -86,8 +86,8 @@ static void test_disabled_timer_3(void) int resolved_1 = 0; int resolved_2 = 0; - tcp_timer_t *t1 = tcp_timer_create(&set, 5, set_int_variable_to_1, &resolved_1); - tcp_timer_t *t2 = tcp_timer_create(&set, 5, set_int_variable_to_1, &resolved_2); + tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved_1); + tcp_timer_t *t2 = tcp_timer_create(&set, 5, "t2", set_int_variable_to_1, &resolved_2); assert(t1); assert(t2); @@ -116,8 +116,8 @@ static void test_disabled_timer_4(void) int resolved_1 = 0; int resolved_2 = 0; - tcp_timer_t *t1 = tcp_timer_create(&set, 5, set_int_variable_to_1, &resolved_1); - tcp_timer_t *t2 = tcp_timer_create(&set, 5, set_int_variable_to_1, &resolved_2); + tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved_1); + tcp_timer_t *t2 = tcp_timer_create(&set, 5, "t2", set_int_variable_to_1, &resolved_2); assert(t1); assert(t2); @@ -146,10 +146,10 @@ static void test_out_of_timers(void) int temp; for (int i = 0; i < TCP_MAX_TIMERS; i++) { - tcp_timer_t *t = tcp_timer_create(&set, 5, set_int_variable_to_1, &temp); + tcp_timer_t *t = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &temp); assert(t != NULL); } - tcp_timer_t *t = tcp_timer_create(&set, 5, set_int_variable_to_1, &temp); + tcp_timer_t *t = tcp_timer_create(&set, 5, "t2", set_int_variable_to_1, &temp); assert(t == NULL); tcp_timerset_free(&set);