79 |
{ |
{ |
80 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, arg=%p, len=%u, copy=%d)\n", (void *)pcb, |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, arg=%p, len=%u, copy=%d)\n", (void *)pcb, |
81 |
arg, len, (unsigned int)copy)); |
arg, len, (unsigned int)copy)); |
82 |
if (pcb->state == SYN_SENT || |
/* connection is in valid state for data transmission? */ |
83 |
pcb->state == SYN_RCVD || |
if (pcb->state == ESTABLISHED || |
84 |
pcb->state == ESTABLISHED || |
pcb->state == CLOSE_WAIT || |
85 |
pcb->state == CLOSE_WAIT) { |
pcb->state == SYN_SENT || |
86 |
|
pcb->state == SYN_RCVD) { |
87 |
if (len > 0) { |
if (len > 0) { |
88 |
return tcp_enqueue(pcb, (void *)arg, len, 0, copy, NULL, 0); |
return tcp_enqueue(pcb, (void *)arg, len, 0, copy, NULL, 0); |
89 |
} |
} |
94 |
} |
} |
95 |
} |
} |
96 |
|
|
97 |
|
/** |
98 |
|
* Enqueue data for tranmission |
99 |
|
* |
100 |
|
* @arg pcb Protocol control block for the TCP connection to enqueue data for. |
101 |
|
* @arg arg Pointer to the data to be enqueued for sending. |
102 |
|
* @arg len Data length in bytes |
103 |
|
* @arg flags |
104 |
|
* @arg optdata |
105 |
|
* @arg optlen |
106 |
|
*/ |
107 |
err_t |
err_t |
108 |
tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, |
tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, |
109 |
u8_t flags, u8_t copy, |
u8_t flags, u8_t copy, |
110 |
u8_t *optdata, u8_t optlen) |
u8_t *optdata, u8_t optlen) |
111 |
{ |
{ |
112 |
struct pbuf *p; |
struct pbuf *p; |
113 |
struct tcp_seg *seg, *useg, *queue; |
struct tcp_seg *seg, *useg, *queue; |
118 |
|
|
119 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%u, flags=%x, copy=%u)\n", |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%u, flags=%x, copy=%u)\n", |
120 |
(void *)pcb, arg, len, (unsigned int)flags, (unsigned int)copy)); |
(void *)pcb, arg, len, (unsigned int)flags, (unsigned int)copy)); |
|
left = len; |
|
|
ptr = arg; |
|
121 |
/* fail on too much data */ |
/* fail on too much data */ |
122 |
if (len > pcb->snd_buf) { |
if (len > pcb->snd_buf) { |
123 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%u > snd_buf=%u)\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)); |
124 |
return ERR_MEM; |
return ERR_MEM; |
125 |
} |
} |
126 |
|
left = len; |
127 |
|
ptr = arg; |
128 |
|
|
129 |
/* seqno will be the sequence number of the first segment enqueued |
/* seqno will be the sequence number of the first segment enqueued |
130 |
* by the call to this function. */ |
* by the call to this function. */ |
131 |
seqno = pcb->snd_lbb; |
seqno = pcb->snd_lbb; |
132 |
|
|
|
queue = NULL; |
|
133 |
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %u\n", (unsigned int)pcb->snd_queuelen)); |
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %u\n", (unsigned int)pcb->snd_queuelen)); |
134 |
|
|
135 |
/* Check if the queue length exceeds the configured maximum queue |
/* Check if the queue length exceeds the configured maximum queue |
139 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %u (max %u)\n", queuelen, TCP_SND_QUEUELEN)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %u (max %u)\n", queuelen, TCP_SND_QUEUELEN)); |
140 |
goto memerr; |
goto memerr; |
141 |
} |
} |
142 |
|
if (queuelen != 0) { |
143 |
if (pcb->snd_queuelen != 0) { |
LWIP_ASSERT("tcp_enqueue: queue length non-zero and at least one queue non-empty", |
144 |
LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
pcb->unacked != NULL || pcb->unsent != NULL); |
145 |
pcb->unsent != NULL); |
} else { |
146 |
|
LWIP_ASSERT("tcp_enqueue: queue length zero and queues empty", |
147 |
|
pcb->unacked == NULL && pcb->unsent == NULL); |
148 |
} |
} |
149 |
|
|
|
seg = useg = NULL; |
|
|
seglen = 0; |
|
|
|
|
150 |
/* First, break up the data into segments and tuck them together in |
/* First, break up the data into segments and tuck them together in |
151 |
* the local "queue" variable. */ |
* the local "queue" variable. */ |
152 |
|
useg = queue = seg = NULL; |
153 |
|
seglen = 0; |
154 |
while (queue == NULL || left > 0) { |
while (queue == NULL || left > 0) { |
155 |
|
|
156 |
/* The segment length should be the MSS if the data to be enqueued |
/* The segment length should be the MSS if the data to be enqueued |
166 |
seg->next = NULL; |
seg->next = NULL; |
167 |
seg->p = NULL; |
seg->p = NULL; |
168 |
|
|
169 |
|
/* first segment of to-be-queued data? */ |
170 |
if (queue == NULL) { |
if (queue == NULL) { |
171 |
useg = queue = seg; |
useg = queue = seg; |
172 |
} |
} |
173 |
|
/* subsequent segments of to-be-queued data */ |
174 |
else { |
else { |
175 |
/* Attach the segment to the end of the queued segments. */ |
/* Attach the segment to the end of the queued segments. */ |
176 |
LWIP_ASSERT("useg != NULL", useg != NULL); |
LWIP_ASSERT("useg != NULL", useg != NULL); |
177 |
useg->next = seg; |
useg->next = seg; |
178 |
|
/* remember last segment of to-be-queued data for next iteration */ |
179 |
useg = seg; |
useg = seg; |
180 |
} |
} |
181 |
|
/* { useg == seg } */ |
182 |
|
|
183 |
/* If copy is set, memory should be allocated |
/* If copy is set, memory should be allocated |
184 |
* and data copied into pbuf, otherwise data comes from |
* and data copied into pbuf, otherwise data comes from |
185 |
* ROM or other static memory, and need not be copied. If |
* ROM or other static memory, and need not be copied. If |
186 |
* optdata is != NULL, we have options instead of data. */ |
* optdata is != NULL, we have options instead of data. */ |
187 |
|
|
188 |
|
/* options? */ |
189 |
if (optdata != NULL) { |
if (optdata != NULL) { |
190 |
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { |
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { |
191 |
goto memerr; |
goto memerr; |
193 |
++queuelen; |
++queuelen; |
194 |
seg->dataptr = seg->p->payload; |
seg->dataptr = seg->p->payload; |
195 |
} |
} |
196 |
|
/* copy from volatile memory? */ |
197 |
else if (copy) { |
else if (copy) { |
198 |
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { |
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { |
199 |
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)); |
208 |
/* do not copy data */ |
/* do not copy data */ |
209 |
else { |
else { |
210 |
|
|
211 |
/* first, allocate a pbuf for holding the data. |
/* First, allocate a pbuf for holding the data. |
212 |
* since the referenced data is available at least until it is sent out on the |
* since the referenced data is available at least until it is sent out on the |
213 |
* link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM |
* link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM |
214 |
* instead of PBUF_REF here. |
* instead of PBUF_REF here. |
218 |
goto memerr; |
goto memerr; |
219 |
} |
} |
220 |
++queuelen; |
++queuelen; |
221 |
|
/* reference the non-volatile payload data */ |
222 |
p->payload = ptr; |
p->payload = ptr; |
223 |
seg->dataptr = ptr; |
seg->dataptr = ptr; |
224 |
|
|
233 |
++queuelen; |
++queuelen; |
234 |
|
|
235 |
/* Concatenate the headers and data pbufs together. */ |
/* Concatenate the headers and data pbufs together. */ |
236 |
pbuf_cat(seg->p, p); |
pbuf_cat(seg->p/*header*/, p/*data*/); |
237 |
p = NULL; |
p = NULL; |
238 |
} |
} |
239 |
|
|
245 |
} |
} |
246 |
|
|
247 |
seg->len = seglen; |
seg->len = seglen; |
|
#if 0 /* Was commented out. TODO: can someone say why this is here? */ |
|
|
if ((flags & TCP_SYN) || (flags & TCP_FIN)) { |
|
|
++seg->len; |
|
|
} |
|
|
#endif |
|
|
/* Build TCP header. */ |
|
|
if (pbuf_header(seg->p, TCP_HLEN)) { |
|
248 |
|
|
249 |
|
/* build TCP header */ |
250 |
|
if (pbuf_header(seg->p, TCP_HLEN)) { |
251 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n")); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n")); |
|
|
|
252 |
TCP_STATS_INC(tcp.err); |
TCP_STATS_INC(tcp.err); |
253 |
goto memerr; |
goto memerr; |
254 |
} |
} |
281 |
ptr = (void *)((char *)ptr + seglen); |
ptr = (void *)((char *)ptr + seglen); |
282 |
} |
} |
283 |
|
|
|
|
|
284 |
/* Now that the data to be enqueued has been broken up into TCP |
/* Now that the data to be enqueued has been broken up into TCP |
285 |
segments in the queue variable, we add them to the end of the |
segments in the queue variable, we add them to the end of the |
286 |
pcb->unsent queue. */ |
pcb->unsent queue. */ |
290 |
else { |
else { |
291 |
for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); |
for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); |
292 |
} |
} |
293 |
|
/* { useg is last segment on the unsent queue, NULL if list is empty } */ |
294 |
|
|
295 |
/* If there is room in the last pbuf on the unsent queue, |
/* If there is room in the last pbuf on the unsent queue, |
296 |
chain the first pbuf on the queue together with that. */ |
chain the first pbuf on the queue together with that. */ |
298 |
TCP_TCPLEN(useg) != 0 && |
TCP_TCPLEN(useg) != 0 && |
299 |
!(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && |
!(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && |
300 |
!(flags & (TCP_SYN | TCP_FIN)) && |
!(flags & (TCP_SYN | TCP_FIN)) && |
301 |
|
/* fit within max seg size */ |
302 |
useg->len + queue->len <= pcb->mss) { |
useg->len + queue->len <= pcb->mss) { |
303 |
/* Remove TCP header from first segment. */ |
/* Remove TCP header from first segment of our to-be-queued list */ |
304 |
pbuf_header(queue->p, -TCP_HLEN); |
pbuf_header(queue->p, -TCP_HLEN); |
305 |
pbuf_cat(useg->p, queue->p); |
pbuf_cat(useg->p, queue->p); |
306 |
useg->len += queue->len; |
useg->len += queue->len; |
307 |
useg->next = queue->next; |
useg->next = queue->next; |
308 |
|
|
309 |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE | DBG_STATE, ("tcp_enqueue: chaining, new len %u\n", useg->len)); |
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE | DBG_STATE, ("tcp_enqueue: chaining segments, new len %u\n", useg->len)); |
310 |
if (seg == queue) { |
if (seg == queue) { |
311 |
seg = NULL; |
seg = NULL; |
312 |
} |
} |
313 |
memp_free(MEMP_TCP_SEG, queue); |
memp_free(MEMP_TCP_SEG, queue); |
314 |
} |
} |
315 |
else { |
else { |
316 |
|
/* empty list */ |
317 |
if (useg == NULL) { |
if (useg == NULL) { |
318 |
|
/* initialize list with this segment */ |
319 |
pcb->unsent = queue; |
pcb->unsent = queue; |
|
|
|
320 |
} |
} |
321 |
|
/* enqueue segment */ |
322 |
else { |
else { |
323 |
useg->next = queue; |
useg->next = queue; |
324 |
} |
} |
331 |
pcb->snd_queuelen = queuelen; |
pcb->snd_queuelen = queuelen; |
332 |
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (after enqueued)\n", pcb->snd_queuelen)); |
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (after enqueued)\n", pcb->snd_queuelen)); |
333 |
if (pcb->snd_queuelen != 0) { |
if (pcb->snd_queuelen != 0) { |
334 |
LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
LWIP_ASSERT("tcp_enqueue: valid queue length", |
335 |
pcb->unsent != NULL); |
pcb->unacked != NULL || pcb->unsent != NULL); |
|
|
|
336 |
} |
} |
337 |
|
|
338 |
/* Set the PSH flag in the last segment that we enqueued, but only |
/* Set the PSH flag in the last segment that we enqueued, but only |
351 |
if (pcb->snd_queuelen != 0) { |
if (pcb->snd_queuelen != 0) { |
352 |
LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || |
353 |
pcb->unsent != NULL); |
pcb->unsent != NULL); |
|
|
|
354 |
} |
} |
355 |
LWIP_DEBUGF(TCP_QLEN_DEBUG | DBG_STATE, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen)); |
LWIP_DEBUGF(TCP_QLEN_DEBUG | DBG_STATE, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen)); |
356 |
return ERR_MEM; |
return ERR_MEM; |
378 |
|
|
379 |
wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); |
wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); |
380 |
|
|
|
|
|
381 |
seg = pcb->unsent; |
seg = pcb->unsent; |
382 |
|
|
383 |
/* useg should point to last segment on unacked queue */ |
/* useg should point to last segment on unacked queue */ |
385 |
if (useg != NULL) { |
if (useg != NULL) { |
386 |
for (; useg->next != NULL; useg = useg->next); |
for (; useg->next != NULL; useg = useg->next); |
387 |
} |
} |
|
|
|
388 |
|
|
389 |
/* If the TF_ACK_NOW flag is set, we check if there is data that is |
/* If the TF_ACK_NOW flag is set and no data will be sent (either |
390 |
to be sent. If data is to be sent out, we'll just piggyback our |
* because the ->unsent queue is empty or because the window does |
391 |
acknowledgement with the outgoing segment. If no data will be |
* not allow it), construct an empty ACK segment and send it. |
392 |
sent (either because the ->unsent queue is empty or because the |
* |
393 |
window doesn't allow it) we'll have to construct an empty ACK |
* If data is to be sent, we will just piggyback the ACK (see below). |
394 |
segment and send it. */ |
*/ |
395 |
if (pcb->flags & TF_ACK_NOW && |
if (pcb->flags & TF_ACK_NOW && |
396 |
(seg == NULL || |
(seg == NULL || |
397 |
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { |
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { |
|
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); |
|
398 |
p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); |
p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); |
399 |
if (p == NULL) { |
if (p == NULL) { |
400 |
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")); |
401 |
return ERR_BUF; |
return ERR_BUF; |
402 |
} |
} |
403 |
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)); |
404 |
|
/* remove ACK flags from the PCB, as we send an empty ACK now */ |
405 |
|
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); |
406 |
|
|
407 |
tcphdr = p->payload; |
tcphdr = p->payload; |
408 |
tcphdr->src = htons(pcb->local_port); |
tcphdr->src = htons(pcb->local_port); |
419 |
tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), |
tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), |
420 |
IP_PROTO_TCP, p->tot_len); |
IP_PROTO_TCP, p->tot_len); |
421 |
#endif |
#endif |
|
|
|
422 |
ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, |
ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, |
423 |
IP_PROTO_TCP); |
IP_PROTO_TCP); |
424 |
pbuf_free(p); |
pbuf_free(p); |
443 |
ntohl(seg->tcphdr->seqno), pcb->lastack)); |
ntohl(seg->tcphdr->seqno), pcb->lastack)); |
444 |
} |
} |
445 |
#endif /* TCP_CWND_DEBUG */ |
#endif /* TCP_CWND_DEBUG */ |
446 |
|
/* data available and window allows it to be sent? */ |
447 |
while (seg != NULL && |
while (seg != NULL && |
448 |
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { |
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { |
449 |
#if TCP_CWND_DEBUG |
#if TCP_CWND_DEBUG |
470 |
/* put segment on unacknowledged list if length > 0 */ |
/* put segment on unacknowledged list if length > 0 */ |
471 |
if (TCP_TCPLEN(seg) > 0) { |
if (TCP_TCPLEN(seg) > 0) { |
472 |
seg->next = NULL; |
seg->next = NULL; |
473 |
|
/* unacked list is empty? */ |
474 |
if (pcb->unacked == NULL) { |
if (pcb->unacked == NULL) { |
475 |
pcb->unacked = seg; |
pcb->unacked = seg; |
476 |
useg = seg; |
useg = seg; |
477 |
|
/* unacked list is not empty? */ |
478 |
} else { |
} else { |
479 |
/* In the case of fast retransmit, the packet should not go to the end |
/* In the case of fast retransmit, the packet should not go to the tail |
480 |
* of the unacked queue, but rather at the start. We need to check for |
* of the unacked queue, but rather at the head. We need to check for |
481 |
* this case. -STJ Jul 27, 2004 */ |
* this case. -STJ Jul 27, 2004 */ |
482 |
if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){ |
if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){ |
483 |
|
/* add segment to head of unacked list */ |
484 |
seg->next = pcb->unacked; |
seg->next = pcb->unacked; |
485 |
pcb->unacked = seg; |
pcb->unacked = seg; |
486 |
} else { |
} else { |
487 |
|
/* add segment to tail of unacked list */ |
488 |
useg->next = seg; |
useg->next = seg; |
489 |
useg = useg->next; |
useg = useg->next; |
490 |
} |
} |
491 |
} |
} |
492 |
|
/* do not queue empty segments on the unacked list */ |
493 |
} else { |
} else { |
494 |
tcp_seg_free(seg); |
tcp_seg_free(seg); |
495 |
} |
} |
498 |
return ERR_OK; |
return ERR_OK; |
499 |
} |
} |
500 |
|
|
501 |
|
/** |
502 |
|
* Actually send a TCP segment over IP |
503 |
|
*/ |
504 |
static void |
static void |
505 |
tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) |
tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) |
506 |
{ |
{ |
596 |
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %lu ackno %lu.\n", seqno, ackno)); |
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %lu ackno %lu.\n", seqno, ackno)); |
597 |
} |
} |
598 |
|
|
599 |
|
/* requeue all unacked segments for retransmission */ |
600 |
void |
void |
601 |
tcp_rexmit_rto(struct tcp_pcb *pcb) |
tcp_rexmit_rto(struct tcp_pcb *pcb) |
602 |
{ |
{ |
606 |
return; |
return; |
607 |
} |
} |
608 |
|
|
609 |
/* Move all unacked segments to the unsent queue. */ |
/* Move all unacked segments to the head of the unsent queue */ |
610 |
for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); |
for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); |
611 |
|
/* concatenate unsent queue after unacked queue */ |
612 |
seg->next = pcb->unsent; |
seg->next = pcb->unsent; |
613 |
|
/* unsent queue is the concatenated queue (of unacked, unsent) */ |
614 |
pcb->unsent = pcb->unacked; |
pcb->unsent = pcb->unacked; |
615 |
|
/* unacked queue is now empty */ |
616 |
pcb->unacked = NULL; |
pcb->unacked = NULL; |
617 |
|
|
618 |
pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); |
pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); |
619 |
|
/* increment number of retransmissions */ |
620 |
++pcb->nrtx; |
++pcb->nrtx; |
621 |
|
|
622 |
/* Don't take any rtt measurements after retransmitting. */ |
/* Don't take any RTT measurements after retransmitting. */ |
623 |
pcb->rttest = 0; |
pcb->rttest = 0; |
624 |
|
|
625 |
/* Do the actual retransmission. */ |
/* Do the actual retransmission */ |
626 |
tcp_output(pcb); |
tcp_output(pcb); |
|
|
|
627 |
} |
} |
628 |
|
|
629 |
void |
void |