mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2025-01-26 18:35:28 +00:00
Kieran Mansley - 14th July 2004
* Fixed whitespace indenting in parts of tcp_in.c * Changed adjustment of ssthresh in response to fast retransmit * Commented out iteration of unsent list when new ACK received as we no longer put all unacked data on unsent list when retransmitting
This commit is contained in:
parent
8d052ecf24
commit
c356f560e8
@ -700,43 +700,48 @@ tcp_receive(struct tcp_pcb *pcb)
|
||||
pcb->acked = 0;
|
||||
|
||||
if (pcb->snd_wl1 + pcb->snd_wnd == right_wnd_edge){
|
||||
++pcb->dupacks;
|
||||
if (pcb->dupacks >= 3 && pcb->unacked != NULL) {
|
||||
if (!(pcb->flags & TF_INFR)) {
|
||||
/* This is fast retransmit. Retransmit the first unacked segment. */
|
||||
LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupacks %u (%lu), fast retransmit %lu\n",
|
||||
(unsigned int)pcb->dupacks, pcb->lastack,
|
||||
ntohl(pcb->unacked->tcphdr->seqno)));
|
||||
tcp_rexmit(pcb);
|
||||
/* Set ssthresh to max (FlightSize / 2, 2*SMSS) */
|
||||
pcb->ssthresh = LWIP_MAX((pcb->snd_max -
|
||||
pcb->lastack) / 2,
|
||||
2 * pcb->mss);
|
||||
++pcb->dupacks;
|
||||
if (pcb->dupacks >= 3 && pcb->unacked != NULL) {
|
||||
if (!(pcb->flags & TF_INFR)) {
|
||||
/* This is fast retransmit. Retransmit the first unacked segment. */
|
||||
LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupacks %u (%lu), fast retransmit %lu\n",
|
||||
(unsigned int)pcb->dupacks, pcb->lastack,
|
||||
ntohl(pcb->unacked->tcphdr->seqno)));
|
||||
tcp_rexmit(pcb);
|
||||
/* Set ssthresh to max (FlightSize / 2, 2*SMSS) */
|
||||
/*pcb->ssthresh = LWIP_MAX((pcb->snd_max -
|
||||
pcb->lastack) / 2,
|
||||
2 * pcb->mss);*/
|
||||
/* Set ssthresh to half of the minimum of the currenct cwnd and the advertised window */
|
||||
if(pcb->cwnd > pcb->snd_wnd)
|
||||
pcb->ssthresh = pcb->snd_wnd / 2;
|
||||
else
|
||||
pcb->ssthresh = pcb->cwnd / 2;
|
||||
|
||||
pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
|
||||
pcb->flags |= TF_INFR;
|
||||
} else {
|
||||
/* Inflate the congestion window, but not if it means that
|
||||
the value overflows. */
|
||||
if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
|
||||
pcb->cwnd += pcb->mss;
|
||||
}
|
||||
}
|
||||
}
|
||||
pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
|
||||
pcb->flags |= TF_INFR;
|
||||
} else {
|
||||
/* Inflate the congestion window, but not if it means that
|
||||
the value overflows. */
|
||||
if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
|
||||
pcb->cwnd += pcb->mss;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupack averted %lu %lu\n",
|
||||
pcb->snd_wl1 + pcb->snd_wnd, right_wnd_edge));
|
||||
LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupack averted %lu %lu\n",
|
||||
pcb->snd_wl1 + pcb->snd_wnd, right_wnd_edge));
|
||||
}
|
||||
} else if (TCP_SEQ_LT(pcb->lastack, ackno) &&
|
||||
TCP_SEQ_LEQ(ackno, pcb->snd_max)) {
|
||||
TCP_SEQ_LEQ(ackno, pcb->snd_max)) {
|
||||
/* We come here when the ACK acknowledges new data. */
|
||||
|
||||
|
||||
/* Reset the "IN Fast Retransmit" flag, since we are no longer
|
||||
in fast retransmit. Also reset the congestion window to the
|
||||
slow start threshold. */
|
||||
if (pcb->flags & TF_INFR) {
|
||||
pcb->flags &= ~TF_INFR;
|
||||
pcb->cwnd = pcb->ssthresh;
|
||||
pcb->flags &= ~TF_INFR;
|
||||
pcb->cwnd = pcb->ssthresh;
|
||||
}
|
||||
|
||||
/* Reset the number of retransmissions. */
|
||||
@ -757,86 +762,91 @@ tcp_receive(struct tcp_pcb *pcb)
|
||||
ssthresh). */
|
||||
if (pcb->state >= ESTABLISHED) {
|
||||
if (pcb->cwnd < pcb->ssthresh) {
|
||||
if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
|
||||
pcb->cwnd += pcb->mss;
|
||||
}
|
||||
if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
|
||||
pcb->cwnd += pcb->mss;
|
||||
}
|
||||
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %u\n", pcb->cwnd));
|
||||
} else {
|
||||
u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
|
||||
if (new_cwnd > pcb->cwnd) {
|
||||
pcb->cwnd = new_cwnd;
|
||||
}
|
||||
u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
|
||||
if (new_cwnd > pcb->cwnd) {
|
||||
pcb->cwnd = new_cwnd;
|
||||
}
|
||||
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %u\n", pcb->cwnd));
|
||||
}
|
||||
}
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %lu, unacked->seqno %lu:%lu\n",
|
||||
ackno,
|
||||
pcb->unacked != NULL?
|
||||
ntohl(pcb->unacked->tcphdr->seqno): 0,
|
||||
pcb->unacked != NULL?
|
||||
ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
|
||||
ackno,
|
||||
pcb->unacked != NULL?
|
||||
ntohl(pcb->unacked->tcphdr->seqno): 0,
|
||||
pcb->unacked != NULL?
|
||||
ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
|
||||
|
||||
/* Remove segment from the unacknowledged list if the incoming
|
||||
ACK acknowlegdes them. */
|
||||
ACK acknowlegdes them. */
|
||||
while (pcb->unacked != NULL &&
|
||||
TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
|
||||
TCP_TCPLEN(pcb->unacked), ackno)) {
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %lu:%lu from pcb->unacked\n",
|
||||
ntohl(pcb->unacked->tcphdr->seqno),
|
||||
ntohl(pcb->unacked->tcphdr->seqno) +
|
||||
TCP_TCPLEN(pcb->unacked)));
|
||||
TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
|
||||
TCP_TCPLEN(pcb->unacked), ackno)) {
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %lu:%lu from pcb->unacked\n",
|
||||
ntohl(pcb->unacked->tcphdr->seqno),
|
||||
ntohl(pcb->unacked->tcphdr->seqno) +
|
||||
TCP_TCPLEN(pcb->unacked)));
|
||||
|
||||
next = pcb->unacked;
|
||||
pcb->unacked = pcb->unacked->next;
|
||||
next = pcb->unacked;
|
||||
pcb->unacked = pcb->unacked->next;
|
||||
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %u ... ", (unsigned int)pcb->snd_queuelen));
|
||||
pcb->snd_queuelen -= pbuf_clen(next->p);
|
||||
tcp_seg_free(next);
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %u ... ", (unsigned int)pcb->snd_queuelen));
|
||||
pcb->snd_queuelen -= pbuf_clen(next->p);
|
||||
tcp_seg_free(next);
|
||||
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%u (after freeing unacked)\n", (unsigned int)pcb->snd_queuelen));
|
||||
if (pcb->snd_queuelen != 0) {
|
||||
LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
|
||||
pcb->unsent != NULL);
|
||||
}
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%u (after freeing unacked)\n", (unsigned int)pcb->snd_queuelen));
|
||||
if (pcb->snd_queuelen != 0) {
|
||||
LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
|
||||
pcb->unsent != NULL);
|
||||
}
|
||||
}
|
||||
pcb->polltmr = 0;
|
||||
}
|
||||
|
||||
/* We go through the ->unsent list to see if any of the segments
|
||||
on the list are acknowledged by the ACK. This may seem
|
||||
strange since an "unsent" segment shouldn't be acked. The
|
||||
rationale is that lwIP puts all outstanding segments on the
|
||||
->unsent list after a retransmission, so these segments may
|
||||
in fact have been sent once. */
|
||||
while (pcb->unsent != NULL &&
|
||||
TCP_SEQ_LEQ(ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent),
|
||||
ackno) &&
|
||||
TCP_SEQ_LEQ(ackno, pcb->snd_max)) {
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %lu:%lu from pcb->unsent\n",
|
||||
ntohl(pcb->unsent->tcphdr->seqno),
|
||||
ntohl(pcb->unsent->tcphdr->seqno) +
|
||||
TCP_TCPLEN(pcb->unsent)));
|
||||
/* We go through the ->unsent list to see if any of the segments
|
||||
on the list are acknowledged by the ACK. This may seem
|
||||
strange since an "unsent" segment shouldn't be acked. The
|
||||
rationale is that lwIP puts all outstanding segments on the
|
||||
->unsent list after a retransmission, so these segments may
|
||||
in fact have been sent once. */
|
||||
/* KJM 13th July 2004
|
||||
I don't think is is necessary as we no longer move all unacked
|
||||
segments on the unsent queue when performing retransmit */
|
||||
/*
|
||||
while (pcb->unsent != NULL &&
|
||||
TCP_SEQ_LEQ(ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent),
|
||||
ackno) &&
|
||||
TCP_SEQ_LEQ(ackno, pcb->snd_max)) {
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %lu:%lu from pcb->unsent\n",
|
||||
ntohl(pcb->unsent->tcphdr->seqno),
|
||||
ntohl(pcb->unsent->tcphdr->seqno) +
|
||||
TCP_TCPLEN(pcb->unsent)));
|
||||
|
||||
next = pcb->unsent;
|
||||
pcb->unsent = pcb->unsent->next;
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %u ... ", (unsigned int)pcb->snd_queuelen));
|
||||
pcb->snd_queuelen -= pbuf_clen(next->p);
|
||||
tcp_seg_free(next);
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%u (after freeing unsent)\n", (unsigned int)pcb->snd_queuelen));
|
||||
if (pcb->snd_queuelen != 0) {
|
||||
LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
|
||||
pcb->unsent != NULL);
|
||||
}
|
||||
|
||||
if (pcb->unsent != NULL) {
|
||||
pcb->snd_nxt = htonl(pcb->unsent->tcphdr->seqno);
|
||||
}
|
||||
next = pcb->unsent;
|
||||
pcb->unsent = pcb->unsent->next;
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %u ... ", (unsigned int)pcb->snd_queuelen));
|
||||
pcb->snd_queuelen -= pbuf_clen(next->p);
|
||||
tcp_seg_free(next);
|
||||
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%u (after freeing unsent)\n", (unsigned int)pcb->snd_queuelen));
|
||||
if (pcb->snd_queuelen != 0) {
|
||||
LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
|
||||
pcb->unsent != NULL);
|
||||
}
|
||||
|
||||
if (pcb->unsent != NULL) {
|
||||
pcb->snd_nxt = htonl(pcb->unsent->tcphdr->seqno);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* End of ACK for new data processing. */
|
||||
|
||||
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %u rtseq %lu ackno %lu\n",
|
||||
pcb->rttest, pcb->rtseq, ackno));
|
||||
pcb->rttest, pcb->rtseq, ackno));
|
||||
|
||||
/* RTT estimation calculations. This is done by checking if the
|
||||
incoming segment acknowledges the segment we use to take a
|
||||
@ -845,20 +855,20 @@ tcp_receive(struct tcp_pcb *pcb)
|
||||
m = tcp_ticks - pcb->rttest;
|
||||
|
||||
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %u ticks (%u msec).\n",
|
||||
m, m * TCP_SLOW_INTERVAL));
|
||||
m, m * TCP_SLOW_INTERVAL));
|
||||
|
||||
/* This is taken directly from VJs original code in his paper */
|
||||
m = m - (pcb->sa >> 3);
|
||||
pcb->sa += m;
|
||||
if (m < 0) {
|
||||
m = -m;
|
||||
m = -m;
|
||||
}
|
||||
m = m - (pcb->sv >> 2);
|
||||
pcb->sv += m;
|
||||
pcb->rto = (pcb->sa >> 3) + pcb->sv;
|
||||
|
||||
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %u (%u miliseconds)\n",
|
||||
pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
|
||||
pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
|
||||
|
||||
pcb->rttest = 0;
|
||||
}
|
||||
@ -869,26 +879,26 @@ tcp_receive(struct tcp_pcb *pcb)
|
||||
if (tcplen > 0) {
|
||||
/* This code basically does three things:
|
||||
|
||||
+) If the incoming segment contains data that is the next
|
||||
in-sequence data, this data is passed to the application. This
|
||||
might involve trimming the first edge of the data. The rcv_nxt
|
||||
variable and the advertised window are adjusted.
|
||||
+) If the incoming segment contains data that is the next
|
||||
in-sequence data, this data is passed to the application. This
|
||||
might involve trimming the first edge of the data. The rcv_nxt
|
||||
variable and the advertised window are adjusted.
|
||||
|
||||
+) If the incoming segment has data that is above the next
|
||||
sequence number expected (->rcv_nxt), the segment is placed on
|
||||
the ->ooseq queue. This is done by finding the appropriate
|
||||
place in the ->ooseq queue (which is ordered by sequence
|
||||
number) and trim the segment in both ends if needed. An
|
||||
immediate ACK is sent to indicate that we received an
|
||||
out-of-sequence segment.
|
||||
+) If the incoming segment has data that is above the next
|
||||
sequence number expected (->rcv_nxt), the segment is placed on
|
||||
the ->ooseq queue. This is done by finding the appropriate
|
||||
place in the ->ooseq queue (which is ordered by sequence
|
||||
number) and trim the segment in both ends if needed. An
|
||||
immediate ACK is sent to indicate that we received an
|
||||
out-of-sequence segment.
|
||||
|
||||
+) Finally, we check if the first segment on the ->ooseq queue
|
||||
now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
|
||||
rcv_nxt > ooseq->seqno, we must trim the first edge of the
|
||||
segment on ->ooseq before we adjust rcv_nxt. The data in the
|
||||
segments that are now on sequence are chained onto the
|
||||
incoming segment so that we only need to call the application
|
||||
once.
|
||||
+) Finally, we check if the first segment on the ->ooseq queue
|
||||
now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
|
||||
rcv_nxt > ooseq->seqno, we must trim the first edge of the
|
||||
segment on ->ooseq before we adjust rcv_nxt. The data in the
|
||||
segments that are now on sequence are chained onto the
|
||||
incoming segment so that we only need to call the application
|
||||
once.
|
||||
*/
|
||||
|
||||
/* First, we check if we must trim the first edge. We have to do
|
||||
@ -897,25 +907,25 @@ tcp_receive(struct tcp_pcb *pcb)
|
||||
segment is larger than rcv_nxt. */
|
||||
if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
|
||||
if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {
|
||||
/* Trimming the first edge is done by pushing the payload
|
||||
pointer in the pbuf downwards. This is somewhat tricky since
|
||||
we do not want to discard the full contents of the pbuf up to
|
||||
the new starting point of the data since we have to keep the
|
||||
TCP header which is present in the first pbuf in the chain.
|
||||
/* Trimming the first edge is done by pushing the payload
|
||||
pointer in the pbuf downwards. This is somewhat tricky since
|
||||
we do not want to discard the full contents of the pbuf up to
|
||||
the new starting point of the data since we have to keep the
|
||||
TCP header which is present in the first pbuf in the chain.
|
||||
|
||||
What is done is really quite a nasty hack: the first pbuf in
|
||||
the pbuf chain is pointed to by inseg.p. Since we need to be
|
||||
able to deallocate the whole pbuf, we cannot change this
|
||||
inseg.p pointer to point to any of the later pbufs in the
|
||||
chain. Instead, we point the ->payload pointer in the first
|
||||
pbuf to data in one of the later pbufs. We also set the
|
||||
inseg.data pointer to point to the right place. This way, the
|
||||
->p pointer will still point to the first pbuf, but the
|
||||
->p->payload pointer will point to data in another pbuf.
|
||||
What is done is really quite a nasty hack: the first pbuf in
|
||||
the pbuf chain is pointed to by inseg.p. Since we need to be
|
||||
able to deallocate the whole pbuf, we cannot change this
|
||||
inseg.p pointer to point to any of the later pbufs in the
|
||||
chain. Instead, we point the ->payload pointer in the first
|
||||
pbuf to data in one of the later pbufs. We also set the
|
||||
inseg.data pointer to point to the right place. This way, the
|
||||
->p pointer will still point to the first pbuf, but the
|
||||
->p->payload pointer will point to data in another pbuf.
|
||||
|
||||
After we are done with adjusting the pbuf pointers we must
|
||||
adjust the ->data pointer in the seg and the segment
|
||||
length.*/
|
||||
After we are done with adjusting the pbuf pointers we must
|
||||
adjust the ->data pointer in the seg and the segment
|
||||
length.*/
|
||||
|
||||
off = pcb->rcv_nxt - seqno;
|
||||
p = inseg.p;
|
||||
@ -941,11 +951,11 @@ tcp_receive(struct tcp_pcb *pcb)
|
||||
inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
|
||||
}
|
||||
else{
|
||||
/* the whole segment is < rcv_nxt */
|
||||
/* must be a duplicate of a packet that has already been correctly handled */
|
||||
/* the whole segment is < rcv_nxt */
|
||||
/* must be a duplicate of a packet that has already been correctly handled */
|
||||
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %lu\n", seqno));
|
||||
tcp_ack_now(pcb);
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %lu\n", seqno));
|
||||
tcp_ack_now(pcb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -953,204 +963,204 @@ tcp_receive(struct tcp_pcb *pcb)
|
||||
and below rcv_nxt + rcv_wnd) in order to be further
|
||||
processed. */
|
||||
if (TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) &&
|
||||
TCP_SEQ_LT(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {
|
||||
TCP_SEQ_LT(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {
|
||||
if (pcb->rcv_nxt == seqno) {
|
||||
/* The incoming segment is the next in sequence. We check if
|
||||
/* The incoming segment is the next in sequence. We check if
|
||||
we have to trim the end of the segment and update rcv_nxt
|
||||
and pass the data to the application. */
|
||||
#if TCP_QUEUE_OOSEQ
|
||||
if (pcb->ooseq != NULL &&
|
||||
TCP_SEQ_LEQ(pcb->ooseq->tcphdr->seqno, seqno + inseg.len)) {
|
||||
/* We have to trim the second edge of the incoming
|
||||
if (pcb->ooseq != NULL &&
|
||||
TCP_SEQ_LEQ(pcb->ooseq->tcphdr->seqno, seqno + inseg.len)) {
|
||||
/* We have to trim the second edge of the incoming
|
||||
segment. */
|
||||
inseg.len = pcb->ooseq->tcphdr->seqno - seqno;
|
||||
pbuf_realloc(inseg.p, inseg.len);
|
||||
}
|
||||
inseg.len = pcb->ooseq->tcphdr->seqno - seqno;
|
||||
pbuf_realloc(inseg.p, inseg.len);
|
||||
}
|
||||
#endif /* TCP_QUEUE_OOSEQ */
|
||||
|
||||
tcplen = TCP_TCPLEN(&inseg);
|
||||
tcplen = TCP_TCPLEN(&inseg);
|
||||
|
||||
pcb->rcv_nxt += tcplen;
|
||||
pcb->rcv_nxt += tcplen;
|
||||
|
||||
/* Update the receiver's (our) window. */
|
||||
if (pcb->rcv_wnd < tcplen) {
|
||||
pcb->rcv_wnd = 0;
|
||||
} else {
|
||||
pcb->rcv_wnd -= tcplen;
|
||||
}
|
||||
/* Update the receiver's (our) window. */
|
||||
if (pcb->rcv_wnd < tcplen) {
|
||||
pcb->rcv_wnd = 0;
|
||||
} else {
|
||||
pcb->rcv_wnd -= tcplen;
|
||||
}
|
||||
|
||||
/* 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
|
||||
application. The code for reassembling out-of-sequence data
|
||||
chains its data on this pbuf as well.
|
||||
/* 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
|
||||
application. The code for reassembling out-of-sequence data
|
||||
chains its data on this pbuf as well.
|
||||
|
||||
If the segment was a FIN, we set the TF_GOT_FIN flag that will
|
||||
be used to indicate to the application that the remote side has
|
||||
closed its end of the connection. */
|
||||
if (inseg.p->tot_len > 0) {
|
||||
recv_data = inseg.p;
|
||||
/* Since this pbuf now is the responsibility of the
|
||||
application, we delete our reference to it so that we won't
|
||||
(mistakingly) deallocate it. */
|
||||
inseg.p = NULL;
|
||||
}
|
||||
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
|
||||
recv_flags = TF_GOT_FIN;
|
||||
}
|
||||
If the segment was a FIN, we set the TF_GOT_FIN flag that will
|
||||
be used to indicate to the application that the remote side has
|
||||
closed its end of the connection. */
|
||||
if (inseg.p->tot_len > 0) {
|
||||
recv_data = inseg.p;
|
||||
/* Since this pbuf now is the responsibility of the
|
||||
application, we delete our reference to it so that we won't
|
||||
(mistakingly) deallocate it. */
|
||||
inseg.p = NULL;
|
||||
}
|
||||
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
|
||||
recv_flags = TF_GOT_FIN;
|
||||
}
|
||||
|
||||
#if TCP_QUEUE_OOSEQ
|
||||
/* We now check if we have segments on the ->ooseq queue that
|
||||
/* We now check if we have segments on the ->ooseq queue that
|
||||
is now in sequence. */
|
||||
while (pcb->ooseq != NULL &&
|
||||
pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
|
||||
while (pcb->ooseq != NULL &&
|
||||
pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
|
||||
|
||||
cseg = pcb->ooseq;
|
||||
seqno = pcb->ooseq->tcphdr->seqno;
|
||||
cseg = pcb->ooseq;
|
||||
seqno = pcb->ooseq->tcphdr->seqno;
|
||||
|
||||
pcb->rcv_nxt += TCP_TCPLEN(cseg);
|
||||
if (pcb->rcv_wnd < TCP_TCPLEN(cseg)) {
|
||||
pcb->rcv_wnd = 0;
|
||||
} else {
|
||||
pcb->rcv_wnd -= TCP_TCPLEN(cseg);
|
||||
}
|
||||
if (cseg->p->tot_len > 0) {
|
||||
/* Chain this pbuf onto the pbuf that we will pass to
|
||||
the application. */
|
||||
if (recv_data) {
|
||||
pcb->rcv_nxt += TCP_TCPLEN(cseg);
|
||||
if (pcb->rcv_wnd < TCP_TCPLEN(cseg)) {
|
||||
pcb->rcv_wnd = 0;
|
||||
} else {
|
||||
pcb->rcv_wnd -= TCP_TCPLEN(cseg);
|
||||
}
|
||||
if (cseg->p->tot_len > 0) {
|
||||
/* Chain this pbuf onto the pbuf that we will pass to
|
||||
the application. */
|
||||
if (recv_data) {
|
||||
pbuf_cat(recv_data, cseg->p);
|
||||
} else {
|
||||
recv_data = cseg->p;
|
||||
}
|
||||
cseg->p = NULL;
|
||||
}
|
||||
if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
|
||||
recv_flags = TF_GOT_FIN;
|
||||
}
|
||||
recv_data = cseg->p;
|
||||
}
|
||||
cseg->p = NULL;
|
||||
}
|
||||
if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
|
||||
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
|
||||
recv_flags = TF_GOT_FIN;
|
||||
}
|
||||
|
||||
|
||||
pcb->ooseq = cseg->next;
|
||||
tcp_seg_free(cseg);
|
||||
}
|
||||
pcb->ooseq = cseg->next;
|
||||
tcp_seg_free(cseg);
|
||||
}
|
||||
#endif /* TCP_QUEUE_OOSEQ */
|
||||
|
||||
|
||||
/* Acknowledge the segment(s). */
|
||||
tcp_ack(pcb);
|
||||
/* Acknowledge the segment(s). */
|
||||
tcp_ack(pcb);
|
||||
|
||||
} else {
|
||||
/* We get here if the incoming segment is out-of-sequence. */
|
||||
tcp_ack_now(pcb);
|
||||
/* We get here if the incoming segment is out-of-sequence. */
|
||||
tcp_ack_now(pcb);
|
||||
#if TCP_QUEUE_OOSEQ
|
||||
/* We queue the segment on the ->ooseq queue. */
|
||||
if (pcb->ooseq == NULL) {
|
||||
pcb->ooseq = tcp_seg_copy(&inseg);
|
||||
} else {
|
||||
/* If the queue is not empty, we walk through the queue and
|
||||
try to find a place where the sequence number of the
|
||||
incoming segment is between the sequence numbers of the
|
||||
previous and the next segment on the ->ooseq queue. That is
|
||||
the place where we put the incoming segment. If needed, we
|
||||
trim the second edges of the previous and the incoming
|
||||
segment so that it will fit into the sequence.
|
||||
/* We queue the segment on the ->ooseq queue. */
|
||||
if (pcb->ooseq == NULL) {
|
||||
pcb->ooseq = tcp_seg_copy(&inseg);
|
||||
} else {
|
||||
/* If the queue is not empty, we walk through the queue and
|
||||
try to find a place where the sequence number of the
|
||||
incoming segment is between the sequence numbers of the
|
||||
previous and the next segment on the ->ooseq queue. That is
|
||||
the place where we put the incoming segment. If needed, we
|
||||
trim the second edges of the previous and the incoming
|
||||
segment so that it will fit into the sequence.
|
||||
|
||||
If the incoming segment has the same sequence number as a
|
||||
segment on the ->ooseq queue, we discard the segment that
|
||||
contains less data. */
|
||||
If the incoming segment has the same sequence number as a
|
||||
segment on the ->ooseq queue, we discard the segment that
|
||||
contains less data. */
|
||||
|
||||
prev = NULL;
|
||||
for(next = pcb->ooseq; next != NULL; next = next->next) {
|
||||
if (seqno == next->tcphdr->seqno) {
|
||||
/* The sequence number of the incoming segment is the
|
||||
prev = NULL;
|
||||
for(next = pcb->ooseq; next != NULL; next = next->next) {
|
||||
if (seqno == next->tcphdr->seqno) {
|
||||
/* The sequence number of the incoming segment is the
|
||||
same as the sequence number of the segment on
|
||||
->ooseq. We check the lengths to see which one to
|
||||
discard. */
|
||||
if (inseg.len > next->len) {
|
||||
/* The incoming segment is larger than the old
|
||||
if (inseg.len > next->len) {
|
||||
/* The incoming segment is larger than the old
|
||||
segment. We replace the old segment with the new
|
||||
one. */
|
||||
cseg = tcp_seg_copy(&inseg);
|
||||
if (cseg != NULL) {
|
||||
cseg->next = next->next;
|
||||
if (prev != NULL) {
|
||||
prev->next = cseg;
|
||||
} else {
|
||||
pcb->ooseq = cseg;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
/* Either the lenghts are the same or the incoming
|
||||
cseg = tcp_seg_copy(&inseg);
|
||||
if (cseg != NULL) {
|
||||
cseg->next = next->next;
|
||||
if (prev != NULL) {
|
||||
prev->next = cseg;
|
||||
} else {
|
||||
pcb->ooseq = cseg;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
/* Either the lenghts are the same or the incoming
|
||||
segment was smaller than the old one; in either
|
||||
case, we ditch the incoming segment. */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (prev == NULL) {
|
||||
if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
|
||||
/* The sequence number of the incoming segment is lower
|
||||
than the sequence number of the first segment on the
|
||||
queue. We put the incoming segment first on the
|
||||
queue. */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (prev == NULL) {
|
||||
if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
|
||||
/* The sequence number of the incoming segment is lower
|
||||
than the sequence number of the first segment on the
|
||||
queue. We put the incoming segment first on the
|
||||
queue. */
|
||||
|
||||
if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) {
|
||||
/* We need to trim the incoming segment. */
|
||||
inseg.len = next->tcphdr->seqno - seqno;
|
||||
pbuf_realloc(inseg.p, inseg.len);
|
||||
}
|
||||
cseg = tcp_seg_copy(&inseg);
|
||||
if (cseg != NULL) {
|
||||
cseg->next = next;
|
||||
pcb->ooseq = cseg;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
|
||||
TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
|
||||
/* The sequence number of the incoming segment is in
|
||||
if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) {
|
||||
/* We need to trim the incoming segment. */
|
||||
inseg.len = next->tcphdr->seqno - seqno;
|
||||
pbuf_realloc(inseg.p, inseg.len);
|
||||
}
|
||||
cseg = tcp_seg_copy(&inseg);
|
||||
if (cseg != NULL) {
|
||||
cseg->next = next;
|
||||
pcb->ooseq = cseg;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
|
||||
TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
|
||||
/* The sequence number of the incoming segment is in
|
||||
between the sequence numbers of the previous and
|
||||
the next segment on ->ooseq. We trim and insert the
|
||||
incoming segment and trim the previous segment, if
|
||||
needed. */
|
||||
if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) {
|
||||
/* We need to trim the incoming segment. */
|
||||
inseg.len = next->tcphdr->seqno - seqno;
|
||||
pbuf_realloc(inseg.p, inseg.len);
|
||||
}
|
||||
if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) {
|
||||
/* We need to trim the incoming segment. */
|
||||
inseg.len = next->tcphdr->seqno - seqno;
|
||||
pbuf_realloc(inseg.p, inseg.len);
|
||||
}
|
||||
|
||||
cseg = tcp_seg_copy(&inseg);
|
||||
if (cseg != NULL) {
|
||||
cseg->next = next;
|
||||
prev->next = cseg;
|
||||
if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
|
||||
/* We need to trim the prev segment. */
|
||||
prev->len = seqno - prev->tcphdr->seqno;
|
||||
pbuf_realloc(prev->p, prev->len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* If the "next" segment is the last segment on the
|
||||
cseg = tcp_seg_copy(&inseg);
|
||||
if (cseg != NULL) {
|
||||
cseg->next = next;
|
||||
prev->next = cseg;
|
||||
if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
|
||||
/* We need to trim the prev segment. */
|
||||
prev->len = seqno - prev->tcphdr->seqno;
|
||||
pbuf_realloc(prev->p, prev->len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* If the "next" segment is the last segment on the
|
||||
ooseq queue, we add the incoming segment to the end
|
||||
of the list. */
|
||||
if (next->next == NULL &&
|
||||
TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
|
||||
next->next = tcp_seg_copy(&inseg);
|
||||
if (next->next != NULL) {
|
||||
if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
|
||||
/* We need to trim the last segment. */
|
||||
next->len = seqno - next->tcphdr->seqno;
|
||||
pbuf_realloc(next->p, next->len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
if (next->next == NULL &&
|
||||
TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
|
||||
next->next = tcp_seg_copy(&inseg);
|
||||
if (next->next != NULL) {
|
||||
if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
|
||||
/* We need to trim the last segment. */
|
||||
next->len = seqno - next->tcphdr->seqno;
|
||||
pbuf_realloc(next->p, next->len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
#endif /* TCP_QUEUE_OOSEQ */
|
||||
|
||||
}
|
||||
@ -1159,7 +1169,7 @@ tcp_receive(struct tcp_pcb *pcb)
|
||||
/* Segments with length 0 is taken care of here. Segments that
|
||||
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)) {
|
||||
TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {
|
||||
tcp_ack_now(pcb);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user