5 |
*/ |
*/ |
6 |
/* |
/* |
7 |
* Copyright (c) 2001-2003 Swedish Institute of Computer Science. |
* Copyright (c) 2001-2003 Swedish Institute of Computer Science. |
8 |
* All rights reserved. |
* All rights reserved. |
9 |
* |
* |
10 |
* Redistribution and use in source and binary forms, with or without modification, |
* Redistribution and use in source and binary forms, with or without modification, |
11 |
* are permitted provided that the following conditions are met: |
* are permitted provided that the following conditions are met: |
12 |
* |
* |
13 |
* 1. Redistributions of source code must retain the above copyright notice, |
* 1. Redistributions of source code must retain the above copyright notice, |
16 |
* this list of conditions and the following disclaimer in the documentation |
* this list of conditions and the following disclaimer in the documentation |
17 |
* and/or other materials provided with the distribution. |
* and/or other materials provided with the distribution. |
18 |
* 3. The name of the author may not be used to endorse or promote products |
* 3. The name of the author may not be used to endorse or promote products |
19 |
* derived from this software without specific prior written permission. |
* derived from this software without specific prior written permission. |
20 |
* |
* |
21 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
22 |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
23 |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
24 |
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
25 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
26 |
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
29 |
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
30 |
* OF SUCH DAMAGE. |
* OF SUCH DAMAGE. |
31 |
* |
* |
32 |
* This file is part of the lwIP TCP/IP stack. |
* This file is part of the lwIP TCP/IP stack. |
33 |
* |
* |
34 |
* Author: Adam Dunkels <adam@sics.se> |
* Author: Adam Dunkels <adam@sics.se> |
35 |
* |
* |
36 |
*/ |
*/ |
75 |
err_t |
err_t |
76 |
tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t copy) |
tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t copy) |
77 |
{ |
{ |
78 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, arg=%p, len=%u, copy=%d)\n", (void *)pcb, arg, len, copy)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, arg=%p, len=%u, copy=%d)\n", (void *)pcb, |
79 |
|
arg, len, (unsigned int)copy)); |
80 |
if (pcb->state == SYN_SENT || |
if (pcb->state == SYN_SENT || |
81 |
pcb->state == SYN_RCVD || |
pcb->state == SYN_RCVD || |
82 |
pcb->state == ESTABLISHED || |
pcb->state == ESTABLISHED || |
103 |
void *ptr; |
void *ptr; |
104 |
u8_t queuelen; |
u8_t queuelen; |
105 |
|
|
106 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%u, flags=%x, copy=%d)\n", (void *)pcb, arg, len, flags, copy)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%u, flags=%x, copy=%u)\n", |
107 |
|
(void *)pcb, arg, len, (unsigned int)flags, (unsigned int)copy)); |
108 |
left = len; |
left = len; |
109 |
ptr = arg; |
ptr = arg; |
110 |
/* fail on too much data */ |
/* fail on too much data */ |
111 |
if (len > pcb->snd_buf) { |
if (len > pcb->snd_buf) { |
112 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%d > snd_buf=%d)\n", len, pcb->snd_buf)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%u > snd_buf=%u)\n", len, pcb->snd_buf)); |
113 |
return ERR_MEM; |
return ERR_MEM; |
114 |
} |
} |
115 |
|
|
118 |
seqno = pcb->snd_lbb; |
seqno = pcb->snd_lbb; |
119 |
|
|
120 |
queue = NULL; |
queue = NULL; |
121 |
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %d\n", pcb->snd_queuelen)); |
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %u\n", (unsigned int)pcb->snd_queuelen)); |
122 |
|
|
123 |
/* Check if the queue length exceeds the configured maximum queue |
/* Check if the queue length exceeds the configured maximum queue |
124 |
* length. If so, we return an error. */ |
* length. If so, we return an error. */ |
125 |
queuelen = pcb->snd_queuelen; |
queuelen = pcb->snd_queuelen; |
126 |
if (queuelen >= TCP_SND_QUEUELEN) { |
if (queuelen >= TCP_SND_QUEUELEN) { |
127 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %d (max %d)\n", queuelen, TCP_SND_QUEUELEN)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %u (max %u)\n", queuelen, TCP_SND_QUEUELEN)); |
128 |
goto memerr; |
goto memerr; |
129 |
} |
} |
130 |
|
|
131 |
if (pcb->snd_queuelen != 0) { |
if (pcb->snd_queuelen != 0) { |
132 |
LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
133 |
pcb->unsent != NULL); |
pcb->unsent != NULL); |
134 |
} |
} |
135 |
|
|
136 |
seg = NULL; |
seg = NULL; |
175 |
} |
} |
176 |
else if (copy) { |
else if (copy) { |
177 |
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { |
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { |
178 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %u\n", seglen)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %u\n", seglen)); |
179 |
goto memerr; |
goto memerr; |
180 |
} |
} |
181 |
++queuelen; |
++queuelen; |
193 |
* instead of PBUF_REF here. |
* instead of PBUF_REF here. |
194 |
*/ |
*/ |
195 |
if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { |
if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { |
196 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); |
197 |
goto memerr; |
goto memerr; |
198 |
} |
} |
199 |
++queuelen; |
++queuelen; |
205 |
/* If allocation fails, we have to deallocate the data pbuf as |
/* If allocation fails, we have to deallocate the data pbuf as |
206 |
* well. */ |
* well. */ |
207 |
pbuf_free(p); |
pbuf_free(p); |
208 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n")); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n")); |
209 |
goto memerr; |
goto memerr; |
210 |
} |
} |
211 |
++queuelen; |
++queuelen; |
219 |
/* Now that there are more segments queued, we check again if the |
/* Now that there are more segments queued, we check again if the |
220 |
length of the queue exceeds the configured maximum. */ |
length of the queue exceeds the configured maximum. */ |
221 |
if (queuelen > TCP_SND_QUEUELEN) { |
if (queuelen > TCP_SND_QUEUELEN) { |
222 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %d (%d)\n", queuelen, TCP_SND_QUEUELEN)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %u (%u)\n", queuelen, TCP_SND_QUEUELEN)); |
223 |
goto memerr; |
goto memerr; |
224 |
} |
} |
225 |
|
|
226 |
seg->len = seglen; |
seg->len = seglen; |
227 |
#if 0 /* Was commented out. TODO: can someone say why this is here? */ |
#if 0 /* Was commented out. TODO: can someone say why this is here? */ |
228 |
if ((flags & TCP_SYN) || (flags & TCP_FIN)) { |
if ((flags & TCP_SYN) || (flags & TCP_FIN)) { |
229 |
++seg->len; |
++seg->len; |
230 |
} |
} |
231 |
#endif |
#endif |
302 |
} |
} |
303 |
memp_free(MEMP_TCP_SEG, queue); |
memp_free(MEMP_TCP_SEG, queue); |
304 |
} |
} |
305 |
else { |
else { |
306 |
if (useg == NULL) { |
if (useg == NULL) { |
307 |
pcb->unsent = queue; |
pcb->unsent = queue; |
308 |
|
|
367 |
if (tcp_input_pcb == pcb) { |
if (tcp_input_pcb == pcb) { |
368 |
return ERR_OK; |
return ERR_OK; |
369 |
} |
} |
370 |
|
|
371 |
wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); |
wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); |
372 |
|
|
373 |
|
|
374 |
seg = pcb->unsent; |
seg = pcb->unsent; |
375 |
|
|
376 |
/* If the TF_ACK_NOW flag is set, we check if there is data that is |
/* If the TF_ACK_NOW flag is set, we check if there is data that is |
388 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); |
389 |
return ERR_BUF; |
return ERR_BUF; |
390 |
} |
} |
391 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %lu\n", pcb->rcv_nxt)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %lu\n", pcb->rcv_nxt)); |
392 |
|
|
393 |
tcphdr = p->payload; |
tcphdr = p->payload; |
394 |
tcphdr->src = htons(pcb->local_port); |
tcphdr->src = htons(pcb->local_port); |
395 |
tcphdr->dest = htons(pcb->remote_port); |
tcphdr->dest = htons(pcb->remote_port); |
399 |
tcphdr->wnd = htons(pcb->rcv_wnd); |
tcphdr->wnd = htons(pcb->rcv_wnd); |
400 |
tcphdr->urgp = 0; |
tcphdr->urgp = 0; |
401 |
TCPH_OFFSET_SET(tcphdr, 5 << 4); |
TCPH_OFFSET_SET(tcphdr, 5 << 4); |
402 |
|
|
403 |
tcphdr->chksum = 0; |
tcphdr->chksum = 0; |
404 |
tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), |
tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), |
405 |
IP_PROTO_TCP, p->tot_len); |
IP_PROTO_TCP, p->tot_len); |
409 |
pbuf_free(p); |
pbuf_free(p); |
410 |
|
|
411 |
return ERR_OK; |
return ERR_OK; |
412 |
} |
} |
413 |
|
|
414 |
#if TCP_OUTPUT_DEBUG |
#if TCP_OUTPUT_DEBUG |
415 |
if (seg == NULL) { |
if (seg == NULL) { |
416 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", pcb->unsent)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", pcb->unsent)); |
428 |
ntohl(seg->tcphdr->seqno), pcb->lastack)); |
ntohl(seg->tcphdr->seqno), pcb->lastack)); |
429 |
} |
} |
430 |
#endif /* TCP_CWND_DEBUG */ |
#endif /* TCP_CWND_DEBUG */ |
431 |
|
|
432 |
while (seg != NULL && |
while (seg != NULL && |
433 |
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { |
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { |
434 |
#if TCP_CWND_DEBUG |
#if TCP_CWND_DEBUG |
441 |
#endif /* TCP_CWND_DEBUG */ |
#endif /* TCP_CWND_DEBUG */ |
442 |
|
|
443 |
pcb->unsent = seg->next; |
pcb->unsent = seg->next; |
444 |
|
|
445 |
if (pcb->state != SYN_SENT) { |
if (pcb->state != SYN_SENT) { |
446 |
TCPH_FLAGS_SET(seg->tcphdr, TCPH_FLAGS(seg->tcphdr) | TCP_ACK); |
TCPH_FLAGS_SET(seg->tcphdr, TCPH_FLAGS(seg->tcphdr) | TCP_ACK); |
447 |
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); |
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); |
448 |
} |
} |
449 |
|
|
450 |
tcp_output_segment(seg, pcb); |
tcp_output_segment(seg, pcb); |
451 |
pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); |
pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); |
452 |
if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) { |
if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) { |
457 |
seg->next = NULL; |
seg->next = NULL; |
458 |
if (pcb->unacked == NULL) { |
if (pcb->unacked == NULL) { |
459 |
pcb->unacked = seg; |
pcb->unacked = seg; |
460 |
|
|
461 |
|
|
462 |
} else { |
} else { |
463 |
for (useg = pcb->unacked; useg->next != NULL; useg = useg->next); |
for (useg = pcb->unacked; useg->next != NULL; useg = useg->next); |
464 |
useg->next = seg; |
useg->next = seg; |
467 |
tcp_seg_free(seg); |
tcp_seg_free(seg); |
468 |
} |
} |
469 |
seg = pcb->unsent; |
seg = pcb->unsent; |
470 |
} |
} |
471 |
return ERR_OK; |
return ERR_OK; |
472 |
} |
} |
473 |
/*-----------------------------------------------------------------------------------*/ |
/*-----------------------------------------------------------------------------------*/ |
500 |
} |
} |
501 |
|
|
502 |
pcb->rtime = 0; |
pcb->rtime = 0; |
503 |
|
|
504 |
if (pcb->rttest == 0) { |
if (pcb->rttest == 0) { |
505 |
pcb->rttest = tcp_ticks; |
pcb->rttest = tcp_ticks; |
506 |
pcb->rtseq = ntohl(seg->tcphdr->seqno); |
pcb->rtseq = ntohl(seg->tcphdr->seqno); |
512 |
seg->len)); |
seg->len)); |
513 |
|
|
514 |
len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); |
len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); |
515 |
|
|
516 |
seg->p->len -= len; |
seg->p->len -= len; |
517 |
seg->p->tot_len -= len; |
seg->p->tot_len -= len; |
518 |
|
|
519 |
seg->p->payload = seg->tcphdr; |
seg->p->payload = seg->tcphdr; |
520 |
|
|
521 |
seg->tcphdr->chksum = 0; |
seg->tcphdr->chksum = 0; |
522 |
seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, |
seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, |
523 |
&(pcb->local_ip), |
&(pcb->local_ip), |
553 |
tcphdr->wnd = htons(TCP_WND); |
tcphdr->wnd = htons(TCP_WND); |
554 |
tcphdr->urgp = 0; |
tcphdr->urgp = 0; |
555 |
TCPH_OFFSET_SET(tcphdr, 5 << 4); |
TCPH_OFFSET_SET(tcphdr, 5 << 4); |
556 |
|
|
557 |
tcphdr->chksum = 0; |
tcphdr->chksum = 0; |
558 |
tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip, |
tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip, |
559 |
IP_PROTO_TCP, p->tot_len); |
IP_PROTO_TCP, p->tot_len); |
574 |
if (pcb->unacked == NULL) { |
if (pcb->unacked == NULL) { |
575 |
return; |
return; |
576 |
} |
} |
577 |
|
|
578 |
/* Move all unacked segments to the unsent queue. */ |
/* Move all unacked segments to the unsent queue. */ |
579 |
for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); |
for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); |
580 |
|
|
581 |
seg->next = pcb->unsent; |
seg->next = pcb->unsent; |
582 |
pcb->unsent = pcb->unacked; |
pcb->unsent = pcb->unacked; |
583 |
|
|
584 |
pcb->unacked = NULL; |
pcb->unacked = NULL; |
585 |
|
|
586 |
|
|
587 |
pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); |
pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); |
588 |
|
|
589 |
++pcb->nrtx; |
++pcb->nrtx; |
590 |
|
|
591 |
/* Don't take any rtt measurements after retransmitting. */ |
/* Don't take any rtt measurements after retransmitting. */ |
592 |
pcb->rttest = 0; |
pcb->rttest = 0; |
593 |
|
|
594 |
/* Do the actual retransmission. */ |
/* Do the actual retransmission. */ |
595 |
tcp_output(pcb); |
tcp_output(pcb); |
596 |
|
|