END_TEST /** Send data with sequence numbers that wrap around the u32_t range. * Then, provoke RTO retransmission and check that all * segment lists are still properly sorted. */ START_TEST(test_tcp_rto_rexmit_wraparound) { struct netif netif; struct test_tcp_txcounters txcounters; struct test_tcp_counters counters; struct tcp_pcb* pcb; ip_addr_t remote_ip, local_ip, netmask; u16_t remote_port = 0x100, local_port = 0x101; err_t err; #define SEQNO1 (0xFFFFFF00 - TCP_MSS) #define ISS 6510 u16_t i, sent_total = 0; u32_t seqnos[] = { SEQNO1, SEQNO1 + (1 * TCP_MSS), SEQNO1 + (2 * TCP_MSS), SEQNO1 + (3 * TCP_MSS), SEQNO1 + (4 * TCP_MSS), SEQNO1 + (5 * TCP_MSS)}; LWIP_UNUSED_ARG(_i); for (i = 0; i < sizeof(tx_data); i++) { tx_data[i] = (u8_t)i; } /* initialize local vars */ IP_ADDR4(&local_ip, 192, 168, 1, 1); IP_ADDR4(&remote_ip, 192, 168, 1, 2); IP_ADDR4(&netmask, 255, 255, 255, 0); test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); memset(&counters, 0, sizeof(counters)); /* create and initialize the pcb */ tcp_ticks = 0; tcp_ticks = 0 - tcp_next_iss(); tcp_ticks = SEQNO1 - tcp_next_iss(); pcb = test_tcp_new_counters_pcb(&counters); EXPECT_RET(pcb != NULL); EXPECT(pcb->lastack == SEQNO1); tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); pcb->mss = TCP_MSS; /* disable initial congestion window (we don't send a SYN here...) */ pcb->cwnd = 2*TCP_MSS; /* send 6 mss-sized segments */ for (i = 0; i < 6; i++) { err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); sent_total += TCP_MSS; } check_seqnos(pcb->unsent, 6, seqnos); EXPECT(pcb->unacked == NULL); err = tcp_output(pcb); EXPECT(txcounters.num_tx_calls == 2); EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); check_seqnos(pcb->unacked, 2, seqnos); check_seqnos(pcb->unsent, 4, &seqnos[2]); /* call the tcp timer some times */ for (i = 0; i < 10; i++) { test_tcp_tmr(); EXPECT(txcounters.num_tx_calls == 0); } /* 11th call to tcp_tmr: RTO rexmit fires */ test_tcp_tmr(); EXPECT(txcounters.num_tx_calls == 1); check_seqnos(pcb->unacked, 1, seqnos); check_seqnos(pcb->unsent, 5, &seqnos[1]); /* fake greater cwnd */ pcb->cwnd = pcb->snd_wnd; /* send more data */ err = tcp_output(pcb); EXPECT(err == ERR_OK); /* check queues are sorted */ EXPECT(pcb->unsent == NULL); check_seqnos(pcb->unacked, 6, seqnos); /* make sure the pcb is freed */ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); tcp_abort(pcb); EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); }
END_TEST /** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data. * At the end, send more data. */ static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent) { struct netif netif; struct test_tcp_txcounters txcounters; struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf *p; ip_addr_t remote_ip, local_ip, netmask; u16_t remote_port = 0x100, local_port = 0x101; err_t err; u16_t sent_total, i; u8_t expected = 0xFE; for (i = 0; i < sizeof(tx_data); i++) { u8_t d = (u8_t)i; if (d == 0xFE) { d = 0xF0; } tx_data[i] = d; } if (zero_window_probe_from_unsent) { tx_data[TCP_WND] = expected; } else { tx_data[0] = expected; } /* initialize local vars */ IP_ADDR4(&local_ip, 192, 168, 1, 1); IP_ADDR4(&remote_ip, 192, 168, 1, 2); IP_ADDR4(&netmask, 255, 255, 255, 0); test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); memset(&counters, 0, sizeof(counters)); memset(&txcounters, 0, sizeof(txcounters)); /* create and initialize the pcb */ pcb = test_tcp_new_counters_pcb(&counters); EXPECT_RET(pcb != NULL); tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); pcb->mss = TCP_MSS; /* disable initial congestion window (we don't send a SYN here...) */ pcb->cwnd = pcb->snd_wnd; /* send a full window (minus 1 packets) of TCP data in MSS-sized chunks */ sent_total = 0; if ((TCP_WND - TCP_MSS) % TCP_MSS != 0) { u16_t initial_data_len = (TCP_WND - TCP_MSS) % TCP_MSS; err = tcp_write(pcb, &tx_data[sent_total], initial_data_len, TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == initial_data_len + 40U); memset(&txcounters, 0, sizeof(txcounters)); sent_total += initial_data_len; } for (; sent_total < (TCP_WND - TCP_MSS); sent_total += TCP_MSS) { err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); memset(&txcounters, 0, sizeof(txcounters)); } EXPECT(sent_total == (TCP_WND - TCP_MSS)); /* now ACK the packet before the first */ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); test_tcp_input(p, &netif); /* ensure this didn't trigger a retransmission */ EXPECT(txcounters.num_tx_calls == 0); EXPECT(txcounters.num_tx_bytes == 0); EXPECT(pcb->persist_backoff == 0); /* send the last packet, now a complete window has been sent */ err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); sent_total += TCP_MSS; EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); memset(&txcounters, 0, sizeof(txcounters)); EXPECT(pcb->persist_backoff == 0); if (zero_window_probe_from_unsent) { /* ACK all data but close the TX window */ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_WND, TCP_ACK, 0); test_tcp_input(p, &netif); /* ensure this didn't trigger any transmission */ EXPECT(txcounters.num_tx_calls == 0); EXPECT(txcounters.num_tx_bytes == 0); EXPECT(pcb->persist_backoff == 1); } /* send one byte more (out of window) -> persist timer starts */ err = tcp_write(pcb, &tx_data[sent_total], 1, TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); EXPECT(txcounters.num_tx_calls == 0); EXPECT(txcounters.num_tx_bytes == 0); memset(&txcounters, 0, sizeof(txcounters)); if (!zero_window_probe_from_unsent) { /* no persist timer unless a zero window announcement has been received */ EXPECT(pcb->persist_backoff == 0); } else { EXPECT(pcb->persist_backoff == 1); /* call tcp_timer some more times to let persist timer count up */ for (i = 0; i < 4; i++) { test_tcp_tmr(); EXPECT(txcounters.num_tx_calls == 0); EXPECT(txcounters.num_tx_bytes == 0); } /* this should trigger the zero-window-probe */ txcounters.copy_tx_packets = 1; test_tcp_tmr(); txcounters.copy_tx_packets = 0; EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == 1 + 40U); EXPECT(txcounters.tx_packets != NULL); if (txcounters.tx_packets != NULL) { u8_t sent; u16_t ret; ret = pbuf_copy_partial(txcounters.tx_packets, &sent, 1, 40U); EXPECT(ret == 1); EXPECT(sent == expected); } if (txcounters.tx_packets != NULL) { pbuf_free(txcounters.tx_packets); txcounters.tx_packets = NULL; } } /* make sure the pcb is freed */ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); tcp_abort(pcb); EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); }
END_TEST START_TEST(test_tcp_rto_tracking) { struct netif netif; struct test_tcp_txcounters txcounters; struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf* p; err_t err; size_t i; u16_t sent_total = 0; LWIP_UNUSED_ARG(_i); for (i = 0; i < sizeof(tx_data); i++) { tx_data[i] = (u8_t)i; } /* initialize local vars */ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); memset(&counters, 0, sizeof(counters)); /* create and initialize the pcb */ tcp_ticks = SEQNO1 - ISS; pcb = test_tcp_new_counters_pcb(&counters); EXPECT_RET(pcb != NULL); tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); pcb->mss = TCP_MSS; /* Set congestion window large enough to send all our segments */ pcb->cwnd = 5*TCP_MSS; /* send 5 mss-sized segments */ for (i = 0; i < 5; i++) { err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); sent_total += TCP_MSS; } check_seqnos(pcb->unsent, 5, seqnos); EXPECT(pcb->unacked == NULL); err = tcp_output(pcb); EXPECT(txcounters.num_tx_calls == 5); EXPECT(txcounters.num_tx_bytes == 5 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); /* Check all 5 are in-flight */ EXPECT(pcb->unsent == NULL); check_seqnos(pcb->unacked, 5, seqnos); /* Force us into retransmisson timeout */ while (!(pcb->flags & TF_RTO)) { test_tcp_tmr(); } /* Ensure 4 remaining segments are back on unsent, ready for retransmission */ check_seqnos(pcb->unsent, 4, &seqnos[1]); /* Ensure 1st segment is on unacked (already retransmitted) */ check_seqnos(pcb->unacked, 1, seqnos); EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); memset(&txcounters, 0, sizeof(txcounters)); /* Ensure rto_end points to next byte */ EXPECT(pcb->rto_end == seqnos[5]); EXPECT(pcb->rto_end == pcb->snd_nxt); /* Check cwnd was reset */ EXPECT(pcb->cwnd == pcb->mss); /* Add another segment to send buffer which is outside of RTO */ err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); sent_total += TCP_MSS; check_seqnos(pcb->unsent, 5, &seqnos[1]); /* Ensure no new data was sent */ EXPECT(txcounters.num_tx_calls == 0); EXPECT(txcounters.num_tx_bytes == 0); EXPECT(pcb->rto_end == pcb->snd_nxt); /* ACK first segment */ p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK); test_tcp_input(p, &netif); /* Next two retranmissions should go out, due to cwnd in slow start */ EXPECT(txcounters.num_tx_calls == 2); EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); check_seqnos(pcb->unacked, 2, &seqnos[1]); check_seqnos(pcb->unsent, 3, &seqnos[3]); /* RTO should still be marked */ EXPECT(pcb->flags & TF_RTO); /* cwnd should have only grown by 1 MSS */ EXPECT(pcb->cwnd == (tcpwnd_size_t)(2 * pcb->mss)); /* Ensure no new data was sent */ EXPECT(pcb->rto_end == pcb->snd_nxt); /* ACK the next two segments */ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 2*TCP_MSS, TCP_ACK); test_tcp_input(p, &netif); /* Final 2 retransmissions and 1 new data should go out */ EXPECT(txcounters.num_tx_calls == 3); EXPECT(txcounters.num_tx_bytes == 3 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); check_seqnos(pcb->unacked, 3, &seqnos[3]); EXPECT(pcb->unsent == NULL); /* RTO should still be marked */ EXPECT(pcb->flags & TF_RTO); /* cwnd should have only grown by 1 MSS */ EXPECT(pcb->cwnd == (tcpwnd_size_t)(3 * pcb->mss)); /* snd_nxt should have been advanced past rto_end */ EXPECT(TCP_SEQ_GT(pcb->snd_nxt, pcb->rto_end)); /* ACK the next two segments, finishing our RTO, leaving new segment unacked */ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 2*TCP_MSS, TCP_ACK); test_tcp_input(p, &netif); EXPECT(!(pcb->flags & TF_RTO)); check_seqnos(pcb->unacked, 1, &seqnos[5]); /* We should be in ABC congestion avoidance, so no change in cwnd */ EXPECT(pcb->cwnd == (tcpwnd_size_t)(3 * pcb->mss)); EXPECT(pcb->cwnd >= pcb->ssthresh); /* Ensure ABC congestion avoidance is tracking bytes acked */ EXPECT(pcb->bytes_acked == (tcpwnd_size_t)(2 * pcb->mss)); /* make sure the pcb is freed */ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }
END_TEST /** Send data with sequence numbers that wrap around the u32_t range. * Then, provoke RTO retransmission and check that all * segment lists are still properly sorted. */ START_TEST(test_tcp_rto_rexmit_wraparound) { struct netif netif; struct test_tcp_txcounters txcounters; struct test_tcp_counters counters; struct tcp_pcb* pcb; err_t err; size_t i; u16_t sent_total = 0; LWIP_UNUSED_ARG(_i); for (i = 0; i < sizeof(tx_data); i++) { tx_data[i] = (u8_t)i; } /* initialize local vars */ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); memset(&counters, 0, sizeof(counters)); /* create and initialize the pcb */ tcp_ticks = 0; tcp_ticks = 0 - tcp_next_iss(NULL); tcp_ticks = SEQNO1 - tcp_next_iss(NULL); pcb = test_tcp_new_counters_pcb(&counters); EXPECT_RET(pcb != NULL); tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); pcb->mss = TCP_MSS; /* disable initial congestion window (we don't send a SYN here...) */ pcb->cwnd = 2*TCP_MSS; /* send 6 mss-sized segments */ for (i = 0; i < 6; i++) { err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); sent_total += TCP_MSS; } check_seqnos(pcb->unsent, 6, seqnos); EXPECT(pcb->unacked == NULL); err = tcp_output(pcb); EXPECT(txcounters.num_tx_calls == 2); EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); check_seqnos(pcb->unacked, 2, seqnos); check_seqnos(pcb->unsent, 4, &seqnos[2]); /* call the tcp timer some times */ for (i = 0; i < 10; i++) { test_tcp_tmr(); EXPECT(txcounters.num_tx_calls == 0); } /* 11th call to tcp_tmr: RTO rexmit fires */ test_tcp_tmr(); EXPECT(txcounters.num_tx_calls == 1); check_seqnos(pcb->unacked, 1, seqnos); check_seqnos(pcb->unsent, 5, &seqnos[1]); /* fake greater cwnd */ pcb->cwnd = pcb->snd_wnd; /* send more data */ err = tcp_output(pcb); EXPECT(err == ERR_OK); /* check queues are sorted */ EXPECT(pcb->unsent == NULL); check_seqnos(pcb->unacked, 6, seqnos); /* make sure the pcb is freed */ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }
END_TEST START_TEST(test_tcp_persist_split) { struct netif netif; struct test_tcp_txcounters txcounters; struct test_tcp_counters counters; struct tcp_pcb *pcb; struct pbuf* p; err_t err; size_t i; LWIP_UNUSED_ARG(_i); /* Setup data for four segments */ for (i = 0; i < 4 * TCP_MSS; i++) { tx_data[i] = (u8_t)i; } /* initialize local vars */ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); memset(&counters, 0, sizeof(counters)); /* create and initialize the pcb */ tcp_ticks = SEQNO1 - ISS; pcb = test_tcp_new_counters_pcb(&counters); EXPECT_RET(pcb != NULL); tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); pcb->mss = TCP_MSS; /* set window to three segments */ pcb->cwnd = 3 * TCP_MSS; pcb->snd_wnd = 3 * TCP_MSS; pcb->snd_wnd_max = 3 * TCP_MSS; /* send three segments */ err = tcp_write(pcb, &tx_data[0], 3 * TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT(err == ERR_OK); err = tcp_output(pcb); EXPECT(err == ERR_OK); /* verify segments are in-flight */ EXPECT(pcb->unsent == NULL); EXPECT(pcb->unacked != NULL); check_seqnos(pcb->unacked, 3, seqnos); EXPECT(txcounters.num_tx_calls == 3); EXPECT(txcounters.num_tx_bytes == 3 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); /* ACK the segments and update the window to only 1/2 TCP_MSS */ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, 3 * TCP_MSS, TCP_ACK, TCP_MSS / 2); test_tcp_input(p, &netif); EXPECT(pcb->unacked == NULL); EXPECT(pcb->unsent == NULL); EXPECT(pcb->persist_backoff == 0); EXPECT(pcb->snd_wnd == TCP_MSS / 2); /* send fourth segment, which is larger than snd_wnd */ err = tcp_write(pcb, &tx_data[3 * TCP_MSS], TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT(err == ERR_OK); err = tcp_output(pcb); EXPECT(err == ERR_OK); /* ensure it is buffered and persist timer started */ EXPECT(pcb->unacked == NULL); EXPECT(pcb->unsent != NULL); check_seqnos(pcb->unsent, 1, &seqnos[3]); EXPECT(txcounters.num_tx_calls == 0); EXPECT(txcounters.num_tx_bytes == 0); EXPECT(pcb->persist_backoff == 1); /* ensure no errors have been recorded */ EXPECT(counters.err_calls == 0); EXPECT(counters.last_err == ERR_OK); /* call tcp_timer some more times to let persist timer count up */ for (i = 0; i < 4; i++) { test_tcp_tmr(); EXPECT(txcounters.num_tx_calls == 0); EXPECT(txcounters.num_tx_bytes == 0); } /* this should be the first timer shot, which should split the * segment and send a runt (of the remaining window size) */ txcounters.copy_tx_packets = 1; test_tcp_tmr(); txcounters.copy_tx_packets = 0; /* persist will be disabled as RTO timer takes over */ EXPECT(pcb->persist_backoff == 0); EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == ((TCP_MSS /2) + 40U)); /* verify half segment sent, half still buffered */ EXPECT(pcb->unsent != NULL); EXPECT(pcb->unsent->len == TCP_MSS / 2); EXPECT(pcb->unacked != NULL); EXPECT(pcb->unacked->len == TCP_MSS / 2); /* verify first half segment */ EXPECT(txcounters.tx_packets != NULL); if (txcounters.tx_packets != NULL) { u8_t sent[TCP_MSS / 2]; u16_t ret; ret = pbuf_copy_partial(txcounters.tx_packets, &sent, TCP_MSS / 2, 40U); EXPECT(ret == TCP_MSS / 2); EXPECT(memcmp(sent, &tx_data[3 * TCP_MSS], TCP_MSS / 2) == 0); } if (txcounters.tx_packets != NULL) { pbuf_free(txcounters.tx_packets); txcounters.tx_packets = NULL; } memset(&txcounters, 0, sizeof(txcounters)); /* ACK the half segment, leave window at half segment */ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_MSS / 2, TCP_ACK, TCP_MSS / 2); txcounters.copy_tx_packets = 1; test_tcp_input(p, &netif); txcounters.copy_tx_packets = 0; /* ensure remaining half segment was sent */ EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == ((TCP_MSS /2 ) + 40U)); EXPECT(pcb->unsent == NULL); EXPECT(pcb->unacked != NULL); EXPECT(pcb->unacked->len == TCP_MSS / 2); EXPECT(pcb->snd_wnd == TCP_MSS / 2); /* verify second half segment */ EXPECT(txcounters.tx_packets != NULL); if (txcounters.tx_packets != NULL) { u8_t sent[TCP_MSS / 2]; u16_t ret; ret = pbuf_copy_partial(txcounters.tx_packets, &sent, TCP_MSS / 2, 40U); EXPECT(ret == TCP_MSS / 2); EXPECT(memcmp(sent, &tx_data[(3 * TCP_MSS) + TCP_MSS / 2], TCP_MSS / 2) == 0); } if (txcounters.tx_packets != NULL) { pbuf_free(txcounters.tx_packets); txcounters.tx_packets = NULL; } /* ensure no errors have been recorded */ EXPECT(counters.err_calls == 0); EXPECT(counters.last_err == ERR_OK); /* make sure the pcb is freed */ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }
END_TEST START_TEST(test_tcp_zwp_timeout) { struct netif netif; struct test_tcp_txcounters txcounters; struct test_tcp_counters counters; struct tcp_pcb *pcb, *cur; struct pbuf* p; err_t err; size_t i; LWIP_UNUSED_ARG(_i); /* Setup data for two segments */ for (i = 0; i < 2*TCP_MSS; i++) { tx_data[i] = (u8_t)i; } /* initialize local vars */ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); memset(&counters, 0, sizeof(counters)); /* create and initialize the pcb */ tcp_ticks = SEQNO1 - ISS; pcb = test_tcp_new_counters_pcb(&counters); EXPECT_RET(pcb != NULL); tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); pcb->mss = TCP_MSS; pcb->cwnd = TCP_MSS; /* send first segment */ err = tcp_write(pcb, &tx_data[0], TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT(err == ERR_OK); err = tcp_output(pcb); EXPECT(err == ERR_OK); /* verify segment is in-flight */ EXPECT(pcb->unsent == NULL); check_seqnos(pcb->unacked, 1, seqnos); EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); /* ACK the segment and close the TX window */ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK, 0); test_tcp_input(p, &netif); EXPECT(pcb->unacked == NULL); EXPECT(pcb->unsent == NULL); /* send buffer empty, persist should be off */ EXPECT(pcb->persist_backoff == 0); EXPECT(pcb->snd_wnd == 0); /* send second segment, should be buffered */ err = tcp_write(pcb, &tx_data[TCP_MSS], TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT(err == ERR_OK); err = tcp_output(pcb); EXPECT(err == ERR_OK); /* ensure it is buffered and persist timer started */ EXPECT(pcb->unacked == NULL); check_seqnos(pcb->unsent, 1, &seqnos[1]); EXPECT(txcounters.num_tx_calls == 0); EXPECT(txcounters.num_tx_bytes == 0); EXPECT(pcb->persist_backoff == 1); /* ensure no errors have been recorded */ EXPECT(counters.err_calls == 0); EXPECT(counters.last_err == ERR_OK); /* run timer till first probe */ EXPECT(pcb->persist_probe == 0); while (pcb->persist_probe == 0) { test_tcp_tmr(); } EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == (1 + 40U)); memset(&txcounters, 0, sizeof(txcounters)); /* respond to probe with remote's current SEQ, ACK, and zero-window */ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, 0, TCP_ACK, 0); test_tcp_input(p, &netif); /* ensure zero-window is still active, but probe count reset */ EXPECT(pcb->persist_backoff > 1); EXPECT(pcb->persist_probe == 0); EXPECT(pcb->snd_wnd == 0); /* ensure no errors have been recorded */ EXPECT(counters.err_calls == 0); EXPECT(counters.last_err == ERR_OK); /* now run the timer till we hit our maximum probe count */ while (counters.last_err == ERR_OK) { test_tcp_tmr(); } /* check maximum number of 1 byte probes were sent */ EXPECT(txcounters.num_tx_calls == TCP_MAXRTX); EXPECT(txcounters.num_tx_bytes == TCP_MAXRTX * (1 + 40U)); /* check the connection (pcb) has been aborted */ EXPECT(counters.err_calls == 1); EXPECT(counters.last_err == ERR_ABRT); /* check our pcb is no longer active */ for (cur = tcp_active_pcbs; cur != NULL; cur = cur->next) { EXPECT(cur != pcb); } EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }
END_TEST START_TEST(test_tcp_rto_timeout) { struct netif netif; struct test_tcp_txcounters txcounters; struct test_tcp_counters counters; struct tcp_pcb *pcb, *cur; err_t err; size_t i; LWIP_UNUSED_ARG(_i); /* Setup data for a single segment */ for (i = 0; i < TCP_MSS; i++) { tx_data[i] = (u8_t)i; } /* initialize local vars */ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); memset(&counters, 0, sizeof(counters)); /* create and initialize the pcb */ tcp_ticks = SEQNO1 - ISS; pcb = test_tcp_new_counters_pcb(&counters); EXPECT_RET(pcb != NULL); tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); pcb->mss = TCP_MSS; pcb->cwnd = TCP_MSS; /* send our segment */ err = tcp_write(pcb, &tx_data[0], TCP_MSS, TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); /* ensure no errors have been recorded */ EXPECT(counters.err_calls == 0); EXPECT(counters.last_err == ERR_OK); /* Force us into retransmisson timeout */ while (!(pcb->flags & TF_RTO)) { test_tcp_tmr(); } /* check first rexmit */ EXPECT(pcb->nrtx == 1); EXPECT(txcounters.num_tx_calls == 1); EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U)); /* still no error expected */ EXPECT(counters.err_calls == 0); EXPECT(counters.last_err == ERR_OK); /* keep running the timer till we hit our maximum RTO */ while (counters.last_err == ERR_OK) { test_tcp_tmr(); } /* check number of retransmissions */ EXPECT(txcounters.num_tx_calls == TCP_MAXRTX); EXPECT(txcounters.num_tx_bytes == TCP_MAXRTX * (TCP_MSS + 40U)); /* check the connection (pcb) has been aborted */ EXPECT(counters.err_calls == 1); EXPECT(counters.last_err == ERR_ABRT); /* check our pcb is no longer active */ for (cur = tcp_active_pcbs; cur != NULL; cur = cur->next) { EXPECT(cur != pcb); } EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }