long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s) { unsigned long n; /* make sure socket still alive */ if (stream->tcpsi < 0) return NIL; /* can transfer bytes from buffer? */ if (n = min (size,stream->ictr)) { memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */ s += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } if (size) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_TCPREAD,NIL); while (size > 0) { /* until request satisfied */ time_t tl = time (0); time_t now = tl; time_t ti = ttmo_read ? now + ttmo_read : 0; if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG); tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ /* set bit in selection vectors */ FD_SET (stream->tcpsi,&fds); FD_SET (stream->tcpsi,&efds); errno = NIL; /* initially no error */ do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == EINTR)); if (i) { /* non-timeout result from select? */ if (i > 0) /* read what we can */ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) && (errno == EINTR)); if (i <= 0) { /* error seen? */ if (tcpdebug) { char tmp[MAILTMPLEN]; if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno); else s = "TCP buffer read end of file"; mm_log (s,TCPDEBUG); } return tcp_abort (stream); } s += i; /* success, point at new place to write */ size -= i; /* reduce byte count */ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG); } /* timeout, punt unless told not to */ else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) { if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG); return tcp_abort (stream); } } (*bn) (BLOCK_NONE,NIL); } *s = '\0'; /* tie off string */ return LONGT; }
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); }
int lwip_connect(int s, struct sockaddr *name, socklen_t namelen) { sockfd_t * fd; struct ip_addr ip; int port, rv = 0; s = sock_for_fd(s); if (s < 0) { errno = EBADF; return -1; } fd = fds + s; // Make sure it's an internet address we understand. if (namelen != sizeof(struct sockaddr_in)) { errno = ENAMETOOLONG; return -1; } // Get access mutex_lock(fd->mutex); // Copy it over memcpy(&fd->name, name, namelen); // Convert this to an lwIP-happy format ip.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; port = ((struct sockaddr_in *)name)->sin_port; // Are we TCP or UDP? switch (fd->type) { case SOCK_STREAM: // This might have gotten made already, bind is valid on // outgoing sockets too. if (!fd->tcppcb) fd->tcppcb = tcp_new(); tcp_arg(fd->tcppcb, (void *)s); tcp_recv(fd->tcppcb, recv_tcp); tcp_sent(fd->tcppcb, sent_tcp); tcp_poll(fd->tcppcb, poll_tcp, 4); // 4 == 4 TCP timer intervals tcp_err(fd->tcppcb, err_tcp); if (tcp_connect(fd->tcppcb, &ip, ntohs(port), connect_tcp) != ERR_OK) { if (tcp_close(fd->tcppcb) != ERR_OK) tcp_abort(fd->tcppcb); fd->tcppcb = NULL; errno = EINVAL; rv = -1; goto out; } break; case SOCK_DGRAM: // This might have gotten made already, bind is valid on // outgoing sockets too. if (!fd->udppcb) fd->udppcb = udp_new(); udp_recv(fd->udppcb, recv_udp, (void *)s); udp_connect(fd->udppcb, &ip, ntohs(port)); break; default: assert( 0 ); errno = EINVAL; rv = -1; goto out; } // If we are doing a TCP connect, we need to wait for the results // of the operation. if (fd->type == SOCK_STREAM) { // Wait for the result fd->connerr = 10; while (fd->connerr > 0) { cond_wait(fd->connect, fd->mutex); } // Convert error codes switch (fd->connerr) { case ERR_OK: break; case ERR_MEM: case ERR_BUF: case ERR_VAL: case ERR_ARG: case ERR_IF: errno = EINVAL; rv = -1; goto out; case ERR_ABRT: case ERR_RST: case ERR_CLSD: case ERR_CONN: errno = ECONNREFUSED; rv = -1; goto out; case ERR_RTE: errno = ENETUNREACH; rv = -1; goto out; case ERR_USE: errno = EADDRINUSE; rv = -1; goto out; } if (fd->connerr == ERR_OK) { // Init our counters fd->recv = 0; fd->send = tcp_sndbuf(fd->tcppcb); } } out: mutex_unlock(fd->mutex); return rv; }
static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { struct mg_connection *nc = (struct mg_connection *) arg; DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err)); if (p == NULL) { if (nc != NULL) { mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } else { /* Tombstoned connection, do nothing. */ } return ERR_OK; } else if (nc == NULL) { tcp_abort(tpcb); return ERR_ARG; } struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; /* * If we get a chain of more than one segment at once, we need to bump * refcount on the subsequent bufs to make them independent. */ if (p->next != NULL) { struct pbuf *q = p->next; for (; q != NULL; q = q->next) pbuf_ref(q); } if (cs->rx_chain == NULL) { cs->rx_chain = p; cs->rx_offset = 0; } else { if (pbuf_clen(cs->rx_chain) >= 4) { /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX * will be completely blocked. We already have at least 4 in the chain, * this one is, so we have to make a copy and release this one. */ struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if (np != NULL) { pbuf_copy(np, p); pbuf_free(p); p = np; } } pbuf_chain(cs->rx_chain, p); } #ifdef SSL_KRYPTON if (nc->ssl != NULL) { if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { mg_lwip_ssl_recv(nc); } else { mg_lwip_ssl_do_hs(nc); } return ERR_OK; } #endif while (cs->rx_chain != NULL) { struct pbuf *seg = cs->rx_chain; size_t len = (seg->len - cs->rx_offset); char *data = (char *) malloc(len); if (data == NULL) { DBG(("OOM")); return ERR_MEM; } pbuf_copy_partial(seg, data, len, cs->rx_offset); mg_if_recv_tcp_cb(nc, data, len); /* callee takes over data */ cs->rx_offset += len; if (cs->rx_offset == cs->rx_chain->len) { cs->rx_chain = pbuf_dechain(cs->rx_chain); pbuf_free(seg); cs->rx_offset = 0; } } if (nc->send_mbuf.len > 0) { mg_lwip_mgr_schedule_poll(nc->mgr); } return ERR_OK; }
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}; ip_addr_t remote_ip, local_ip, netmask; u16_t data_len; u16_t remote_port = 0x100, local_port = 0x101; struct netif netif; 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, NULL, &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 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); }
/** * Implements the TCP state machine. Called by tcp_input. In some * states tcp_receive() is called to receive data. The tcp_seg * argument will be freed by the caller (tcp_input()) unless the * recv_data pointer in the pcb is set. * * @param pcb the tcp_pcb for which a segment arrived * * @note the segment which arrived is saved in global variables, therefore only the pcb * involved is passed as a parameter to this function */ static err_t tcp_process(struct tcp_pcb *pcb) { struct tcp_seg *rseg; u8_t acceptable = 0; err_t err; u8_t accepted_inseq; err = ERR_OK; /* Process incoming RST segments. */ if (flags & TCP_RST) { /* First, determine if the reset is acceptable. */ if (pcb->state == SYN_SENT) { if (ackno == pcb->snd_nxt) { acceptable = 1; } } else { if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_ann_wnd)) { acceptable = 1; } } if (acceptable) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); recv_flags = TF_RESET; pcb->flags &= ~TF_ACK_DELAY; return ERR_RST; } else { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", seqno, pcb->rcv_nxt)); LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", seqno, pcb->rcv_nxt)); return ERR_OK; } } /* Update the PCB (in)activity timer. */ pcb->tmr = tcp_ticks; pcb->keep_cnt_sent = 0; /* Do different things depending on the TCP state. */ switch (pcb->state) { case SYN_SENT: LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno, pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno))); /* received SYN ACK with expected sequence number? */ if ((flags & TCP_ACK) && (flags & TCP_SYN) && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { pcb->snd_buf++; pcb->rcv_nxt = seqno + 1; pcb->lastack = ackno; pcb->snd_wnd = tcphdr->wnd; pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ pcb->state = ESTABLISHED; /* Parse any options in the SYNACK before using pcb->mss since that * can be changed by the received options! */ tcp_parseopt(pcb); #if TCP_CALCULATE_EFF_SEND_MSS pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip)); #endif /* TCP_CALCULATE_EFF_SEND_MSS */ /* Set ssthresh again after changing pcb->mss (already set in tcp_connect * but for the default value of pcb->mss) */ pcb->ssthresh = pcb->mss * 10; pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss); LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); --pcb->snd_queuelen; LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); rseg = pcb->unacked; pcb->unacked = rseg->next; /* If there's nothing left to acknowledge, stop the retransmit timer, otherwise reset it to start again */ if(pcb->unacked == NULL) pcb->rtime = -1; else { pcb->rtime = 0; pcb->nrtx = 0; } tcp_seg_free(rseg); /* Call the user specified function to call when sucessfully * connected. */ TCP_EVENT_CONNECTED(pcb, ERR_OK, err); tcp_ack_now(pcb); } /* received ACK? possibly a half-open connection */ else if (flags & TCP_ACK) { /* send a RST to bring the other side in a non-synchronized state. */ tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), tcphdr->dest, tcphdr->src); } break; case SYN_RCVD: if (flags & TCP_ACK && !(flags & TCP_RST)) { /* expected ACK number? */ if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { u16_t old_cwnd; pcb->state = ESTABLISHED; LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); #if LWIP_CALLBACK_API LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL); #endif /* Call the accept function. */ TCP_EVENT_ACCEPT(pcb, ERR_OK, err); if (err != ERR_OK) { /* If the accept function returns with an error, we abort * the connection. */ tcp_abort(pcb); return ERR_ABRT; } old_cwnd = pcb->cwnd; /* If there was any data contained within this ACK, * we'd better pass it on to the application as well. */ accepted_inseq = tcp_receive(pcb); pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss); if ((flags & TCP_FIN) && accepted_inseq) { tcp_ack_now(pcb); pcb->state = CLOSE_WAIT; } } /* incorrect ACK number */ else { /* send RST */ tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), tcphdr->dest, tcphdr->src); } } break; case CLOSE_WAIT: /* FALLTHROUGH */ case ESTABLISHED: accepted_inseq = tcp_receive(pcb); if ((flags & TCP_FIN) && accepted_inseq) { /* passive close */ tcp_ack_now(pcb); pcb->state = CLOSE_WAIT; } break; case FIN_WAIT_1: tcp_receive(pcb); if (flags & TCP_FIN) { if (flags & TCP_ACK && ackno == pcb->snd_nxt) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_ack_now(pcb); tcp_pcb_purge(pcb); TCP_RMV(&tcp_active_pcbs, pcb); pcb->state = TIME_WAIT; TCP_REG(&tcp_tw_pcbs, pcb); } else { tcp_ack_now(pcb); pcb->state = CLOSING; } } else if (flags & TCP_ACK && ackno == pcb->snd_nxt) { pcb->state = FIN_WAIT_2; } break; case FIN_WAIT_2: tcp_receive(pcb); if (flags & TCP_FIN) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_ack_now(pcb); tcp_pcb_purge(pcb); TCP_RMV(&tcp_active_pcbs, pcb); pcb->state = TIME_WAIT; TCP_REG(&tcp_tw_pcbs, pcb); } break; case CLOSING: tcp_receive(pcb); if (flags & TCP_ACK && ackno == pcb->snd_nxt) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_ack_now(pcb); tcp_pcb_purge(pcb); TCP_RMV(&tcp_active_pcbs, pcb); pcb->state = TIME_WAIT; TCP_REG(&tcp_tw_pcbs, pcb); } break; case LAST_ACK: tcp_receive(pcb); if (flags & TCP_ACK && ackno == pcb->snd_nxt) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ recv_flags = TF_CLOSED; } break; default: break; } return ERR_OK; }
static err_t cos_net_lwip_tcp_recv(void *arg, struct tcp_pcb *tp, struct pbuf *p, err_t err) { struct intern_connection *ic; struct packet_queue *pq, *last; void *headers; struct pbuf *first; ic = (struct intern_connection*)arg; assert(NULL != ic); assert(TCP == ic->conn_type); if (NULL == p) { assert(ic->conn.tp == tp); /* * This should call our registered error function * above with ERR_ABRT, which will make progress * towards closing the connection. * * Later, when the app calls some function in the API, * TCP_CLOSED will be seen and the internal connection * will be deallocated, and the application notified. */ tcp_abort(tp); assert(ic->conn_type == TCP_CLOSED && NULL == ic->conn.tp); /* tcp_close(tp); // Jiguo: aggressive close */ return ERR_CLSD; } first = p; while (p) { struct pbuf *q; if (p->ref != 1) printc("pbuf with len %d, totlen %d and refcnt %d", p->len, p->tot_len, p->ref); assert(p->len > 0); assert(p->type == PBUF_ROM || p->type == PBUF_REF); headers = cos_net_header_start(p, TCP); assert (NULL != headers); pq = net_packet_pq(headers); pq->data = p->payload; pq->len = p->len; pq->next = NULL; #ifdef TEST_TIMING pq->ts_start = timing_record(RECV, pq->ts_start); #endif assert((NULL == ic->incoming) == (NULL == ic->incoming_last)); /* Is the queue empty? */ if (NULL == ic->incoming) { assert(NULL == ic->incoming_last); ic->incoming = ic->incoming_last = pq; } else { last = ic->incoming_last; last->next = pq; ic->incoming_last = pq; } ic->incoming_size += p->len; //assert(1 == p->ref); q = p->next; p->payload = p->alloc_track = NULL; assert(NULL != q || p->len == p->tot_len); assert(p->ref == 1); p = q; } /* Just make sure lwip is doing what we think its doing */ assert(first->ref == 1); /* This should deallocate the entire chain */ pbuf_free(first); /* printc("thd in %ld tcp_recv call trigger evt id %d\n", cos_get_thd_id(), ic->data); */ if (-1 != ic->data && evt_trigger(cos_spd_id(), ic->data)) BUG(); tcp_recv_cnt++; /* /\* If the thread blocked waiting for a packet, wake it up *\/ */ /* if (RECVING == ic->thd_status) { */ /* ic->thd_status = ACTIVE; */ /* assert(ic->thd_status == ACTIVE); /\* Detect races *\/ */ /* if (sched_wakeup(cos_spd_id(), ic->tid)) BUG(); */ /* } */ return ERR_OK; }
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); }
static err_t _tcp_abort_api(struct tcpip_api_call *api_call_msg){ tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; msg->err = 0; tcp_abort(msg->pcb); return msg->err; }
static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { struct mg_connection *nc = (struct mg_connection *) arg; DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err)); if (p == NULL) { if (nc != NULL) { system_os_post(MG_TASK_PRIORITY, MG_SIG_CLOSE_CONN, (uint32_t) nc); } else { /* Tombstoned connection, do nothing. */ } return ERR_OK; } else if (nc == NULL) { tcp_abort(tpcb); return ERR_ARG; } struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; /* * If we get a chain of more than one segment at once, we need to bump * refcount on the subsequent bufs to make them independent. */ if (p->next != NULL) { struct pbuf *q = p->next; for (; q != NULL; q = q->next) pbuf_ref(q); } if (cs->rx_chain == NULL) { cs->rx_chain = p; cs->rx_offset = 0; } else { pbuf_chain(cs->rx_chain, p); } #ifdef ESP_SSL_KRYPTON if (nc->ssl != NULL) { if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { mg_lwip_ssl_recv(nc); } else { mg_lwip_ssl_do_hs(nc); } return ERR_OK; } #endif while (cs->rx_chain != NULL) { struct pbuf *seg = cs->rx_chain; size_t len = (seg->len - cs->rx_offset); char *data = (char *) malloc(len); if (data == NULL) { DBG(("OOM")); return ERR_MEM; } pbuf_copy_partial(seg, data, len, cs->rx_offset); mg_if_recv_tcp_cb(nc, data, len); /* callee takes over data */ cs->rx_offset += len; if (cs->rx_offset == cs->rx_chain->len) { cs->rx_chain = pbuf_dechain(cs->rx_chain); pbuf_free(seg); cs->rx_offset = 0; } } if (nc->send_mbuf.len > 0) { mg_lwip_mgr_schedule_poll(nc->mgr); } return ERR_OK; }
/* 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; ip_addr_t remote_ip, local_ip, netmask; u16_t remote_port = 0x100, local_port = 0x101; 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 */ 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, NULL, &local_ip, &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, local_port, 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; ip_addr_t remote_ip, local_ip, netmask; u16_t remote_port = 0x100, local_port = 0x101; 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 */ 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, NULL, &local_ip, &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, local_port, 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; ip_addr_t remote_ip, local_ip, netmask; u16_t remote_port = 0x100, local_port = 0x101; 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 */ 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, NULL, &local_ip, &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, local_port, 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 /** 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}; u16_t data_len, chksum; struct netif netif; struct test_tcp_txcounters txcounters; struct tcp_hdr *hdr; 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); 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, &test_remote_ip, &test_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(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }
/** * Change the IP address of a network interface * * @param netif the network interface to change * @param ipaddr the new IP address * * @note call netif_set_addr() if you also want to change netmask and * default gateway */ void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) { /* TODO: Handling of obsolete pcbs */ /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */ #if LWIP_TCP struct tcp_pcb *pcb; struct tcp_pcb_listen *lpcb; /* address is actually being changed? */ if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n")); sys_lock_acquire( &tcp_lock ); pcb = tcp_active_pcbs; tcp_active_pcbs = NULL; sys_lock_release( &tcp_lock ); while (pcb != NULL) { /* PCB bound to current local interface address? */ if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr)) #if LWIP_AUTOIP /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ && !ip_addr_islinklocal(&(pcb->local_ip)) #endif /* LWIP_AUTOIP */ ) { /* this connection must be aborted */ struct tcp_pcb *next = pcb->next; LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); tcp_abort(pcb); pcb = next; } else { pcb = pcb->next; } } for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { /* PCB bound to current local interface address? */ if ((!(ip_addr_isany(&(lpcb->local_ip)))) && (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) { /* The PCB is listening to the old ipaddr and * is set to listen to the new one instead */ ip_addr_set(&(lpcb->local_ip), ipaddr); } } } #endif snmp_delete_ipaddridx_tree(netif); snmp_delete_iprteidx_tree(0,netif); /* set new IP address to netif */ ip_addr_set(&(netif->ip_addr), ipaddr); snmp_insert_ipaddridx_tree(netif); snmp_insert_iprteidx_tree(0,netif); LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", netif->name[0], netif->name[1], ip4_addr1_16(&netif->ip_addr), ip4_addr2_16(&netif->ip_addr), ip4_addr3_16(&netif->ip_addr), ip4_addr4_16(&netif->ip_addr))); }
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[TCP_MSS] = {21, 22, 23, 24}; err_t err; LWIP_UNUSED_ARG(_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 */ 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 = 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(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; 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; err_t err; size_t i; u16_t sent_total; 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 */ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_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, &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 = 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); /* window is completely full, but persist timer is off since send buffer is empty */ EXPECT(pcb->snd_wnd == 0); EXPECT(pcb->persist_backoff == 0); } /* 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(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); tcp_abort(pcb); EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); }
static void tcp_backlog_free(void * data) { tcp_abort((struct tcp_pcb *) data); }