Port of LwIP performed by Ralf in 2010. Not recommended for use with recent mbed libraries, but good demos of raw LwIP possible
Dependents: LwIP_raw_API_serverExample tiny-dtls
tcp_out.c
00001 /** 00002 * @file 00003 * Transmission Control Protocol, outgoing traffic 00004 * 00005 * The output functions of TCP. 00006 * 00007 */ 00008 00009 /* 00010 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 00011 * All rights reserved. 00012 * 00013 * Redistribution and use in source and binary forms, with or without modification, 00014 * are permitted provided that the following conditions are met: 00015 * 00016 * 1. Redistributions of source code must retain the above copyright notice, 00017 * this list of conditions and the following disclaimer. 00018 * 2. Redistributions in binary form must reproduce the above copyright notice, 00019 * this list of conditions and the following disclaimer in the documentation 00020 * and/or other materials provided with the distribution. 00021 * 3. The name of the author may not be used to endorse or promote products 00022 * derived from this software without specific prior written permission. 00023 * 00024 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 00025 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 00026 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 00027 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00028 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 00029 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00030 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00031 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 00032 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 00033 * OF SUCH DAMAGE. 00034 * 00035 * This file is part of the lwIP TCP/IP stack. 00036 * 00037 * Author: Adam Dunkels <adam@sics.se> 00038 * 00039 */ 00040 00041 #include "lwip/opt.h" 00042 00043 #if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ 00044 00045 #include "lwip/tcp.h" 00046 #include "lwip/def.h" 00047 #include "lwip/mem.h" 00048 #include "lwip/memp.h" 00049 #include "lwip/sys.h" 00050 #include "lwip/ip_addr.h" 00051 #include "lwip/netif.h" 00052 #include "lwip/inet.h" 00053 #include "lwip/inet_chksum.h" 00054 #include "lwip/stats.h" 00055 #include "lwip/snmp.h" 00056 00057 #include <string.h> 00058 00059 /* Forward declarations.*/ 00060 static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); 00061 00062 static struct tcp_hdr * 00063 tcp_output_set_header(struct tcp_pcb *pcb, struct pbuf *p, int optlen, 00064 u32_t seqno_be /* already in network byte order */) 00065 { 00066 struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload; 00067 tcphdr->src = htons(pcb->local_port); 00068 tcphdr->dest = htons(pcb->remote_port); 00069 tcphdr->seqno = seqno_be; 00070 tcphdr->ackno = htonl(pcb->rcv_nxt); 00071 TCPH_FLAGS_SET(tcphdr, TCP_ACK); 00072 tcphdr->wnd = htons(pcb->rcv_ann_wnd); 00073 tcphdr->urgp = 0; 00074 TCPH_HDRLEN_SET(tcphdr, (5 + optlen / 4)); 00075 tcphdr->chksum = 0; 00076 00077 /* If we're sending a packet, update the announced right window edge */ 00078 pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; 00079 00080 return tcphdr; 00081 } 00082 00083 /** 00084 * Called by tcp_close() to send a segment including flags but not data. 00085 * 00086 * @param pcb the tcp_pcb over which to send a segment 00087 * @param flags the flags to set in the segment header 00088 * @return ERR_OK if sent, another err_t otherwise 00089 */ 00090 err_t 00091 tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags) 00092 { 00093 /* no data, no length, flags, copy=1, no optdata */ 00094 return tcp_enqueue(pcb, NULL, 0, flags, TCP_WRITE_FLAG_COPY, 0); 00095 } 00096 00097 /** 00098 * Write data for sending (but does not send it immediately). 00099 * 00100 * It waits in the expectation of more data being sent soon (as 00101 * it can send them more efficiently by combining them together). 00102 * To prompt the system to send data now, call tcp_output() after 00103 * calling tcp_write(). 00104 * 00105 * @param pcb Protocol control block of the TCP connection to enqueue data for. 00106 * @param data pointer to the data to send 00107 * @param len length (in bytes) of the data to send 00108 * @param apiflags combination of following flags : 00109 * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack 00110 * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, 00111 * @return ERR_OK if enqueued, another err_t on error 00112 * 00113 * @see tcp_write() 00114 */ 00115 err_t 00116 tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags) 00117 { 00118 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", (void *)pcb, 00119 data, len, (u16_t)apiflags)); 00120 /* connection is in valid state for data transmission? */ 00121 if (pcb->state == ESTABLISHED || 00122 pcb->state == CLOSE_WAIT || 00123 pcb->state == SYN_SENT || 00124 pcb->state == SYN_RCVD) { 00125 if (len > 0) { 00126 #if LWIP_TCP_TIMESTAMPS 00127 return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, 00128 pcb->flags & TF_TIMESTAMP ? TF_SEG_OPTS_TS : 0); 00129 #else 00130 return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, 0); 00131 #endif 00132 } 00133 return ERR_OK; 00134 } else { 00135 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | 3, ("tcp_write() called in invalid state\n")); 00136 return ERR_CONN; 00137 } 00138 } 00139 00140 /** 00141 * Enqueue data and/or TCP options for transmission 00142 * 00143 * Called by tcp_connect(), tcp_listen_input(), tcp_send_ctrl() and tcp_write(). 00144 * 00145 * @param pcb Protocol control block for the TCP connection to enqueue data for. 00146 * @param arg Pointer to the data to be enqueued for sending. 00147 * @param len Data length in bytes 00148 * @param flags tcp header flags to set in the outgoing segment 00149 * @param apiflags combination of following flags : 00150 * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack 00151 * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, 00152 * @param optflags options to include in segment later on (see definition of struct tcp_seg) 00153 */ 00154 err_t 00155 tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, 00156 u8_t flags, u8_t apiflags, u8_t optflags) 00157 { 00158 struct pbuf *p; 00159 struct tcp_seg *seg, *useg, *queue; 00160 u32_t seqno; 00161 u16_t left, seglen; 00162 void *ptr; 00163 u16_t queuelen; 00164 u8_t optlen; 00165 00166 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, 00167 ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", apiflags=%"U16_F")\n", 00168 (void *)pcb, arg, len, (u16_t)flags, (u16_t)apiflags)); 00169 LWIP_ERROR("tcp_enqueue: packet needs payload, options, or SYN/FIN (programmer violates API)", 00170 ((len != 0) || (optflags != 0) || ((flags & (TCP_SYN | TCP_FIN)) != 0)), 00171 return ERR_ARG;); 00172 LWIP_ERROR("tcp_enqueue: len != 0 || arg == NULL (programmer violates API)", 00173 ((len != 0) || (arg == NULL)), return ERR_ARG;); 00174 00175 /* fail on too much data */ 00176 if (len > pcb->snd_buf) { 00177 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf)); 00178 pcb->flags |= TF_NAGLEMEMERR; 00179 return ERR_MEM; 00180 } 00181 left = len; 00182 ptr = arg; 00183 00184 optlen = LWIP_TCP_OPT_LENGTH(optflags); 00185 00186 /* seqno will be the sequence number of the first segment enqueued 00187 * by the call to this function. */ 00188 seqno = pcb->snd_lbb; 00189 00190 LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); 00191 00192 /* If total number of pbufs on the unsent/unacked queues exceeds the 00193 * configured maximum, return an error */ 00194 queuelen = pcb->snd_queuelen; 00195 /* check for configured max queuelen and possible overflow */ 00196 if ((queuelen >= TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { 00197 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %"U16_F" (max %"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); 00198 TCP_STATS_INC(tcp.memerr); 00199 pcb->flags |= TF_NAGLEMEMERR; 00200 return ERR_MEM; 00201 } 00202 if (queuelen != 0) { 00203 LWIP_ASSERT("tcp_enqueue: pbufs on queue => at least one queue non-empty", 00204 pcb->unacked != NULL || pcb->unsent != NULL); 00205 } else { 00206 LWIP_ASSERT("tcp_enqueue: no pbufs on queue => both queues empty", 00207 pcb->unacked == NULL && pcb->unsent == NULL); 00208 } 00209 00210 /* First, break up the data into segments and tuck them together in 00211 * the local "queue" variable. */ 00212 useg = queue = seg = NULL; 00213 seglen = 0; 00214 while (queue == NULL || left > 0) { 00215 /* The segment length (including options) should be at most the MSS */ 00216 seglen = left > (pcb->mss - optlen) ? (pcb->mss - optlen) : left; 00217 00218 /* Allocate memory for tcp_seg, and fill in fields. */ 00219 seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG); 00220 if (seg == NULL) { 00221 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, 00222 ("tcp_enqueue: could not allocate memory for tcp_seg\n")); 00223 goto memerr; 00224 } 00225 seg->next = NULL; 00226 seg->p = NULL; 00227 00228 /* first segment of to-be-queued data? */ 00229 if (queue == NULL) { 00230 queue = seg; 00231 } 00232 /* subsequent segments of to-be-queued data */ 00233 else { 00234 /* Attach the segment to the end of the queued segments */ 00235 LWIP_ASSERT("useg != NULL", useg != NULL); 00236 useg->next = seg; 00237 } 00238 /* remember last segment of to-be-queued data for next iteration */ 00239 useg = seg; 00240 00241 /* If copy is set, memory should be allocated 00242 * and data copied into pbuf, otherwise data comes from 00243 * ROM or other static memory, and need not be copied. */ 00244 if (apiflags & TCP_WRITE_FLAG_COPY) { 00245 if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen + optlen, PBUF_RAM)) == NULL) { 00246 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, 00247 ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); 00248 goto memerr; 00249 } 00250 LWIP_ASSERT("check that first pbuf can hold the complete seglen", 00251 (seg->p->len >= seglen + optlen)); 00252 queuelen += pbuf_clen(seg->p); 00253 if (arg != NULL) { 00254 MEMCPY((char *)seg->p->payload + optlen, ptr, seglen); 00255 } 00256 seg->dataptr = seg->p->payload; 00257 } 00258 /* do not copy data */ 00259 else { 00260 /* First, allocate a pbuf for the headers. */ 00261 if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { 00262 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, 00263 ("tcp_enqueue: could not allocate memory for header pbuf\n")); 00264 goto memerr; 00265 } 00266 queuelen += pbuf_clen(seg->p); 00267 00268 /* Second, allocate a pbuf for holding the data. 00269 * since the referenced data is available at least until it is sent out on the 00270 * link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM 00271 * instead of PBUF_REF here. 00272 */ 00273 if (left > 0) { 00274 if ((p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { 00275 /* If allocation fails, we have to deallocate the header pbuf as well. */ 00276 pbuf_free(seg->p); 00277 seg->p = NULL; 00278 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, 00279 ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); 00280 goto memerr; 00281 } 00282 ++queuelen; 00283 /* reference the non-volatile payload data */ 00284 p->payload = ptr; 00285 seg->dataptr = ptr; 00286 00287 /* Concatenate the headers and data pbufs together. */ 00288 pbuf_cat(seg->p/*header*/, p/*data*/); 00289 p = NULL; 00290 } 00291 } 00292 00293 /* Now that there are more segments queued, we check again if the 00294 length of the queue exceeds the configured maximum or overflows. */ 00295 if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { 00296 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); 00297 goto memerr; 00298 } 00299 00300 seg->len = seglen; 00301 00302 /* build TCP header */ 00303 if (pbuf_header(seg->p, TCP_HLEN)) { 00304 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n")); 00305 TCP_STATS_INC(tcp.err); 00306 goto memerr; 00307 } 00308 seg->tcphdr = (struct tcp_hdr *)seg->p->payload; 00309 seg->tcphdr->src = htons(pcb->local_port); 00310 seg->tcphdr->dest = htons(pcb->remote_port); 00311 seg->tcphdr->seqno = htonl(seqno); 00312 seg->tcphdr->urgp = 0; 00313 TCPH_FLAGS_SET(seg->tcphdr, flags); 00314 /* don't fill in tcphdr->ackno and tcphdr->wnd until later */ 00315 00316 seg->flags = optflags; 00317 00318 /* Set the length of the header */ 00319 TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4)); 00320 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", 00321 ntohl(seg->tcphdr->seqno), 00322 ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), 00323 (u16_t)flags)); 00324 00325 left -= seglen; 00326 seqno += seglen; 00327 ptr = (void *)((u8_t *)ptr + seglen); 00328 } 00329 00330 /* Now that the data to be enqueued has been broken up into TCP 00331 segments in the queue variable, we add them to the end of the 00332 pcb->unsent queue. */ 00333 if (pcb->unsent == NULL) { 00334 useg = NULL; 00335 } 00336 else { 00337 for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); 00338 } 00339 /* { useg is last segment on the unsent queue, NULL if list is empty } */ 00340 00341 /* If there is room in the last pbuf on the unsent queue, 00342 chain the first pbuf on the queue together with that. */ 00343 if (useg != NULL && 00344 TCP_TCPLEN(useg) != 0 && 00345 !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && 00346 !(flags & (TCP_SYN | TCP_FIN)) && 00347 /* fit within max seg size */ 00348 (useg->len + queue->len <= pcb->mss) && 00349 /* only concatenate segments with the same options */ 00350 (useg->flags == queue->flags) && 00351 /* segments are consecutive */ 00352 (ntohl(useg->tcphdr->seqno) + useg->len == ntohl(queue->tcphdr->seqno)) ) { 00353 /* Remove TCP header from first segment of our to-be-queued list */ 00354 if(pbuf_header(queue->p, -(TCP_HLEN + optlen))) { 00355 /* Can we cope with this failing? Just assert for now */ 00356 LWIP_ASSERT("pbuf_header failed\n", 0); 00357 TCP_STATS_INC(tcp.err); 00358 goto memerr; 00359 } 00360 if (queue->p->len == 0) { 00361 /* free the first (header-only) pbuf if it is now empty (contained only headers) */ 00362 struct pbuf *old_q = queue->p; 00363 queue->p = queue->p->next; 00364 old_q->next = NULL; 00365 queuelen--; 00366 pbuf_free(old_q); 00367 } 00368 LWIP_ASSERT("zero-length pbuf", (queue->p != NULL) && (queue->p->len > 0)); 00369 pbuf_cat(useg->p, queue->p); 00370 useg->len += queue->len; 00371 useg->next = queue->next; 00372 00373 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len)); 00374 if (seg == queue) { 00375 seg = useg; 00376 seglen = useg->len; 00377 } 00378 memp_free(MEMP_TCP_SEG, queue); 00379 } 00380 else { 00381 /* empty list */ 00382 if (useg == NULL) { 00383 /* initialize list with this segment */ 00384 pcb->unsent = queue; 00385 } 00386 /* enqueue segment */ 00387 else { 00388 useg->next = queue; 00389 } 00390 } 00391 if ((flags & TCP_SYN) || (flags & TCP_FIN)) { 00392 ++len; 00393 } 00394 if (flags & TCP_FIN) { 00395 pcb->flags |= TF_FIN; 00396 } 00397 pcb->snd_lbb += len; 00398 00399 pcb->snd_buf -= len; 00400 00401 /* update number of segments on the queues */ 00402 pcb->snd_queuelen = queuelen; 00403 LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); 00404 if (pcb->snd_queuelen != 0) { 00405 LWIP_ASSERT("tcp_enqueue: valid queue length", 00406 pcb->unacked != NULL || pcb->unsent != NULL); 00407 } 00408 00409 /* Set the PSH flag in the last segment that we enqueued, but only 00410 if the segment has data (indicated by seglen > 0). */ 00411 if (seg != NULL && seglen > 0 && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { 00412 TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); 00413 } 00414 00415 return ERR_OK; 00416 memerr: 00417 pcb->flags |= TF_NAGLEMEMERR; 00418 TCP_STATS_INC(tcp.memerr); 00419 00420 if (queue != NULL) { 00421 tcp_segs_free(queue); 00422 } 00423 if (pcb->snd_queuelen != 0) { 00424 LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || 00425 pcb->unsent != NULL); 00426 } 00427 LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); 00428 return ERR_MEM; 00429 } 00430 00431 00432 #if LWIP_TCP_TIMESTAMPS 00433 /* Build a timestamp option (12 bytes long) at the specified options pointer) 00434 * 00435 * @param pcb tcp_pcb 00436 * @param opts option pointer where to store the timestamp option 00437 */ 00438 static void 00439 tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts) 00440 { 00441 /* Pad with two NOP options to make everything nicely aligned */ 00442 opts[0] = htonl(0x0101080A); 00443 opts[1] = htonl(sys_now()); 00444 opts[2] = htonl(pcb->ts_recent); 00445 } 00446 #endif 00447 00448 00449 /** 00450 * Find out what we can send and send it 00451 * 00452 * @param pcb Protocol control block for the TCP connection to send data 00453 * @return ERR_OK if data has been sent or nothing to send 00454 * another err_t on error 00455 */ 00456 err_t 00457 tcp_output(struct tcp_pcb *pcb) 00458 { 00459 struct pbuf *p; 00460 struct tcp_hdr *tcphdr; 00461 struct tcp_seg *seg, *useg; 00462 u32_t wnd, snd_nxt; 00463 #if TCP_CWND_DEBUG 00464 s16_t i = 0; 00465 #endif /* TCP_CWND_DEBUG */ 00466 u8_t optlen = 0; 00467 00468 /* First, check if we are invoked by the TCP input processing 00469 code. If so, we do not output anything. Instead, we rely on the 00470 input processing code to call us when input processing is done 00471 with. */ 00472 if (tcp_input_pcb == pcb) { 00473 return ERR_OK; 00474 } 00475 00476 wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); 00477 00478 seg = pcb->unsent; 00479 00480 /* useg should point to last segment on unacked queue */ 00481 useg = pcb->unacked; 00482 if (useg != NULL) { 00483 for (; useg->next != NULL; useg = useg->next); 00484 } 00485 00486 /* If the TF_ACK_NOW flag is set and no data will be sent (either 00487 * because the ->unsent queue is empty or because the window does 00488 * not allow it), construct an empty ACK segment and send it. 00489 * 00490 * If data is to be sent, we will just piggyback the ACK (see below). 00491 */ 00492 if (pcb->flags & TF_ACK_NOW && 00493 (seg == NULL || 00494 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { 00495 #if LWIP_TCP_TIMESTAMPS 00496 if (pcb->flags & TF_TIMESTAMP) 00497 optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); 00498 #endif 00499 p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen, PBUF_RAM); 00500 if (p == NULL) { 00501 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); 00502 return ERR_BUF; 00503 } 00504 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, 00505 ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); 00506 /* remove ACK flags from the PCB, as we send an empty ACK now */ 00507 pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); 00508 00509 tcphdr = tcp_output_set_header(pcb, p, optlen, htonl(pcb->snd_nxt)); 00510 00511 /* NB. MSS option is only sent on SYNs, so ignore it here */ 00512 #if LWIP_TCP_TIMESTAMPS 00513 pcb->ts_lastacksent = pcb->rcv_nxt; 00514 00515 if (pcb->flags & TF_TIMESTAMP) 00516 tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1)); 00517 #endif 00518 00519 #if CHECKSUM_GEN_TCP 00520 tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), 00521 IP_PROTO_TCP, p->tot_len); 00522 #endif 00523 #if LWIP_NETIF_HWADDRHINT 00524 ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, 00525 IP_PROTO_TCP, &(pcb->addr_hint)); 00526 #else /* LWIP_NETIF_HWADDRHINT*/ 00527 ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, 00528 IP_PROTO_TCP); 00529 #endif /* LWIP_NETIF_HWADDRHINT*/ 00530 pbuf_free(p); 00531 00532 return ERR_OK; 00533 } 00534 00535 #if TCP_OUTPUT_DEBUG 00536 if (seg == NULL) { 00537 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", 00538 (void*)pcb->unsent)); 00539 } 00540 #endif /* TCP_OUTPUT_DEBUG */ 00541 #if TCP_CWND_DEBUG 00542 if (seg == NULL) { 00543 LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F 00544 ", cwnd %"U16_F", wnd %"U32_F 00545 ", seg == NULL, ack %"U32_F"\n", 00546 pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); 00547 } else { 00548 LWIP_DEBUGF(TCP_CWND_DEBUG, 00549 ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F 00550 ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", 00551 pcb->snd_wnd, pcb->cwnd, wnd, 00552 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, 00553 ntohl(seg->tcphdr->seqno), pcb->lastack)); 00554 } 00555 #endif /* TCP_CWND_DEBUG */ 00556 /* data available and window allows it to be sent? */ 00557 while (seg != NULL && 00558 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { 00559 LWIP_ASSERT("RST not expected here!", 00560 (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); 00561 /* Stop sending if the nagle algorithm would prevent it 00562 * Don't stop: 00563 * - if tcp_enqueue had a memory error before (prevent delayed ACK timeout) or 00564 * - if FIN was already enqueued for this PCB (SYN is always alone in a segment - 00565 * either seg->next != NULL or pcb->unacked == NULL; 00566 * RST is no sent using tcp_enqueue/tcp_output. 00567 */ 00568 if((tcp_do_output_nagle(pcb) == 0) && 00569 ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){ 00570 break; 00571 } 00572 #if TCP_CWND_DEBUG 00573 LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", 00574 pcb->snd_wnd, pcb->cwnd, wnd, 00575 ntohl(seg->tcphdr->seqno) + seg->len - 00576 pcb->lastack, 00577 ntohl(seg->tcphdr->seqno), pcb->lastack, i)); 00578 ++i; 00579 #endif /* TCP_CWND_DEBUG */ 00580 00581 pcb->unsent = seg->next; 00582 00583 if (pcb->state != SYN_SENT) { 00584 TCPH_SET_FLAG(seg->tcphdr, TCP_ACK); 00585 pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); 00586 } 00587 00588 tcp_output_segment(seg, pcb); 00589 snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); 00590 if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { 00591 pcb->snd_nxt = snd_nxt; 00592 } 00593 /* put segment on unacknowledged list if length > 0 */ 00594 if (TCP_TCPLEN(seg) > 0) { 00595 seg->next = NULL; 00596 /* unacked list is empty? */ 00597 if (pcb->unacked == NULL) { 00598 pcb->unacked = seg; 00599 useg = seg; 00600 /* unacked list is not empty? */ 00601 } else { 00602 /* In the case of fast retransmit, the packet should not go to the tail 00603 * of the unacked queue, but rather somewhere before it. We need to check for 00604 * this case. -STJ Jul 27, 2004 */ 00605 if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){ 00606 /* add segment to before tail of unacked list, keeping the list sorted */ 00607 struct tcp_seg **cur_seg = &(pcb->unacked); 00608 while (*cur_seg && 00609 TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { 00610 cur_seg = &((*cur_seg)->next ); 00611 } 00612 seg->next = (*cur_seg); 00613 (*cur_seg) = seg; 00614 } else { 00615 /* add segment to tail of unacked list */ 00616 useg->next = seg; 00617 useg = useg->next; 00618 } 00619 } 00620 /* do not queue empty segments on the unacked list */ 00621 } else { 00622 tcp_seg_free(seg); 00623 } 00624 seg = pcb->unsent; 00625 } 00626 00627 if (seg != NULL && pcb->persist_backoff == 0 && 00628 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) { 00629 /* prepare for persist timer */ 00630 pcb->persist_cnt = 0; 00631 pcb->persist_backoff = 1; 00632 } 00633 00634 pcb->flags &= ~TF_NAGLEMEMERR; 00635 return ERR_OK; 00636 } 00637 00638 /** 00639 * Called by tcp_output() to actually send a TCP segment over IP. 00640 * 00641 * @param seg the tcp_seg to send 00642 * @param pcb the tcp_pcb for the TCP connection used to send the segment 00643 */ 00644 static void 00645 tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) 00646 { 00647 u16_t len; 00648 struct netif *netif; 00649 u32_t *opts; 00650 00651 /** @bug Exclude retransmitted segments from this count. */ 00652 snmp_inc_tcpoutsegs(); 00653 00654 /* The TCP header has already been constructed, but the ackno and 00655 wnd fields remain. */ 00656 seg->tcphdr->ackno = htonl(pcb->rcv_nxt); 00657 00658 /* advertise our receive window size in this TCP segment */ 00659 seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); 00660 00661 pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; 00662 00663 /* Add any requested options. NB MSS option is only set on SYN 00664 packets, so ignore it here */ 00665 opts = (u32_t *)(seg->tcphdr + 1); 00666 if (seg->flags & TF_SEG_OPTS_MSS) { 00667 TCP_BUILD_MSS_OPTION(*opts); 00668 opts += 1; 00669 } 00670 #if LWIP_TCP_TIMESTAMPS 00671 pcb->ts_lastacksent = pcb->rcv_nxt; 00672 00673 if (seg->flags & TF_SEG_OPTS_TS) { 00674 tcp_build_timestamp_option(pcb, opts); 00675 opts += 3; 00676 } 00677 #endif 00678 00679 /* If we don't have a local IP address, we get one by 00680 calling ip_route(). */ 00681 if (ip_addr_isany(&(pcb->local_ip))) { 00682 netif = ip_route(&(pcb->remote_ip)); 00683 if (netif == NULL) { 00684 return; 00685 } 00686 ip_addr_set(&(pcb->local_ip), &(netif->ip_addr)); 00687 } 00688 00689 /* Set retransmission timer running if it is not currently enabled */ 00690 if(pcb->rtime == -1) 00691 pcb->rtime = 0; 00692 00693 if (pcb->rttest == 0) { 00694 pcb->rttest = tcp_ticks; 00695 pcb->rtseq = ntohl(seg->tcphdr->seqno); 00696 00697 LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); 00698 } 00699 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", 00700 htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + 00701 seg->len)); 00702 00703 len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); 00704 00705 seg->p->len -= len; 00706 seg->p->tot_len -= len; 00707 00708 seg->p->payload = seg->tcphdr; 00709 00710 seg->tcphdr->chksum = 0; 00711 #if CHECKSUM_GEN_TCP 00712 seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, 00713 &(pcb->local_ip), 00714 &(pcb->remote_ip), 00715 IP_PROTO_TCP, seg->p->tot_len); 00716 #endif 00717 TCP_STATS_INC(tcp.xmit); 00718 00719 #if LWIP_NETIF_HWADDRHINT 00720 ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, 00721 IP_PROTO_TCP, &(pcb->addr_hint)); 00722 #else /* LWIP_NETIF_HWADDRHINT*/ 00723 ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, 00724 IP_PROTO_TCP); 00725 #endif /* LWIP_NETIF_HWADDRHINT*/ 00726 } 00727 00728 /** 00729 * Send a TCP RESET packet (empty segment with RST flag set) either to 00730 * abort a connection or to show that there is no matching local connection 00731 * for a received segment. 00732 * 00733 * Called by tcp_abort() (to abort a local connection), tcp_input() (if no 00734 * matching local pcb was found), tcp_listen_input() (if incoming segment 00735 * has ACK flag set) and tcp_process() (received segment in the wrong state) 00736 * 00737 * Since a RST segment is in most cases not sent for an active connection, 00738 * tcp_rst() has a number of arguments that are taken from a tcp_pcb for 00739 * most other segment output functions. 00740 * 00741 * @param seqno the sequence number to use for the outgoing segment 00742 * @param ackno the acknowledge number to use for the outgoing segment 00743 * @param local_ip the local IP address to send the segment from 00744 * @param remote_ip the remote IP address to send the segment to 00745 * @param local_port the local TCP port to send the segment from 00746 * @param remote_port the remote TCP port to send the segment to 00747 */ 00748 void 00749 tcp_rst(u32_t seqno, u32_t ackno, 00750 struct ip_addr *local_ip, struct ip_addr *remote_ip, 00751 u16_t local_port, u16_t remote_port) 00752 { 00753 struct pbuf *p; 00754 struct tcp_hdr *tcphdr; 00755 p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); 00756 if (p == NULL) { 00757 LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); 00758 return; 00759 } 00760 LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", 00761 (p->len >= sizeof(struct tcp_hdr))); 00762 00763 tcphdr = (struct tcp_hdr *)p->payload; 00764 tcphdr->src = htons(local_port); 00765 tcphdr->dest = htons(remote_port); 00766 tcphdr->seqno = htonl(seqno); 00767 tcphdr->ackno = htonl(ackno); 00768 TCPH_FLAGS_SET(tcphdr, TCP_RST | TCP_ACK); 00769 tcphdr->wnd = htons(TCP_WND); 00770 tcphdr->urgp = 0; 00771 TCPH_HDRLEN_SET(tcphdr, 5); 00772 00773 tcphdr->chksum = 0; 00774 #if CHECKSUM_GEN_TCP 00775 tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip, 00776 IP_PROTO_TCP, p->tot_len); 00777 #endif 00778 TCP_STATS_INC(tcp.xmit); 00779 snmp_inc_tcpoutrsts(); 00780 /* Send output with hardcoded TTL since we have no access to the pcb */ 00781 ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); 00782 pbuf_free(p); 00783 LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); 00784 } 00785 00786 /** 00787 * Requeue all unacked segments for retransmission 00788 * 00789 * Called by tcp_slowtmr() for slow retransmission. 00790 * 00791 * @param pcb the tcp_pcb for which to re-enqueue all unacked segments 00792 */ 00793 void 00794 tcp_rexmit_rto(struct tcp_pcb *pcb) 00795 { 00796 struct tcp_seg *seg; 00797 00798 if (pcb->unacked == NULL) { 00799 return; 00800 } 00801 00802 /* Move all unacked segments to the head of the unsent queue */ 00803 for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); 00804 /* concatenate unsent queue after unacked queue */ 00805 seg->next = pcb->unsent; 00806 /* unsent queue is the concatenated queue (of unacked, unsent) */ 00807 pcb->unsent = pcb->unacked; 00808 /* unacked queue is now empty */ 00809 pcb->unacked = NULL; 00810 00811 /* increment number of retransmissions */ 00812 ++pcb->nrtx; 00813 00814 /* Don't take any RTT measurements after retransmitting. */ 00815 pcb->rttest = 0; 00816 00817 /* Do the actual retransmission */ 00818 tcp_output(pcb); 00819 } 00820 00821 /** 00822 * Requeue the first unacked segment for retransmission 00823 * 00824 * Called by tcp_receive() for fast retramsmit. 00825 * 00826 * @param pcb the tcp_pcb for which to retransmit the first unacked segment 00827 */ 00828 void 00829 tcp_rexmit(struct tcp_pcb *pcb) 00830 { 00831 struct tcp_seg *seg; 00832 struct tcp_seg **cur_seg; 00833 00834 if (pcb->unacked == NULL) { 00835 return; 00836 } 00837 00838 /* Move the first unacked segment to the unsent queue */ 00839 /* Keep the unsent queue sorted. */ 00840 seg = pcb->unacked; 00841 pcb->unacked = seg->next; 00842 00843 cur_seg = &(pcb->unsent); 00844 while (*cur_seg && 00845 TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { 00846 cur_seg = &((*cur_seg)->next ); 00847 } 00848 seg->next = *cur_seg; 00849 *cur_seg = seg; 00850 00851 ++pcb->nrtx; 00852 00853 /* Don't take any rtt measurements after retransmitting. */ 00854 pcb->rttest = 0; 00855 00856 /* Do the actual retransmission. */ 00857 snmp_inc_tcpretranssegs(); 00858 tcp_output(pcb); 00859 } 00860 00861 00862 /** 00863 * Handle retransmission after three dupacks received 00864 * 00865 * @param pcb the tcp_pcb for which to retransmit the first unacked segment 00866 */ 00867 void 00868 tcp_rexmit_fast(struct tcp_pcb *pcb) 00869 { 00870 if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) { 00871 /* This is fast retransmit. Retransmit the first unacked segment. */ 00872 LWIP_DEBUGF(TCP_FR_DEBUG, 00873 ("tcp_receive: dupacks %"U16_F" (%"U32_F 00874 "), fast retransmit %"U32_F"\n", 00875 (u16_t)pcb->dupacks, pcb->lastack, 00876 ntohl(pcb->unacked->tcphdr->seqno))); 00877 tcp_rexmit(pcb); 00878 00879 /* Set ssthresh to half of the minimum of the current 00880 * cwnd and the advertised window */ 00881 if (pcb->cwnd > pcb->snd_wnd) 00882 pcb->ssthresh = pcb->snd_wnd / 2; 00883 else 00884 pcb->ssthresh = pcb->cwnd / 2; 00885 00886 /* The minimum value for ssthresh should be 2 MSS */ 00887 if (pcb->ssthresh < 2*pcb->mss) { 00888 LWIP_DEBUGF(TCP_FR_DEBUG, 00889 ("tcp_receive: The minimum value for ssthresh %"U16_F 00890 " should be min 2 mss %"U16_F"...\n", 00891 pcb->ssthresh, 2*pcb->mss)); 00892 pcb->ssthresh = 2*pcb->mss; 00893 } 00894 00895 pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; 00896 pcb->flags |= TF_INFR; 00897 } 00898 } 00899 00900 00901 /** 00902 * Send keepalive packets to keep a connection active although 00903 * no data is sent over it. 00904 * 00905 * Called by tcp_slowtmr() 00906 * 00907 * @param pcb the tcp_pcb for which to send a keepalive packet 00908 */ 00909 void 00910 tcp_keepalive(struct tcp_pcb *pcb) 00911 { 00912 struct pbuf *p; 00913 struct tcp_hdr *tcphdr; 00914 00915 LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", 00916 ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), 00917 ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); 00918 00919 LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", 00920 tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); 00921 00922 p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); 00923 00924 if(p == NULL) { 00925 LWIP_DEBUGF(TCP_DEBUG, 00926 ("tcp_keepalive: could not allocate memory for pbuf\n")); 00927 return; 00928 } 00929 LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", 00930 (p->len >= sizeof(struct tcp_hdr))); 00931 00932 tcphdr = tcp_output_set_header(pcb, p, 0, htonl(pcb->snd_nxt - 1)); 00933 00934 #if CHECKSUM_GEN_TCP 00935 tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, 00936 IP_PROTO_TCP, p->tot_len); 00937 #endif 00938 TCP_STATS_INC(tcp.xmit); 00939 00940 /* Send output to IP */ 00941 #if LWIP_NETIF_HWADDRHINT 00942 ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, 00943 &(pcb->addr_hint)); 00944 #else /* LWIP_NETIF_HWADDRHINT*/ 00945 ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); 00946 #endif /* LWIP_NETIF_HWADDRHINT*/ 00947 00948 pbuf_free(p); 00949 00950 LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", 00951 pcb->snd_nxt - 1, pcb->rcv_nxt)); 00952 } 00953 00954 00955 /** 00956 * Send persist timer zero-window probes to keep a connection active 00957 * when a window update is lost. 00958 * 00959 * Called by tcp_slowtmr() 00960 * 00961 * @param pcb the tcp_pcb for which to send a zero-window probe packet 00962 */ 00963 void 00964 tcp_zero_window_probe(struct tcp_pcb *pcb) 00965 { 00966 struct pbuf *p; 00967 struct tcp_hdr *tcphdr; 00968 struct tcp_seg *seg; 00969 u16_t len; 00970 u8_t is_fin; 00971 00972 LWIP_DEBUGF(TCP_DEBUG, 00973 ("tcp_zero_window_probe: sending ZERO WINDOW probe to %" 00974 U16_F".%"U16_F".%"U16_F".%"U16_F"\n", 00975 ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), 00976 ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); 00977 00978 LWIP_DEBUGF(TCP_DEBUG, 00979 ("tcp_zero_window_probe: tcp_ticks %"U32_F 00980 " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", 00981 tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); 00982 00983 seg = pcb->unacked; 00984 00985 if(seg == NULL) 00986 seg = pcb->unsent; 00987 00988 if(seg == NULL) 00989 return; 00990 00991 is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0); 00992 len = is_fin ? TCP_HLEN : TCP_HLEN + 1; 00993 00994 p = pbuf_alloc(PBUF_IP, len, PBUF_RAM); 00995 if(p == NULL) { 00996 LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); 00997 return; 00998 } 00999 LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", 01000 (p->len >= sizeof(struct tcp_hdr))); 01001 01002 tcphdr = tcp_output_set_header(pcb, p, 0, seg->tcphdr->seqno); 01003 01004 if (is_fin) { 01005 /* FIN segment, no data */ 01006 TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN); 01007 } else { 01008 /* Data segment, copy in one byte from the head of the unacked queue */ 01009 *((char *)p->payload + sizeof(struct tcp_hdr)) = *(char *)seg->dataptr; 01010 } 01011 01012 #if CHECKSUM_GEN_TCP 01013 tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, 01014 IP_PROTO_TCP, p->tot_len); 01015 #endif 01016 TCP_STATS_INC(tcp.xmit); 01017 01018 /* Send output to IP */ 01019 #if LWIP_NETIF_HWADDRHINT 01020 ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, 01021 &(pcb->addr_hint)); 01022 #else /* LWIP_NETIF_HWADDRHINT*/ 01023 ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); 01024 #endif /* LWIP_NETIF_HWADDRHINT*/ 01025 01026 pbuf_free(p); 01027 01028 LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F 01029 " ackno %"U32_F".\n", 01030 pcb->snd_nxt - 1, pcb->rcv_nxt)); 01031 } 01032 #endif /* LWIP_TCP */
Generated on Tue Jul 12 2022 18:50:03 by 1.7.2