My fork of the HTTPServer (working)

Dependents:   DGWWebServer LAN2

Committer:
screamer
Date:
Tue Nov 20 12:18:53 2012 +0000
Revision:
1:284f2df30cf9
Parent:
0:7a64fbb4069d
local changes

Who changed what in which revision?

UserRevisionLine numberNew contents of line
screamer 0:7a64fbb4069d 1 /**
screamer 0:7a64fbb4069d 2 * @file
screamer 0:7a64fbb4069d 3 * Transmission Control Protocol, outgoing traffic
screamer 0:7a64fbb4069d 4 *
screamer 0:7a64fbb4069d 5 * The output functions of TCP.
screamer 0:7a64fbb4069d 6 *
screamer 0:7a64fbb4069d 7 */
screamer 0:7a64fbb4069d 8
screamer 0:7a64fbb4069d 9 /*
screamer 0:7a64fbb4069d 10 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
screamer 0:7a64fbb4069d 11 * All rights reserved.
screamer 0:7a64fbb4069d 12 *
screamer 0:7a64fbb4069d 13 * Redistribution and use in source and binary forms, with or without modification,
screamer 0:7a64fbb4069d 14 * are permitted provided that the following conditions are met:
screamer 0:7a64fbb4069d 15 *
screamer 0:7a64fbb4069d 16 * 1. Redistributions of source code must retain the above copyright notice,
screamer 0:7a64fbb4069d 17 * this list of conditions and the following disclaimer.
screamer 0:7a64fbb4069d 18 * 2. Redistributions in binary form must reproduce the above copyright notice,
screamer 0:7a64fbb4069d 19 * this list of conditions and the following disclaimer in the documentation
screamer 0:7a64fbb4069d 20 * and/or other materials provided with the distribution.
screamer 0:7a64fbb4069d 21 * 3. The name of the author may not be used to endorse or promote products
screamer 0:7a64fbb4069d 22 * derived from this software without specific prior written permission.
screamer 0:7a64fbb4069d 23 *
screamer 0:7a64fbb4069d 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
screamer 0:7a64fbb4069d 25 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
screamer 0:7a64fbb4069d 26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
screamer 0:7a64fbb4069d 27 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
screamer 0:7a64fbb4069d 28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
screamer 0:7a64fbb4069d 29 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
screamer 0:7a64fbb4069d 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
screamer 0:7a64fbb4069d 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
screamer 0:7a64fbb4069d 32 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
screamer 0:7a64fbb4069d 33 * OF SUCH DAMAGE.
screamer 0:7a64fbb4069d 34 *
screamer 0:7a64fbb4069d 35 * This file is part of the lwIP TCP/IP stack.
screamer 0:7a64fbb4069d 36 *
screamer 0:7a64fbb4069d 37 * Author: Adam Dunkels <adam@sics.se>
screamer 0:7a64fbb4069d 38 *
screamer 0:7a64fbb4069d 39 */
screamer 0:7a64fbb4069d 40
screamer 0:7a64fbb4069d 41 #include "lwip/opt.h"
screamer 0:7a64fbb4069d 42
screamer 0:7a64fbb4069d 43 #if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
screamer 0:7a64fbb4069d 44
screamer 0:7a64fbb4069d 45 #include "lwip/tcp.h"
screamer 0:7a64fbb4069d 46 #include "lwip/def.h"
screamer 0:7a64fbb4069d 47 #include "lwip/mem.h"
screamer 0:7a64fbb4069d 48 #include "lwip/memp.h"
screamer 0:7a64fbb4069d 49 #include "lwip/sys.h"
screamer 0:7a64fbb4069d 50 #include "lwip/ip_addr.h"
screamer 0:7a64fbb4069d 51 #include "lwip/netif.h"
screamer 0:7a64fbb4069d 52 #include "lwip/inet.h"
screamer 0:7a64fbb4069d 53 #include "lwip/inet_chksum.h"
screamer 0:7a64fbb4069d 54 #include "lwip/stats.h"
screamer 0:7a64fbb4069d 55 #include "lwip/snmp.h"
screamer 0:7a64fbb4069d 56
screamer 0:7a64fbb4069d 57 #include <string.h>
screamer 0:7a64fbb4069d 58
screamer 0:7a64fbb4069d 59 /* Forward declarations.*/
screamer 0:7a64fbb4069d 60 static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
screamer 0:7a64fbb4069d 61
screamer 0:7a64fbb4069d 62 static struct tcp_hdr *
screamer 0:7a64fbb4069d 63 tcp_output_set_header(struct tcp_pcb *pcb, struct pbuf *p, int optlen)
screamer 0:7a64fbb4069d 64 {
screamer 0:7a64fbb4069d 65 struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload;
screamer 0:7a64fbb4069d 66 tcphdr->src = htons(pcb->local_port);
screamer 0:7a64fbb4069d 67 tcphdr->dest = htons(pcb->remote_port);
screamer 0:7a64fbb4069d 68 tcphdr->seqno = htonl(pcb->snd_nxt);
screamer 0:7a64fbb4069d 69 tcphdr->ackno = htonl(pcb->rcv_nxt);
screamer 0:7a64fbb4069d 70 TCPH_FLAGS_SET(tcphdr, TCP_ACK);
screamer 0:7a64fbb4069d 71 tcphdr->wnd = htons(pcb->rcv_ann_wnd);
screamer 0:7a64fbb4069d 72 tcphdr->urgp = 0;
screamer 0:7a64fbb4069d 73 TCPH_HDRLEN_SET(tcphdr, (5 + optlen / 4));
screamer 0:7a64fbb4069d 74 tcphdr->chksum = 0;
screamer 0:7a64fbb4069d 75
screamer 0:7a64fbb4069d 76 /* If we're sending a packet, update the announced right window edge */
screamer 0:7a64fbb4069d 77 pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
screamer 0:7a64fbb4069d 78
screamer 0:7a64fbb4069d 79 return tcphdr;
screamer 0:7a64fbb4069d 80 }
screamer 0:7a64fbb4069d 81
screamer 0:7a64fbb4069d 82 /**
screamer 0:7a64fbb4069d 83 * Called by tcp_close() to send a segment including flags but not data.
screamer 0:7a64fbb4069d 84 *
screamer 0:7a64fbb4069d 85 * @param pcb the tcp_pcb over which to send a segment
screamer 0:7a64fbb4069d 86 * @param flags the flags to set in the segment header
screamer 0:7a64fbb4069d 87 * @return ERR_OK if sent, another err_t otherwise
screamer 0:7a64fbb4069d 88 */
screamer 0:7a64fbb4069d 89 err_t
screamer 0:7a64fbb4069d 90 tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags)
screamer 0:7a64fbb4069d 91 {
screamer 0:7a64fbb4069d 92 /* no data, no length, flags, copy=1, no optdata */
screamer 0:7a64fbb4069d 93 return tcp_enqueue(pcb, NULL, 0, flags, TCP_WRITE_FLAG_COPY, 0);
screamer 0:7a64fbb4069d 94 }
screamer 0:7a64fbb4069d 95
screamer 0:7a64fbb4069d 96 /**
screamer 0:7a64fbb4069d 97 * Write data for sending (but does not send it immediately).
screamer 0:7a64fbb4069d 98 *
screamer 0:7a64fbb4069d 99 * It waits in the expectation of more data being sent soon (as
screamer 0:7a64fbb4069d 100 * it can send them more efficiently by combining them together).
screamer 0:7a64fbb4069d 101 * To prompt the system to send data now, call tcp_output() after
screamer 0:7a64fbb4069d 102 * calling tcp_write().
screamer 0:7a64fbb4069d 103 *
screamer 0:7a64fbb4069d 104 * @param pcb Protocol control block of the TCP connection to enqueue data for.
screamer 0:7a64fbb4069d 105 * @param data pointer to the data to send
screamer 0:7a64fbb4069d 106 * @param len length (in bytes) of the data to send
screamer 0:7a64fbb4069d 107 * @param apiflags combination of following flags :
screamer 0:7a64fbb4069d 108 * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
screamer 0:7a64fbb4069d 109 * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
screamer 0:7a64fbb4069d 110 * @return ERR_OK if enqueued, another err_t on error
screamer 0:7a64fbb4069d 111 *
screamer 0:7a64fbb4069d 112 * @see tcp_write()
screamer 0:7a64fbb4069d 113 */
screamer 0:7a64fbb4069d 114 err_t
screamer 0:7a64fbb4069d 115 tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags)
screamer 0:7a64fbb4069d 116 {
screamer 0:7a64fbb4069d 117 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", (void *)pcb,
screamer 0:7a64fbb4069d 118 data, len, (u16_t)apiflags));
screamer 0:7a64fbb4069d 119 /* connection is in valid state for data transmission? */
screamer 0:7a64fbb4069d 120 if (pcb->state == ESTABLISHED ||
screamer 0:7a64fbb4069d 121 pcb->state == CLOSE_WAIT ||
screamer 0:7a64fbb4069d 122 pcb->state == SYN_SENT ||
screamer 0:7a64fbb4069d 123 pcb->state == SYN_RCVD) {
screamer 0:7a64fbb4069d 124 if (len > 0) {
screamer 0:7a64fbb4069d 125 #if LWIP_TCP_TIMESTAMPS
screamer 0:7a64fbb4069d 126 return tcp_enqueue(pcb, (void *)data, len, 0, apiflags,
screamer 0:7a64fbb4069d 127 pcb->flags & TF_TIMESTAMP ? TF_SEG_OPTS_TS : 0);
screamer 0:7a64fbb4069d 128 #else
screamer 0:7a64fbb4069d 129 return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, 0);
screamer 0:7a64fbb4069d 130 #endif
screamer 0:7a64fbb4069d 131 }
screamer 0:7a64fbb4069d 132 return ERR_OK;
screamer 0:7a64fbb4069d 133 } else {
screamer 0:7a64fbb4069d 134 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | 3, ("tcp_write() called in invalid state\n"));
screamer 0:7a64fbb4069d 135 return ERR_CONN;
screamer 0:7a64fbb4069d 136 }
screamer 0:7a64fbb4069d 137 }
screamer 0:7a64fbb4069d 138
screamer 0:7a64fbb4069d 139 /**
screamer 0:7a64fbb4069d 140 * Enqueue data and/or TCP options for transmission
screamer 0:7a64fbb4069d 141 *
screamer 0:7a64fbb4069d 142 * Called by tcp_connect(), tcp_listen_input(), tcp_send_ctrl() and tcp_write().
screamer 0:7a64fbb4069d 143 *
screamer 0:7a64fbb4069d 144 * @param pcb Protocol control block for the TCP connection to enqueue data for.
screamer 0:7a64fbb4069d 145 * @param arg Pointer to the data to be enqueued for sending.
screamer 0:7a64fbb4069d 146 * @param len Data length in bytes
screamer 0:7a64fbb4069d 147 * @param flags tcp header flags to set in the outgoing segment
screamer 0:7a64fbb4069d 148 * @param apiflags combination of following flags :
screamer 0:7a64fbb4069d 149 * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
screamer 0:7a64fbb4069d 150 * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
screamer 0:7a64fbb4069d 151 * @param optflags options to include in segment later on (see definition of struct tcp_seg)
screamer 0:7a64fbb4069d 152 */
screamer 0:7a64fbb4069d 153 err_t
screamer 0:7a64fbb4069d 154 tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
screamer 0:7a64fbb4069d 155 u8_t flags, u8_t apiflags, u8_t optflags)
screamer 0:7a64fbb4069d 156 {
screamer 0:7a64fbb4069d 157 struct pbuf *p;
screamer 0:7a64fbb4069d 158 struct tcp_seg *seg, *useg, *queue;
screamer 0:7a64fbb4069d 159 u32_t seqno;
screamer 0:7a64fbb4069d 160 u16_t left, seglen;
screamer 0:7a64fbb4069d 161 void *ptr;
screamer 0:7a64fbb4069d 162 u16_t queuelen;
screamer 0:7a64fbb4069d 163 u8_t optlen;
screamer 0:7a64fbb4069d 164
screamer 0:7a64fbb4069d 165 LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
screamer 0:7a64fbb4069d 166 ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", apiflags=%"U16_F")\n",
screamer 0:7a64fbb4069d 167 (void *)pcb, arg, len, (u16_t)flags, (u16_t)apiflags));
screamer 0:7a64fbb4069d 168 LWIP_ERROR("tcp_enqueue: packet needs payload, options, or SYN/FIN (programmer violates API)",
screamer 0:7a64fbb4069d 169 ((len != 0) || (optflags != 0) || ((flags & (TCP_SYN | TCP_FIN)) != 0)),
screamer 0:7a64fbb4069d 170 return ERR_ARG;);
screamer 0:7a64fbb4069d 171 LWIP_ERROR("tcp_enqueue: len != 0 || arg == NULL (programmer violates API)",
screamer 0:7a64fbb4069d 172 ((len != 0) || (arg == NULL)), return ERR_ARG;);
screamer 0:7a64fbb4069d 173
screamer 0:7a64fbb4069d 174 /* fail on too much data */
screamer 0:7a64fbb4069d 175 if (len > pcb->snd_buf) {
screamer 0:7a64fbb4069d 176 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf));
screamer 0:7a64fbb4069d 177 pcb->flags |= TF_NAGLEMEMERR;
screamer 0:7a64fbb4069d 178 return ERR_MEM;
screamer 0:7a64fbb4069d 179 }
screamer 0:7a64fbb4069d 180 left = len;
screamer 0:7a64fbb4069d 181 ptr = arg;
screamer 0:7a64fbb4069d 182
screamer 0:7a64fbb4069d 183 optlen = LWIP_TCP_OPT_LENGTH(optflags);
screamer 0:7a64fbb4069d 184
screamer 0:7a64fbb4069d 185 /* seqno will be the sequence number of the first segment enqueued
screamer 0:7a64fbb4069d 186 * by the call to this function. */
screamer 0:7a64fbb4069d 187 seqno = pcb->snd_lbb;
screamer 0:7a64fbb4069d 188
screamer 0:7a64fbb4069d 189 LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
screamer 0:7a64fbb4069d 190
screamer 0:7a64fbb4069d 191 /* If total number of pbufs on the unsent/unacked queues exceeds the
screamer 0:7a64fbb4069d 192 * configured maximum, return an error */
screamer 0:7a64fbb4069d 193 queuelen = pcb->snd_queuelen;
screamer 0:7a64fbb4069d 194 /* check for configured max queuelen and possible overflow */
screamer 0:7a64fbb4069d 195 if ((queuelen >= TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
screamer 0:7a64fbb4069d 196 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %"U16_F" (max %"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
screamer 0:7a64fbb4069d 197 TCP_STATS_INC(tcp.memerr);
screamer 0:7a64fbb4069d 198 pcb->flags |= TF_NAGLEMEMERR;
screamer 0:7a64fbb4069d 199 return ERR_MEM;
screamer 0:7a64fbb4069d 200 }
screamer 0:7a64fbb4069d 201 if (queuelen != 0) {
screamer 0:7a64fbb4069d 202 LWIP_ASSERT("tcp_enqueue: pbufs on queue => at least one queue non-empty",
screamer 0:7a64fbb4069d 203 pcb->unacked != NULL || pcb->unsent != NULL);
screamer 0:7a64fbb4069d 204 } else {
screamer 0:7a64fbb4069d 205 LWIP_ASSERT("tcp_enqueue: no pbufs on queue => both queues empty",
screamer 0:7a64fbb4069d 206 pcb->unacked == NULL && pcb->unsent == NULL);
screamer 0:7a64fbb4069d 207 }
screamer 0:7a64fbb4069d 208
screamer 0:7a64fbb4069d 209 /* First, break up the data into segments and tuck them together in
screamer 0:7a64fbb4069d 210 * the local "queue" variable. */
screamer 0:7a64fbb4069d 211 useg = queue = seg = NULL;
screamer 0:7a64fbb4069d 212 seglen = 0;
screamer 0:7a64fbb4069d 213 while (queue == NULL || left > 0) {
screamer 0:7a64fbb4069d 214 /* The segment length (including options) should be at most the MSS */
screamer 0:7a64fbb4069d 215 seglen = left > (pcb->mss - optlen) ? (pcb->mss - optlen) : left;
screamer 0:7a64fbb4069d 216
screamer 0:7a64fbb4069d 217 /* Allocate memory for tcp_seg, and fill in fields. */
screamer 0:7a64fbb4069d 218 seg = (struct tcp_seg *)(memp_malloc(MEMP_TCP_SEG));
screamer 0:7a64fbb4069d 219 if (seg == NULL) {
screamer 0:7a64fbb4069d 220 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
screamer 0:7a64fbb4069d 221 ("tcp_enqueue: could not allocate memory for tcp_seg\n"));
screamer 0:7a64fbb4069d 222 goto memerr;
screamer 0:7a64fbb4069d 223 }
screamer 0:7a64fbb4069d 224 seg->next = NULL;
screamer 0:7a64fbb4069d 225 seg->p = NULL;
screamer 0:7a64fbb4069d 226
screamer 0:7a64fbb4069d 227 /* first segment of to-be-queued data? */
screamer 0:7a64fbb4069d 228 if (queue == NULL) {
screamer 0:7a64fbb4069d 229 queue = seg;
screamer 0:7a64fbb4069d 230 }
screamer 0:7a64fbb4069d 231 /* subsequent segments of to-be-queued data */
screamer 0:7a64fbb4069d 232 else {
screamer 0:7a64fbb4069d 233 /* Attach the segment to the end of the queued segments */
screamer 0:7a64fbb4069d 234 LWIP_ASSERT("useg != NULL", useg != NULL);
screamer 0:7a64fbb4069d 235 useg->next = seg;
screamer 0:7a64fbb4069d 236 }
screamer 0:7a64fbb4069d 237 /* remember last segment of to-be-queued data for next iteration */
screamer 0:7a64fbb4069d 238 useg = seg;
screamer 0:7a64fbb4069d 239
screamer 0:7a64fbb4069d 240 /* If copy is set, memory should be allocated
screamer 0:7a64fbb4069d 241 * and data copied into pbuf, otherwise data comes from
screamer 0:7a64fbb4069d 242 * ROM or other static memory, and need not be copied. */
screamer 0:7a64fbb4069d 243 if (apiflags & TCP_WRITE_FLAG_COPY) {
screamer 0:7a64fbb4069d 244 if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen + optlen, PBUF_RAM)) == NULL) {
screamer 0:7a64fbb4069d 245 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
screamer 0:7a64fbb4069d 246 ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
screamer 0:7a64fbb4069d 247 goto memerr;
screamer 0:7a64fbb4069d 248 }
screamer 0:7a64fbb4069d 249 LWIP_ASSERT("check that first pbuf can hold the complete seglen",
screamer 0:7a64fbb4069d 250 (seg->p->len >= seglen + optlen));
screamer 0:7a64fbb4069d 251 queuelen += pbuf_clen(seg->p);
screamer 0:7a64fbb4069d 252 if (arg != NULL) {
screamer 0:7a64fbb4069d 253 MEMCPY((char *)seg->p->payload + optlen, ptr, seglen);
screamer 0:7a64fbb4069d 254 }
screamer 0:7a64fbb4069d 255 seg->dataptr = seg->p->payload;
screamer 0:7a64fbb4069d 256 }
screamer 0:7a64fbb4069d 257 /* do not copy data */
screamer 0:7a64fbb4069d 258 else {
screamer 0:7a64fbb4069d 259 /* First, allocate a pbuf for the headers. */
screamer 0:7a64fbb4069d 260 if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
screamer 0:7a64fbb4069d 261 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
screamer 0:7a64fbb4069d 262 ("tcp_enqueue: could not allocate memory for header pbuf\n"));
screamer 0:7a64fbb4069d 263 goto memerr;
screamer 0:7a64fbb4069d 264 }
screamer 0:7a64fbb4069d 265 queuelen += pbuf_clen(seg->p);
screamer 0:7a64fbb4069d 266
screamer 0:7a64fbb4069d 267 /* Second, allocate a pbuf for holding the data.
screamer 0:7a64fbb4069d 268 * since the referenced data is available at least until it is sent out on the
screamer 0:7a64fbb4069d 269 * link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM
screamer 0:7a64fbb4069d 270 * instead of PBUF_REF here.
screamer 0:7a64fbb4069d 271 */
screamer 0:7a64fbb4069d 272 if (left > 0) {
screamer 0:7a64fbb4069d 273 if ((p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
screamer 0:7a64fbb4069d 274 /* If allocation fails, we have to deallocate the header pbuf as well. */
screamer 0:7a64fbb4069d 275 pbuf_free(seg->p);
screamer 0:7a64fbb4069d 276 seg->p = NULL;
screamer 0:7a64fbb4069d 277 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
screamer 0:7a64fbb4069d 278 ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n"));
screamer 0:7a64fbb4069d 279 goto memerr;
screamer 0:7a64fbb4069d 280 }
screamer 0:7a64fbb4069d 281 ++queuelen;
screamer 0:7a64fbb4069d 282 /* reference the non-volatile payload data */
screamer 0:7a64fbb4069d 283 p->payload = ptr;
screamer 0:7a64fbb4069d 284 seg->dataptr = ptr;
screamer 0:7a64fbb4069d 285
screamer 0:7a64fbb4069d 286 /* Concatenate the headers and data pbufs together. */
screamer 0:7a64fbb4069d 287 pbuf_cat(seg->p/*header*/, p/*data*/);
screamer 0:7a64fbb4069d 288 p = NULL;
screamer 0:7a64fbb4069d 289 }
screamer 0:7a64fbb4069d 290 }
screamer 0:7a64fbb4069d 291
screamer 0:7a64fbb4069d 292 /* Now that there are more segments queued, we check again if the
screamer 0:7a64fbb4069d 293 length of the queue exceeds the configured maximum or overflows. */
screamer 0:7a64fbb4069d 294 if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
screamer 0:7a64fbb4069d 295 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
screamer 0:7a64fbb4069d 296 goto memerr;
screamer 0:7a64fbb4069d 297 }
screamer 0:7a64fbb4069d 298
screamer 0:7a64fbb4069d 299 seg->len = seglen;
screamer 0:7a64fbb4069d 300
screamer 0:7a64fbb4069d 301 /* build TCP header */
screamer 0:7a64fbb4069d 302 if (pbuf_header(seg->p, TCP_HLEN)) {
screamer 0:7a64fbb4069d 303 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n"));
screamer 0:7a64fbb4069d 304 TCP_STATS_INC(tcp.err);
screamer 0:7a64fbb4069d 305 goto memerr;
screamer 0:7a64fbb4069d 306 }
screamer 0:7a64fbb4069d 307 seg->tcphdr = (struct tcp_hdr *)(seg->p->payload);
screamer 0:7a64fbb4069d 308 seg->tcphdr->src = htons(pcb->local_port);
screamer 0:7a64fbb4069d 309 seg->tcphdr->dest = htons(pcb->remote_port);
screamer 0:7a64fbb4069d 310 seg->tcphdr->seqno = htonl(seqno);
screamer 0:7a64fbb4069d 311 seg->tcphdr->urgp = 0;
screamer 0:7a64fbb4069d 312 TCPH_FLAGS_SET(seg->tcphdr, flags);
screamer 0:7a64fbb4069d 313 /* don't fill in tcphdr->ackno and tcphdr->wnd until later */
screamer 0:7a64fbb4069d 314
screamer 0:7a64fbb4069d 315 seg->flags = optflags;
screamer 0:7a64fbb4069d 316
screamer 0:7a64fbb4069d 317 /* Set the length of the header */
screamer 0:7a64fbb4069d 318 TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4));
screamer 0:7a64fbb4069d 319 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
screamer 0:7a64fbb4069d 320 ntohl(seg->tcphdr->seqno),
screamer 0:7a64fbb4069d 321 ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
screamer 0:7a64fbb4069d 322 (u16_t)flags));
screamer 0:7a64fbb4069d 323
screamer 0:7a64fbb4069d 324 left -= seglen;
screamer 0:7a64fbb4069d 325 seqno += seglen;
screamer 0:7a64fbb4069d 326 ptr = (void *)((u8_t *)ptr + seglen);
screamer 0:7a64fbb4069d 327 }
screamer 0:7a64fbb4069d 328
screamer 0:7a64fbb4069d 329 /* Now that the data to be enqueued has been broken up into TCP
screamer 0:7a64fbb4069d 330 segments in the queue variable, we add them to the end of the
screamer 0:7a64fbb4069d 331 pcb->unsent queue. */
screamer 0:7a64fbb4069d 332 if (pcb->unsent == NULL) {
screamer 0:7a64fbb4069d 333 useg = NULL;
screamer 0:7a64fbb4069d 334 }
screamer 0:7a64fbb4069d 335 else {
screamer 0:7a64fbb4069d 336 for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
screamer 0:7a64fbb4069d 337 }
screamer 0:7a64fbb4069d 338 /* { useg is last segment on the unsent queue, NULL if list is empty } */
screamer 0:7a64fbb4069d 339
screamer 0:7a64fbb4069d 340 /* If there is room in the last pbuf on the unsent queue,
screamer 0:7a64fbb4069d 341 chain the first pbuf on the queue together with that. */
screamer 0:7a64fbb4069d 342 if (useg != NULL &&
screamer 0:7a64fbb4069d 343 TCP_TCPLEN(useg) != 0 &&
screamer 0:7a64fbb4069d 344 !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) &&
screamer 0:7a64fbb4069d 345 !(flags & (TCP_SYN | TCP_FIN)) &&
screamer 0:7a64fbb4069d 346 /* fit within max seg size */
screamer 0:7a64fbb4069d 347 (useg->len + queue->len <= pcb->mss) &&
screamer 0:7a64fbb4069d 348 /* only concatenate segments with the same options */
screamer 0:7a64fbb4069d 349 (useg->flags == queue->flags)) {
screamer 0:7a64fbb4069d 350 /* Remove TCP header from first segment of our to-be-queued list */
screamer 0:7a64fbb4069d 351 if(pbuf_header(queue->p, -(TCP_HLEN + optlen))) {
screamer 0:7a64fbb4069d 352 /* Can we cope with this failing? Just assert for now */
screamer 0:7a64fbb4069d 353 LWIP_ASSERT("pbuf_header failed\n", 0);
screamer 0:7a64fbb4069d 354 TCP_STATS_INC(tcp.err);
screamer 0:7a64fbb4069d 355 goto memerr;
screamer 0:7a64fbb4069d 356 }
screamer 0:7a64fbb4069d 357 if (queue->p->len == 0) {
screamer 0:7a64fbb4069d 358 /* free the first (header-only) pbuf if it is now empty (contained only headers) */
screamer 0:7a64fbb4069d 359 struct pbuf *old_q = queue->p;
screamer 0:7a64fbb4069d 360 queue->p = queue->p->next;
screamer 0:7a64fbb4069d 361 old_q->next = NULL;
screamer 0:7a64fbb4069d 362 queuelen--;
screamer 0:7a64fbb4069d 363 pbuf_free(old_q);
screamer 0:7a64fbb4069d 364 }
screamer 0:7a64fbb4069d 365 LWIP_ASSERT("zero-length pbuf", (queue->p != NULL) && (queue->p->len > 0));
screamer 0:7a64fbb4069d 366 pbuf_cat(useg->p, queue->p);
screamer 0:7a64fbb4069d 367 useg->len += queue->len;
screamer 0:7a64fbb4069d 368 useg->next = queue->next;
screamer 0:7a64fbb4069d 369
screamer 0:7a64fbb4069d 370 LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len));
screamer 0:7a64fbb4069d 371 if (seg == queue) {
screamer 0:7a64fbb4069d 372 seg = useg;
screamer 0:7a64fbb4069d 373 seglen = useg->len;
screamer 0:7a64fbb4069d 374 }
screamer 0:7a64fbb4069d 375 memp_free(MEMP_TCP_SEG, queue);
screamer 0:7a64fbb4069d 376 }
screamer 0:7a64fbb4069d 377 else {
screamer 0:7a64fbb4069d 378 /* empty list */
screamer 0:7a64fbb4069d 379 if (useg == NULL) {
screamer 0:7a64fbb4069d 380 /* initialize list with this segment */
screamer 0:7a64fbb4069d 381 pcb->unsent = queue;
screamer 0:7a64fbb4069d 382 }
screamer 0:7a64fbb4069d 383 /* enqueue segment */
screamer 0:7a64fbb4069d 384 else {
screamer 0:7a64fbb4069d 385 useg->next = queue;
screamer 0:7a64fbb4069d 386 }
screamer 0:7a64fbb4069d 387 }
screamer 0:7a64fbb4069d 388 if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
screamer 0:7a64fbb4069d 389 ++len;
screamer 0:7a64fbb4069d 390 }
screamer 0:7a64fbb4069d 391 if (flags & TCP_FIN) {
screamer 0:7a64fbb4069d 392 pcb->flags |= TF_FIN;
screamer 0:7a64fbb4069d 393 }
screamer 0:7a64fbb4069d 394 pcb->snd_lbb += len;
screamer 0:7a64fbb4069d 395
screamer 0:7a64fbb4069d 396 pcb->snd_buf -= len;
screamer 0:7a64fbb4069d 397
screamer 0:7a64fbb4069d 398 /* update number of segments on the queues */
screamer 0:7a64fbb4069d 399 pcb->snd_queuelen = queuelen;
screamer 0:7a64fbb4069d 400 LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
screamer 0:7a64fbb4069d 401 if (pcb->snd_queuelen != 0) {
screamer 0:7a64fbb4069d 402 LWIP_ASSERT("tcp_enqueue: valid queue length",
screamer 0:7a64fbb4069d 403 pcb->unacked != NULL || pcb->unsent != NULL);
screamer 0:7a64fbb4069d 404 }
screamer 0:7a64fbb4069d 405
screamer 0:7a64fbb4069d 406 /* Set the PSH flag in the last segment that we enqueued, but only
screamer 0:7a64fbb4069d 407 if the segment has data (indicated by seglen > 0). */
screamer 0:7a64fbb4069d 408 if (seg != NULL && seglen > 0 && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
screamer 0:7a64fbb4069d 409 TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
screamer 0:7a64fbb4069d 410 }
screamer 0:7a64fbb4069d 411
screamer 0:7a64fbb4069d 412 return ERR_OK;
screamer 0:7a64fbb4069d 413 memerr:
screamer 0:7a64fbb4069d 414 pcb->flags |= TF_NAGLEMEMERR;
screamer 0:7a64fbb4069d 415 TCP_STATS_INC(tcp.memerr);
screamer 0:7a64fbb4069d 416
screamer 0:7a64fbb4069d 417 if (queue != NULL) {
screamer 0:7a64fbb4069d 418 tcp_segs_free(queue);
screamer 0:7a64fbb4069d 419 }
screamer 0:7a64fbb4069d 420 if (pcb->snd_queuelen != 0) {
screamer 0:7a64fbb4069d 421 LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
screamer 0:7a64fbb4069d 422 pcb->unsent != NULL);
screamer 0:7a64fbb4069d 423 }
screamer 0:7a64fbb4069d 424 LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
screamer 0:7a64fbb4069d 425 return ERR_MEM;
screamer 0:7a64fbb4069d 426 }
screamer 0:7a64fbb4069d 427
screamer 0:7a64fbb4069d 428
screamer 0:7a64fbb4069d 429 #if LWIP_TCP_TIMESTAMPS
screamer 0:7a64fbb4069d 430 /* Build a timestamp option (12 bytes long) at the specified options pointer)
screamer 0:7a64fbb4069d 431 *
screamer 0:7a64fbb4069d 432 * @param pcb tcp_pcb
screamer 0:7a64fbb4069d 433 * @param opts option pointer where to store the timestamp option
screamer 0:7a64fbb4069d 434 */
screamer 0:7a64fbb4069d 435 static void
screamer 0:7a64fbb4069d 436 tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
screamer 0:7a64fbb4069d 437 {
screamer 0:7a64fbb4069d 438 /* Pad with two NOP options to make everything nicely aligned */
screamer 0:7a64fbb4069d 439 opts[0] = htonl(0x0101080A);
screamer 0:7a64fbb4069d 440 opts[1] = htonl(sys_now());
screamer 0:7a64fbb4069d 441 opts[2] = htonl(pcb->ts_recent);
screamer 0:7a64fbb4069d 442 }
screamer 0:7a64fbb4069d 443 #endif
screamer 0:7a64fbb4069d 444
screamer 0:7a64fbb4069d 445
screamer 0:7a64fbb4069d 446 /**
screamer 0:7a64fbb4069d 447 * Find out what we can send and send it
screamer 0:7a64fbb4069d 448 *
screamer 0:7a64fbb4069d 449 * @param pcb Protocol control block for the TCP connection to send data
screamer 0:7a64fbb4069d 450 * @return ERR_OK if data has been sent or nothing to send
screamer 0:7a64fbb4069d 451 * another err_t on error
screamer 0:7a64fbb4069d 452 */
screamer 0:7a64fbb4069d 453 err_t
screamer 0:7a64fbb4069d 454 tcp_output(struct tcp_pcb *pcb)
screamer 0:7a64fbb4069d 455 {
screamer 0:7a64fbb4069d 456 struct pbuf *p;
screamer 0:7a64fbb4069d 457 struct tcp_hdr *tcphdr;
screamer 0:7a64fbb4069d 458 struct tcp_seg *seg, *useg;
screamer 0:7a64fbb4069d 459 u32_t wnd;
screamer 0:7a64fbb4069d 460 #if TCP_CWND_DEBUG
screamer 0:7a64fbb4069d 461 s16_t i = 0;
screamer 0:7a64fbb4069d 462 #endif /* TCP_CWND_DEBUG */
screamer 0:7a64fbb4069d 463 u8_t optlen = 0;
screamer 0:7a64fbb4069d 464
screamer 0:7a64fbb4069d 465 /* First, check if we are invoked by the TCP input processing
screamer 0:7a64fbb4069d 466 code. If so, we do not output anything. Instead, we rely on the
screamer 0:7a64fbb4069d 467 input processing code to call us when input processing is done
screamer 0:7a64fbb4069d 468 with. */
screamer 0:7a64fbb4069d 469 if (tcp_input_pcb == pcb) {
screamer 0:7a64fbb4069d 470 return ERR_OK;
screamer 0:7a64fbb4069d 471 }
screamer 0:7a64fbb4069d 472
screamer 0:7a64fbb4069d 473 wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
screamer 0:7a64fbb4069d 474
screamer 0:7a64fbb4069d 475 seg = pcb->unsent;
screamer 0:7a64fbb4069d 476
screamer 0:7a64fbb4069d 477 /* useg should point to last segment on unacked queue */
screamer 0:7a64fbb4069d 478 useg = pcb->unacked;
screamer 0:7a64fbb4069d 479 if (useg != NULL) {
screamer 0:7a64fbb4069d 480 for (; useg->next != NULL; useg = useg->next);
screamer 0:7a64fbb4069d 481 }
screamer 0:7a64fbb4069d 482
screamer 0:7a64fbb4069d 483 /* If the TF_ACK_NOW flag is set and no data will be sent (either
screamer 0:7a64fbb4069d 484 * because the ->unsent queue is empty or because the window does
screamer 0:7a64fbb4069d 485 * not allow it), construct an empty ACK segment and send it.
screamer 0:7a64fbb4069d 486 *
screamer 0:7a64fbb4069d 487 * If data is to be sent, we will just piggyback the ACK (see below).
screamer 0:7a64fbb4069d 488 */
screamer 0:7a64fbb4069d 489 if (pcb->flags & TF_ACK_NOW &&
screamer 0:7a64fbb4069d 490 (seg == NULL ||
screamer 0:7a64fbb4069d 491 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
screamer 0:7a64fbb4069d 492 #if LWIP_TCP_TIMESTAMPS
screamer 0:7a64fbb4069d 493 if (pcb->flags & TF_TIMESTAMP)
screamer 0:7a64fbb4069d 494 optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
screamer 0:7a64fbb4069d 495 #endif
screamer 0:7a64fbb4069d 496 p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen, PBUF_RAM);
screamer 0:7a64fbb4069d 497 if (p == NULL) {
screamer 0:7a64fbb4069d 498 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
screamer 0:7a64fbb4069d 499 return ERR_BUF;
screamer 0:7a64fbb4069d 500 }
screamer 0:7a64fbb4069d 501 LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
screamer 0:7a64fbb4069d 502 ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
screamer 0:7a64fbb4069d 503 /* remove ACK flags from the PCB, as we send an empty ACK now */
screamer 0:7a64fbb4069d 504 pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
screamer 0:7a64fbb4069d 505
screamer 0:7a64fbb4069d 506 tcphdr = tcp_output_set_header(pcb, p, optlen);
screamer 0:7a64fbb4069d 507
screamer 0:7a64fbb4069d 508 /* NB. MSS option is only sent on SYNs, so ignore it here */
screamer 0:7a64fbb4069d 509 #if LWIP_TCP_TIMESTAMPS
screamer 0:7a64fbb4069d 510 pcb->ts_lastacksent = pcb->rcv_nxt;
screamer 0:7a64fbb4069d 511
screamer 0:7a64fbb4069d 512 if (pcb->flags & TF_TIMESTAMP)
screamer 0:7a64fbb4069d 513 tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
screamer 0:7a64fbb4069d 514 #endif
screamer 0:7a64fbb4069d 515
screamer 0:7a64fbb4069d 516 #if CHECKSUM_GEN_TCP
screamer 0:7a64fbb4069d 517 tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
screamer 0:7a64fbb4069d 518 IP_PROTO_TCP, p->tot_len);
screamer 0:7a64fbb4069d 519 #endif
screamer 0:7a64fbb4069d 520 #if LWIP_NETIF_HWADDRHINT
screamer 0:7a64fbb4069d 521 ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
screamer 0:7a64fbb4069d 522 IP_PROTO_TCP, &(pcb->addr_hint));
screamer 0:7a64fbb4069d 523 #else /* LWIP_NETIF_HWADDRHINT*/
screamer 0:7a64fbb4069d 524 ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
screamer 0:7a64fbb4069d 525 IP_PROTO_TCP);
screamer 0:7a64fbb4069d 526 #endif /* LWIP_NETIF_HWADDRHINT*/
screamer 0:7a64fbb4069d 527 pbuf_free(p);
screamer 0:7a64fbb4069d 528
screamer 0:7a64fbb4069d 529 return ERR_OK;
screamer 0:7a64fbb4069d 530 }
screamer 0:7a64fbb4069d 531
screamer 0:7a64fbb4069d 532 #if TCP_OUTPUT_DEBUG
screamer 0:7a64fbb4069d 533 if (seg == NULL) {
screamer 0:7a64fbb4069d 534 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
screamer 0:7a64fbb4069d 535 (void*)pcb->unsent));
screamer 0:7a64fbb4069d 536 }
screamer 0:7a64fbb4069d 537 #endif /* TCP_OUTPUT_DEBUG */
screamer 0:7a64fbb4069d 538 #if TCP_CWND_DEBUG
screamer 0:7a64fbb4069d 539 if (seg == NULL) {
screamer 0:7a64fbb4069d 540 LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
screamer 0:7a64fbb4069d 541 ", cwnd %"U16_F", wnd %"U32_F
screamer 0:7a64fbb4069d 542 ", seg == NULL, ack %"U32_F"\n",
screamer 0:7a64fbb4069d 543 pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
screamer 0:7a64fbb4069d 544 } else {
screamer 0:7a64fbb4069d 545 LWIP_DEBUGF(TCP_CWND_DEBUG,
screamer 0:7a64fbb4069d 546 ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F
screamer 0:7a64fbb4069d 547 ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
screamer 0:7a64fbb4069d 548 pcb->snd_wnd, pcb->cwnd, wnd,
screamer 0:7a64fbb4069d 549 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
screamer 0:7a64fbb4069d 550 ntohl(seg->tcphdr->seqno), pcb->lastack));
screamer 0:7a64fbb4069d 551 }
screamer 0:7a64fbb4069d 552 #endif /* TCP_CWND_DEBUG */
screamer 0:7a64fbb4069d 553 /* data available and window allows it to be sent? */
screamer 0:7a64fbb4069d 554 while (seg != NULL &&
screamer 0:7a64fbb4069d 555 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
screamer 0:7a64fbb4069d 556 LWIP_ASSERT("RST not expected here!",
screamer 0:7a64fbb4069d 557 (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
screamer 0:7a64fbb4069d 558 /* Stop sending if the nagle algorithm would prevent it
screamer 0:7a64fbb4069d 559 * Don't stop:
screamer 0:7a64fbb4069d 560 * - if tcp_enqueue had a memory error before (prevent delayed ACK timeout) or
screamer 0:7a64fbb4069d 561 * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
screamer 0:7a64fbb4069d 562 * either seg->next != NULL or pcb->unacked == NULL;
screamer 0:7a64fbb4069d 563 * RST is no sent using tcp_enqueue/tcp_output.
screamer 0:7a64fbb4069d 564 */
screamer 0:7a64fbb4069d 565 if((tcp_do_output_nagle(pcb) == 0) &&
screamer 0:7a64fbb4069d 566 ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){
screamer 0:7a64fbb4069d 567 break;
screamer 0:7a64fbb4069d 568 }
screamer 0:7a64fbb4069d 569 #if TCP_CWND_DEBUG
screamer 0:7a64fbb4069d 570 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",
screamer 0:7a64fbb4069d 571 pcb->snd_wnd, pcb->cwnd, wnd,
screamer 0:7a64fbb4069d 572 ntohl(seg->tcphdr->seqno) + seg->len -
screamer 0:7a64fbb4069d 573 pcb->lastack,
screamer 0:7a64fbb4069d 574 ntohl(seg->tcphdr->seqno), pcb->lastack, i));
screamer 0:7a64fbb4069d 575 ++i;
screamer 0:7a64fbb4069d 576 #endif /* TCP_CWND_DEBUG */
screamer 0:7a64fbb4069d 577
screamer 0:7a64fbb4069d 578 pcb->unsent = seg->next;
screamer 0:7a64fbb4069d 579
screamer 0:7a64fbb4069d 580 if (pcb->state != SYN_SENT) {
screamer 0:7a64fbb4069d 581 TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
screamer 0:7a64fbb4069d 582 pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
screamer 0:7a64fbb4069d 583 }
screamer 0:7a64fbb4069d 584
screamer 0:7a64fbb4069d 585 tcp_output_segment(seg, pcb);
screamer 0:7a64fbb4069d 586 pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
screamer 0:7a64fbb4069d 587 if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) {
screamer 0:7a64fbb4069d 588 pcb->snd_max = pcb->snd_nxt;
screamer 0:7a64fbb4069d 589 }
screamer 0:7a64fbb4069d 590 /* put segment on unacknowledged list if length > 0 */
screamer 0:7a64fbb4069d 591 if (TCP_TCPLEN(seg) > 0) {
screamer 0:7a64fbb4069d 592 seg->next = NULL;
screamer 0:7a64fbb4069d 593 /* unacked list is empty? */
screamer 0:7a64fbb4069d 594 if (pcb->unacked == NULL) {
screamer 0:7a64fbb4069d 595 pcb->unacked = seg;
screamer 0:7a64fbb4069d 596 useg = seg;
screamer 0:7a64fbb4069d 597 /* unacked list is not empty? */
screamer 0:7a64fbb4069d 598 } else {
screamer 0:7a64fbb4069d 599 /* In the case of fast retransmit, the packet should not go to the tail
screamer 0:7a64fbb4069d 600 * of the unacked queue, but rather somewhere before it. We need to check for
screamer 0:7a64fbb4069d 601 * this case. -STJ Jul 27, 2004 */
screamer 0:7a64fbb4069d 602 if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){
screamer 0:7a64fbb4069d 603 /* add segment to before tail of unacked list, keeping the list sorted */
screamer 0:7a64fbb4069d 604 struct tcp_seg **cur_seg = &(pcb->unacked);
screamer 0:7a64fbb4069d 605 while (*cur_seg &&
screamer 0:7a64fbb4069d 606 TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
screamer 0:7a64fbb4069d 607 cur_seg = &((*cur_seg)->next );
screamer 0:7a64fbb4069d 608 }
screamer 0:7a64fbb4069d 609 seg->next = (*cur_seg);
screamer 0:7a64fbb4069d 610 (*cur_seg) = seg;
screamer 0:7a64fbb4069d 611 } else {
screamer 0:7a64fbb4069d 612 /* add segment to tail of unacked list */
screamer 0:7a64fbb4069d 613 useg->next = seg;
screamer 0:7a64fbb4069d 614 useg = useg->next;
screamer 0:7a64fbb4069d 615 }
screamer 0:7a64fbb4069d 616 }
screamer 0:7a64fbb4069d 617 /* do not queue empty segments on the unacked list */
screamer 0:7a64fbb4069d 618 } else {
screamer 0:7a64fbb4069d 619 tcp_seg_free(seg);
screamer 0:7a64fbb4069d 620 }
screamer 0:7a64fbb4069d 621 seg = pcb->unsent;
screamer 0:7a64fbb4069d 622 }
screamer 0:7a64fbb4069d 623
screamer 0:7a64fbb4069d 624 if (seg != NULL && pcb->persist_backoff == 0 &&
screamer 0:7a64fbb4069d 625 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) {
screamer 0:7a64fbb4069d 626 /* prepare for persist timer */
screamer 0:7a64fbb4069d 627 pcb->persist_cnt = 0;
screamer 0:7a64fbb4069d 628 pcb->persist_backoff = 1;
screamer 0:7a64fbb4069d 629 }
screamer 0:7a64fbb4069d 630
screamer 0:7a64fbb4069d 631 pcb->flags &= ~TF_NAGLEMEMERR;
screamer 0:7a64fbb4069d 632 return ERR_OK;
screamer 0:7a64fbb4069d 633 }
screamer 0:7a64fbb4069d 634
screamer 0:7a64fbb4069d 635 /**
screamer 0:7a64fbb4069d 636 * Called by tcp_output() to actually send a TCP segment over IP.
screamer 0:7a64fbb4069d 637 *
screamer 0:7a64fbb4069d 638 * @param seg the tcp_seg to send
screamer 0:7a64fbb4069d 639 * @param pcb the tcp_pcb for the TCP connection used to send the segment
screamer 0:7a64fbb4069d 640 */
screamer 0:7a64fbb4069d 641 static void
screamer 0:7a64fbb4069d 642 tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
screamer 0:7a64fbb4069d 643 {
screamer 0:7a64fbb4069d 644 u16_t len;
screamer 0:7a64fbb4069d 645 struct netif *netif;
screamer 0:7a64fbb4069d 646 u32_t *opts;
screamer 0:7a64fbb4069d 647
screamer 0:7a64fbb4069d 648 /** @bug Exclude retransmitted segments from this count. */
screamer 0:7a64fbb4069d 649 snmp_inc_tcpoutsegs();
screamer 0:7a64fbb4069d 650
screamer 0:7a64fbb4069d 651 /* The TCP header has already been constructed, but the ackno and
screamer 0:7a64fbb4069d 652 wnd fields remain. */
screamer 0:7a64fbb4069d 653 seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
screamer 0:7a64fbb4069d 654
screamer 0:7a64fbb4069d 655 /* advertise our receive window size in this TCP segment */
screamer 0:7a64fbb4069d 656 seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
screamer 0:7a64fbb4069d 657
screamer 0:7a64fbb4069d 658 pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
screamer 0:7a64fbb4069d 659
screamer 0:7a64fbb4069d 660 /* Add any requested options. NB MSS option is only set on SYN
screamer 0:7a64fbb4069d 661 packets, so ignore it here */
screamer 0:7a64fbb4069d 662 opts = (u32_t *)(seg->tcphdr + 1);
screamer 0:7a64fbb4069d 663 if (seg->flags & TF_SEG_OPTS_MSS) {
screamer 0:7a64fbb4069d 664 TCP_BUILD_MSS_OPTION(*opts);
screamer 0:7a64fbb4069d 665 opts += 1;
screamer 0:7a64fbb4069d 666 }
screamer 0:7a64fbb4069d 667 #if LWIP_TCP_TIMESTAMPS
screamer 0:7a64fbb4069d 668 pcb->ts_lastacksent = pcb->rcv_nxt;
screamer 0:7a64fbb4069d 669
screamer 0:7a64fbb4069d 670 if (seg->flags & TF_SEG_OPTS_TS) {
screamer 0:7a64fbb4069d 671 tcp_build_timestamp_option(pcb, opts);
screamer 0:7a64fbb4069d 672 opts += 3;
screamer 0:7a64fbb4069d 673 }
screamer 0:7a64fbb4069d 674 #endif
screamer 0:7a64fbb4069d 675
screamer 0:7a64fbb4069d 676 /* If we don't have a local IP address, we get one by
screamer 0:7a64fbb4069d 677 calling ip_route(). */
screamer 0:7a64fbb4069d 678 if (ip_addr_isany(&(pcb->local_ip))) {
screamer 0:7a64fbb4069d 679 netif = ip_route(&(pcb->remote_ip));
screamer 0:7a64fbb4069d 680 if (netif == NULL) {
screamer 0:7a64fbb4069d 681 return;
screamer 0:7a64fbb4069d 682 }
screamer 0:7a64fbb4069d 683 ip_addr_set(&(pcb->local_ip), &(netif->ip_addr));
screamer 0:7a64fbb4069d 684 }
screamer 0:7a64fbb4069d 685
screamer 0:7a64fbb4069d 686 /* Set retransmission timer running if it is not currently enabled */
screamer 0:7a64fbb4069d 687 if(pcb->rtime == -1)
screamer 0:7a64fbb4069d 688 pcb->rtime = 0;
screamer 0:7a64fbb4069d 689
screamer 0:7a64fbb4069d 690 if (pcb->rttest == 0) {
screamer 0:7a64fbb4069d 691 pcb->rttest = tcp_ticks;
screamer 0:7a64fbb4069d 692 pcb->rtseq = ntohl(seg->tcphdr->seqno);
screamer 0:7a64fbb4069d 693
screamer 0:7a64fbb4069d 694 LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
screamer 0:7a64fbb4069d 695 }
screamer 0:7a64fbb4069d 696 LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
screamer 0:7a64fbb4069d 697 htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
screamer 0:7a64fbb4069d 698 seg->len));
screamer 0:7a64fbb4069d 699
screamer 0:7a64fbb4069d 700 len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
screamer 0:7a64fbb4069d 701
screamer 0:7a64fbb4069d 702 seg->p->len -= len;
screamer 0:7a64fbb4069d 703 seg->p->tot_len -= len;
screamer 0:7a64fbb4069d 704
screamer 0:7a64fbb4069d 705 seg->p->payload = seg->tcphdr;
screamer 0:7a64fbb4069d 706
screamer 0:7a64fbb4069d 707 seg->tcphdr->chksum = 0;
screamer 0:7a64fbb4069d 708 #if CHECKSUM_GEN_TCP
screamer 0:7a64fbb4069d 709 seg->tcphdr->chksum = inet_chksum_pseudo(seg->p,
screamer 0:7a64fbb4069d 710 &(pcb->local_ip),
screamer 0:7a64fbb4069d 711 &(pcb->remote_ip),
screamer 0:7a64fbb4069d 712 IP_PROTO_TCP, seg->p->tot_len);
screamer 0:7a64fbb4069d 713 #endif
screamer 0:7a64fbb4069d 714 TCP_STATS_INC(tcp.xmit);
screamer 0:7a64fbb4069d 715
screamer 0:7a64fbb4069d 716 #if LWIP_NETIF_HWADDRHINT
screamer 0:7a64fbb4069d 717 ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
screamer 0:7a64fbb4069d 718 IP_PROTO_TCP, &(pcb->addr_hint));
screamer 0:7a64fbb4069d 719 #else /* LWIP_NETIF_HWADDRHINT*/
screamer 0:7a64fbb4069d 720 ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
screamer 0:7a64fbb4069d 721 IP_PROTO_TCP);
screamer 0:7a64fbb4069d 722 #endif /* LWIP_NETIF_HWADDRHINT*/
screamer 0:7a64fbb4069d 723 }
screamer 0:7a64fbb4069d 724
screamer 0:7a64fbb4069d 725 /**
screamer 0:7a64fbb4069d 726 * Send a TCP RESET packet (empty segment with RST flag set) either to
screamer 0:7a64fbb4069d 727 * abort a connection or to show that there is no matching local connection
screamer 0:7a64fbb4069d 728 * for a received segment.
screamer 0:7a64fbb4069d 729 *
screamer 0:7a64fbb4069d 730 * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
screamer 0:7a64fbb4069d 731 * matching local pcb was found), tcp_listen_input() (if incoming segment
screamer 0:7a64fbb4069d 732 * has ACK flag set) and tcp_process() (received segment in the wrong state)
screamer 0:7a64fbb4069d 733 *
screamer 0:7a64fbb4069d 734 * Since a RST segment is in most cases not sent for an active connection,
screamer 0:7a64fbb4069d 735 * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
screamer 0:7a64fbb4069d 736 * most other segment output functions.
screamer 0:7a64fbb4069d 737 *
screamer 0:7a64fbb4069d 738 * @param seqno the sequence number to use for the outgoing segment
screamer 0:7a64fbb4069d 739 * @param ackno the acknowledge number to use for the outgoing segment
screamer 0:7a64fbb4069d 740 * @param local_ip the local IP address to send the segment from
screamer 0:7a64fbb4069d 741 * @param remote_ip the remote IP address to send the segment to
screamer 0:7a64fbb4069d 742 * @param local_port the local TCP port to send the segment from
screamer 0:7a64fbb4069d 743 * @param remote_port the remote TCP port to send the segment to
screamer 0:7a64fbb4069d 744 */
screamer 0:7a64fbb4069d 745 void
screamer 0:7a64fbb4069d 746 tcp_rst(u32_t seqno, u32_t ackno,
screamer 0:7a64fbb4069d 747 struct ip_addr *local_ip, struct ip_addr *remote_ip,
screamer 0:7a64fbb4069d 748 u16_t local_port, u16_t remote_port)
screamer 0:7a64fbb4069d 749 {
screamer 0:7a64fbb4069d 750 struct pbuf *p;
screamer 0:7a64fbb4069d 751 struct tcp_hdr *tcphdr;
screamer 0:7a64fbb4069d 752 p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
screamer 0:7a64fbb4069d 753 if (p == NULL) {
screamer 0:7a64fbb4069d 754 LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
screamer 0:7a64fbb4069d 755 return;
screamer 0:7a64fbb4069d 756 }
screamer 0:7a64fbb4069d 757 LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
screamer 0:7a64fbb4069d 758 (p->len >= sizeof(struct tcp_hdr)));
screamer 0:7a64fbb4069d 759
screamer 0:7a64fbb4069d 760 tcphdr = (struct tcp_hdr *)(p->payload);
screamer 0:7a64fbb4069d 761 tcphdr->src = htons(local_port);
screamer 0:7a64fbb4069d 762 tcphdr->dest = htons(remote_port);
screamer 0:7a64fbb4069d 763 tcphdr->seqno = htonl(seqno);
screamer 0:7a64fbb4069d 764 tcphdr->ackno = htonl(ackno);
screamer 0:7a64fbb4069d 765 TCPH_FLAGS_SET(tcphdr, TCP_RST | TCP_ACK);
screamer 0:7a64fbb4069d 766 tcphdr->wnd = htons(TCP_WND);
screamer 0:7a64fbb4069d 767 tcphdr->urgp = 0;
screamer 0:7a64fbb4069d 768 TCPH_HDRLEN_SET(tcphdr, 5);
screamer 0:7a64fbb4069d 769
screamer 0:7a64fbb4069d 770 tcphdr->chksum = 0;
screamer 0:7a64fbb4069d 771 #if CHECKSUM_GEN_TCP
screamer 0:7a64fbb4069d 772 tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip,
screamer 0:7a64fbb4069d 773 IP_PROTO_TCP, p->tot_len);
screamer 0:7a64fbb4069d 774 #endif
screamer 0:7a64fbb4069d 775 TCP_STATS_INC(tcp.xmit);
screamer 0:7a64fbb4069d 776 snmp_inc_tcpoutrsts();
screamer 0:7a64fbb4069d 777 /* Send output with hardcoded TTL since we have no access to the pcb */
screamer 0:7a64fbb4069d 778 ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP);
screamer 0:7a64fbb4069d 779 pbuf_free(p);
screamer 0:7a64fbb4069d 780 LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
screamer 0:7a64fbb4069d 781 }
screamer 0:7a64fbb4069d 782
screamer 0:7a64fbb4069d 783 /**
screamer 0:7a64fbb4069d 784 * Requeue all unacked segments for retransmission
screamer 0:7a64fbb4069d 785 *
screamer 0:7a64fbb4069d 786 * Called by tcp_slowtmr() for slow retransmission.
screamer 0:7a64fbb4069d 787 *
screamer 0:7a64fbb4069d 788 * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
screamer 0:7a64fbb4069d 789 */
screamer 0:7a64fbb4069d 790 void
screamer 0:7a64fbb4069d 791 tcp_rexmit_rto(struct tcp_pcb *pcb)
screamer 0:7a64fbb4069d 792 {
screamer 0:7a64fbb4069d 793 struct tcp_seg *seg;
screamer 0:7a64fbb4069d 794
screamer 0:7a64fbb4069d 795 if (pcb->unacked == NULL) {
screamer 0:7a64fbb4069d 796 return;
screamer 0:7a64fbb4069d 797 }
screamer 0:7a64fbb4069d 798
screamer 0:7a64fbb4069d 799 /* Move all unacked segments to the head of the unsent queue */
screamer 0:7a64fbb4069d 800 for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
screamer 0:7a64fbb4069d 801 /* concatenate unsent queue after unacked queue */
screamer 0:7a64fbb4069d 802 seg->next = pcb->unsent;
screamer 0:7a64fbb4069d 803 /* unsent queue is the concatenated queue (of unacked, unsent) */
screamer 0:7a64fbb4069d 804 pcb->unsent = pcb->unacked;
screamer 0:7a64fbb4069d 805 /* unacked queue is now empty */
screamer 0:7a64fbb4069d 806 pcb->unacked = NULL;
screamer 0:7a64fbb4069d 807
screamer 0:7a64fbb4069d 808 pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno);
screamer 0:7a64fbb4069d 809 /* increment number of retransmissions */
screamer 0:7a64fbb4069d 810 ++pcb->nrtx;
screamer 0:7a64fbb4069d 811
screamer 0:7a64fbb4069d 812 /* Don't take any RTT measurements after retransmitting. */
screamer 0:7a64fbb4069d 813 pcb->rttest = 0;
screamer 0:7a64fbb4069d 814
screamer 0:7a64fbb4069d 815 /* Do the actual retransmission */
screamer 0:7a64fbb4069d 816 tcp_output(pcb);
screamer 0:7a64fbb4069d 817 }
screamer 0:7a64fbb4069d 818
screamer 0:7a64fbb4069d 819 /**
screamer 0:7a64fbb4069d 820 * Requeue the first unacked segment for retransmission
screamer 0:7a64fbb4069d 821 *
screamer 0:7a64fbb4069d 822 * Called by tcp_receive() for fast retramsmit.
screamer 0:7a64fbb4069d 823 *
screamer 0:7a64fbb4069d 824 * @param pcb the tcp_pcb for which to retransmit the first unacked segment
screamer 0:7a64fbb4069d 825 */
screamer 0:7a64fbb4069d 826 void
screamer 0:7a64fbb4069d 827 tcp_rexmit(struct tcp_pcb *pcb)
screamer 0:7a64fbb4069d 828 {
screamer 0:7a64fbb4069d 829 struct tcp_seg *seg;
screamer 0:7a64fbb4069d 830 struct tcp_seg **cur_seg;
screamer 0:7a64fbb4069d 831
screamer 0:7a64fbb4069d 832 if (pcb->unacked == NULL) {
screamer 0:7a64fbb4069d 833 return;
screamer 0:7a64fbb4069d 834 }
screamer 0:7a64fbb4069d 835
screamer 0:7a64fbb4069d 836 /* Move the first unacked segment to the unsent queue */
screamer 0:7a64fbb4069d 837 /* Keep the unsent queue sorted. */
screamer 0:7a64fbb4069d 838 seg = pcb->unacked;
screamer 0:7a64fbb4069d 839 pcb->unacked = seg->next;
screamer 0:7a64fbb4069d 840
screamer 0:7a64fbb4069d 841 cur_seg = &(pcb->unsent);
screamer 0:7a64fbb4069d 842 while (*cur_seg &&
screamer 0:7a64fbb4069d 843 TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
screamer 0:7a64fbb4069d 844 cur_seg = &((*cur_seg)->next );
screamer 0:7a64fbb4069d 845 }
screamer 0:7a64fbb4069d 846 seg->next = *cur_seg;
screamer 0:7a64fbb4069d 847 *cur_seg = seg;
screamer 0:7a64fbb4069d 848
screamer 0:7a64fbb4069d 849 pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno);
screamer 0:7a64fbb4069d 850
screamer 0:7a64fbb4069d 851 ++pcb->nrtx;
screamer 0:7a64fbb4069d 852
screamer 0:7a64fbb4069d 853 /* Don't take any rtt measurements after retransmitting. */
screamer 0:7a64fbb4069d 854 pcb->rttest = 0;
screamer 0:7a64fbb4069d 855
screamer 0:7a64fbb4069d 856 /* Do the actual retransmission. */
screamer 0:7a64fbb4069d 857 snmp_inc_tcpretranssegs();
screamer 0:7a64fbb4069d 858 tcp_output(pcb);
screamer 0:7a64fbb4069d 859 }
screamer 0:7a64fbb4069d 860
screamer 0:7a64fbb4069d 861 /**
screamer 0:7a64fbb4069d 862 * Send keepalive packets to keep a connection active although
screamer 0:7a64fbb4069d 863 * no data is sent over it.
screamer 0:7a64fbb4069d 864 *
screamer 0:7a64fbb4069d 865 * Called by tcp_slowtmr()
screamer 0:7a64fbb4069d 866 *
screamer 0:7a64fbb4069d 867 * @param pcb the tcp_pcb for which to send a keepalive packet
screamer 0:7a64fbb4069d 868 */
screamer 0:7a64fbb4069d 869 void
screamer 0:7a64fbb4069d 870 tcp_keepalive(struct tcp_pcb *pcb)
screamer 0:7a64fbb4069d 871 {
screamer 0:7a64fbb4069d 872 struct pbuf *p;
screamer 0:7a64fbb4069d 873 struct tcp_hdr *tcphdr;
screamer 0:7a64fbb4069d 874
screamer 0:7a64fbb4069d 875 LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
screamer 0:7a64fbb4069d 876 ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
screamer 0:7a64fbb4069d 877 ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip)));
screamer 0:7a64fbb4069d 878
screamer 0:7a64fbb4069d 879 LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
screamer 0:7a64fbb4069d 880 tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
screamer 0:7a64fbb4069d 881
screamer 0:7a64fbb4069d 882 p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
screamer 0:7a64fbb4069d 883
screamer 0:7a64fbb4069d 884 if(p == NULL) {
screamer 0:7a64fbb4069d 885 LWIP_DEBUGF(TCP_DEBUG,
screamer 0:7a64fbb4069d 886 ("tcp_keepalive: could not allocate memory for pbuf\n"));
screamer 0:7a64fbb4069d 887 return;
screamer 0:7a64fbb4069d 888 }
screamer 0:7a64fbb4069d 889 LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
screamer 0:7a64fbb4069d 890 (p->len >= sizeof(struct tcp_hdr)));
screamer 0:7a64fbb4069d 891
screamer 0:7a64fbb4069d 892 tcphdr = tcp_output_set_header(pcb, p, 0);
screamer 0:7a64fbb4069d 893
screamer 0:7a64fbb4069d 894 tcphdr->seqno = htonl(pcb->snd_nxt - 1);
screamer 0:7a64fbb4069d 895
screamer 0:7a64fbb4069d 896 #if CHECKSUM_GEN_TCP
screamer 0:7a64fbb4069d 897 tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
screamer 0:7a64fbb4069d 898 IP_PROTO_TCP, p->tot_len);
screamer 0:7a64fbb4069d 899 #endif
screamer 0:7a64fbb4069d 900 TCP_STATS_INC(tcp.xmit);
screamer 0:7a64fbb4069d 901
screamer 0:7a64fbb4069d 902 /* Send output to IP */
screamer 0:7a64fbb4069d 903 #if LWIP_NETIF_HWADDRHINT
screamer 0:7a64fbb4069d 904 ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
screamer 0:7a64fbb4069d 905 &(pcb->addr_hint));
screamer 0:7a64fbb4069d 906 #else /* LWIP_NETIF_HWADDRHINT*/
screamer 0:7a64fbb4069d 907 ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
screamer 0:7a64fbb4069d 908 #endif /* LWIP_NETIF_HWADDRHINT*/
screamer 0:7a64fbb4069d 909
screamer 0:7a64fbb4069d 910 pbuf_free(p);
screamer 0:7a64fbb4069d 911
screamer 0:7a64fbb4069d 912 LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
screamer 0:7a64fbb4069d 913 pcb->snd_nxt - 1, pcb->rcv_nxt));
screamer 0:7a64fbb4069d 914 }
screamer 0:7a64fbb4069d 915
screamer 0:7a64fbb4069d 916
screamer 0:7a64fbb4069d 917 /**
screamer 0:7a64fbb4069d 918 * Send persist timer zero-window probes to keep a connection active
screamer 0:7a64fbb4069d 919 * when a window update is lost.
screamer 0:7a64fbb4069d 920 *
screamer 0:7a64fbb4069d 921 * Called by tcp_slowtmr()
screamer 0:7a64fbb4069d 922 *
screamer 0:7a64fbb4069d 923 * @param pcb the tcp_pcb for which to send a zero-window probe packet
screamer 0:7a64fbb4069d 924 */
screamer 0:7a64fbb4069d 925 void
screamer 0:7a64fbb4069d 926 tcp_zero_window_probe(struct tcp_pcb *pcb)
screamer 0:7a64fbb4069d 927 {
screamer 0:7a64fbb4069d 928 struct pbuf *p;
screamer 0:7a64fbb4069d 929 struct tcp_hdr *tcphdr;
screamer 0:7a64fbb4069d 930 struct tcp_seg *seg;
screamer 0:7a64fbb4069d 931
screamer 0:7a64fbb4069d 932 LWIP_DEBUGF(TCP_DEBUG,
screamer 0:7a64fbb4069d 933 ("tcp_zero_window_probe: sending ZERO WINDOW probe to %"
screamer 0:7a64fbb4069d 934 U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
screamer 0:7a64fbb4069d 935 ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
screamer 0:7a64fbb4069d 936 ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip)));
screamer 0:7a64fbb4069d 937
screamer 0:7a64fbb4069d 938 LWIP_DEBUGF(TCP_DEBUG,
screamer 0:7a64fbb4069d 939 ("tcp_zero_window_probe: tcp_ticks %"U32_F
screamer 0:7a64fbb4069d 940 " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
screamer 0:7a64fbb4069d 941 tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
screamer 0:7a64fbb4069d 942
screamer 0:7a64fbb4069d 943 seg = pcb->unacked;
screamer 0:7a64fbb4069d 944
screamer 0:7a64fbb4069d 945 if(seg == NULL)
screamer 0:7a64fbb4069d 946 seg = pcb->unsent;
screamer 0:7a64fbb4069d 947
screamer 0:7a64fbb4069d 948 if(seg == NULL)
screamer 0:7a64fbb4069d 949 return;
screamer 0:7a64fbb4069d 950
screamer 0:7a64fbb4069d 951 p = pbuf_alloc(PBUF_IP, TCP_HLEN + 1, PBUF_RAM);
screamer 0:7a64fbb4069d 952
screamer 0:7a64fbb4069d 953 if(p == NULL) {
screamer 0:7a64fbb4069d 954 LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
screamer 0:7a64fbb4069d 955 return;
screamer 0:7a64fbb4069d 956 }
screamer 0:7a64fbb4069d 957 LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
screamer 0:7a64fbb4069d 958 (p->len >= sizeof(struct tcp_hdr)));
screamer 0:7a64fbb4069d 959
screamer 0:7a64fbb4069d 960 tcphdr = tcp_output_set_header(pcb, p, 0);
screamer 0:7a64fbb4069d 961
screamer 0:7a64fbb4069d 962 tcphdr->seqno = seg->tcphdr->seqno;
screamer 0:7a64fbb4069d 963
screamer 0:7a64fbb4069d 964 /* Copy in one byte from the head of the unacked queue */
screamer 0:7a64fbb4069d 965 *((char *)p->payload + sizeof(struct tcp_hdr)) = *(char *)seg->dataptr;
screamer 0:7a64fbb4069d 966
screamer 0:7a64fbb4069d 967 #if CHECKSUM_GEN_TCP
screamer 0:7a64fbb4069d 968 tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
screamer 0:7a64fbb4069d 969 IP_PROTO_TCP, p->tot_len);
screamer 0:7a64fbb4069d 970 #endif
screamer 0:7a64fbb4069d 971 TCP_STATS_INC(tcp.xmit);
screamer 0:7a64fbb4069d 972
screamer 0:7a64fbb4069d 973 /* Send output to IP */
screamer 0:7a64fbb4069d 974 #if LWIP_NETIF_HWADDRHINT
screamer 0:7a64fbb4069d 975 ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
screamer 0:7a64fbb4069d 976 &(pcb->addr_hint));
screamer 0:7a64fbb4069d 977 #else /* LWIP_NETIF_HWADDRHINT*/
screamer 0:7a64fbb4069d 978 ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
screamer 0:7a64fbb4069d 979 #endif /* LWIP_NETIF_HWADDRHINT*/
screamer 0:7a64fbb4069d 980
screamer 0:7a64fbb4069d 981 pbuf_free(p);
screamer 0:7a64fbb4069d 982
screamer 0:7a64fbb4069d 983 LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
screamer 0:7a64fbb4069d 984 " ackno %"U32_F".\n",
screamer 0:7a64fbb4069d 985 pcb->snd_nxt - 1, pcb->rcv_nxt));
screamer 0:7a64fbb4069d 986 }
screamer 0:7a64fbb4069d 987 #endif /* LWIP_TCP */