diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 7124598..43d99f1 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -5652,7 +5652,7 @@ START_TEST(test_tcp_rto_cb_non_established_noop) memset(ts, 0, sizeof(*ts)); ts->proto = WI_IPPROTO_TCP; ts->S = &s; - ts->sock.tcp.state = TCP_SYN_SENT; + ts->sock.tcp.state = TCP_CLOSED; ts->sock.tcp.rto_backoff = 2; tcp_rto_cb(ts); @@ -5660,6 +5660,183 @@ START_TEST(test_tcp_rto_cb_non_established_noop) } END_TEST +START_TEST(test_tcp_rto_cb_syn_sent_requeues_syn_and_arms_timer) +{ + struct wolfIP s; + struct tsocket *ts; + struct pkt_desc *desc; + struct wolfIP_tcp_seg *seg; + + wolfIP_init(&s); + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_SYN_SENT; + ts->sock.tcp.rto = 100; + ts->src_port = 12345; + ts->dst_port = 5001; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); + + s.last_tick = 1000; + tcp_rto_cb(ts); + + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + seg = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); + ck_assert_uint_eq(seg->flags, 0x02); + ck_assert_uint_eq(ts->sock.tcp.ctrl_rto_retries, 1); + ck_assert_int_ne(ts->sock.tcp.tmr_rto, NO_TIMER); +} +END_TEST + +START_TEST(test_tcp_input_synack_cancels_control_rto) +{ + struct wolfIP s; + struct tsocket *ts; + struct wolfIP_tcp_seg synack; + struct wolfIP_timer tmr; + + wolfIP_init(&s); + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_SYN_SENT; + ts->sock.tcp.seq = 101; + ts->sock.tcp.ctrl_rto_retries = 3; + ts->sock.tcp.ctrl_rto_active = 1; + ts->src_port = 2222; + ts->dst_port = 5001; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + + memset(&tmr, 0, sizeof(tmr)); + tmr.cb = test_timer_cb; + tmr.expires = 100; + tmr.arg = ts; + ts->sock.tcp.tmr_rto = timers_binheap_insert(&s.timers, tmr); + ck_assert_int_ne(ts->sock.tcp.tmr_rto, NO_TIMER); + + memset(&synack, 0, sizeof(synack)); + synack.ip.ttl = 64; + synack.ip.src = ee32(0x0A000002U); + synack.ip.dst = ee32(0x0A000001U); + synack.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); + synack.src_port = ee16(5001); + synack.dst_port = ee16(ts->src_port); + synack.seq = ee32(1000); + synack.ack = ee32(ts->sock.tcp.seq + 1); + synack.hlen = TCP_HEADER_LEN << 2; + synack.flags = 0x12; + synack.win = ee16(65535); + + tcp_input(&s, TEST_PRIMARY_IF, &synack, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN)); + + ck_assert_int_eq(ts->sock.tcp.state, TCP_ESTABLISHED); + ck_assert_int_eq(ts->sock.tcp.tmr_rto, NO_TIMER); + ck_assert_uint_eq(ts->sock.tcp.ctrl_rto_retries, 0); + ck_assert_uint_eq(ts->sock.tcp.ctrl_rto_active, 0); +} +END_TEST + +START_TEST(test_tcp_rto_cb_last_ack_requeues_finack_and_arms_timer) +{ + struct wolfIP s; + struct tsocket *ts; + struct pkt_desc *desc; + struct wolfIP_tcp_seg *seg; + + wolfIP_init(&s); + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_LAST_ACK; + ts->sock.tcp.rto = 100; + ts->src_port = 12345; + ts->dst_port = 5001; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); + + s.last_tick = 1000; + tcp_rto_cb(ts); + + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + seg = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); + ck_assert_uint_eq(seg->flags, 0x11); + ck_assert_uint_eq(ts->sock.tcp.ctrl_rto_retries, 1); + ck_assert_int_ne(ts->sock.tcp.tmr_rto, NO_TIMER); +} +END_TEST + +START_TEST(test_tcp_ack_fin_wait_1_ack_of_fin_moves_to_fin_wait_2_and_stops_timer) +{ + struct wolfIP s; + struct tsocket *ts; + struct wolfIP_tcp_seg ackseg; + struct wolfIP_timer tmr; + + wolfIP_init(&s); + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_FIN_WAIT_1; + ts->sock.tcp.last = 100; + ts->sock.tcp.snd_una = 100; + ts->sock.tcp.seq = 1000; + ts->sock.tcp.rto = 100; + ts->sock.tcp.ctrl_rto_active = 1; + ts->sock.tcp.ctrl_rto_retries = 2; + + memset(&tmr, 0, sizeof(tmr)); + tmr.cb = test_timer_cb; + tmr.expires = 200; + tmr.arg = ts; + ts->sock.tcp.tmr_rto = timers_binheap_insert(&s.timers, tmr); + ck_assert_int_ne(ts->sock.tcp.tmr_rto, NO_TIMER); + + memset(&ackseg, 0, sizeof(ackseg)); + ackseg.hlen = TCP_HEADER_LEN << 2; + ackseg.flags = 0x10; + ackseg.ack = ee32(101); + ackseg.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); + + tcp_ack(ts, &ackseg); + + ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_2); + ck_assert_int_eq(ts->sock.tcp.tmr_rto, NO_TIMER); + ck_assert_uint_eq(ts->sock.tcp.ctrl_rto_active, 0); + ck_assert_uint_eq(ts->sock.tcp.ctrl_rto_retries, 0); +} +END_TEST + +START_TEST(test_tcp_rto_cb_control_retry_cap_closes_socket) +{ + struct wolfIP s; + struct tsocket *ts; + + wolfIP_init(&s); + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_SYN_SENT; + ts->sock.tcp.rto = 100; + ts->sock.tcp.ctrl_rto_active = 1; + ts->sock.tcp.ctrl_rto_retries = TCP_CTRL_RTO_MAXRTX; + + tcp_rto_cb(ts); + ck_assert_int_eq(ts->proto, 0); +} +END_TEST + START_TEST(test_tcp_rto_cb_cancels_existing_timer) { struct wolfIP s; @@ -9949,7 +10126,7 @@ START_TEST(test_tcp_ack_closes_last_ack_socket) desc->flags |= PKT_FLAG_SENT; memset(&ackseg, 0, sizeof(ackseg)); - ackseg.ack = ee32(seq); + ackseg.ack = ee32(seq + 1); ackseg.hlen = TCP_HEADER_LEN << 2; ackseg.flags = 0x10; @@ -14381,6 +14558,11 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_rto_cb_non_tcp_noop); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_tcp_rto_cb_non_established_noop); + tcase_add_test(tc_utils, test_tcp_rto_cb_syn_sent_requeues_syn_and_arms_timer); + tcase_add_test(tc_utils, test_tcp_input_synack_cancels_control_rto); + tcase_add_test(tc_utils, test_tcp_rto_cb_last_ack_requeues_finack_and_arms_timer); + tcase_add_test(tc_utils, test_tcp_ack_fin_wait_1_ack_of_fin_moves_to_fin_wait_2_and_stops_timer); + tcase_add_test(tc_utils, test_tcp_rto_cb_control_retry_cap_closes_socket); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_tcp_rto_cb_cancels_existing_timer); suite_add_tcase(s, tc_utils); diff --git a/src/wolfip.c b/src/wolfip.c index 534c3a2..2b29d36 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -113,6 +113,7 @@ struct wolfIP_icmp_packet; #define WI_IP_MTU 1500 #define TCP_MSS (WI_IP_MTU - (IP_HEADER_LEN + TCP_HEADER_LEN)) #define TCP_DEFAULT_MSS 536U +#define TCP_CTRL_RTO_MAXRTX 6U /* Arbitrary upper limit to avoid monopolizing the CPU during poll loops. */ #define WOLFIP_POLL_BUDGET 128 @@ -979,6 +980,8 @@ struct tcpsocket { uint32_t last_early_rexmit_ack; uint8_t dup_acks; uint8_t early_rexmit_done; + uint8_t ctrl_rto_retries; + uint8_t ctrl_rto_active; ip4 local_ip, remote_ip; uint32_t peer_rwnd; uint16_t peer_mss; @@ -1022,6 +1025,10 @@ static void close_socket(struct tsocket *ts); static inline uint32_t tcp_seq_inc(uint32_t seq, uint32_t n); static inline int tcp_seq_leq(uint32_t a, uint32_t b); static inline int tcp_seq_lt(uint32_t a, uint32_t b); +static void tcp_rto_cb(void *arg); +static void tcp_ctrl_rto_start(struct tsocket *t, uint64_t now); +static void tcp_ctrl_rto_stop(struct tsocket *t); +static int tcp_ctrl_state_needs_rto(const struct tsocket *t); #ifdef ETHERNET struct PACKED arp_packet { @@ -1650,6 +1657,8 @@ static struct tsocket *tcp_new_socket(struct wolfIP *s) t->sock.tcp.snd_una = t->sock.tcp.seq; t->sock.tcp.dup_acks = 0; t->sock.tcp.early_rexmit_done = 0; + t->sock.tcp.ctrl_rto_retries = 0; + t->sock.tcp.ctrl_rto_active = 0; t->sock.tcp.last_early_rexmit_ack = 0; t->sock.tcp.peer_rwnd = 0xFFFF; t->sock.tcp.cwnd = tcp_initial_cwnd(t->sock.tcp.peer_rwnd); @@ -2097,6 +2106,46 @@ static void tcp_send_syn(struct tsocket *t, uint8_t flags) fifo_push(&t->sock.tcp.txbuf, tcp, sizeof(struct wolfIP_tcp_seg) + opt_len); } +static int tcp_ctrl_state_needs_rto(const struct tsocket *t) +{ + if (!t || t->proto != WI_IPPROTO_TCP) + return 0; + return (t->sock.tcp.state == TCP_SYN_SENT) || + (t->sock.tcp.state == TCP_SYN_RCVD) || + (t->sock.tcp.state == TCP_FIN_WAIT_1) || + (t->sock.tcp.state == TCP_LAST_ACK); +} + +static void tcp_ctrl_rto_stop(struct tsocket *t) +{ + if (!t || t->proto != WI_IPPROTO_TCP) + return; + if (t->sock.tcp.tmr_rto != NO_TIMER) { + timer_binheap_cancel(&t->S->timers, t->sock.tcp.tmr_rto); + t->sock.tcp.tmr_rto = NO_TIMER; + } + t->sock.tcp.ctrl_rto_active = 0; + t->sock.tcp.ctrl_rto_retries = 0; +} + +static void tcp_ctrl_rto_start(struct tsocket *t, uint64_t now) +{ + struct wolfIP_timer tmr = {0}; + uint64_t shift_rto; + if (!t || t->proto != WI_IPPROTO_TCP) + return; + if (t->sock.tcp.tmr_rto != NO_TIMER) { + timer_binheap_cancel(&t->S->timers, t->sock.tcp.tmr_rto); + t->sock.tcp.tmr_rto = NO_TIMER; + } + shift_rto = (uint64_t)t->sock.tcp.rto << t->sock.tcp.ctrl_rto_retries; + tmr.expires = now + shift_rto; + tmr.arg = t; + tmr.cb = tcp_rto_cb; + t->sock.tcp.tmr_rto = timers_binheap_insert(&t->S->timers, tmr); + t->sock.tcp.ctrl_rto_active = 1; +} + /* Increment a TCP sequence number (wraps at 2^32) */ static inline uint32_t tcp_seq_inc(uint32_t seq, uint32_t n) { @@ -2553,11 +2602,23 @@ static int tcp_mark_unsacked_for_retransmit(struct tsocket *t, uint32_t ack) static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) { uint32_t ack = ee32(tcp->ack); + uint32_t fin_acked = tcp_seq_inc(t->sock.tcp.last, 1); struct pkt_desc *desc; int ack_count = 0; int ack_advanced = 0; uint32_t inflight_pre = t->sock.tcp.bytes_in_flight; + if (t->sock.tcp.state == TCP_LAST_ACK && tcp_seq_leq(fin_acked, ack)) { + tcp_ctrl_rto_stop(t); + t->sock.tcp.state = TCP_CLOSED; + close_socket(t); + return; + } + if (t->sock.tcp.state == TCP_FIN_WAIT_1 && tcp_seq_leq(fin_acked, ack)) { + t->sock.tcp.state = TCP_FIN_WAIT_2; + tcp_ctrl_rto_stop(t); + } + tcp_process_sack(t, tcp, (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + (tcp->hlen >> 2))); desc = fifo_peek(&t->sock.tcp.txbuf); @@ -2571,13 +2632,6 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) desc = fifo_peek(&t->sock.tcp.txbuf); continue; } - if (ee32(seg->seq) == t->sock.tcp.last && ee32(seg->seq) == ack) { - if (t->sock.tcp.state == TCP_LAST_ACK) { - t->sock.tcp.state = TCP_CLOSED; - close_socket(t); - return; - } - } if (tcp_seq_leq(ee32(seg->seq) + seg_len, ack)) { desc->flags |= PKT_FLAG_ACKED; desc->flags &= ~PKT_FLAG_SENT; @@ -2844,6 +2898,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, } else if (t->sock.tcp.state == TCP_SYN_SENT) { if (tcp->flags == 0x12) { t->sock.tcp.state = TCP_ESTABLISHED; + tcp_ctrl_rto_stop(t); t->sock.tcp.ack = tcp_seq_inc(ee32(tcp->seq), 1); t->sock.tcp.seq = ee32(tcp->ack); t->sock.tcp.snd_una = t->sock.tcp.seq; @@ -2860,6 +2915,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, if ((tcplen == 0) && (t->sock.tcp.state == TCP_SYN_RCVD)) { if (tcp->flags == 0x10) { t->sock.tcp.state = TCP_ESTABLISHED; + tcp_ctrl_rto_stop(t); t->sock.tcp.ack = ee32(tcp->seq); t->sock.tcp.seq = ee32(tcp->ack); t->sock.tcp.snd_una = t->sock.tcp.seq; @@ -2917,7 +2973,31 @@ static void tcp_rto_cb(void *arg) uint32_t budget; uint32_t first_sent_seq = 0; uint32_t prev_cwnd; - if ((ts->proto != WI_IPPROTO_TCP) || (ts->sock.tcp.state != TCP_ESTABLISHED)) + if (ts->proto != WI_IPPROTO_TCP) + return; + if (tcp_ctrl_state_needs_rto(ts) || ts->sock.tcp.ctrl_rto_active) { + if (!tcp_ctrl_state_needs_rto(ts)) { + tcp_ctrl_rto_stop(ts); + return; + } + if (ts->sock.tcp.ctrl_rto_retries >= TCP_CTRL_RTO_MAXRTX) { + tcp_ctrl_rto_stop(ts); + ts->sock.tcp.state = TCP_CLOSED; + close_socket(ts); + return; + } + ts->sock.tcp.ctrl_rto_retries++; + if (ts->sock.tcp.state == TCP_SYN_SENT) { + tcp_send_syn(ts, 0x02); + } else if (ts->sock.tcp.state == TCP_SYN_RCVD) { + tcp_send_syn(ts, 0x12); + } else if (ts->sock.tcp.state == TCP_FIN_WAIT_1 || ts->sock.tcp.state == TCP_LAST_ACK) { + tcp_send_finack(ts); + } + tcp_ctrl_rto_start(ts, ts->S->last_tick); + return; + } + if (ts->sock.tcp.state != TCP_ESTABLISHED) return; /* RFC 6675 / RFC 2018 guidance: after an RTO, SACK scoreboard must not be * trusted (receiver may renege). Fall back to cumulative-ACK driven @@ -3027,6 +3107,8 @@ static void tcp_resync_inflight(struct wolfIP *s, struct tsocket *ts, uint64_t n if (!s || !ts) return; + if (tcp_ctrl_state_needs_rto(ts) || ts->sock.tcp.ctrl_rto_active) + return; budget = fifo_desc_budget(&ts->sock.tcp.txbuf); scan = fifo_peek(&ts->sock.tcp.txbuf); while (scan && guard++ < budget) { @@ -3245,7 +3327,9 @@ int wolfIP_sock_connect(struct wolfIP *s, int sockfd, const struct wolfIP_sockad ts->sock.tcp.state = TCP_CLOSED; return -1; } + ts->sock.tcp.ctrl_rto_retries = 0; tcp_send_syn(ts, 0x02); + tcp_ctrl_rto_start(ts, s->last_tick); return -WOLFIP_EAGAIN; } return -WOLFIP_EINVAL; @@ -3693,7 +3777,9 @@ int wolfIP_sock_close(struct wolfIP *s, int sockfd) ts = &s->tcpsockets[SOCKET_UNMARK(sockfd)]; if (ts->sock.tcp.state == TCP_ESTABLISHED) { ts->sock.tcp.state = TCP_FIN_WAIT_1; + ts->sock.tcp.ctrl_rto_retries = 0; tcp_send_finack(ts); + tcp_ctrl_rto_start(ts, s->last_tick); return -WOLFIP_EAGAIN; } else if (ts->sock.tcp.state == TCP_LISTEN) { ts->sock.tcp.state = TCP_CLOSED; @@ -3704,7 +3790,9 @@ int wolfIP_sock_close(struct wolfIP *s, int sockfd) return 0; } else if (ts->sock.tcp.state == TCP_CLOSE_WAIT) { ts->sock.tcp.state = TCP_LAST_ACK; + ts->sock.tcp.ctrl_rto_retries = 0; tcp_send_finack(ts); + tcp_ctrl_rto_start(ts, s->last_tick); return -WOLFIP_EAGAIN; } else if (ts->sock.tcp.state == TCP_CLOSING) { ts->sock.tcp.state = TCP_TIME_WAIT;