From 99afb15cedd87dba2f570121ee9e1054596a99a8 Mon Sep 17 00:00:00 2001 From: kieranm Date: Mon, 31 Dec 2007 13:34:47 +0000 Subject: [PATCH] Add persist timer --- CHANGELOG | 2 + src/core/tcp.c | 70 +++++++++++++++--------- src/core/tcp_in.c | 3 ++ src/core/tcp_out.c | 117 +++++++++++++++++++++++++++++++++++++---- src/include/lwip/tcp.h | 6 +++ 5 files changed, 161 insertions(+), 37 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e65ce51d..8f7b5a2e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,8 @@ HISTORY * [Enter new changes just after this line - do not remove this line] ++ New features: + 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer 2007-12-31 Frédéric Bernon, Luca Ceresoli * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets diff --git a/src/core/tcp.c b/src/core/tcp.c index a0de5a32..7f819398 100644 --- a/src/core/tcp.c +++ b/src/core/tcp.c @@ -56,6 +56,8 @@ u32_t tcp_ticks; const u8_t tcp_backoff[13] = { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + /* Times per slowtmr hits */ +const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; /* The TCP PCB lists. */ @@ -572,36 +574,52 @@ tcp_slowtmr(void) ++pcb_remove; LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); } else { - /* Increase the retransmission timer if it is running */ - if(pcb->rtime >= 0) - ++pcb->rtime; - - if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { - /* Time for a retransmission. */ - LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F" pcb->rto %"S16_F"\n", - pcb->rtime, pcb->rto)); - - /* Double retransmission time-out unless we are trying to - * connect to somebody (i.e., we are in SYN_SENT). */ - if (pcb->state != SYN_SENT) { - pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; + if (pcb->persist_backoff > 0) { + /* If snd_wnd is zero, use persist timer to send 1 byte probes + * instead of using the standard retransmission mechanism. */ + pcb->persist_cnt++; + if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { + pcb->persist_cnt = 0; + if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { + pcb->persist_backoff++; + } + tcp_zero_window_probe(pcb); } + } else { + /* Increase the retransmission timer if it is running */ + if(pcb->rtime >= 0) + ++pcb->rtime; - /* Reset the retransmission timer. */ - pcb->rtime = 0; + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F + " pcb->rto %"S16_F"\n", + pcb->rtime, pcb->rto)); - /* Reduce congestion window and ssthresh. */ - eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); - pcb->ssthresh = eff_wnd >> 1; - if (pcb->ssthresh < pcb->mss) { - pcb->ssthresh = pcb->mss * 2; - } - pcb->cwnd = pcb->mss; - LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F" ssthresh %"U16_F"\n", - pcb->cwnd, pcb->ssthresh)); + /* Double retransmission time-out unless we are trying to + * connect to somebody (i.e., we are in SYN_SENT). */ + if (pcb->state != SYN_SENT) { + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; + } + + /* Reset the retransmission timer. */ + pcb->rtime = 0; + + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if (pcb->ssthresh < pcb->mss) { + pcb->ssthresh = pcb->mss * 2; + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F + " ssthresh %"U16_F"\n", + pcb->cwnd, pcb->ssthresh)); - /* The following needs to be called AFTER cwnd is set to one mss - STJ */ - tcp_rexmit_rto(pcb); + /* The following needs to be called AFTER cwnd is set to one + mss - STJ */ + tcp_rexmit_rto(pcb); + } } } /* Check if this PCB has stayed too long in FIN-WAIT-2 */ diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c index 489624c9..f0ba1a33 100644 --- a/src/core/tcp_in.c +++ b/src/core/tcp_in.c @@ -707,6 +707,9 @@ tcp_receive(struct tcp_pcb *pcb) pcb->snd_wnd = tcphdr->wnd; pcb->snd_wl1 = seqno; pcb->snd_wl2 = ackno; + if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) { + pcb->persist_backoff = 0; + } LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd)); #if TCP_WND_DEBUG } else { diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c index eedf061d..c80e637b 100644 --- a/src/core/tcp_out.c +++ b/src/core/tcp_out.c @@ -491,25 +491,34 @@ tcp_output(struct tcp_pcb *pcb) #if TCP_OUTPUT_DEBUG if (seg == NULL) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", (void*)pcb->unsent)); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", + (void*)pcb->unsent)); } #endif /* TCP_OUTPUT_DEBUG */ #if TCP_CWND_DEBUG if (seg == NULL) { - LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", seg == NULL, ack %"U32_F"\n", - pcb->snd_wnd, pcb->cwnd, wnd, - pcb->lastack)); + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F + ", cwnd %"U16_F", wnd %"U32_F + ", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); } else { - LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", - pcb->snd_wnd, pcb->cwnd, wnd, - ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, - ntohl(seg->tcphdr->seqno), pcb->lastack)); + LWIP_DEBUGF(TCP_CWND_DEBUG, + ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F + ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + ntohl(seg->tcphdr->seqno), pcb->lastack)); } #endif /* TCP_CWND_DEBUG */ - /* data available and window allows it to be sent? */ + if (seg != NULL && pcb->snd_wnd == 0 && pcb->persist_backoff == 0) { + /* prepare for persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } /* data available and window allows it to be sent? */ while (seg != NULL && - ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { - LWIP_ASSERT("RST not expected here!", (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + LWIP_ASSERT("RST not expected here!", + (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); /* Stop sending if the nagle algorithm would prevent it * Don't stop: * - if tcp_enqueue had a memory error before (prevent delayed ACK timeout) or @@ -851,4 +860,90 @@ tcp_keepalive(struct tcp_pcb *pcb) pcb->snd_nxt - 1, pcb->rcv_nxt)); } + +/** + * Send persist timer zero-window probes to keep a connection active + * when a window update is lost. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a zero-window probe packet + */ +void +tcp_zero_window_probe(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg; + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: sending ZERO WINDOW probe to %" + U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: tcp_ticks %"U32_F + " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + seg = pcb->unacked; + + if(seg == NULL) + seg = pcb->unsent; + + if(seg == NULL) + return; + + p = pbuf_alloc(PBUF_IP, TCP_HLEN + 1, PBUF_RAM); + + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = seg->tcphdr->seqno; + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_FLAGS_SET(tcphdr, 0); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, 5); + + /* Copy in one byte from the head of the unacked queue */ + *((char *)p->payload + sizeof(struct tcp_hdr)) = *(char *)seg->dataptr; + + tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + { + struct netif *netif; + netif = ip_route(&pcb->remote_ip); + if(netif != NULL){ + netif->addr_hint = &(pcb->addr_hint); + ip_output_if(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, + 0, IP_PROTO_TCP, netif); + netif->addr_hint = NULL; + } + } +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F + " ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} #endif /* LWIP_TCP */ diff --git a/src/include/lwip/tcp.h b/src/include/lwip/tcp.h index 50f1459e..d51071f7 100644 --- a/src/include/lwip/tcp.h +++ b/src/include/lwip/tcp.h @@ -397,6 +397,11 @@ struct tcp_pcb { u32_t keep_cnt; #endif /* LWIP_TCP_KEEPALIVE */ + /* Persist timer counter */ + u32_t persist_cnt; + /* Persist timer back-off */ + u8_t persist_backoff; + /* KEEPALIVE counter */ u8_t keep_cnt_sent; }; @@ -517,6 +522,7 @@ void tcp_rst(u32_t seqno, u32_t ackno, u32_t tcp_next_iss(void); void tcp_keepalive(struct tcp_pcb *pcb); +void tcp_zero_window_probe(struct tcp_pcb *pcb); #if LWIP_CALCULATE_EFF_SEND_MSS u16_t tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr);