partly fixed bug #25882: TCP hangs on MSS > pcb->snd_wnd (by not creating segments bigger than half the window)

This commit is contained in:
goldsimon 2012-02-12 14:14:19 +01:00
parent 16555ad12e
commit 7d0dab9d7d
4 changed files with 19 additions and 6 deletions

View File

@ -76,6 +76,10 @@ HISTORY
++ Bugfixes:
2012-02-12: Simon Goldschmidt
* tcp.h, tcp_in.c, tcp_out.c: partly fixed bug #25882: TCP hangs on
MSS > pcb->snd_wnd (by not creating segments bigger than half the window)
2012-02-11: Simon Goldschmidt
* tcp.c: fixed bug #35435: No pcb state check before adding it to time-wait
queue while closing

View File

@ -501,6 +501,7 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
npcb->rcv_nxt = seqno + 1;
npcb->rcv_ann_right_edge = npcb->rcv_nxt;
npcb->snd_wnd = tcphdr->wnd;
npcb->snd_wnd_max = tcphdr->wnd;
npcb->ssthresh = npcb->snd_wnd;
npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
npcb->callback_arg = pcb->callback_arg;
@ -653,6 +654,7 @@ tcp_process(struct tcp_pcb *pcb)
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
pcb->lastack = ackno;
pcb->snd_wnd = tcphdr->wnd;
pcb->snd_wnd_max = tcphdr->wnd;
pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
pcb->state = ESTABLISHED;
@ -853,7 +855,7 @@ tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
* data, and if so frees the memory of the buffered data. Next, is places the
* segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
* is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
* i it has been removed from the buffer.
* it has been removed from the buffer.
*
* If the incoming segment constitutes an ACK for a segment that was used for RTT
* estimation, the RTT is estimated here as well.
@ -888,6 +890,11 @@ tcp_receive(struct tcp_pcb *pcb)
(pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
(pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
pcb->snd_wnd = tcphdr->wnd;
/* keep track of the biggest window announced by the remote host to calculate
the maximum segment size */
if (pcb->snd_wnd_max < tcphdr->wnd) {
pcb->snd_wnd_max = tcphdr->wnd;
}
pcb->snd_wl1 = seqno;
pcb->snd_wl2 = ackno;
if (pcb->snd_wnd == 0) {

View File

@ -232,7 +232,7 @@ tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
LWIP_UNUSED_ARG(apiflags);
LWIP_UNUSED_ARG(first_seg);
/* always create MSS-sized pbufs */
alloc = pcb->mss;
alloc = max_length;
#else /* LWIP_NETIF_TX_SINGLE_PBUF */
if (length < max_length) {
/* Should we allocate an oversized pbuf, or just the minimum
@ -372,6 +372,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
u16_t concat_chksummed = 0;
#endif /* TCP_CHECKSUM_ON_COPY */
err_t err;
/* don't allocate segments bigger than half the maximum window we ever received */
u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2);
#if LWIP_NETIF_TX_SINGLE_PBUF
/* Always copy to try to create single pbufs for TX */
@ -430,7 +432,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
/* Usable space at the end of the last unsent segment */
unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
space = pcb->mss - (last_unsent->len + unsent_optlen);
space = mss_local - (last_unsent->len + unsent_optlen);
/*
* Phase 1: Copy data directly into an oversized pbuf.
@ -523,7 +525,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
while (pos < len) {
struct pbuf *p;
u16_t left = len - pos;
u16_t max_len = pcb->mss - optlen;
u16_t max_len = mss_local - optlen;
u16_t seglen = left > max_len ? max_len : left;
#if TCP_CHECKSUM_ON_COPY
u16_t chksum = 0;
@ -533,7 +535,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
if (apiflags & TCP_WRITE_FLAG_COPY) {
/* If copy is set, memory should be allocated and data copied
* into pbuf */
if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
goto memerr;
}
@ -1067,7 +1069,6 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
/* Add any requested options. NB MSS option is only set on SYN
packets, so ignore it here */
//LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
opts = (u32_t *)(void *)(seg->tcphdr + 1);
if (seg->flags & TF_SEG_OPTS_MSS) {
*opts = TCP_BUILD_MSS_OPTION(pcb->mss);

View File

@ -227,6 +227,7 @@ struct tcp_pcb {
window update. */
u32_t snd_lbb; /* Sequence number of next byte to be buffered. */
u16_t snd_wnd; /* sender window */
u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */
u16_t acked;