diff -ru lwip/src/core/pbuf.c lwip.trunk/src/core/pbuf.c --- lwip/src/core/pbuf.c 2007-09-10 20:25:15.000000000 +0200 +++ lwip.trunk/src/core/pbuf.c 2007-12-18 11:29:24.703125000 +0100 @@ -617,6 +642,38 @@ } /** + * Split two pbufs (or pbuf chains) from each other. + * + * The caller must keep references to head and tail. + * + * @param h head pbuf (chain) + * @param t tail pbuf (chain) + * + * The ->tot_len fields of all pbufs of the head chain are adjusted. + * The ->next field of the last pbuf of the head chain is adjusted. + * + */ +void +pbuf_split(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p, *q; + q = NULL; + + LWIP_ASSERT("h != NULL (programmer violates API)", h != NULL); + if ((h == NULL) || (t == NULL)) return; + + for (p = h; p != t && p != NULL; p = p->next); + + if (p == t) { + for (p = h; p != t; p = p->next) { + p->tot_len -= t->tot_len; + q = p; + } + q->next = NULL; + } +} + +/** * Dechains the first pbuf from its succeeding pbufs in the chain. * * Makes p->tot_len field equal to p->len. diff -ru lwip/src/core/tcp.c lwip.trunk/src/core/tcp.c --- lwip/src/core/tcp.c 2007-09-08 01:02:00.000000000 +0200 +++ lwip.trunk/src/core/tcp.c 2007-12-18 11:29:25.234375000 +0100 @@ -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}; +/* Also seen 1.5, 3, 6, 12, 25, 48, 60 */ +const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; /* Times per slowtmr hits */ /* The TCP PCB lists. */ @@ -390,9 +392,14 @@ { if ((u32_t)pcb->rcv_wnd + len > TCP_WND) { pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; } else { pcb->rcv_wnd += len; + if (pcb->rcv_wnd >= pcb->mss) { + pcb->rcv_ann_wnd = pcb->rcv_wnd; + } } + if (!(pcb->flags & TF_ACK_DELAY) && !(pcb->flags & TF_ACK_NOW)) { /* @@ -497,6 +504,7 @@ pcb->lastack = iss - 1; pcb->snd_lbb = iss - 1; pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; pcb->snd_wnd = TCP_WND; pcb->mss = TCP_MSS; pcb->cwnd = 1; @@ -564,36 +572,56 @@ ++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]; - } - - /* 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; + 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++; + } + pcb->persist_probe++; + if (pcb->unacked != NULL) { + /* XXX: can we really assume there's only the 1 byte probe + * in the unacked queue? */ + tcp_rexmit(pcb); + } else { + tcp_output(pcb); + } } - pcb->cwnd = pcb->mss; - LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F" ssthresh %"U16_F"\n", - pcb->cwnd, pcb->ssthresh)); + } 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]; + } + + /* 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 < 2 * 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 */ @@ -938,6 +966,7 @@ pcb->snd_buf = TCP_SND_BUF; pcb->snd_queuelen = 0; pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; pcb->tos = 0; pcb->ttl = TCP_TTL; pcb->mss = TCP_MSS; diff -ru lwip/src/core/tcp_in.c lwip.trunk/src/core/tcp_in.c --- lwip/src/core/tcp_in.c 2007-09-08 01:02:00.000000000 +0200 +++ lwip.trunk/src/core/tcp_in.c 2007-12-18 11:29:25.296875000 +0100 @@ -479,7 +484,7 @@ /*if (TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) && TCP_SEQ_LEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) { */ - if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_ann_wnd)) { acceptable = 1; } } @@ -539,7 +544,7 @@ /* Call the user specified function to call when sucessfully * connected. */ TCP_EVENT_CONNECTED(pcb, ERR_OK, err); - tcp_ack(pcb); + tcp_ack_now(pcb); } /* received ACK? possibly a half-open connection */ else if (flags & TCP_ACK) { @@ -689,6 +694,9 @@ 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 { @@ -990,7 +998,7 @@ processed. */ /*if (TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) && TCP_SEQ_LT(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ - if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd - 1)){ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_ann_wnd - 1)){ if (pcb->rcv_nxt == seqno) { accepted_inseq = 1; /* The incoming segment is the next in sequence. We check if @@ -1017,11 +1025,11 @@ /* Update the receiver's (our) window. */ if (pcb->rcv_wnd < tcplen) { - pcb->rcv_wnd = 0; + pcb->rcv_wnd = 0; } else { - pcb->rcv_wnd -= tcplen; + pcb->rcv_wnd -= tcplen; } - + pcb->rcv_ann_wnd = pcb->rcv_wnd; /* If there is data in the segment, we make preparations to pass this up to the application. The ->recv_data variable is used for holding the pbuf that goes to the @@ -1055,8 +1063,10 @@ pcb->rcv_nxt += TCP_TCPLEN(cseg); if (pcb->rcv_wnd < TCP_TCPLEN(cseg)) { pcb->rcv_wnd = 0; + pcb->rcv_ann_wnd = 0; } else { pcb->rcv_wnd -= TCP_TCPLEN(cseg); + pcb->rcv_ann_wnd -= TCP_TCPLEN(cseg); } if (cseg->p->tot_len > 0) { /* Chain this pbuf onto the pbuf that we will pass to @@ -1214,7 +1224,7 @@ } else { /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ - if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_ann_wnd-1)){ tcp_ack_now(pcb); } } @@ -1223,7 +1233,8 @@ fall out of the window are ACKed. */ /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ - if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_ann_wnd-1) && + !(pcb->snd_wnd == 0)) { tcp_ack_now(pcb); } } diff -ru lwip/src/core/tcp_out.c lwip.trunk/src/core/tcp_out.c --- lwip/src/core/tcp_out.c 2007-09-08 01:02:00.000000000 +0200 +++ lwip.trunk/src/core/tcp_out.c 2007-12-18 11:29:25.343750000 +0100 @@ -57,6 +57,7 @@ /* Forward declarations.*/ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); +static err_t tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t len); /** * Called by tcp_close() to send a segment including flags but not data. @@ -451,7 +452,7 @@ tcphdr->seqno = htonl(pcb->snd_nxt); tcphdr->ackno = htonl(pcb->rcv_nxt); TCPH_FLAGS_SET(tcphdr, TCP_ACK); - tcphdr->wnd = htons(pcb->rcv_wnd); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); tcphdr->urgp = 0; TCPH_HDRLEN_SET(tcphdr, 5); @@ -497,9 +498,32 @@ ntohl(seg->tcphdr->seqno), pcb->lastack)); } #endif /* TCP_CWND_DEBUG */ + /* If the next segment in the unsent queue doesn't fit into the sending window, + * then split it. + */ + if (seg != NULL && ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd) { + if (pcb->snd_wnd == 0) { + if (pcb->persist_backoff == 0) { + /* prepare for persist timer */ + if (tcp_split_unsent_seg(pcb, 1) == ERR_OK) { + /* delay 1 byte probe by starting the presist timer */ + seg = pcb->unsent; + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + pcb->persist_probe = 0; + } + } + } else { + tcp_split_unsent_seg(pcb, wnd); + /* pcb->unsent is updated */ + seg = pcb->unsent; + } + } + /* data available and window allows it to be sent? */ while (seg != NULL && - ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + ((ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) || + pcb->persist_probe)) { #if TCP_CWND_DEBUG 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", i %"S16_F"\n", pcb->snd_wnd, pcb->cwnd, wnd, @@ -508,7 +532,9 @@ ntohl(seg->tcphdr->seqno), pcb->lastack, i)); ++i; #endif /* TCP_CWND_DEBUG */ - + if (pcb->persist_probe) { + pcb->persist_probe = 0; + } pcb->unsent = seg->next; if (pcb->state != SYN_SENT) { @@ -571,13 +597,7 @@ wnd fields remain. */ seg->tcphdr->ackno = htonl(pcb->rcv_nxt); - /* silly window avoidance */ - if (pcb->rcv_wnd < pcb->mss) { - seg->tcphdr->wnd = 0; - } else { - /* advertise our receive window size in this TCP segment */ - seg->tcphdr->wnd = htons(pcb->rcv_wnd); - } + seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); /* If we don't have a local IP address, we get one by calling ip_route(). */ @@ -799,7 +819,7 @@ tcphdr->seqno = htonl(pcb->snd_nxt - 1); tcphdr->ackno = htonl(pcb->rcv_nxt); TCPH_FLAGS_SET(tcphdr, 0); - tcphdr->wnd = htons(pcb->rcv_wnd); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); tcphdr->urgp = 0; TCPH_HDRLEN_SET(tcphdr, 5); @@ -830,4 +850,189 @@ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", pcb->snd_nxt - 1, pcb->rcv_nxt)); } + +static err_t +tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t len) +{ + struct tcp_seg *seg, *useg; + struct pbuf *p, *q, *before_p; + u8_t *ptr; + u16_t offset, shrink; + u8_t queuelen; + u8_t method = 0; + + useg = pcb->unsent; + if (useg == NULL) { + return ERR_MEM; + } + + if (len == 0 || useg->len <= len) { + return ERR_OK; + } + + LWIP_ASSERT("len <= mss", len <= pcb->mss); + LWIP_ASSERT("pcb->unsent->len > 0", pcb->unsent->len > 0); + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: split_unsent_seg: %u\n", (unsigned int)pcb->snd_queuelen)); + + /* We should check that we don't exceed TCP_SND_QUEUELEN but we need + * to split this packet so we may actually exceed the max value by + * one! + */ + queuelen = pcb->snd_queuelen; + + /* Allocate memory for tcp_seg, and fill in fields. */ + seg = memp_malloc(MEMP_TCP_SEG); + if (seg == NULL) { + LWIP_DEBUGF(TCP_RESIZE_DEBUG | 2, ("tcp_split_unsent_seg: could not allocate memory for tcp_seg\n")); + goto memerr; + } + seg->p = NULL; + + /* copy from volatile memory? */ + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(TCP_RESIZE_DEBUG | 2, ("tcp_split_unsent_seg: could not allocate memory for pbuf copy size %u\n", len)); + goto memerr; + } + ptr = (u8_t*) seg->p->payload; + /* Shrink original packet, this is somewhat messy. + * We know the first pbuf in chain contains the complete TCP + * header and eventually the start of the TCP data. + */ + shrink = len; + + /* Find pbuf holding the first TCP data byte. Necessacy because this + * packet can have been split before and therefore only tcp headers + * may be present int the first pbuf */ + p = useg->p; + before_p = NULL; + while (!((u8_t*)useg->dataptr >= (u8_t*)p->payload && (u8_t*)useg->dataptr < ((u8_t*)p->payload + p->len))) { + before_p = p; + p = p->next; + LWIP_ASSERT("Chain contains data", p != NULL); + } + offset = (u8_t*)useg->dataptr - (u8_t*)p->payload; + + while (p != NULL && shrink > 0) { + if (p->len == 0 || shrink >= p->len - offset) { + /* Remove pbuf, update related pbufs in chain */ + if (offset > 0) { + /* Keep header in pbuf, this is the first pbuf in the segment */ + LWIP_ASSERT("TCP header in first pbuf", before_p == NULL); + method |= 0x01; + q = p->next; + pbuf_split(useg->p, q); + MEMCPY(ptr, (u8_t*)p->payload + offset, p->len - offset); + ptr += p->len - offset; + shrink -= p->len - offset; + pbuf_realloc(p, offset); /* p now only contains headers */ + LWIP_ASSERT("TCP data in next chain", q != NULL); + pbuf_cat(useg->p, q); + useg->dataptr = q->payload; + before_p = p; + } else { + /* Remove complete pbuf */ + method |= 0x02; + LWIP_ASSERT("Don't remove TCP header in first pbuf", before_p != NULL); + pbuf_split(useg->p, p); + q = p->next; + pbuf_split(p, q); + MEMCPY(ptr, (u8_t*)p->payload, p->len); + ptr += p->len; + shrink -= p->len; + pbuf_free(p); + queuelen--; + LWIP_ASSERT("Don't remove all data", q != NULL); + pbuf_cat(useg->p, q); + useg->dataptr = q->payload; + } + p = q; + LWIP_ASSERT("No pbuf/data length mismatch", !(p == NULL && shrink > 0)); + } else { + /* Resize pbuf */ + method |= 0x04; + q = p->next; + pbuf_split(useg->p, q); + MEMCPY(ptr, (u8_t*)p->payload + offset, shrink); + ptr += shrink; + MEMCPY((u8_t*)p->payload + offset, (u8_t*)p->payload + offset + shrink, p->len - offset - shrink); + if (before_p != NULL) { + LWIP_ASSERT("before_p is updated", before_p->next == p); + pbuf_split(useg->p, p); + } + pbuf_realloc(p, p->len - shrink); + if (before_p != NULL) { + pbuf_cat(useg->p, p); + } + if (q != NULL) { + pbuf_cat(useg->p, q); + } + useg->dataptr = (u8_t*)p->payload + offset; + p = q; + shrink = 0; + } + offset = 0; /* Offset is in first pbuf so it's not relevant any more */ + } + LWIP_ASSERT("All data removed from original pbuf", shrink == 0); + LWIP_ASSERT("All removed data copied to new segment", ptr == (u8_t*)seg->p->payload + len); + + useg->len -= len; + + seg->len = len; + seg->dataptr = seg->p->payload; + queuelen++; + + /* build TCP header */ + if (pbuf_header(seg->p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_RESIZE_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + goto memerr; + } + seg->tcphdr = seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = pcb->unsent->tcphdr->seqno; + seg->tcphdr->urgp = 0; + TCPH_FLAGS_SET(seg->tcphdr, 0); + /* don't fill in tcphdr->ackno and tcphdr->wnd until later */ + + TCPH_HDRLEN_SET(seg->tcphdr, 5); + + /* We don't have to touch pcb->snd_buf because the total amount of + * data is constant when packet is split */ + + /* update number of segments on the queues. Note that length now may + * exceed TCP_SND_QUEUELEN! */ + pcb->snd_queuelen = queuelen; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_split_unsent_seg: %d (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_split_unsent_seg: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued, but only + if the segment has data (indicated by seglen > 0). */ + if (seg->tcphdr != NULL) { + TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); + } + + /* Update original segment with a new sequence number */ + pcb->unsent->tcphdr->seqno = htonl(ntohl(pcb->unsent->tcphdr->seqno) + len); + + /* Finally insert new segment first in queue */ + seg->next = pcb->unsent; + pcb->unsent = seg; + + return ERR_OK; +memerr: + TCP_STATS_INC(tcp.memerr); + + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_split_unsent_seg: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | DBG_STATE, ("tcp_split_unsent_seg: %d (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} #endif /* LWIP_TCP */ diff -ru lwip/src/include/lwip/opt.h lwip.trunk/src/include/lwip/opt.h --- lwip/src/include/lwip/opt.h 2007-09-10 20:12:13.000000000 +0200 +++ lwip.trunk/src/include/lwip/opt.h 2007-12-18 11:29:26.687500000 +0100 @@ -1422,6 +1425,13 @@ #endif /** + * TCP_RESIZE_DEBUG: Enable debugging for resizing of TCP packets in send queue. + */ +#ifndef TCP_RESIZE_DEBUG +#define TCP_RESIZE_DEBUG LWIP_DBG_OFF +#endif + +/** * UDP_DEBUG: Enable debugging in UDP. */ #ifndef UDP_DEBUG diff -ru lwip/src/include/lwip/pbuf.h lwip.trunk/src/include/lwip/pbuf.h --- lwip/src/include/lwip/pbuf.h 2007-09-08 01:02:03.000000000 +0200 +++ lwip.trunk/src/include/lwip/pbuf.h 2007-12-18 11:29:26.734375000 +0100 @@ -107,6 +110,7 @@ u8_t pbuf_clen(struct pbuf *p); void pbuf_cat(struct pbuf *h, struct pbuf *t); void pbuf_chain(struct pbuf *h, struct pbuf *t); +void pbuf_split(struct pbuf *h, struct pbuf *t); struct pbuf *pbuf_dechain(struct pbuf *p); err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from); u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset); diff -ru lwip/src/include/lwip/tcp.h lwip.trunk/src/include/lwip/tcp.h --- lwip/src/include/lwip/tcp.h 2007-09-13 19:46:13.000000000 +0200 +++ lwip.trunk/src/include/lwip/tcp.h 2007-12-18 11:29:27.218750000 +0100 @@ -266,6 +266,7 @@ /* receiver variables */ u32_t rcv_nxt; /* next seqno expected */ u16_t rcv_wnd; /* receiver window */ + u16_t rcv_ann_wnd; /* the window announced to the other part */ /* Timers */ u32_t tmr; @@ -378,6 +379,15 @@ /* KEEPALIVE counter */ u8_t keep_cnt_sent; + + /* Persist timer back-off */ + u8_t persist_backoff; + + /* Persist timer send probe flag*/ + u8_t persist_probe; + + /* Persist timer counter */ + u32_t persist_cnt; }; struct tcp_pcb_listen {