END_TEST /** Create an ESTABLISHED pcb and check if receive callback is called if a segment * overlapping rcv_nxt is received */ START_TEST(test_tcp_recv_inseq_trim) { struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf* p; char data[PBUF_POOL_BUFSIZE*2]; u16_t data_len; struct netif netif; struct test_tcp_txcounters txcounters; const u32_t new_data_len = 40; LWIP_UNUSED_ARG(_i); /* initialize local vars */ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); data_len = sizeof(data); memset(data, 0, sizeof(data)); /* initialize counter struct */ memset(&counters, 0, sizeof(counters)); counters.expected_data_len = data_len; counters.expected_data = data; /* create and initialize the pcb */ 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); /* create a segment (with an overlapping/old seqno so that the new data begins in the 2nd pbuf) */ p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, (u32_t)(0-(data_len-new_data_len)), 0, 0); EXPECT(p != NULL); if (p != NULL) { EXPECT(p->next != NULL); if (p->next != NULL) { EXPECT(p->next->next != NULL); } } if ((p != NULL) && (p->next != NULL) && (p->next->next != NULL)) { /* pass the segment to tcp_input */ test_tcp_input(p, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 1); EXPECT(counters.recved_bytes == new_data_len); EXPECT(counters.err_calls == 0); } /* make sure the pcb is freed */ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }
END_TEST /** Create an ESTABLISHED pcb and check if receive callback is called */ START_TEST(test_tcp_recv_inseq) { struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf* p; char data[] = {1, 2, 3, 4}; ip_addr_t remote_ip, local_ip, netmask; u16_t data_len; u16_t remote_port = 0x100, local_port = 0x101; struct netif netif; struct test_tcp_txcounters txcounters; LWIP_UNUSED_ARG(_i); /* initialize local vars */ memset(&netif, 0, sizeof(netif)); 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); data_len = sizeof(data); /* initialize counter struct */ memset(&counters, 0, sizeof(counters)); counters.expected_data_len = data_len; counters.expected_data = data; /* 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); /* create a segment */ p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); EXPECT(p != NULL); if (p != NULL) { /* pass the segment to tcp_input */ test_tcp_input(p, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 1); EXPECT(counters.recved_bytes == data_len); EXPECT(counters.err_calls == 0); } /* make sure the pcb is freed */ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); tcp_abort(pcb); EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); }
END_TEST /** Create an ESTABLISHED pcb and check if receive callback is called */ START_TEST(test_tcp_recv_inseq) { struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf* p; char data[] = {1, 2, 3, 4}; u16_t data_len; struct netif netif; struct test_tcp_txcounters txcounters; LWIP_UNUSED_ARG(_i); /* initialize local vars */ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); data_len = sizeof(data); /* initialize counter struct */ memset(&counters, 0, sizeof(counters)); counters.expected_data_len = data_len; counters.expected_data = data; /* create and initialize the pcb */ 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); /* create a segment */ p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); EXPECT(p != NULL); if (p != NULL) { /* pass the segment to tcp_input */ test_tcp_input(p, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 1); EXPECT(counters.recved_bytes == data_len); EXPECT(counters.err_calls == 0); } /* make sure the pcb is freed */ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }
/* this test uses 4 packets: * - data (len=TCP_MSS) * - FIN * - data after FIN (len=1) (invalid) * - 2nd FIN (invalid) * * the parameter 'delay_packet' is a bitmask that choses which on these packets is ooseq */ static void test_tcp_recv_ooseq_double_FINs(int delay_packet) { int i, k; struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf *p_normal_fin, *p_data_after_fin, *p, *p_2nd_fin_ooseq; struct netif netif; u32_t exp_rx_calls = 0, exp_rx_bytes = 0, exp_close_calls = 0, exp_oos_pbufs = 0, exp_oos_tcplen = 0; int first_dropped = 0xff; for(i = 0; i < (int)sizeof(data_full_wnd); i++) { data_full_wnd[i] = (char)i; } /* initialize local vars */ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask); /* initialize counter struct */ memset(&counters, 0, sizeof(counters)); counters.expected_data_len = TCP_WND; counters.expected_data = data_full_wnd; /* create and initialize the pcb */ 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->rcv_nxt = 0x8000; /* create segments */ p = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK); p_normal_fin = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS, 0, TCP_ACK|TCP_FIN); k = 1; p_data_after_fin = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS+1], k, TCP_MSS+1, 0, TCP_ACK); p_2nd_fin_ooseq = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS+1+k, 0, TCP_ACK|TCP_FIN); if(delay_packet & 1) { /* drop normal data */ first_dropped = 1; } else { /* send normal data */ test_tcp_input(p, &netif); exp_rx_calls++; exp_rx_bytes += TCP_MSS; } /* check if counters are as expected */ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); if(delay_packet & 2) { /* drop FIN */ if(first_dropped > 2) { first_dropped = 2; } } else { /* send FIN */ test_tcp_input(p_normal_fin, &netif); if (first_dropped < 2) { /* already dropped packets, this one is ooseq */ exp_oos_pbufs++; exp_oos_tcplen++; } else { /* inseq */ exp_close_calls++; } } /* check if counters are as expected */ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); if(delay_packet & 4) { /* drop data-after-FIN */ if(first_dropped > 3) { first_dropped = 3; } } else { /* send data-after-FIN */ test_tcp_input(p_data_after_fin, &netif); if (first_dropped < 3) { /* already dropped packets, this one is ooseq */ if (delay_packet & 2) { /* correct FIN was ooseq */ exp_oos_pbufs++; exp_oos_tcplen += k; } } else { /* inseq: no change */ } } /* check if counters are as expected */ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); if(delay_packet & 8) { /* drop 2nd-FIN */ if(first_dropped > 4) { first_dropped = 4; } } else { /* send 2nd-FIN */ test_tcp_input(p_2nd_fin_ooseq, &netif); if (first_dropped < 3) { /* already dropped packets, this one is ooseq */ if (delay_packet & 2) { /* correct FIN was ooseq */ exp_oos_pbufs++; exp_oos_tcplen++; } } else { /* inseq: no change */ } } /* check if counters are as expected */ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); if(delay_packet & 1) { /* dropped normal data before */ test_tcp_input(p, &netif); exp_rx_calls++; exp_rx_bytes += TCP_MSS; if((delay_packet & 2) == 0) { /* normal FIN was NOT delayed */ exp_close_calls++; exp_oos_pbufs = exp_oos_tcplen = 0; } } /* check if counters are as expected */ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); if(delay_packet & 2) { /* dropped normal FIN before */ test_tcp_input(p_normal_fin, &netif); exp_close_calls++; exp_oos_pbufs = exp_oos_tcplen = 0; } /* check if counters are as expected */ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); if(delay_packet & 4) { /* dropped data-after-FIN before */ test_tcp_input(p_data_after_fin, &netif); } /* check if counters are as expected */ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); if(delay_packet & 8) { /* dropped 2nd-FIN before */ test_tcp_input(p_2nd_fin_ooseq, &netif); } /* check if counters are as expected */ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); /* check that ooseq data has been dumped */ EXPECT(pcb->ooseq == NULL); /* make sure the pcb is freed */ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }
END_TEST START_TEST(test_tcp_recv_ooseq_max_pbufs) { #if TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_PBUFS < ((TCP_WND / TCP_MSS) + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) int i; struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf *p_ovr; struct netif netif; int datalen = 0; int datalen2; for(i = 0; i < sizeof(data_full_wnd); i++) { data_full_wnd[i] = (char)i; } /* initialize local vars */ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask); /* initialize counter struct */ memset(&counters, 0, sizeof(counters)); counters.expected_data_len = TCP_WND; counters.expected_data = data_full_wnd; /* 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, TEST_LOCAL_PORT, TEST_REMOTE_PORT); pcb->rcv_nxt = 0x8000; /* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */ /* create segments and 'recv' them */ for(i = 1; i <= TCP_OOSEQ_MAX_PBUFS; i++) { int count; struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[i], 1, i, 0, TCP_ACK); EXPECT_RET(p != NULL); EXPECT_RET(p->next == NULL); /* pass the segment to tcp_input */ test_tcp_input(p, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); /* check ooseq queue */ count = tcp_oos_pbuf_count(pcb); EXPECT_OOSEQ(count == i); datalen = tcp_oos_tcplen(pcb); EXPECT_OOSEQ(datalen == i); } /* pass in one more segment, overrunning the limit */ p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[i+1], 1, i+1, 0, TCP_ACK); EXPECT_RET(p_ovr != NULL); /* pass the segment to tcp_input */ test_tcp_input(p_ovr, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); /* check ooseq queue (ensure the new segment was not accepted) */ EXPECT_OOSEQ(tcp_oos_count(pcb) == (i-1)); datalen2 = tcp_oos_tcplen(pcb); EXPECT_OOSEQ(datalen2 == (i-1)); /* make sure the pcb is freed */ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); #endif /* TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */ LWIP_UNUSED_ARG(_i); }
END_TEST /** similar to above test, except seqno starts near the max rxwin */ START_TEST(test_tcp_recv_ooseq_overrun_rxwin_edge) { #if !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS int i, k; struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf *pinseq, *p_ovr; struct netif netif; int datalen = 0; int datalen2; for(i = 0; i < (int)sizeof(data_full_wnd); i++) { data_full_wnd[i] = (char)i; } /* initialize local vars */ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask); /* initialize counter struct */ memset(&counters, 0, sizeof(counters)); counters.expected_data_len = TCP_WND; counters.expected_data = data_full_wnd; /* create and initialize the pcb */ 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->rcv_nxt = 0xffffffff - (TCP_WND / 2); /* create segments */ /* pinseq is sent as last segment! */ pinseq = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK); for(i = TCP_MSS, k = 0; i < TCP_WND; i += TCP_MSS, k++) { int count, expected_datalen; struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK); EXPECT_RET(p != NULL); /* pass the segment to tcp_input */ test_tcp_input(p, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); /* check ooseq queue */ count = tcp_oos_count(pcb); EXPECT_OOSEQ(count == k+1); datalen = tcp_oos_tcplen(pcb); if (i + TCP_MSS < TCP_WND) { expected_datalen = (k+1)*TCP_MSS; } else { expected_datalen = TCP_WND - TCP_MSS; } if (datalen != expected_datalen) { EXPECT_OOSEQ(datalen == expected_datalen); } } /* pass in one more segment, cleary overrunning the rxwin */ p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK); EXPECT_RET(p_ovr != NULL); /* pass the segment to tcp_input */ test_tcp_input(p_ovr, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); /* check ooseq queue */ EXPECT_OOSEQ(tcp_oos_count(pcb) == k); datalen2 = tcp_oos_tcplen(pcb); EXPECT_OOSEQ(datalen == datalen2); /* now pass inseq */ test_tcp_input(pinseq, &netif); EXPECT(pcb->ooseq == NULL); /* make sure the pcb is freed */ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); #endif /* !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS */ LWIP_UNUSED_ARG(_i); }
END_TEST /** create multiple segments and pass them to tcp_input in a wrong * order to see if ooseq-caching works correctly * FIN is received IN-SEQUENCE at the end */ START_TEST(test_tcp_recv_ooseq_FIN_INSEQ) { struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf *p_1_2, *p_4_8, *p_3_11, *p_2_12, *p_15_1, *p_15_1a, *pinseq, *pinseqFIN; char data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; u16_t data_len; struct netif netif; LWIP_UNUSED_ARG(_i); /* initialize local vars */ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask); data_len = sizeof(data); /* initialize counter struct */ memset(&counters, 0, sizeof(counters)); counters.expected_data_len = data_len; counters.expected_data = data; /* create and initialize the pcb */ 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); /* create segments */ /* p1: 7 bytes - 2 before FIN */ /* seqno: 1..2 */ p_1_2 = tcp_create_rx_segment(pcb, &data[1], 2, 1, 0, TCP_ACK); /* p2: 4 bytes before p1, including the first 4 bytes of p1 (partly duplicate) */ /* seqno: 4..11 */ p_4_8 = tcp_create_rx_segment(pcb, &data[4], 8, 4, 0, TCP_ACK); /* p3: same as p2 but 2 bytes longer and one byte more at the front */ /* seqno: 3..13 */ p_3_11 = tcp_create_rx_segment(pcb, &data[3], 11, 3, 0, TCP_ACK); /* p4: 13 bytes - 2 before FIN - should be ignored as contained in p1 and p3 */ /* seqno: 2..13 */ p_2_12 = tcp_create_rx_segment(pcb, &data[2], 12, 2, 0, TCP_ACK); /* pinseq is the first segment that is held back to create ooseq! */ /* seqno: 0..3 */ pinseq = tcp_create_rx_segment(pcb, &data[0], 4, 0, 0, TCP_ACK); /* p5: last byte before FIN */ /* seqno: 15 */ p_15_1 = tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK); /* p6: same as p5, should be ignored */ p_15_1a= tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK); /* pinseqFIN: last 2 bytes plus FIN */ /* only segment containing seqno 14 and FIN */ pinseqFIN = tcp_create_rx_segment(pcb, &data[14], 2, 14, 0, TCP_ACK|TCP_FIN); EXPECT(pinseq != NULL); EXPECT(p_1_2 != NULL); EXPECT(p_4_8 != NULL); EXPECT(p_3_11 != NULL); EXPECT(p_2_12 != NULL); EXPECT(p_15_1 != NULL); EXPECT(p_15_1a != NULL); EXPECT(pinseqFIN != NULL); if ((pinseq != NULL) && (p_1_2 != NULL) && (p_4_8 != NULL) && (p_3_11 != NULL) && (p_2_12 != NULL) && (p_15_1 != NULL) && (p_15_1a != NULL) && (pinseqFIN != NULL)) { /* pass the segment to tcp_input */ test_tcp_input(p_1_2, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); /* check ooseq queue */ EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); /* pass the segment to tcp_input */ test_tcp_input(p_4_8, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); /* check ooseq queue */ EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 4); EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 8); /* pass the segment to tcp_input */ test_tcp_input(p_3_11, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); /* check ooseq queue */ EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); /* p_3_11 has removed p_4_8 from ooseq */ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 3); EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 11); /* pass the segment to tcp_input */ test_tcp_input(p_2_12, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); /* check ooseq queue */ EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 2); EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 12); /* pass the segment to tcp_input */ test_tcp_input(pinseq, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 1); EXPECT(counters.recved_bytes == 14); EXPECT(counters.err_calls == 0); EXPECT(pcb->ooseq == NULL); /* pass the segment to tcp_input */ test_tcp_input(p_15_1, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 1); EXPECT(counters.recved_bytes == 14); EXPECT(counters.err_calls == 0); /* check ooseq queue */ EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15); EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); /* pass the segment to tcp_input */ test_tcp_input(p_15_1a, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 1); EXPECT(counters.recved_bytes == 14); EXPECT(counters.err_calls == 0); /* check ooseq queue: unchanged */ EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15); EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); /* pass the segment to tcp_input */ test_tcp_input(pinseqFIN, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 1); EXPECT(counters.recv_calls == 2); EXPECT(counters.recved_bytes == data_len); EXPECT(counters.err_calls == 0); EXPECT(pcb->ooseq == NULL); } /* make sure the pcb is freed */ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 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 /** 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. */ START_TEST(test_tcp_fast_retx_recover) { struct netif netif; struct test_tcp_txcounters txcounters; struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf* p; char data1[] = { 1, 2, 3, 4}; char data2[] = { 5, 6, 7, 8}; char data3[] = { 9, 10, 11, 12}; char data4[] = {13, 14, 15, 16}; char data5[] = {17, 18, 19, 20}; char data6[] = {21, 22, 23, 24}; ip_addr_t remote_ip, local_ip, netmask; u16_t remote_port = 0x100, local_port = 0x101; err_t err; LWIP_UNUSED_ARG(_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 */ 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 data1 */ err = tcp_write(pcb, data1, sizeof(data1), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); EXPECT_RET(txcounters.num_tx_calls == 1); EXPECT_RET(txcounters.num_tx_bytes == sizeof(data1) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); memset(&txcounters, 0, sizeof(txcounters)); /* "recv" ACK for data1 */ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 4, TCP_ACK); EXPECT_RET(p != NULL); test_tcp_input(p, &netif); EXPECT_RET(txcounters.num_tx_calls == 0); EXPECT_RET(pcb->unacked == NULL); /* send data2 */ err = tcp_write(pcb, data2, sizeof(data2), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); EXPECT_RET(txcounters.num_tx_calls == 1); EXPECT_RET(txcounters.num_tx_bytes == sizeof(data2) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); memset(&txcounters, 0, sizeof(txcounters)); /* duplicate ACK for data1 (data2 is lost) */ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); EXPECT_RET(p != NULL); test_tcp_input(p, &netif); EXPECT_RET(txcounters.num_tx_calls == 0); EXPECT_RET(pcb->dupacks == 1); /* send data3 */ err = tcp_write(pcb, data3, sizeof(data3), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); /* nagle enabled, no tx calls */ EXPECT_RET(txcounters.num_tx_calls == 0); EXPECT_RET(txcounters.num_tx_bytes == 0); memset(&txcounters, 0, sizeof(txcounters)); /* 2nd duplicate ACK for data1 (data2 and data3 are lost) */ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); EXPECT_RET(p != NULL); test_tcp_input(p, &netif); EXPECT_RET(txcounters.num_tx_calls == 0); EXPECT_RET(pcb->dupacks == 2); /* queue data4, don't send it (unsent-oversize is != 0) */ err = tcp_write(pcb, data4, sizeof(data4), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); /* 3nd duplicate ACK for data1 (data2 and data3 are lost) -> fast retransmission */ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); EXPECT_RET(p != NULL); test_tcp_input(p, &netif); /*EXPECT_RET(txcounters.num_tx_calls == 1);*/ EXPECT_RET(pcb->dupacks == 3); memset(&txcounters, 0, sizeof(txcounters)); /* @todo: check expected data?*/ /* send data5, not output yet */ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); /*err = tcp_output(pcb); EXPECT_RET(err == ERR_OK);*/ EXPECT_RET(txcounters.num_tx_calls == 0); EXPECT_RET(txcounters.num_tx_bytes == 0); memset(&txcounters, 0, sizeof(txcounters)); { int i = 0; do { err = tcp_write(pcb, data6, TCP_MSS, TCP_WRITE_FLAG_COPY); i++; }while(err == ERR_OK); EXPECT_RET(err != ERR_OK); } err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); /*EXPECT_RET(txcounters.num_tx_calls == 0); EXPECT_RET(txcounters.num_tx_bytes == 0);*/ memset(&txcounters, 0, sizeof(txcounters)); /* send even more data */ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); /* ...and even more data */ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); /* ...and even more data */ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); /* ...and even more data */ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); /* send ACKs for data2 and data3 */ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 12, TCP_ACK); EXPECT_RET(p != NULL); test_tcp_input(p, &netif); /*EXPECT_RET(txcounters.num_tx_calls == 0);*/ /* ...and even more data */ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); /* ...and even more data */ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); EXPECT_RET(err == ERR_OK); err = tcp_output(pcb); EXPECT_RET(err == ERR_OK); #if 0 /* create expected segment */ p1 = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); EXPECT_RET(p != NULL); if (p != NULL) { /* pass the segment to tcp_input */ test_tcp_input(p, &netif); /* check if counters are as expected */ EXPECT_RET(counters.close_calls == 0); EXPECT_RET(counters.recv_calls == 1); EXPECT_RET(counters.recved_bytes == data_len); EXPECT_RET(counters.err_calls == 0); } #endif /* 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 /** Check that we handle malformed tcp headers, and discard the pbuf(s) */ START_TEST(test_tcp_malformed_header) { struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf* p; char data[] = {1, 2, 3, 4}; ip_addr_t remote_ip, local_ip, netmask; u16_t data_len, chksum; u16_t remote_port = 0x100, local_port = 0x101; struct netif netif; struct test_tcp_txcounters txcounters; struct tcp_hdr *hdr; LWIP_UNUSED_ARG(_i); /* initialize local vars */ memset(&netif, 0, sizeof(netif)); 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); data_len = sizeof(data); /* initialize counter struct */ memset(&counters, 0, sizeof(counters)); counters.expected_data_len = data_len; counters.expected_data = data; /* 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); /* create a segment */ p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); pbuf_header(p, -(s16_t)sizeof(struct ip_hdr)); hdr = (struct tcp_hdr *)p->payload; TCPH_HDRLEN_FLAGS_SET(hdr, 15, 0x3d1); hdr->chksum = 0; chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, &remote_ip, &local_ip); hdr->chksum = chksum; pbuf_header(p, sizeof(struct ip_hdr)); EXPECT(p != NULL); EXPECT(p->next == NULL); if (p != NULL) { /* pass the segment to tcp_input */ test_tcp_input(p, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); } /* make sure the pcb is freed */ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); tcp_abort(pcb); EXPECT(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); }