mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-10-01 04:12:07 +00:00
BUG20515: rework way TCP window updates are calculated and sent
This commit is contained in:
parent
4b14621208
commit
f1a9f7ea70
@ -81,6 +81,9 @@ HISTORY
|
|||||||
++ Bugfixes:
|
++ Bugfixes:
|
||||||
|
|
||||||
2009-03-31 Kieran Mansley
|
2009-03-31 Kieran Mansley
|
||||||
|
* tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window
|
||||||
|
updates are calculated and sent (BUG20515)
|
||||||
|
|
||||||
* tcp_in.c: cope with SYN packets received during established states,
|
* tcp_in.c: cope with SYN packets received during established states,
|
||||||
and retransmission of initial SYN.
|
and retransmission of initial SYN.
|
||||||
|
|
||||||
|
@ -383,6 +383,33 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
|
|||||||
return (struct tcp_pcb *)lpcb;
|
return (struct tcp_pcb *)lpcb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the state that tracks the available window space to advertise.
|
||||||
|
*
|
||||||
|
* Returns how much extra window would be advertised if we sent an
|
||||||
|
* update now.
|
||||||
|
*/
|
||||||
|
u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
|
||||||
|
{
|
||||||
|
u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
|
||||||
|
|
||||||
|
if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + pcb->mss)) {
|
||||||
|
/* we can advertise more window */
|
||||||
|
pcb->rcv_ann_wnd = pcb->rcv_wnd;
|
||||||
|
return new_right_edge - pcb->rcv_ann_right_edge;
|
||||||
|
} else {
|
||||||
|
if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
|
||||||
|
/* Can happen due to other end sending out of advertised window,
|
||||||
|
* but within actual available (but not yet advertised) window */
|
||||||
|
pcb->rcv_ann_wnd = 0;
|
||||||
|
} else {
|
||||||
|
/* keep the right edge of window constant */
|
||||||
|
pcb->rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function should be called by the application when it has
|
* This function should be called by the application when it has
|
||||||
* processed the data. The purpose is to advertise a larger window
|
* processed the data. The purpose is to advertise a larger window
|
||||||
@ -394,40 +421,20 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
|
|||||||
void
|
void
|
||||||
tcp_recved(struct tcp_pcb *pcb, u16_t len)
|
tcp_recved(struct tcp_pcb *pcb, u16_t len)
|
||||||
{
|
{
|
||||||
if ((u32_t)pcb->rcv_wnd + len > TCP_WND) {
|
int wnd_inflation;
|
||||||
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->rcv_wnd += len;
|
||||||
!(pcb->flags & TF_ACK_NOW)) {
|
if (pcb->rcv_wnd > TCP_WND)
|
||||||
/*
|
pcb->rcv_wnd = TCP_WND;
|
||||||
* We send an ACK here (if one is not already pending, hence
|
|
||||||
* the above tests) as tcp_recved() implies that the application
|
wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
|
||||||
* has processed some data, and so we can open the receiver's
|
|
||||||
* window to allow more to be transmitted. This could result in
|
/* If the change in the right edge of window is significant (default
|
||||||
* two ACKs being sent for each received packet in some limited cases
|
* watermark is TCP_WND/2), then send an explicit update now.
|
||||||
* (where the application is only receiving data, and is slow to
|
* Otherwise wait for a packet to be sent in the normal course of
|
||||||
* process it) but it is necessary to guarantee that the sender can
|
* events (or more window to be available later) */
|
||||||
* continue to transmit.
|
if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD)
|
||||||
*/
|
|
||||||
tcp_ack(pcb);
|
|
||||||
}
|
|
||||||
else if (pcb->flags & TF_ACK_DELAY && pcb->rcv_wnd >= TCP_WND/2) {
|
|
||||||
/* If we can send a window update such that there is a full
|
|
||||||
* segment available in the window, do so now. This is sort of
|
|
||||||
* nagle-like in its goals, and tries to hit a compromise between
|
|
||||||
* sending acks each time the window is updated, and only sending
|
|
||||||
* window updates when a timer expires. The "threshold" used
|
|
||||||
* above (currently TCP_WND/2) can be tuned to be more or less
|
|
||||||
* aggressive */
|
|
||||||
tcp_ack_now(pcb);
|
tcp_ack_now(pcb);
|
||||||
}
|
|
||||||
|
|
||||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
|
LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
|
||||||
len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
|
len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
|
||||||
@ -510,6 +517,7 @@ tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
|
|||||||
pcb->snd_lbb = iss - 1;
|
pcb->snd_lbb = iss - 1;
|
||||||
pcb->rcv_wnd = TCP_WND;
|
pcb->rcv_wnd = TCP_WND;
|
||||||
pcb->rcv_ann_wnd = TCP_WND;
|
pcb->rcv_ann_wnd = TCP_WND;
|
||||||
|
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
|
||||||
pcb->snd_wnd = TCP_WND;
|
pcb->snd_wnd = TCP_WND;
|
||||||
/* As initial send MSS, we use TCP_MSS but limit it to 536.
|
/* As initial send MSS, we use TCP_MSS but limit it to 536.
|
||||||
The send MSS is updated when an MSS option is received. */
|
The send MSS is updated when an MSS option is received. */
|
||||||
|
@ -430,6 +430,7 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
|
|||||||
npcb->remote_port = tcphdr->src;
|
npcb->remote_port = tcphdr->src;
|
||||||
npcb->state = SYN_RCVD;
|
npcb->state = SYN_RCVD;
|
||||||
npcb->rcv_nxt = seqno + 1;
|
npcb->rcv_nxt = seqno + 1;
|
||||||
|
npcb->rcv_ann_right_edge = npcb->rcv_nxt;
|
||||||
npcb->snd_wnd = tcphdr->wnd;
|
npcb->snd_wnd = tcphdr->wnd;
|
||||||
npcb->ssthresh = npcb->snd_wnd;
|
npcb->ssthresh = npcb->snd_wnd;
|
||||||
npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
|
npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
|
||||||
@ -561,6 +562,7 @@ tcp_process(struct tcp_pcb *pcb)
|
|||||||
&& ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
|
&& ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
|
||||||
pcb->snd_buf++;
|
pcb->snd_buf++;
|
||||||
pcb->rcv_nxt = seqno + 1;
|
pcb->rcv_nxt = seqno + 1;
|
||||||
|
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
|
||||||
pcb->lastack = ackno;
|
pcb->lastack = ackno;
|
||||||
pcb->snd_wnd = tcphdr->wnd;
|
pcb->snd_wnd = tcphdr->wnd;
|
||||||
pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
|
pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
|
||||||
@ -1095,11 +1097,7 @@ tcp_receive(struct tcp_pcb *pcb)
|
|||||||
pcb->rcv_wnd -= tcplen;
|
pcb->rcv_wnd -= tcplen;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pcb->rcv_ann_wnd < tcplen) {
|
tcp_update_rcv_ann_wnd(pcb);
|
||||||
pcb->rcv_ann_wnd = 0;
|
|
||||||
} else {
|
|
||||||
pcb->rcv_ann_wnd -= tcplen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If there is data in the segment, we make preparations to
|
/* If there is data in the segment, we make preparations to
|
||||||
pass this up to the application. The ->recv_data variable
|
pass this up to the application. The ->recv_data variable
|
||||||
@ -1137,11 +1135,8 @@ tcp_receive(struct tcp_pcb *pcb)
|
|||||||
} else {
|
} else {
|
||||||
pcb->rcv_wnd -= TCP_TCPLEN(cseg);
|
pcb->rcv_wnd -= TCP_TCPLEN(cseg);
|
||||||
}
|
}
|
||||||
if (pcb->rcv_ann_wnd < TCP_TCPLEN(cseg)) {
|
|
||||||
pcb->rcv_ann_wnd = 0;
|
tcp_update_rcv_ann_wnd(pcb);
|
||||||
} else {
|
|
||||||
pcb->rcv_ann_wnd -= TCP_TCPLEN(cseg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cseg->p->tot_len > 0) {
|
if (cseg->p->tot_len > 0) {
|
||||||
/* Chain this pbuf onto the pbuf that we will pass to
|
/* Chain this pbuf onto the pbuf that we will pass to
|
||||||
|
@ -59,6 +59,26 @@
|
|||||||
/* Forward declarations.*/
|
/* Forward declarations.*/
|
||||||
static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
|
static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
|
||||||
|
|
||||||
|
static struct tcp_hdr *
|
||||||
|
tcp_output_set_header(struct tcp_pcb *pcb, struct pbuf *p, int optlen)
|
||||||
|
{
|
||||||
|
struct tcp_hdr *tcphdr = p->payload;
|
||||||
|
tcphdr->src = htons(pcb->local_port);
|
||||||
|
tcphdr->dest = htons(pcb->remote_port);
|
||||||
|
tcphdr->seqno = htonl(pcb->snd_nxt);
|
||||||
|
tcphdr->ackno = htonl(pcb->rcv_nxt);
|
||||||
|
TCPH_FLAGS_SET(tcphdr, TCP_ACK);
|
||||||
|
tcphdr->wnd = htons(pcb->rcv_ann_wnd);
|
||||||
|
tcphdr->urgp = 0;
|
||||||
|
TCPH_HDRLEN_SET(tcphdr, (5 + optlen / 4));
|
||||||
|
tcphdr->chksum = 0;
|
||||||
|
|
||||||
|
/* If we're sending a packet, update the announced right window edge */
|
||||||
|
pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
|
||||||
|
|
||||||
|
return tcphdr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by tcp_close() to send a segment including flags but not data.
|
* Called by tcp_close() to send a segment including flags but not data.
|
||||||
*
|
*
|
||||||
@ -466,15 +486,7 @@ tcp_output(struct tcp_pcb *pcb)
|
|||||||
/* remove ACK flags from the PCB, as we send an empty ACK now */
|
/* remove ACK flags from the PCB, as we send an empty ACK now */
|
||||||
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
|
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
|
||||||
|
|
||||||
tcphdr = p->payload;
|
tcphdr = tcp_output_set_header(pcb, p, optlen);
|
||||||
tcphdr->src = htons(pcb->local_port);
|
|
||||||
tcphdr->dest = htons(pcb->remote_port);
|
|
||||||
tcphdr->seqno = htonl(pcb->snd_nxt);
|
|
||||||
tcphdr->ackno = htonl(pcb->rcv_nxt);
|
|
||||||
TCPH_FLAGS_SET(tcphdr, TCP_ACK);
|
|
||||||
tcphdr->wnd = htons(pcb->rcv_ann_wnd);
|
|
||||||
tcphdr->urgp = 0;
|
|
||||||
TCPH_HDRLEN_SET(tcphdr, (5 + optlen / 4));
|
|
||||||
|
|
||||||
/* NB. MSS option is only sent on SYNs, so ignore it here */
|
/* NB. MSS option is only sent on SYNs, so ignore it here */
|
||||||
#if LWIP_TCP_TIMESTAMPS
|
#if LWIP_TCP_TIMESTAMPS
|
||||||
@ -484,7 +496,6 @@ tcp_output(struct tcp_pcb *pcb)
|
|||||||
tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
|
tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tcphdr->chksum = 0;
|
|
||||||
#if CHECKSUM_GEN_TCP
|
#if CHECKSUM_GEN_TCP
|
||||||
tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
|
tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
|
||||||
IP_PROTO_TCP, p->tot_len);
|
IP_PROTO_TCP, p->tot_len);
|
||||||
@ -630,6 +641,8 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
|
|||||||
/* advertise our receive window size in this TCP segment */
|
/* advertise our receive window size in this TCP segment */
|
||||||
seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
|
seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
|
||||||
|
|
||||||
|
pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
|
||||||
|
|
||||||
/* Add any requested options. NB MSS option is only set on SYN
|
/* Add any requested options. NB MSS option is only set on SYN
|
||||||
packets, so ignore it here */
|
packets, so ignore it here */
|
||||||
opts = (u32_t *)(seg->tcphdr + 1);
|
opts = (u32_t *)(seg->tcphdr + 1);
|
||||||
@ -862,17 +875,10 @@ tcp_keepalive(struct tcp_pcb *pcb)
|
|||||||
LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
|
LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
|
||||||
(p->len >= sizeof(struct tcp_hdr)));
|
(p->len >= sizeof(struct tcp_hdr)));
|
||||||
|
|
||||||
tcphdr = p->payload;
|
tcphdr = tcp_output_set_header(pcb, p, 0);
|
||||||
tcphdr->src = htons(pcb->local_port);
|
|
||||||
tcphdr->dest = htons(pcb->remote_port);
|
tcphdr->seqno = htonl(pcb->snd_nxt - 1);
|
||||||
tcphdr->seqno = htonl(pcb->snd_nxt - 1);
|
|
||||||
tcphdr->ackno = htonl(pcb->rcv_nxt);
|
|
||||||
TCPH_FLAGS_SET(tcphdr, TCP_ACK);
|
|
||||||
tcphdr->wnd = htons(pcb->rcv_ann_wnd);
|
|
||||||
tcphdr->urgp = 0;
|
|
||||||
TCPH_HDRLEN_SET(tcphdr, 5);
|
|
||||||
|
|
||||||
tcphdr->chksum = 0;
|
|
||||||
#if CHECKSUM_GEN_TCP
|
#if CHECKSUM_GEN_TCP
|
||||||
tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
|
tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
|
||||||
IP_PROTO_TCP, p->tot_len);
|
IP_PROTO_TCP, p->tot_len);
|
||||||
@ -945,20 +951,13 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
|
|||||||
LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
|
LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
|
||||||
(p->len >= sizeof(struct tcp_hdr)));
|
(p->len >= sizeof(struct tcp_hdr)));
|
||||||
|
|
||||||
tcphdr = p->payload;
|
tcphdr = tcp_output_set_header(pcb, p, 0);
|
||||||
tcphdr->src = htons(pcb->local_port);
|
|
||||||
tcphdr->dest = htons(pcb->remote_port);
|
|
||||||
tcphdr->seqno = seg->tcphdr->seqno;
|
tcphdr->seqno = seg->tcphdr->seqno;
|
||||||
tcphdr->ackno = htonl(pcb->rcv_nxt);
|
|
||||||
TCPH_FLAGS_SET(tcphdr, TCP_ACK);
|
|
||||||
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 */
|
/* Copy in one byte from the head of the unacked queue */
|
||||||
*((char *)p->payload + sizeof(struct tcp_hdr)) = *(char *)seg->dataptr;
|
*((char *)p->payload + sizeof(struct tcp_hdr)) = *(char *)seg->dataptr;
|
||||||
|
|
||||||
tcphdr->chksum = 0;
|
|
||||||
#if CHECKSUM_GEN_TCP
|
#if CHECKSUM_GEN_TCP
|
||||||
tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
|
tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
|
||||||
IP_PROTO_TCP, p->tot_len);
|
IP_PROTO_TCP, p->tot_len);
|
||||||
|
@ -681,7 +681,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TCP_WND: The size of a TCP window.
|
* TCP_WND: The size of a TCP window. This must be at least
|
||||||
|
* (2 * TCP_MSS) for things to work well
|
||||||
*/
|
*/
|
||||||
#ifndef TCP_WND
|
#ifndef TCP_WND
|
||||||
#define TCP_WND 2048
|
#define TCP_WND 2048
|
||||||
@ -780,6 +781,14 @@
|
|||||||
#define LWIP_TCP_TIMESTAMPS 0
|
#define LWIP_TCP_TIMESTAMPS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an
|
||||||
|
* explicit window update
|
||||||
|
*/
|
||||||
|
#ifndef TCP_WND_UPDATE_THRESHOLD
|
||||||
|
#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4)
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1.
|
* LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1.
|
||||||
* LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all
|
* LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all
|
||||||
|
@ -125,6 +125,7 @@ void tcp_input (struct pbuf *p, struct netif *inp);
|
|||||||
err_t tcp_output (struct tcp_pcb *pcb);
|
err_t tcp_output (struct tcp_pcb *pcb);
|
||||||
void tcp_rexmit (struct tcp_pcb *pcb);
|
void tcp_rexmit (struct tcp_pcb *pcb);
|
||||||
void tcp_rexmit_rto (struct tcp_pcb *pcb);
|
void tcp_rexmit_rto (struct tcp_pcb *pcb);
|
||||||
|
u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the Nagle algorithm: inhibit the sending of new TCP
|
* This is the Nagle algorithm: inhibit the sending of new TCP
|
||||||
@ -304,8 +305,9 @@ struct tcp_pcb {
|
|||||||
as we have to do some math with them */
|
as we have to do some math with them */
|
||||||
/* receiver variables */
|
/* receiver variables */
|
||||||
u32_t rcv_nxt; /* next seqno expected */
|
u32_t rcv_nxt; /* next seqno expected */
|
||||||
u16_t rcv_wnd; /* receiver window */
|
u16_t rcv_wnd; /* receiver window available */
|
||||||
u16_t rcv_ann_wnd; /* announced receive window */
|
u16_t rcv_ann_wnd; /* receiver window to announce */
|
||||||
|
u32_t rcv_ann_right_edge; /* announced right edge of window */
|
||||||
|
|
||||||
/* Timers */
|
/* Timers */
|
||||||
u32_t tmr;
|
u32_t tmr;
|
||||||
|
Loading…
Reference in New Issue
Block a user