Example program with HTTPServer and sensor data streaming over TCPSockets, using Donatien Garnier's Net APIs and services code on top of LWIP. Files StreamServer.h and .cpp encapsulate streaming over TCPSockets. Broadcast is done by sendToAll(), and all incoming data is echoed back to the client. Echo code can be replaced with some remote control of the streaming interface. See main() that shows how to periodically send some data to all subscribed clients. To subscribe, a client should open a socket at <mbed_ip> port 123. I used few lines in TCL code to set up a quick sink for the data. HTTP files are served on port 80 concurrently to the streaming.

Dependencies:   mbed

Committer:
iva2k
Date:
Mon Jun 14 03:24:33 2010 +0000
Revision:
1:3ee499525aa5
Parent:
0:e614f7875b60

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
iva2k 0:e614f7875b60 1 /**
iva2k 0:e614f7875b60 2 * @file
iva2k 0:e614f7875b60 3 * Transmission Control Protocol, incoming traffic
iva2k 0:e614f7875b60 4 *
iva2k 0:e614f7875b60 5 * The input processing functions of the TCP layer.
iva2k 0:e614f7875b60 6 *
iva2k 0:e614f7875b60 7 * These functions are generally called in the order (ip_input() ->)
iva2k 0:e614f7875b60 8 * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
iva2k 0:e614f7875b60 9 *
iva2k 0:e614f7875b60 10 */
iva2k 0:e614f7875b60 11
iva2k 0:e614f7875b60 12 /*
iva2k 0:e614f7875b60 13 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
iva2k 0:e614f7875b60 14 * All rights reserved.
iva2k 0:e614f7875b60 15 *
iva2k 0:e614f7875b60 16 * Redistribution and use in source and binary forms, with or without modification,
iva2k 0:e614f7875b60 17 * are permitted provided that the following conditions are met:
iva2k 0:e614f7875b60 18 *
iva2k 0:e614f7875b60 19 * 1. Redistributions of source code must retain the above copyright notice,
iva2k 0:e614f7875b60 20 * this list of conditions and the following disclaimer.
iva2k 0:e614f7875b60 21 * 2. Redistributions in binary form must reproduce the above copyright notice,
iva2k 0:e614f7875b60 22 * this list of conditions and the following disclaimer in the documentation
iva2k 0:e614f7875b60 23 * and/or other materials provided with the distribution.
iva2k 0:e614f7875b60 24 * 3. The name of the author may not be used to endorse or promote products
iva2k 0:e614f7875b60 25 * derived from this software without specific prior written permission.
iva2k 0:e614f7875b60 26 *
iva2k 0:e614f7875b60 27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
iva2k 0:e614f7875b60 28 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
iva2k 0:e614f7875b60 29 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
iva2k 0:e614f7875b60 30 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
iva2k 0:e614f7875b60 31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
iva2k 0:e614f7875b60 32 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
iva2k 0:e614f7875b60 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
iva2k 0:e614f7875b60 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
iva2k 0:e614f7875b60 35 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
iva2k 0:e614f7875b60 36 * OF SUCH DAMAGE.
iva2k 0:e614f7875b60 37 *
iva2k 0:e614f7875b60 38 * This file is part of the lwIP TCP/IP stack.
iva2k 0:e614f7875b60 39 *
iva2k 0:e614f7875b60 40 * Author: Adam Dunkels <adam@sics.se>
iva2k 0:e614f7875b60 41 *
iva2k 0:e614f7875b60 42 */
iva2k 0:e614f7875b60 43
iva2k 0:e614f7875b60 44 #include "lwip/opt.h"
iva2k 0:e614f7875b60 45
iva2k 0:e614f7875b60 46 #if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
iva2k 0:e614f7875b60 47
iva2k 0:e614f7875b60 48 #include "lwip/tcp_impl.h"
iva2k 0:e614f7875b60 49 #include "lwip/def.h"
iva2k 0:e614f7875b60 50 #include "lwip/ip_addr.h"
iva2k 0:e614f7875b60 51 #include "lwip/netif.h"
iva2k 0:e614f7875b60 52 #include "lwip/mem.h"
iva2k 0:e614f7875b60 53 #include "lwip/memp.h"
iva2k 0:e614f7875b60 54 #include "lwip/inet_chksum.h"
iva2k 0:e614f7875b60 55 #include "lwip/stats.h"
iva2k 0:e614f7875b60 56 #include "lwip/snmp.h"
iva2k 0:e614f7875b60 57 #include "arch/perf.h"
iva2k 0:e614f7875b60 58
iva2k 0:e614f7875b60 59 /* These variables are global to all functions involved in the input
iva2k 0:e614f7875b60 60 processing of TCP segments. They are set by the tcp_input()
iva2k 0:e614f7875b60 61 function. */
iva2k 0:e614f7875b60 62 static struct tcp_seg inseg;
iva2k 0:e614f7875b60 63 static struct tcp_hdr *tcphdr;
iva2k 0:e614f7875b60 64 static struct ip_hdr *iphdr;
iva2k 0:e614f7875b60 65 static u32_t seqno, ackno;
iva2k 0:e614f7875b60 66 static u8_t flags;
iva2k 0:e614f7875b60 67 static u16_t tcplen;
iva2k 0:e614f7875b60 68
iva2k 0:e614f7875b60 69 static u8_t recv_flags;
iva2k 0:e614f7875b60 70 static struct pbuf *recv_data;
iva2k 0:e614f7875b60 71
iva2k 0:e614f7875b60 72 struct tcp_pcb *tcp_input_pcb;
iva2k 0:e614f7875b60 73
iva2k 0:e614f7875b60 74 /* Forward declarations. */
iva2k 0:e614f7875b60 75 static err_t tcp_process(struct tcp_pcb *pcb);
iva2k 0:e614f7875b60 76 static void tcp_receive(struct tcp_pcb *pcb);
iva2k 0:e614f7875b60 77 static void tcp_parseopt(struct tcp_pcb *pcb);
iva2k 0:e614f7875b60 78
iva2k 0:e614f7875b60 79 static err_t tcp_listen_input(struct tcp_pcb_listen *pcb);
iva2k 0:e614f7875b60 80 static err_t tcp_timewait_input(struct tcp_pcb *pcb);
iva2k 0:e614f7875b60 81
iva2k 0:e614f7875b60 82 /**
iva2k 0:e614f7875b60 83 * The initial input processing of TCP. It verifies the TCP header, demultiplexes
iva2k 0:e614f7875b60 84 * the segment between the PCBs and passes it on to tcp_process(), which implements
iva2k 0:e614f7875b60 85 * the TCP finite state machine. This function is called by the IP layer (in
iva2k 0:e614f7875b60 86 * ip_input()).
iva2k 0:e614f7875b60 87 *
iva2k 0:e614f7875b60 88 * @param p received TCP segment to process (p->payload pointing to the IP header)
iva2k 0:e614f7875b60 89 * @param inp network interface on which this segment was received
iva2k 0:e614f7875b60 90 */
iva2k 0:e614f7875b60 91 void
iva2k 0:e614f7875b60 92 tcp_input(struct pbuf *p, struct netif *inp)
iva2k 0:e614f7875b60 93 {
iva2k 0:e614f7875b60 94 struct tcp_pcb *pcb, *prev;
iva2k 0:e614f7875b60 95 struct tcp_pcb_listen *lpcb;
iva2k 0:e614f7875b60 96 u8_t hdrlen;
iva2k 0:e614f7875b60 97 err_t err;
iva2k 0:e614f7875b60 98
iva2k 0:e614f7875b60 99 PERF_START;
iva2k 0:e614f7875b60 100
iva2k 0:e614f7875b60 101 TCP_STATS_INC(tcp.recv);
iva2k 0:e614f7875b60 102 snmp_inc_tcpinsegs();
iva2k 0:e614f7875b60 103
iva2k 0:e614f7875b60 104 iphdr = (struct ip_hdr *)p->payload;
iva2k 0:e614f7875b60 105 tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
iva2k 0:e614f7875b60 106
iva2k 0:e614f7875b60 107 #if TCP_INPUT_DEBUG
iva2k 0:e614f7875b60 108 tcp_debug_print(tcphdr);
iva2k 0:e614f7875b60 109 #endif
iva2k 0:e614f7875b60 110
iva2k 0:e614f7875b60 111 /* remove header from payload */
iva2k 0:e614f7875b60 112 if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) {
iva2k 0:e614f7875b60 113 /* drop short packets */
iva2k 0:e614f7875b60 114 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
iva2k 0:e614f7875b60 115 TCP_STATS_INC(tcp.lenerr);
iva2k 0:e614f7875b60 116 TCP_STATS_INC(tcp.drop);
iva2k 0:e614f7875b60 117 snmp_inc_tcpinerrs();
iva2k 0:e614f7875b60 118 pbuf_free(p);
iva2k 0:e614f7875b60 119 return;
iva2k 0:e614f7875b60 120 }
iva2k 0:e614f7875b60 121
iva2k 0:e614f7875b60 122 /* Don't even process incoming broadcasts/multicasts. */
iva2k 0:e614f7875b60 123 if (ip_addr_isbroadcast(&(iphdr->dest), inp) ||
iva2k 0:e614f7875b60 124 ip_addr_ismulticast(&(iphdr->dest))) {
iva2k 0:e614f7875b60 125 TCP_STATS_INC(tcp.proterr);
iva2k 0:e614f7875b60 126 TCP_STATS_INC(tcp.drop);
iva2k 0:e614f7875b60 127 snmp_inc_tcpinerrs();
iva2k 0:e614f7875b60 128 pbuf_free(p);
iva2k 0:e614f7875b60 129 return;
iva2k 0:e614f7875b60 130 }
iva2k 0:e614f7875b60 131
iva2k 0:e614f7875b60 132 #if CHECKSUM_CHECK_TCP
iva2k 0:e614f7875b60 133 /* Verify TCP checksum. */
iva2k 0:e614f7875b60 134 if (inet_chksum_pseudo(p, &iphdr->src, &iphdr->dest,
iva2k 0:e614f7875b60 135 IP_PROTO_TCP, p->tot_len) != 0) {
iva2k 0:e614f7875b60 136 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
iva2k 0:e614f7875b60 137 inet_chksum_pseudo(p, &iphdr->src, &iphdr->dest,
iva2k 0:e614f7875b60 138 IP_PROTO_TCP, p->tot_len)));
iva2k 0:e614f7875b60 139 #if TCP_DEBUG
iva2k 0:e614f7875b60 140 tcp_debug_print(tcphdr);
iva2k 0:e614f7875b60 141 #endif /* TCP_DEBUG */
iva2k 0:e614f7875b60 142 TCP_STATS_INC(tcp.chkerr);
iva2k 0:e614f7875b60 143 TCP_STATS_INC(tcp.drop);
iva2k 0:e614f7875b60 144 snmp_inc_tcpinerrs();
iva2k 0:e614f7875b60 145 pbuf_free(p);
iva2k 0:e614f7875b60 146 return;
iva2k 0:e614f7875b60 147 }
iva2k 0:e614f7875b60 148 #endif
iva2k 0:e614f7875b60 149
iva2k 0:e614f7875b60 150 /* Move the payload pointer in the pbuf so that it points to the
iva2k 0:e614f7875b60 151 TCP data instead of the TCP header. */
iva2k 0:e614f7875b60 152 hdrlen = TCPH_HDRLEN(tcphdr);
iva2k 0:e614f7875b60 153 if(pbuf_header(p, -(hdrlen * 4))){
iva2k 0:e614f7875b60 154 /* drop short packets */
iva2k 0:e614f7875b60 155 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
iva2k 0:e614f7875b60 156 TCP_STATS_INC(tcp.lenerr);
iva2k 0:e614f7875b60 157 TCP_STATS_INC(tcp.drop);
iva2k 0:e614f7875b60 158 snmp_inc_tcpinerrs();
iva2k 0:e614f7875b60 159 pbuf_free(p);
iva2k 0:e614f7875b60 160 return;
iva2k 0:e614f7875b60 161 }
iva2k 0:e614f7875b60 162
iva2k 0:e614f7875b60 163 /* Convert fields in TCP header to host byte order. */
iva2k 0:e614f7875b60 164 tcphdr->src = ntohs(tcphdr->src);
iva2k 0:e614f7875b60 165 tcphdr->dest = ntohs(tcphdr->dest);
iva2k 0:e614f7875b60 166 seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
iva2k 0:e614f7875b60 167 ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
iva2k 0:e614f7875b60 168 tcphdr->wnd = ntohs(tcphdr->wnd);
iva2k 0:e614f7875b60 169
iva2k 0:e614f7875b60 170 flags = TCPH_FLAGS(tcphdr);
iva2k 0:e614f7875b60 171 tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);
iva2k 0:e614f7875b60 172
iva2k 0:e614f7875b60 173 /* Demultiplex an incoming segment. First, we check if it is destined
iva2k 0:e614f7875b60 174 for an active connection. */
iva2k 0:e614f7875b60 175 prev = NULL;
iva2k 0:e614f7875b60 176
iva2k 0:e614f7875b60 177
iva2k 0:e614f7875b60 178 for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
iva2k 0:e614f7875b60 179 LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
iva2k 0:e614f7875b60 180 LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
iva2k 0:e614f7875b60 181 LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
iva2k 0:e614f7875b60 182 if (pcb->remote_port == tcphdr->src &&
iva2k 0:e614f7875b60 183 pcb->local_port == tcphdr->dest &&
iva2k 0:e614f7875b60 184 ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) &&
iva2k 0:e614f7875b60 185 ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) {
iva2k 0:e614f7875b60 186
iva2k 0:e614f7875b60 187 /* Move this PCB to the front of the list so that subsequent
iva2k 0:e614f7875b60 188 lookups will be faster (we exploit locality in TCP segment
iva2k 0:e614f7875b60 189 arrivals). */
iva2k 0:e614f7875b60 190 LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
iva2k 0:e614f7875b60 191 if (prev != NULL) {
iva2k 0:e614f7875b60 192 prev->next = pcb->next;
iva2k 0:e614f7875b60 193 pcb->next = tcp_active_pcbs;
iva2k 0:e614f7875b60 194 tcp_active_pcbs = pcb;
iva2k 0:e614f7875b60 195 }
iva2k 0:e614f7875b60 196 LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
iva2k 0:e614f7875b60 197 break;
iva2k 0:e614f7875b60 198 }
iva2k 0:e614f7875b60 199 prev = pcb;
iva2k 0:e614f7875b60 200 }
iva2k 0:e614f7875b60 201
iva2k 0:e614f7875b60 202 if (pcb == NULL) {
iva2k 0:e614f7875b60 203 /* If it did not go to an active connection, we check the connections
iva2k 0:e614f7875b60 204 in the TIME-WAIT state. */
iva2k 0:e614f7875b60 205 for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
iva2k 0:e614f7875b60 206 LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
iva2k 0:e614f7875b60 207 if (pcb->remote_port == tcphdr->src &&
iva2k 0:e614f7875b60 208 pcb->local_port == tcphdr->dest &&
iva2k 0:e614f7875b60 209 ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) &&
iva2k 0:e614f7875b60 210 ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) {
iva2k 0:e614f7875b60 211 /* We don't really care enough to move this PCB to the front
iva2k 0:e614f7875b60 212 of the list since we are not very likely to receive that
iva2k 0:e614f7875b60 213 many segments for connections in TIME-WAIT. */
iva2k 0:e614f7875b60 214 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
iva2k 0:e614f7875b60 215 tcp_timewait_input(pcb);
iva2k 0:e614f7875b60 216 pbuf_free(p);
iva2k 0:e614f7875b60 217 return;
iva2k 0:e614f7875b60 218 }
iva2k 0:e614f7875b60 219 }
iva2k 0:e614f7875b60 220
iva2k 0:e614f7875b60 221 /* Finally, if we still did not get a match, we check all PCBs that
iva2k 0:e614f7875b60 222 are LISTENing for incoming connections. */
iva2k 0:e614f7875b60 223 prev = NULL;
iva2k 0:e614f7875b60 224 for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
iva2k 0:e614f7875b60 225 if ((ip_addr_isany(&(lpcb->local_ip)) ||
iva2k 0:e614f7875b60 226 ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) &&
iva2k 0:e614f7875b60 227 lpcb->local_port == tcphdr->dest) {
iva2k 0:e614f7875b60 228 /* Move this PCB to the front of the list so that subsequent
iva2k 0:e614f7875b60 229 lookups will be faster (we exploit locality in TCP segment
iva2k 0:e614f7875b60 230 arrivals). */
iva2k 0:e614f7875b60 231 if (prev != NULL) {
iva2k 0:e614f7875b60 232 ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
iva2k 0:e614f7875b60 233 /* our successor is the remainder of the listening list */
iva2k 0:e614f7875b60 234 lpcb->next = tcp_listen_pcbs.listen_pcbs;
iva2k 0:e614f7875b60 235 /* put this listening pcb at the head of the listening list */
iva2k 0:e614f7875b60 236 tcp_listen_pcbs.listen_pcbs = lpcb;
iva2k 0:e614f7875b60 237 }
iva2k 0:e614f7875b60 238
iva2k 0:e614f7875b60 239 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
iva2k 0:e614f7875b60 240 tcp_listen_input(lpcb);
iva2k 0:e614f7875b60 241 pbuf_free(p);
iva2k 0:e614f7875b60 242 return;
iva2k 0:e614f7875b60 243 }
iva2k 0:e614f7875b60 244 prev = (struct tcp_pcb *)lpcb;
iva2k 0:e614f7875b60 245 }
iva2k 0:e614f7875b60 246 }
iva2k 0:e614f7875b60 247
iva2k 0:e614f7875b60 248 #if TCP_INPUT_DEBUG
iva2k 0:e614f7875b60 249 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
iva2k 0:e614f7875b60 250 tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
iva2k 0:e614f7875b60 251 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
iva2k 0:e614f7875b60 252 #endif /* TCP_INPUT_DEBUG */
iva2k 0:e614f7875b60 253
iva2k 0:e614f7875b60 254
iva2k 0:e614f7875b60 255 if (pcb != NULL) {
iva2k 0:e614f7875b60 256 /* The incoming segment belongs to a connection. */
iva2k 0:e614f7875b60 257 #if TCP_INPUT_DEBUG
iva2k 0:e614f7875b60 258 #if TCP_DEBUG
iva2k 0:e614f7875b60 259 tcp_debug_print_state(pcb->state);
iva2k 0:e614f7875b60 260 #endif /* TCP_DEBUG */
iva2k 0:e614f7875b60 261 #endif /* TCP_INPUT_DEBUG */
iva2k 0:e614f7875b60 262
iva2k 0:e614f7875b60 263 /* Set up a tcp_seg structure. */
iva2k 0:e614f7875b60 264 inseg.next = NULL;
iva2k 0:e614f7875b60 265 inseg.len = p->tot_len;
iva2k 0:e614f7875b60 266 inseg.dataptr = p->payload;
iva2k 0:e614f7875b60 267 inseg.p = p;
iva2k 0:e614f7875b60 268 inseg.tcphdr = tcphdr;
iva2k 0:e614f7875b60 269
iva2k 0:e614f7875b60 270 recv_data = NULL;
iva2k 0:e614f7875b60 271 recv_flags = 0;
iva2k 0:e614f7875b60 272
iva2k 0:e614f7875b60 273 /* If there is data which was previously "refused" by upper layer */
iva2k 0:e614f7875b60 274 if (pcb->refused_data != NULL) {
iva2k 0:e614f7875b60 275 /* Notify again application with data previously received. */
iva2k 0:e614f7875b60 276 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
iva2k 0:e614f7875b60 277 TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
iva2k 0:e614f7875b60 278 if (err == ERR_OK) {
iva2k 0:e614f7875b60 279 pcb->refused_data = NULL;
iva2k 0:e614f7875b60 280 } else {
iva2k 0:e614f7875b60 281 /* if err == ERR_ABRT, 'pcb' is already deallocated */
iva2k 0:e614f7875b60 282 /* drop incoming packets, because pcb is "full" */
iva2k 0:e614f7875b60 283 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
iva2k 0:e614f7875b60 284 TCP_STATS_INC(tcp.drop);
iva2k 0:e614f7875b60 285 snmp_inc_tcpinerrs();
iva2k 0:e614f7875b60 286 pbuf_free(p);
iva2k 0:e614f7875b60 287 return;
iva2k 0:e614f7875b60 288 }
iva2k 0:e614f7875b60 289 }
iva2k 0:e614f7875b60 290 tcp_input_pcb = pcb;
iva2k 0:e614f7875b60 291 err = tcp_process(pcb);
iva2k 0:e614f7875b60 292 /* A return value of ERR_ABRT means that tcp_abort() was called
iva2k 0:e614f7875b60 293 and that the pcb has been freed. If so, we don't do anything. */
iva2k 0:e614f7875b60 294 if (err != ERR_ABRT) {
iva2k 0:e614f7875b60 295 if (recv_flags & TF_RESET) {
iva2k 0:e614f7875b60 296 /* TF_RESET means that the connection was reset by the other
iva2k 0:e614f7875b60 297 end. We then call the error callback to inform the
iva2k 0:e614f7875b60 298 application that the connection is dead before we
iva2k 0:e614f7875b60 299 deallocate the PCB. */
iva2k 0:e614f7875b60 300 TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
iva2k 0:e614f7875b60 301 tcp_pcb_remove(&tcp_active_pcbs, pcb);
iva2k 0:e614f7875b60 302 memp_free(MEMP_TCP_PCB, pcb);
iva2k 0:e614f7875b60 303 } else if (recv_flags & TF_CLOSED) {
iva2k 0:e614f7875b60 304 /* The connection has been closed and we will deallocate the
iva2k 0:e614f7875b60 305 PCB. */
iva2k 0:e614f7875b60 306 tcp_pcb_remove(&tcp_active_pcbs, pcb);
iva2k 0:e614f7875b60 307 memp_free(MEMP_TCP_PCB, pcb);
iva2k 0:e614f7875b60 308 } else {
iva2k 0:e614f7875b60 309 err = ERR_OK;
iva2k 0:e614f7875b60 310 /* If the application has registered a "sent" function to be
iva2k 0:e614f7875b60 311 called when new send buffer space is available, we call it
iva2k 0:e614f7875b60 312 now. */
iva2k 0:e614f7875b60 313 if (pcb->acked > 0) {
iva2k 0:e614f7875b60 314 TCP_EVENT_SENT(pcb, pcb->acked, err);
iva2k 0:e614f7875b60 315 if (err == ERR_ABRT) {
iva2k 0:e614f7875b60 316 goto aborted;
iva2k 0:e614f7875b60 317 }
iva2k 0:e614f7875b60 318 }
iva2k 0:e614f7875b60 319
iva2k 0:e614f7875b60 320 if (recv_data != NULL) {
iva2k 0:e614f7875b60 321 if(flags & TCP_PSH) {
iva2k 0:e614f7875b60 322 recv_data->flags |= PBUF_FLAG_PUSH;
iva2k 0:e614f7875b60 323 }
iva2k 0:e614f7875b60 324
iva2k 0:e614f7875b60 325 /* Notify application that data has been received. */
iva2k 0:e614f7875b60 326 TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
iva2k 0:e614f7875b60 327 if (err == ERR_ABRT) {
iva2k 0:e614f7875b60 328 goto aborted;
iva2k 0:e614f7875b60 329 }
iva2k 0:e614f7875b60 330
iva2k 0:e614f7875b60 331 /* If the upper layer can't receive this data, store it */
iva2k 0:e614f7875b60 332 if (err != ERR_OK) {
iva2k 0:e614f7875b60 333 pcb->refused_data = recv_data;
iva2k 0:e614f7875b60 334 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
iva2k 0:e614f7875b60 335 }
iva2k 0:e614f7875b60 336 }
iva2k 0:e614f7875b60 337
iva2k 0:e614f7875b60 338 /* If a FIN segment was received, we call the callback
iva2k 0:e614f7875b60 339 function with a NULL buffer to indicate EOF. */
iva2k 0:e614f7875b60 340 if (recv_flags & TF_GOT_FIN) {
iva2k 0:e614f7875b60 341 /* correct rcv_wnd as the application won't call tcp_recved()
iva2k 0:e614f7875b60 342 for the FIN's seqno */
iva2k 0:e614f7875b60 343 if (pcb->rcv_wnd != TCP_WND) {
iva2k 0:e614f7875b60 344 pcb->rcv_wnd++;
iva2k 0:e614f7875b60 345 }
iva2k 0:e614f7875b60 346 TCP_EVENT_RECV(pcb, NULL, ERR_OK, err);
iva2k 0:e614f7875b60 347 if (err == ERR_ABRT) {
iva2k 0:e614f7875b60 348 goto aborted;
iva2k 0:e614f7875b60 349 }
iva2k 0:e614f7875b60 350 }
iva2k 0:e614f7875b60 351
iva2k 0:e614f7875b60 352 tcp_input_pcb = NULL;
iva2k 0:e614f7875b60 353 /* Try to send something out. */
iva2k 0:e614f7875b60 354 tcp_output(pcb);
iva2k 0:e614f7875b60 355 #if TCP_INPUT_DEBUG
iva2k 0:e614f7875b60 356 #if TCP_DEBUG
iva2k 0:e614f7875b60 357 tcp_debug_print_state(pcb->state);
iva2k 0:e614f7875b60 358 #endif /* TCP_DEBUG */
iva2k 0:e614f7875b60 359 #endif /* TCP_INPUT_DEBUG */
iva2k 0:e614f7875b60 360 }
iva2k 0:e614f7875b60 361 }
iva2k 0:e614f7875b60 362 /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
iva2k 0:e614f7875b60 363 Below this line, 'pcb' may not be dereferenced! */
iva2k 0:e614f7875b60 364 aborted:
iva2k 0:e614f7875b60 365 tcp_input_pcb = NULL;
iva2k 0:e614f7875b60 366
iva2k 0:e614f7875b60 367
iva2k 0:e614f7875b60 368 /* give up our reference to inseg.p */
iva2k 0:e614f7875b60 369 if (inseg.p != NULL)
iva2k 0:e614f7875b60 370 {
iva2k 0:e614f7875b60 371 pbuf_free(inseg.p);
iva2k 0:e614f7875b60 372 inseg.p = NULL;
iva2k 0:e614f7875b60 373 }
iva2k 0:e614f7875b60 374 } else {
iva2k 0:e614f7875b60 375
iva2k 0:e614f7875b60 376 /* If no matching PCB was found, send a TCP RST (reset) to the
iva2k 0:e614f7875b60 377 sender. */
iva2k 0:e614f7875b60 378 LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
iva2k 0:e614f7875b60 379 if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
iva2k 0:e614f7875b60 380 TCP_STATS_INC(tcp.proterr);
iva2k 0:e614f7875b60 381 TCP_STATS_INC(tcp.drop);
iva2k 0:e614f7875b60 382 tcp_rst(ackno, seqno + tcplen,
iva2k 0:e614f7875b60 383 &(iphdr->dest), &(iphdr->src),
iva2k 0:e614f7875b60 384 tcphdr->dest, tcphdr->src);
iva2k 0:e614f7875b60 385 }
iva2k 0:e614f7875b60 386 pbuf_free(p);
iva2k 0:e614f7875b60 387 }
iva2k 0:e614f7875b60 388
iva2k 0:e614f7875b60 389 LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
iva2k 0:e614f7875b60 390 PERF_STOP("tcp_input");
iva2k 0:e614f7875b60 391 }
iva2k 0:e614f7875b60 392
iva2k 0:e614f7875b60 393 /**
iva2k 0:e614f7875b60 394 * Called by tcp_input() when a segment arrives for a listening
iva2k 0:e614f7875b60 395 * connection (from tcp_input()).
iva2k 0:e614f7875b60 396 *
iva2k 0:e614f7875b60 397 * @param pcb the tcp_pcb_listen for which a segment arrived
iva2k 0:e614f7875b60 398 * @return ERR_OK if the segment was processed
iva2k 0:e614f7875b60 399 * another err_t on error
iva2k 0:e614f7875b60 400 *
iva2k 0:e614f7875b60 401 * @note the return value is not (yet?) used in tcp_input()
iva2k 0:e614f7875b60 402 * @note the segment which arrived is saved in global variables, therefore only the pcb
iva2k 0:e614f7875b60 403 * involved is passed as a parameter to this function
iva2k 0:e614f7875b60 404 */
iva2k 0:e614f7875b60 405 static err_t
iva2k 0:e614f7875b60 406 tcp_listen_input(struct tcp_pcb_listen *pcb)
iva2k 0:e614f7875b60 407 {
iva2k 0:e614f7875b60 408 struct tcp_pcb *npcb;
iva2k 0:e614f7875b60 409 err_t rc;
iva2k 0:e614f7875b60 410
iva2k 0:e614f7875b60 411 /* In the LISTEN state, we check for incoming SYN segments,
iva2k 0:e614f7875b60 412 creates a new PCB, and responds with a SYN|ACK. */
iva2k 0:e614f7875b60 413 if (flags & TCP_ACK) {
iva2k 0:e614f7875b60 414 /* For incoming segments with the ACK flag set, respond with a
iva2k 0:e614f7875b60 415 RST. */
iva2k 0:e614f7875b60 416 LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
iva2k 0:e614f7875b60 417 tcp_rst(ackno + 1, seqno + tcplen,
iva2k 0:e614f7875b60 418 &(iphdr->dest), &(iphdr->src),
iva2k 0:e614f7875b60 419 tcphdr->dest, tcphdr->src);
iva2k 0:e614f7875b60 420 } else if (flags & TCP_SYN) {
iva2k 0:e614f7875b60 421 LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
iva2k 0:e614f7875b60 422 #if TCP_LISTEN_BACKLOG
iva2k 0:e614f7875b60 423 if (pcb->accepts_pending >= pcb->backlog) {
iva2k 0:e614f7875b60 424 LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
iva2k 0:e614f7875b60 425 return ERR_ABRT;
iva2k 0:e614f7875b60 426 }
iva2k 0:e614f7875b60 427 #endif /* TCP_LISTEN_BACKLOG */
iva2k 0:e614f7875b60 428 npcb = tcp_alloc(pcb->prio);
iva2k 0:e614f7875b60 429 /* If a new PCB could not be created (probably due to lack of memory),
iva2k 0:e614f7875b60 430 we don't do anything, but rely on the sender will retransmit the
iva2k 0:e614f7875b60 431 SYN at a time when we have more memory available. */
iva2k 0:e614f7875b60 432 if (npcb == NULL) {
iva2k 0:e614f7875b60 433 LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
iva2k 0:e614f7875b60 434 TCP_STATS_INC(tcp.memerr);
iva2k 0:e614f7875b60 435 return ERR_MEM;
iva2k 0:e614f7875b60 436 }
iva2k 0:e614f7875b60 437 #if TCP_LISTEN_BACKLOG
iva2k 0:e614f7875b60 438 pcb->accepts_pending++;
iva2k 0:e614f7875b60 439 #endif /* TCP_LISTEN_BACKLOG */
iva2k 0:e614f7875b60 440 /* Set up the new PCB. */
iva2k 0:e614f7875b60 441 ip_addr_copy(npcb->local_ip, iphdr->dest);
iva2k 0:e614f7875b60 442 npcb->local_port = pcb->local_port;
iva2k 0:e614f7875b60 443 ip_addr_copy(npcb->remote_ip, iphdr->src);
iva2k 0:e614f7875b60 444 npcb->remote_port = tcphdr->src;
iva2k 0:e614f7875b60 445 npcb->state = SYN_RCVD;
iva2k 0:e614f7875b60 446 npcb->rcv_nxt = seqno + 1;
iva2k 0:e614f7875b60 447 npcb->rcv_ann_right_edge = npcb->rcv_nxt;
iva2k 0:e614f7875b60 448 npcb->snd_wnd = tcphdr->wnd;
iva2k 0:e614f7875b60 449 npcb->ssthresh = npcb->snd_wnd;
iva2k 0:e614f7875b60 450 npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
iva2k 0:e614f7875b60 451 npcb->callback_arg = pcb->callback_arg;
iva2k 0:e614f7875b60 452 #if LWIP_CALLBACK_API
iva2k 0:e614f7875b60 453 npcb->accept = pcb->accept;
iva2k 0:e614f7875b60 454 #endif /* LWIP_CALLBACK_API */
iva2k 0:e614f7875b60 455 /* inherit socket options */
iva2k 0:e614f7875b60 456 npcb->so_options = pcb->so_options & (SOF_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER);
iva2k 0:e614f7875b60 457 /* Register the new PCB so that we can begin receiving segments
iva2k 0:e614f7875b60 458 for it. */
iva2k 0:e614f7875b60 459 TCP_REG(&tcp_active_pcbs, npcb);
iva2k 0:e614f7875b60 460
iva2k 0:e614f7875b60 461 /* Parse any options in the SYN. */
iva2k 0:e614f7875b60 462 tcp_parseopt(npcb);
iva2k 0:e614f7875b60 463 #if TCP_CALCULATE_EFF_SEND_MSS
iva2k 0:e614f7875b60 464 npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip));
iva2k 0:e614f7875b60 465 #endif /* TCP_CALCULATE_EFF_SEND_MSS */
iva2k 0:e614f7875b60 466
iva2k 0:e614f7875b60 467 snmp_inc_tcppassiveopens();
iva2k 0:e614f7875b60 468
iva2k 0:e614f7875b60 469 /* Send a SYN|ACK together with the MSS option. */
iva2k 0:e614f7875b60 470 rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
iva2k 0:e614f7875b60 471 if (rc != ERR_OK) {
iva2k 0:e614f7875b60 472 tcp_abandon(npcb, 0);
iva2k 0:e614f7875b60 473 return rc;
iva2k 0:e614f7875b60 474 }
iva2k 0:e614f7875b60 475 return tcp_output(npcb);
iva2k 0:e614f7875b60 476 }
iva2k 0:e614f7875b60 477 return ERR_OK;
iva2k 0:e614f7875b60 478 }
iva2k 0:e614f7875b60 479
iva2k 0:e614f7875b60 480 /**
iva2k 0:e614f7875b60 481 * Called by tcp_input() when a segment arrives for a connection in
iva2k 0:e614f7875b60 482 * TIME_WAIT.
iva2k 0:e614f7875b60 483 *
iva2k 0:e614f7875b60 484 * @param pcb the tcp_pcb for which a segment arrived
iva2k 0:e614f7875b60 485 *
iva2k 0:e614f7875b60 486 * @note the segment which arrived is saved in global variables, therefore only the pcb
iva2k 0:e614f7875b60 487 * involved is passed as a parameter to this function
iva2k 0:e614f7875b60 488 */
iva2k 0:e614f7875b60 489 static err_t
iva2k 0:e614f7875b60 490 tcp_timewait_input(struct tcp_pcb *pcb)
iva2k 0:e614f7875b60 491 {
iva2k 0:e614f7875b60 492 /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
iva2k 0:e614f7875b60 493 /* RFC 793 3.9 Event Processing - Segment Arrives:
iva2k 0:e614f7875b60 494 * - first check sequence number - we skip that one in TIME_WAIT (always
iva2k 0:e614f7875b60 495 * acceptable since we only send ACKs)
iva2k 0:e614f7875b60 496 * - second check the RST bit (... return) */
iva2k 0:e614f7875b60 497 if (flags & TCP_RST) {
iva2k 0:e614f7875b60 498 return ERR_OK;
iva2k 0:e614f7875b60 499 }
iva2k 0:e614f7875b60 500 /* - fourth, check the SYN bit, */
iva2k 0:e614f7875b60 501 if (flags & TCP_SYN) {
iva2k 0:e614f7875b60 502 /* If an incoming segment is not acceptable, an acknowledgment
iva2k 0:e614f7875b60 503 should be sent in reply */
iva2k 0:e614f7875b60 504 if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) {
iva2k 0:e614f7875b60 505 /* If the SYN is in the window it is an error, send a reset */
iva2k 0:e614f7875b60 506 tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
iva2k 0:e614f7875b60 507 tcphdr->dest, tcphdr->src);
iva2k 0:e614f7875b60 508 return ERR_OK;
iva2k 0:e614f7875b60 509 }
iva2k 0:e614f7875b60 510 } else if (flags & TCP_FIN) {
iva2k 0:e614f7875b60 511 /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
iva2k 0:e614f7875b60 512 Restart the 2 MSL time-wait timeout.*/
iva2k 0:e614f7875b60 513 pcb->tmr = tcp_ticks;
iva2k 0:e614f7875b60 514 }
iva2k 0:e614f7875b60 515
iva2k 0:e614f7875b60 516 if ((tcplen > 0)) {
iva2k 0:e614f7875b60 517 /* Acknowledge data, FIN or out-of-window SYN */
iva2k 0:e614f7875b60 518 pcb->flags |= TF_ACK_NOW;
iva2k 0:e614f7875b60 519 return tcp_output(pcb);
iva2k 0:e614f7875b60 520 }
iva2k 0:e614f7875b60 521 return ERR_OK;
iva2k 0:e614f7875b60 522 }
iva2k 0:e614f7875b60 523
iva2k 0:e614f7875b60 524 /**
iva2k 0:e614f7875b60 525 * Implements the TCP state machine. Called by tcp_input. In some
iva2k 0:e614f7875b60 526 * states tcp_receive() is called to receive data. The tcp_seg
iva2k 0:e614f7875b60 527 * argument will be freed by the caller (tcp_input()) unless the
iva2k 0:e614f7875b60 528 * recv_data pointer in the pcb is set.
iva2k 0:e614f7875b60 529 *
iva2k 0:e614f7875b60 530 * @param pcb the tcp_pcb for which a segment arrived
iva2k 0:e614f7875b60 531 *
iva2k 0:e614f7875b60 532 * @note the segment which arrived is saved in global variables, therefore only the pcb
iva2k 0:e614f7875b60 533 * involved is passed as a parameter to this function
iva2k 0:e614f7875b60 534 */
iva2k 0:e614f7875b60 535 static err_t
iva2k 0:e614f7875b60 536 tcp_process(struct tcp_pcb *pcb)
iva2k 0:e614f7875b60 537 {
iva2k 0:e614f7875b60 538 struct tcp_seg *rseg;
iva2k 0:e614f7875b60 539 u8_t acceptable = 0;
iva2k 0:e614f7875b60 540 err_t err;
iva2k 0:e614f7875b60 541
iva2k 0:e614f7875b60 542 err = ERR_OK;
iva2k 0:e614f7875b60 543
iva2k 0:e614f7875b60 544 /* Process incoming RST segments. */
iva2k 0:e614f7875b60 545 if (flags & TCP_RST) {
iva2k 0:e614f7875b60 546 /* First, determine if the reset is acceptable. */
iva2k 0:e614f7875b60 547 if (pcb->state == SYN_SENT) {
iva2k 0:e614f7875b60 548 if (ackno == pcb->snd_nxt) {
iva2k 0:e614f7875b60 549 acceptable = 1;
iva2k 0:e614f7875b60 550 }
iva2k 0:e614f7875b60 551 } else {
iva2k 0:e614f7875b60 552 if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
iva2k 0:e614f7875b60 553 pcb->rcv_nxt+pcb->rcv_wnd)) {
iva2k 0:e614f7875b60 554 acceptable = 1;
iva2k 0:e614f7875b60 555 }
iva2k 0:e614f7875b60 556 }
iva2k 0:e614f7875b60 557
iva2k 0:e614f7875b60 558 if (acceptable) {
iva2k 0:e614f7875b60 559 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
iva2k 0:e614f7875b60 560 LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
iva2k 0:e614f7875b60 561 recv_flags |= TF_RESET;
iva2k 0:e614f7875b60 562 pcb->flags &= ~TF_ACK_DELAY;
iva2k 0:e614f7875b60 563 return ERR_RST;
iva2k 0:e614f7875b60 564 } else {
iva2k 0:e614f7875b60 565 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
iva2k 0:e614f7875b60 566 seqno, pcb->rcv_nxt));
iva2k 0:e614f7875b60 567 LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
iva2k 0:e614f7875b60 568 seqno, pcb->rcv_nxt));
iva2k 0:e614f7875b60 569 return ERR_OK;
iva2k 0:e614f7875b60 570 }
iva2k 0:e614f7875b60 571 }
iva2k 0:e614f7875b60 572
iva2k 0:e614f7875b60 573 if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
iva2k 0:e614f7875b60 574 /* Cope with new connection attempt after remote end crashed */
iva2k 0:e614f7875b60 575 tcp_ack_now(pcb);
iva2k 0:e614f7875b60 576 return ERR_OK;
iva2k 0:e614f7875b60 577 }
iva2k 0:e614f7875b60 578
iva2k 0:e614f7875b60 579 if ((pcb->flags & TF_RXCLOSED) == 0) {
iva2k 0:e614f7875b60 580 /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
iva2k 0:e614f7875b60 581 pcb->tmr = tcp_ticks;
iva2k 0:e614f7875b60 582 }
iva2k 0:e614f7875b60 583 pcb->keep_cnt_sent = 0;
iva2k 0:e614f7875b60 584
iva2k 0:e614f7875b60 585 tcp_parseopt(pcb);
iva2k 0:e614f7875b60 586
iva2k 0:e614f7875b60 587 /* Do different things depending on the TCP state. */
iva2k 0:e614f7875b60 588 switch (pcb->state) {
iva2k 0:e614f7875b60 589 case SYN_SENT:
iva2k 0:e614f7875b60 590 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
iva2k 0:e614f7875b60 591 pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
iva2k 0:e614f7875b60 592 /* received SYN ACK with expected sequence number? */
iva2k 0:e614f7875b60 593 if ((flags & TCP_ACK) && (flags & TCP_SYN)
iva2k 0:e614f7875b60 594 && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
iva2k 0:e614f7875b60 595 pcb->snd_buf++;
iva2k 0:e614f7875b60 596 pcb->rcv_nxt = seqno + 1;
iva2k 0:e614f7875b60 597 pcb->rcv_ann_right_edge = pcb->rcv_nxt;
iva2k 0:e614f7875b60 598 pcb->lastack = ackno;
iva2k 0:e614f7875b60 599 pcb->snd_wnd = tcphdr->wnd;
iva2k 0:e614f7875b60 600 pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
iva2k 0:e614f7875b60 601 pcb->state = ESTABLISHED;
iva2k 0:e614f7875b60 602
iva2k 0:e614f7875b60 603 #if TCP_CALCULATE_EFF_SEND_MSS
iva2k 0:e614f7875b60 604 pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
iva2k 0:e614f7875b60 605 #endif /* TCP_CALCULATE_EFF_SEND_MSS */
iva2k 0:e614f7875b60 606
iva2k 0:e614f7875b60 607 /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
iva2k 0:e614f7875b60 608 * but for the default value of pcb->mss) */
iva2k 0:e614f7875b60 609 pcb->ssthresh = pcb->mss * 10;
iva2k 0:e614f7875b60 610
iva2k 0:e614f7875b60 611 pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
iva2k 0:e614f7875b60 612 LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
iva2k 0:e614f7875b60 613 --pcb->snd_queuelen;
iva2k 0:e614f7875b60 614 LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
iva2k 0:e614f7875b60 615 rseg = pcb->unacked;
iva2k 0:e614f7875b60 616 pcb->unacked = rseg->next;
iva2k 0:e614f7875b60 617
iva2k 0:e614f7875b60 618 /* If there's nothing left to acknowledge, stop the retransmit
iva2k 0:e614f7875b60 619 timer, otherwise reset it to start again */
iva2k 0:e614f7875b60 620 if(pcb->unacked == NULL)
iva2k 0:e614f7875b60 621 pcb->rtime = -1;
iva2k 0:e614f7875b60 622 else {
iva2k 0:e614f7875b60 623 pcb->rtime = 0;
iva2k 0:e614f7875b60 624 pcb->nrtx = 0;
iva2k 0:e614f7875b60 625 }
iva2k 0:e614f7875b60 626
iva2k 0:e614f7875b60 627 tcp_seg_free(rseg);
iva2k 0:e614f7875b60 628
iva2k 0:e614f7875b60 629 /* Call the user specified function to call when sucessfully
iva2k 0:e614f7875b60 630 * connected. */
iva2k 0:e614f7875b60 631 TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
iva2k 0:e614f7875b60 632 if (err == ERR_ABRT) {
iva2k 0:e614f7875b60 633 return ERR_ABRT;
iva2k 0:e614f7875b60 634 }
iva2k 0:e614f7875b60 635 tcp_ack_now(pcb);
iva2k 0:e614f7875b60 636 }
iva2k 0:e614f7875b60 637 /* received ACK? possibly a half-open connection */
iva2k 0:e614f7875b60 638 else if (flags & TCP_ACK) {
iva2k 0:e614f7875b60 639 /* send a RST to bring the other side in a non-synchronized state. */
iva2k 0:e614f7875b60 640 tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
iva2k 0:e614f7875b60 641 tcphdr->dest, tcphdr->src);
iva2k 0:e614f7875b60 642 }
iva2k 0:e614f7875b60 643 break;
iva2k 0:e614f7875b60 644 case SYN_RCVD:
iva2k 0:e614f7875b60 645 if (flags & TCP_ACK) {
iva2k 0:e614f7875b60 646 /* expected ACK number? */
iva2k 0:e614f7875b60 647 if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
iva2k 0:e614f7875b60 648 u16_t old_cwnd;
iva2k 0:e614f7875b60 649 pcb->state = ESTABLISHED;
iva2k 0:e614f7875b60 650 LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
iva2k 0:e614f7875b60 651 #if LWIP_CALLBACK_API
iva2k 0:e614f7875b60 652 LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
iva2k 0:e614f7875b60 653 #endif
iva2k 0:e614f7875b60 654 /* Call the accept function. */
iva2k 0:e614f7875b60 655 TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
iva2k 0:e614f7875b60 656 if (err != ERR_OK) {
iva2k 0:e614f7875b60 657 /* If the accept function returns with an error, we abort
iva2k 0:e614f7875b60 658 * the connection. */
iva2k 0:e614f7875b60 659 /* Already aborted? */
iva2k 0:e614f7875b60 660 if (err != ERR_ABRT) {
iva2k 0:e614f7875b60 661 tcp_abort(pcb);
iva2k 0:e614f7875b60 662 }
iva2k 0:e614f7875b60 663 return ERR_ABRT;
iva2k 0:e614f7875b60 664 }
iva2k 0:e614f7875b60 665 old_cwnd = pcb->cwnd;
iva2k 0:e614f7875b60 666 /* If there was any data contained within this ACK,
iva2k 0:e614f7875b60 667 * we'd better pass it on to the application as well. */
iva2k 0:e614f7875b60 668 tcp_receive(pcb);
iva2k 0:e614f7875b60 669
iva2k 0:e614f7875b60 670 /* Prevent ACK for SYN to generate a sent event */
iva2k 0:e614f7875b60 671 if (pcb->acked != 0) {
iva2k 0:e614f7875b60 672 pcb->acked--;
iva2k 0:e614f7875b60 673 }
iva2k 0:e614f7875b60 674
iva2k 0:e614f7875b60 675 pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
iva2k 0:e614f7875b60 676
iva2k 0:e614f7875b60 677 if (recv_flags & TF_GOT_FIN) {
iva2k 0:e614f7875b60 678 tcp_ack_now(pcb);
iva2k 0:e614f7875b60 679 pcb->state = CLOSE_WAIT;
iva2k 0:e614f7875b60 680 }
iva2k 0:e614f7875b60 681 } else {
iva2k 0:e614f7875b60 682 /* incorrect ACK number, send RST */
iva2k 0:e614f7875b60 683 tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
iva2k 0:e614f7875b60 684 tcphdr->dest, tcphdr->src);
iva2k 0:e614f7875b60 685 }
iva2k 0:e614f7875b60 686 } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
iva2k 0:e614f7875b60 687 /* Looks like another copy of the SYN - retransmit our SYN-ACK */
iva2k 0:e614f7875b60 688 tcp_rexmit(pcb);
iva2k 0:e614f7875b60 689 }
iva2k 0:e614f7875b60 690 break;
iva2k 0:e614f7875b60 691 case CLOSE_WAIT:
iva2k 0:e614f7875b60 692 /* FALLTHROUGH */
iva2k 0:e614f7875b60 693 case ESTABLISHED:
iva2k 0:e614f7875b60 694 tcp_receive(pcb);
iva2k 0:e614f7875b60 695 if (recv_flags & TF_GOT_FIN) { /* passive close */
iva2k 0:e614f7875b60 696 tcp_ack_now(pcb);
iva2k 0:e614f7875b60 697 pcb->state = CLOSE_WAIT;
iva2k 0:e614f7875b60 698 }
iva2k 0:e614f7875b60 699 break;
iva2k 0:e614f7875b60 700 case FIN_WAIT_1:
iva2k 0:e614f7875b60 701 tcp_receive(pcb);
iva2k 0:e614f7875b60 702 if (recv_flags & TF_GOT_FIN) {
iva2k 0:e614f7875b60 703 if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
iva2k 0:e614f7875b60 704 LWIP_DEBUGF(TCP_DEBUG,
iva2k 0:e614f7875b60 705 ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
iva2k 0:e614f7875b60 706 tcp_ack_now(pcb);
iva2k 0:e614f7875b60 707 tcp_pcb_purge(pcb);
iva2k 0:e614f7875b60 708 TCP_RMV(&tcp_active_pcbs, pcb);
iva2k 0:e614f7875b60 709 pcb->state = TIME_WAIT;
iva2k 0:e614f7875b60 710 TCP_REG(&tcp_tw_pcbs, pcb);
iva2k 0:e614f7875b60 711 } else {
iva2k 0:e614f7875b60 712 tcp_ack_now(pcb);
iva2k 0:e614f7875b60 713 pcb->state = CLOSING;
iva2k 0:e614f7875b60 714 }
iva2k 0:e614f7875b60 715 } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
iva2k 0:e614f7875b60 716 pcb->state = FIN_WAIT_2;
iva2k 0:e614f7875b60 717 }
iva2k 0:e614f7875b60 718 break;
iva2k 0:e614f7875b60 719 case FIN_WAIT_2:
iva2k 0:e614f7875b60 720 tcp_receive(pcb);
iva2k 0:e614f7875b60 721 if (recv_flags & TF_GOT_FIN) {
iva2k 0:e614f7875b60 722 LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
iva2k 0:e614f7875b60 723 tcp_ack_now(pcb);
iva2k 0:e614f7875b60 724 tcp_pcb_purge(pcb);
iva2k 0:e614f7875b60 725 TCP_RMV(&tcp_active_pcbs, pcb);
iva2k 0:e614f7875b60 726 pcb->state = TIME_WAIT;
iva2k 0:e614f7875b60 727 TCP_REG(&tcp_tw_pcbs, pcb);
iva2k 0:e614f7875b60 728 }
iva2k 0:e614f7875b60 729 break;
iva2k 0:e614f7875b60 730 case CLOSING:
iva2k 0:e614f7875b60 731 tcp_receive(pcb);
iva2k 0:e614f7875b60 732 if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
iva2k 0:e614f7875b60 733 LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
iva2k 0:e614f7875b60 734 tcp_pcb_purge(pcb);
iva2k 0:e614f7875b60 735 TCP_RMV(&tcp_active_pcbs, pcb);
iva2k 0:e614f7875b60 736 pcb->state = TIME_WAIT;
iva2k 0:e614f7875b60 737 TCP_REG(&tcp_tw_pcbs, pcb);
iva2k 0:e614f7875b60 738 }
iva2k 0:e614f7875b60 739 break;
iva2k 0:e614f7875b60 740 case LAST_ACK:
iva2k 0:e614f7875b60 741 tcp_receive(pcb);
iva2k 0:e614f7875b60 742 if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
iva2k 0:e614f7875b60 743 LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
iva2k 0:e614f7875b60 744 /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
iva2k 0:e614f7875b60 745 recv_flags |= TF_CLOSED;
iva2k 0:e614f7875b60 746 }
iva2k 0:e614f7875b60 747 break;
iva2k 0:e614f7875b60 748 default:
iva2k 0:e614f7875b60 749 break;
iva2k 0:e614f7875b60 750 }
iva2k 0:e614f7875b60 751 return ERR_OK;
iva2k 0:e614f7875b60 752 }
iva2k 0:e614f7875b60 753
iva2k 0:e614f7875b60 754 #if TCP_QUEUE_OOSEQ
iva2k 0:e614f7875b60 755 /**
iva2k 0:e614f7875b60 756 * Insert segment into the list (segments covered with new one will be deleted)
iva2k 0:e614f7875b60 757 *
iva2k 0:e614f7875b60 758 * Called from tcp_receive()
iva2k 0:e614f7875b60 759 */
iva2k 0:e614f7875b60 760 static void
iva2k 0:e614f7875b60 761 tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
iva2k 0:e614f7875b60 762 {
iva2k 0:e614f7875b60 763 struct tcp_seg *old_seg;
iva2k 0:e614f7875b60 764
iva2k 0:e614f7875b60 765 if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
iva2k 0:e614f7875b60 766 /* received segment overlaps all following segments */
iva2k 0:e614f7875b60 767 tcp_segs_free(next);
iva2k 0:e614f7875b60 768 next = NULL;
iva2k 0:e614f7875b60 769 }
iva2k 0:e614f7875b60 770 else {
iva2k 0:e614f7875b60 771 /* delete some following segments
iva2k 0:e614f7875b60 772 oos queue may have segments with FIN flag */
iva2k 0:e614f7875b60 773 while (next &&
iva2k 0:e614f7875b60 774 TCP_SEQ_GEQ((seqno + cseg->len),
iva2k 0:e614f7875b60 775 (next->tcphdr->seqno + next->len))) {
iva2k 0:e614f7875b60 776 /* cseg with FIN already processed */
iva2k 0:e614f7875b60 777 if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
iva2k 0:e614f7875b60 778 TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
iva2k 0:e614f7875b60 779 }
iva2k 0:e614f7875b60 780 old_seg = next;
iva2k 0:e614f7875b60 781 next = next->next;
iva2k 0:e614f7875b60 782 tcp_seg_free(old_seg);
iva2k 0:e614f7875b60 783 }
iva2k 0:e614f7875b60 784 if (next &&
iva2k 0:e614f7875b60 785 TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
iva2k 0:e614f7875b60 786 /* We need to trim the incoming segment. */
iva2k 0:e614f7875b60 787 cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
iva2k 0:e614f7875b60 788 pbuf_realloc(cseg->p, cseg->len);
iva2k 0:e614f7875b60 789 }
iva2k 0:e614f7875b60 790 }
iva2k 0:e614f7875b60 791 cseg->next = next;
iva2k 0:e614f7875b60 792 }
iva2k 0:e614f7875b60 793 #endif /* TCP_QUEUE_OOSEQ */
iva2k 0:e614f7875b60 794
iva2k 0:e614f7875b60 795 /**
iva2k 0:e614f7875b60 796 * Called by tcp_process. Checks if the given segment is an ACK for outstanding
iva2k 0:e614f7875b60 797 * data, and if so frees the memory of the buffered data. Next, is places the
iva2k 0:e614f7875b60 798 * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
iva2k 0:e614f7875b60 799 * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
iva2k 0:e614f7875b60 800 * i it has been removed from the buffer.
iva2k 0:e614f7875b60 801 *
iva2k 0:e614f7875b60 802 * If the incoming segment constitutes an ACK for a segment that was used for RTT
iva2k 0:e614f7875b60 803 * estimation, the RTT is estimated here as well.
iva2k 0:e614f7875b60 804 *
iva2k 0:e614f7875b60 805 * Called from tcp_process().
iva2k 0:e614f7875b60 806 */
iva2k 0:e614f7875b60 807 static void
iva2k 0:e614f7875b60 808 tcp_receive(struct tcp_pcb *pcb)
iva2k 0:e614f7875b60 809 {
iva2k 0:e614f7875b60 810 struct tcp_seg *next;
iva2k 0:e614f7875b60 811 #if TCP_QUEUE_OOSEQ
iva2k 0:e614f7875b60 812 struct tcp_seg *prev, *cseg;
iva2k 0:e614f7875b60 813 #endif /* TCP_QUEUE_OOSEQ */
iva2k 0:e614f7875b60 814 struct pbuf *p;
iva2k 0:e614f7875b60 815 s32_t off;
iva2k 0:e614f7875b60 816 s16_t m;
iva2k 0:e614f7875b60 817 u32_t right_wnd_edge;
iva2k 0:e614f7875b60 818 u16_t new_tot_len;
iva2k 0:e614f7875b60 819 int found_dupack = 0;
iva2k 0:e614f7875b60 820
iva2k 0:e614f7875b60 821 if (flags & TCP_ACK) {
iva2k 0:e614f7875b60 822 right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
iva2k 0:e614f7875b60 823
iva2k 0:e614f7875b60 824 /* Update window. */
iva2k 0:e614f7875b60 825 if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
iva2k 0:e614f7875b60 826 (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
iva2k 0:e614f7875b60 827 (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
iva2k 0:e614f7875b60 828 pcb->snd_wnd = tcphdr->wnd;
iva2k 0:e614f7875b60 829 pcb->snd_wl1 = seqno;
iva2k 0:e614f7875b60 830 pcb->snd_wl2 = ackno;
iva2k 0:e614f7875b60 831 if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) {
iva2k 0:e614f7875b60 832 pcb->persist_backoff = 0;
iva2k 0:e614f7875b60 833 }
iva2k 0:e614f7875b60 834 LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd));
iva2k 0:e614f7875b60 835 #if TCP_WND_DEBUG
iva2k 0:e614f7875b60 836 } else {
iva2k 0:e614f7875b60 837 if (pcb->snd_wnd != tcphdr->wnd) {
iva2k 0:e614f7875b60 838 LWIP_DEBUGF(TCP_WND_DEBUG,
iva2k 0:e614f7875b60 839 ("tcp_receive: no window update lastack %"U32_F" ackno %"
iva2k 0:e614f7875b60 840 U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
iva2k 0:e614f7875b60 841 pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
iva2k 0:e614f7875b60 842 }
iva2k 0:e614f7875b60 843 #endif /* TCP_WND_DEBUG */
iva2k 0:e614f7875b60 844 }
iva2k 0:e614f7875b60 845
iva2k 0:e614f7875b60 846 /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
iva2k 0:e614f7875b60 847 * duplicate ack if:
iva2k 0:e614f7875b60 848 * 1) It doesn't ACK new data
iva2k 0:e614f7875b60 849 * 2) length of received packet is zero (i.e. no payload)
iva2k 0:e614f7875b60 850 * 3) the advertised window hasn't changed
iva2k 0:e614f7875b60 851 * 4) There is outstanding unacknowledged data (retransmission timer running)
iva2k 0:e614f7875b60 852 * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
iva2k 0:e614f7875b60 853 *
iva2k 0:e614f7875b60 854 * If it passes all five, should process as a dupack:
iva2k 0:e614f7875b60 855 * a) dupacks < 3: do nothing
iva2k 0:e614f7875b60 856 * b) dupacks == 3: fast retransmit
iva2k 0:e614f7875b60 857 * c) dupacks > 3: increase cwnd
iva2k 0:e614f7875b60 858 *
iva2k 0:e614f7875b60 859 * If it only passes 1-3, should reset dupack counter (and add to
iva2k 0:e614f7875b60 860 * stats, which we don't do in lwIP)
iva2k 0:e614f7875b60 861 *
iva2k 0:e614f7875b60 862 * If it only passes 1, should reset dupack counter
iva2k 0:e614f7875b60 863 *
iva2k 0:e614f7875b60 864 */
iva2k 0:e614f7875b60 865
iva2k 0:e614f7875b60 866 /* Clause 1 */
iva2k 0:e614f7875b60 867 if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
iva2k 0:e614f7875b60 868 pcb->acked = 0;
iva2k 0:e614f7875b60 869 /* Clause 2 */
iva2k 0:e614f7875b60 870 if (tcplen == 0) {
iva2k 0:e614f7875b60 871 /* Clause 3 */
iva2k 0:e614f7875b60 872 if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
iva2k 0:e614f7875b60 873 /* Clause 4 */
iva2k 0:e614f7875b60 874 if (pcb->rtime >= 0) {
iva2k 0:e614f7875b60 875 /* Clause 5 */
iva2k 0:e614f7875b60 876 if (pcb->lastack == ackno) {
iva2k 0:e614f7875b60 877 found_dupack = 1;
iva2k 0:e614f7875b60 878 if (pcb->dupacks + 1 > pcb->dupacks)
iva2k 0:e614f7875b60 879 ++pcb->dupacks;
iva2k 0:e614f7875b60 880 if (pcb->dupacks > 3) {
iva2k 0:e614f7875b60 881 /* Inflate the congestion window, but not if it means that
iva2k 0:e614f7875b60 882 the value overflows. */
iva2k 0:e614f7875b60 883 if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
iva2k 0:e614f7875b60 884 pcb->cwnd += pcb->mss;
iva2k 0:e614f7875b60 885 }
iva2k 0:e614f7875b60 886 } else if (pcb->dupacks == 3) {
iva2k 0:e614f7875b60 887 /* Do fast retransmit */
iva2k 0:e614f7875b60 888 tcp_rexmit_fast(pcb);
iva2k 0:e614f7875b60 889 }
iva2k 0:e614f7875b60 890 }
iva2k 0:e614f7875b60 891 }
iva2k 0:e614f7875b60 892 }
iva2k 0:e614f7875b60 893 }
iva2k 0:e614f7875b60 894 /* If Clause (1) or more is true, but not a duplicate ack, reset
iva2k 0:e614f7875b60 895 * count of consecutive duplicate acks */
iva2k 0:e614f7875b60 896 if (!found_dupack) {
iva2k 0:e614f7875b60 897 pcb->dupacks = 0;
iva2k 0:e614f7875b60 898 }
iva2k 0:e614f7875b60 899 } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){
iva2k 0:e614f7875b60 900 /* We come here when the ACK acknowledges new data. */
iva2k 0:e614f7875b60 901
iva2k 0:e614f7875b60 902 /* Reset the "IN Fast Retransmit" flag, since we are no longer
iva2k 0:e614f7875b60 903 in fast retransmit. Also reset the congestion window to the
iva2k 0:e614f7875b60 904 slow start threshold. */
iva2k 0:e614f7875b60 905 if (pcb->flags & TF_INFR) {
iva2k 0:e614f7875b60 906 pcb->flags &= ~TF_INFR;
iva2k 0:e614f7875b60 907 pcb->cwnd = pcb->ssthresh;
iva2k 0:e614f7875b60 908 }
iva2k 0:e614f7875b60 909
iva2k 0:e614f7875b60 910 /* Reset the number of retransmissions. */
iva2k 0:e614f7875b60 911 pcb->nrtx = 0;
iva2k 0:e614f7875b60 912
iva2k 0:e614f7875b60 913 /* Reset the retransmission time-out. */
iva2k 0:e614f7875b60 914 pcb->rto = (pcb->sa >> 3) + pcb->sv;
iva2k 0:e614f7875b60 915
iva2k 0:e614f7875b60 916 /* Update the send buffer space. Diff between the two can never exceed 64K? */
iva2k 0:e614f7875b60 917 pcb->acked = (u16_t)(ackno - pcb->lastack);
iva2k 0:e614f7875b60 918
iva2k 0:e614f7875b60 919 pcb->snd_buf += pcb->acked;
iva2k 0:e614f7875b60 920
iva2k 0:e614f7875b60 921 /* Reset the fast retransmit variables. */
iva2k 0:e614f7875b60 922 pcb->dupacks = 0;
iva2k 0:e614f7875b60 923 pcb->lastack = ackno;
iva2k 0:e614f7875b60 924
iva2k 0:e614f7875b60 925 /* Update the congestion control variables (cwnd and
iva2k 0:e614f7875b60 926 ssthresh). */
iva2k 0:e614f7875b60 927 if (pcb->state >= ESTABLISHED) {
iva2k 0:e614f7875b60 928 if (pcb->cwnd < pcb->ssthresh) {
iva2k 0:e614f7875b60 929 if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
iva2k 0:e614f7875b60 930 pcb->cwnd += pcb->mss;
iva2k 0:e614f7875b60 931 }
iva2k 0:e614f7875b60 932 LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));
iva2k 0:e614f7875b60 933 } else {
iva2k 0:e614f7875b60 934 u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
iva2k 0:e614f7875b60 935 if (new_cwnd > pcb->cwnd) {
iva2k 0:e614f7875b60 936 pcb->cwnd = new_cwnd;
iva2k 0:e614f7875b60 937 }
iva2k 0:e614f7875b60 938 LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd));
iva2k 0:e614f7875b60 939 }
iva2k 0:e614f7875b60 940 }
iva2k 0:e614f7875b60 941 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
iva2k 0:e614f7875b60 942 ackno,
iva2k 0:e614f7875b60 943 pcb->unacked != NULL?
iva2k 0:e614f7875b60 944 ntohl(pcb->unacked->tcphdr->seqno): 0,
iva2k 0:e614f7875b60 945 pcb->unacked != NULL?
iva2k 0:e614f7875b60 946 ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
iva2k 0:e614f7875b60 947
iva2k 0:e614f7875b60 948 /* Remove segment from the unacknowledged list if the incoming
iva2k 0:e614f7875b60 949 ACK acknowlegdes them. */
iva2k 0:e614f7875b60 950 while (pcb->unacked != NULL &&
iva2k 0:e614f7875b60 951 TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
iva2k 0:e614f7875b60 952 TCP_TCPLEN(pcb->unacked), ackno)) {
iva2k 0:e614f7875b60 953 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
iva2k 0:e614f7875b60 954 ntohl(pcb->unacked->tcphdr->seqno),
iva2k 0:e614f7875b60 955 ntohl(pcb->unacked->tcphdr->seqno) +
iva2k 0:e614f7875b60 956 TCP_TCPLEN(pcb->unacked)));
iva2k 0:e614f7875b60 957
iva2k 0:e614f7875b60 958 next = pcb->unacked;
iva2k 0:e614f7875b60 959 pcb->unacked = pcb->unacked->next;
iva2k 0:e614f7875b60 960
iva2k 0:e614f7875b60 961 LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
iva2k 0:e614f7875b60 962 LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
iva2k 0:e614f7875b60 963 /* Prevent ACK for FIN to generate a sent event */
iva2k 0:e614f7875b60 964 if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
iva2k 0:e614f7875b60 965 pcb->acked--;
iva2k 0:e614f7875b60 966 }
iva2k 0:e614f7875b60 967
iva2k 0:e614f7875b60 968 pcb->snd_queuelen -= pbuf_clen(next->p);
iva2k 0:e614f7875b60 969 tcp_seg_free(next);
iva2k 0:e614f7875b60 970
iva2k 0:e614f7875b60 971 LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen));
iva2k 0:e614f7875b60 972 if (pcb->snd_queuelen != 0) {
iva2k 0:e614f7875b60 973 LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
iva2k 0:e614f7875b60 974 pcb->unsent != NULL);
iva2k 0:e614f7875b60 975 }
iva2k 0:e614f7875b60 976 }
iva2k 0:e614f7875b60 977
iva2k 0:e614f7875b60 978 /* If there's nothing left to acknowledge, stop the retransmit
iva2k 0:e614f7875b60 979 timer, otherwise reset it to start again */
iva2k 0:e614f7875b60 980 if(pcb->unacked == NULL)
iva2k 0:e614f7875b60 981 pcb->rtime = -1;
iva2k 0:e614f7875b60 982 else
iva2k 0:e614f7875b60 983 pcb->rtime = 0;
iva2k 0:e614f7875b60 984
iva2k 0:e614f7875b60 985 pcb->polltmr = 0;
iva2k 0:e614f7875b60 986 } else {
iva2k 0:e614f7875b60 987 /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
iva2k 0:e614f7875b60 988 pcb->acked = 0;
iva2k 0:e614f7875b60 989 }
iva2k 0:e614f7875b60 990
iva2k 0:e614f7875b60 991 /* We go through the ->unsent list to see if any of the segments
iva2k 0:e614f7875b60 992 on the list are acknowledged by the ACK. This may seem
iva2k 0:e614f7875b60 993 strange since an "unsent" segment shouldn't be acked. The
iva2k 0:e614f7875b60 994 rationale is that lwIP puts all outstanding segments on the
iva2k 0:e614f7875b60 995 ->unsent list after a retransmission, so these segments may
iva2k 0:e614f7875b60 996 in fact have been sent once. */
iva2k 0:e614f7875b60 997 while (pcb->unsent != NULL &&
iva2k 0:e614f7875b60 998 TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) +
iva2k 0:e614f7875b60 999 TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
iva2k 0:e614f7875b60 1000 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
iva2k 0:e614f7875b60 1001 ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) +
iva2k 0:e614f7875b60 1002 TCP_TCPLEN(pcb->unsent)));
iva2k 0:e614f7875b60 1003
iva2k 0:e614f7875b60 1004 next = pcb->unsent;
iva2k 0:e614f7875b60 1005 pcb->unsent = pcb->unsent->next;
iva2k 0:e614f7875b60 1006 LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
iva2k 0:e614f7875b60 1007 LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
iva2k 0:e614f7875b60 1008 /* Prevent ACK for FIN to generate a sent event */
iva2k 0:e614f7875b60 1009 if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
iva2k 0:e614f7875b60 1010 pcb->acked--;
iva2k 0:e614f7875b60 1011 }
iva2k 0:e614f7875b60 1012 pcb->snd_queuelen -= pbuf_clen(next->p);
iva2k 0:e614f7875b60 1013 tcp_seg_free(next);
iva2k 0:e614f7875b60 1014 LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen));
iva2k 0:e614f7875b60 1015 if (pcb->snd_queuelen != 0) {
iva2k 0:e614f7875b60 1016 LWIP_ASSERT("tcp_receive: valid queue length",
iva2k 0:e614f7875b60 1017 pcb->unacked != NULL || pcb->unsent != NULL);
iva2k 0:e614f7875b60 1018 }
iva2k 0:e614f7875b60 1019 }
iva2k 0:e614f7875b60 1020 /* End of ACK for new data processing. */
iva2k 0:e614f7875b60 1021
iva2k 0:e614f7875b60 1022 LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
iva2k 0:e614f7875b60 1023 pcb->rttest, pcb->rtseq, ackno));
iva2k 0:e614f7875b60 1024
iva2k 0:e614f7875b60 1025 /* RTT estimation calculations. This is done by checking if the
iva2k 0:e614f7875b60 1026 incoming segment acknowledges the segment we use to take a
iva2k 0:e614f7875b60 1027 round-trip time measurement. */
iva2k 0:e614f7875b60 1028 if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
iva2k 0:e614f7875b60 1029 /* diff between this shouldn't exceed 32K since this are tcp timer ticks
iva2k 0:e614f7875b60 1030 and a round-trip shouldn't be that long... */
iva2k 0:e614f7875b60 1031 m = (s16_t)(tcp_ticks - pcb->rttest);
iva2k 0:e614f7875b60 1032
iva2k 0:e614f7875b60 1033 LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
iva2k 0:e614f7875b60 1034 m, m * TCP_SLOW_INTERVAL));
iva2k 0:e614f7875b60 1035
iva2k 0:e614f7875b60 1036 /* This is taken directly from VJs original code in his paper */
iva2k 0:e614f7875b60 1037 m = m - (pcb->sa >> 3);
iva2k 0:e614f7875b60 1038 pcb->sa += m;
iva2k 0:e614f7875b60 1039 if (m < 0) {
iva2k 0:e614f7875b60 1040 m = -m;
iva2k 0:e614f7875b60 1041 }
iva2k 0:e614f7875b60 1042 m = m - (pcb->sv >> 2);
iva2k 0:e614f7875b60 1043 pcb->sv += m;
iva2k 0:e614f7875b60 1044 pcb->rto = (pcb->sa >> 3) + pcb->sv;
iva2k 0:e614f7875b60 1045
iva2k 0:e614f7875b60 1046 LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
iva2k 0:e614f7875b60 1047 pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
iva2k 0:e614f7875b60 1048
iva2k 0:e614f7875b60 1049 pcb->rttest = 0;
iva2k 0:e614f7875b60 1050 }
iva2k 0:e614f7875b60 1051 }
iva2k 0:e614f7875b60 1052
iva2k 0:e614f7875b60 1053 /* If the incoming segment contains data, we must process it
iva2k 0:e614f7875b60 1054 further. */
iva2k 0:e614f7875b60 1055 if (tcplen > 0) {
iva2k 0:e614f7875b60 1056 /* This code basically does three things:
iva2k 0:e614f7875b60 1057
iva2k 0:e614f7875b60 1058 +) If the incoming segment contains data that is the next
iva2k 0:e614f7875b60 1059 in-sequence data, this data is passed to the application. This
iva2k 0:e614f7875b60 1060 might involve trimming the first edge of the data. The rcv_nxt
iva2k 0:e614f7875b60 1061 variable and the advertised window are adjusted.
iva2k 0:e614f7875b60 1062
iva2k 0:e614f7875b60 1063 +) If the incoming segment has data that is above the next
iva2k 0:e614f7875b60 1064 sequence number expected (->rcv_nxt), the segment is placed on
iva2k 0:e614f7875b60 1065 the ->ooseq queue. This is done by finding the appropriate
iva2k 0:e614f7875b60 1066 place in the ->ooseq queue (which is ordered by sequence
iva2k 0:e614f7875b60 1067 number) and trim the segment in both ends if needed. An
iva2k 0:e614f7875b60 1068 immediate ACK is sent to indicate that we received an
iva2k 0:e614f7875b60 1069 out-of-sequence segment.
iva2k 0:e614f7875b60 1070
iva2k 0:e614f7875b60 1071 +) Finally, we check if the first segment on the ->ooseq queue
iva2k 0:e614f7875b60 1072 now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
iva2k 0:e614f7875b60 1073 rcv_nxt > ooseq->seqno, we must trim the first edge of the
iva2k 0:e614f7875b60 1074 segment on ->ooseq before we adjust rcv_nxt. The data in the
iva2k 0:e614f7875b60 1075 segments that are now on sequence are chained onto the
iva2k 0:e614f7875b60 1076 incoming segment so that we only need to call the application
iva2k 0:e614f7875b60 1077 once.
iva2k 0:e614f7875b60 1078 */
iva2k 0:e614f7875b60 1079
iva2k 0:e614f7875b60 1080 /* First, we check if we must trim the first edge. We have to do
iva2k 0:e614f7875b60 1081 this if the sequence number of the incoming segment is less
iva2k 0:e614f7875b60 1082 than rcv_nxt, and the sequence number plus the length of the
iva2k 0:e614f7875b60 1083 segment is larger than rcv_nxt. */
iva2k 0:e614f7875b60 1084 /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
iva2k 0:e614f7875b60 1085 if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
iva2k 0:e614f7875b60 1086 if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){
iva2k 0:e614f7875b60 1087 /* Trimming the first edge is done by pushing the payload
iva2k 0:e614f7875b60 1088 pointer in the pbuf downwards. This is somewhat tricky since
iva2k 0:e614f7875b60 1089 we do not want to discard the full contents of the pbuf up to
iva2k 0:e614f7875b60 1090 the new starting point of the data since we have to keep the
iva2k 0:e614f7875b60 1091 TCP header which is present in the first pbuf in the chain.
iva2k 0:e614f7875b60 1092
iva2k 0:e614f7875b60 1093 What is done is really quite a nasty hack: the first pbuf in
iva2k 0:e614f7875b60 1094 the pbuf chain is pointed to by inseg.p. Since we need to be
iva2k 0:e614f7875b60 1095 able to deallocate the whole pbuf, we cannot change this
iva2k 0:e614f7875b60 1096 inseg.p pointer to point to any of the later pbufs in the
iva2k 0:e614f7875b60 1097 chain. Instead, we point the ->payload pointer in the first
iva2k 0:e614f7875b60 1098 pbuf to data in one of the later pbufs. We also set the
iva2k 0:e614f7875b60 1099 inseg.data pointer to point to the right place. This way, the
iva2k 0:e614f7875b60 1100 ->p pointer will still point to the first pbuf, but the
iva2k 0:e614f7875b60 1101 ->p->payload pointer will point to data in another pbuf.
iva2k 0:e614f7875b60 1102
iva2k 0:e614f7875b60 1103 After we are done with adjusting the pbuf pointers we must
iva2k 0:e614f7875b60 1104 adjust the ->data pointer in the seg and the segment
iva2k 0:e614f7875b60 1105 length.*/
iva2k 0:e614f7875b60 1106
iva2k 0:e614f7875b60 1107 off = pcb->rcv_nxt - seqno;
iva2k 0:e614f7875b60 1108 p = inseg.p;
iva2k 0:e614f7875b60 1109 LWIP_ASSERT("inseg.p != NULL", inseg.p);
iva2k 0:e614f7875b60 1110 LWIP_ASSERT("insane offset!", (off < 0x7fff));
iva2k 0:e614f7875b60 1111 if (inseg.p->len < off) {
iva2k 0:e614f7875b60 1112 LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
iva2k 0:e614f7875b60 1113 new_tot_len = (u16_t)(inseg.p->tot_len - off);
iva2k 0:e614f7875b60 1114 while (p->len < off) {
iva2k 0:e614f7875b60 1115 off -= p->len;
iva2k 0:e614f7875b60 1116 /* KJM following line changed (with addition of new_tot_len var)
iva2k 0:e614f7875b60 1117 to fix bug #9076
iva2k 0:e614f7875b60 1118 inseg.p->tot_len -= p->len; */
iva2k 0:e614f7875b60 1119 p->tot_len = new_tot_len;
iva2k 0:e614f7875b60 1120 p->len = 0;
iva2k 0:e614f7875b60 1121 p = p->next;
iva2k 0:e614f7875b60 1122 }
iva2k 0:e614f7875b60 1123 if(pbuf_header(p, (s16_t)-off)) {
iva2k 0:e614f7875b60 1124 /* Do we need to cope with this failing? Assert for now */
iva2k 0:e614f7875b60 1125 LWIP_ASSERT("pbuf_header failed", 0);
iva2k 0:e614f7875b60 1126 }
iva2k 0:e614f7875b60 1127 } else {
iva2k 0:e614f7875b60 1128 if(pbuf_header(inseg.p, (s16_t)-off)) {
iva2k 0:e614f7875b60 1129 /* Do we need to cope with this failing? Assert for now */
iva2k 0:e614f7875b60 1130 LWIP_ASSERT("pbuf_header failed", 0);
iva2k 0:e614f7875b60 1131 }
iva2k 0:e614f7875b60 1132 }
iva2k 0:e614f7875b60 1133 /* KJM following line changed to use p->payload rather than inseg->p->payload
iva2k 0:e614f7875b60 1134 to fix bug #9076 */
iva2k 0:e614f7875b60 1135 inseg.dataptr = p->payload;
iva2k 0:e614f7875b60 1136 inseg.len -= (u16_t)(pcb->rcv_nxt - seqno);
iva2k 0:e614f7875b60 1137 inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
iva2k 0:e614f7875b60 1138 }
iva2k 0:e614f7875b60 1139 else {
iva2k 0:e614f7875b60 1140 if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
iva2k 0:e614f7875b60 1141 /* the whole segment is < rcv_nxt */
iva2k 0:e614f7875b60 1142 /* must be a duplicate of a packet that has already been correctly handled */
iva2k 0:e614f7875b60 1143
iva2k 0:e614f7875b60 1144 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
iva2k 0:e614f7875b60 1145 tcp_ack_now(pcb);
iva2k 0:e614f7875b60 1146 }
iva2k 0:e614f7875b60 1147 }
iva2k 0:e614f7875b60 1148
iva2k 0:e614f7875b60 1149 /* The sequence number must be within the window (above rcv_nxt
iva2k 0:e614f7875b60 1150 and below rcv_nxt + rcv_wnd) in order to be further
iva2k 0:e614f7875b60 1151 processed. */
iva2k 0:e614f7875b60 1152 if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
iva2k 0:e614f7875b60 1153 pcb->rcv_nxt + pcb->rcv_wnd - 1)){
iva2k 0:e614f7875b60 1154 if (pcb->rcv_nxt == seqno) {
iva2k 0:e614f7875b60 1155 /* The incoming segment is the next in sequence. We check if
iva2k 0:e614f7875b60 1156 we have to trim the end of the segment and update rcv_nxt
iva2k 0:e614f7875b60 1157 and pass the data to the application. */
iva2k 0:e614f7875b60 1158 tcplen = TCP_TCPLEN(&inseg);
iva2k 0:e614f7875b60 1159
iva2k 0:e614f7875b60 1160 if (tcplen > pcb->rcv_wnd) {
iva2k 0:e614f7875b60 1161 LWIP_DEBUGF(TCP_INPUT_DEBUG,
iva2k 0:e614f7875b60 1162 ("tcp_receive: other end overran receive window"
iva2k 0:e614f7875b60 1163 "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
iva2k 0:e614f7875b60 1164 seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
iva2k 0:e614f7875b60 1165 if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
iva2k 0:e614f7875b60 1166 /* Must remove the FIN from the header as we're trimming
iva2k 0:e614f7875b60 1167 * that byte of sequence-space from the packet */
iva2k 0:e614f7875b60 1168 TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN);
iva2k 0:e614f7875b60 1169 }
iva2k 0:e614f7875b60 1170 /* Adjust length of segment to fit in the window. */
iva2k 0:e614f7875b60 1171 inseg.len = pcb->rcv_wnd;
iva2k 0:e614f7875b60 1172 if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
iva2k 0:e614f7875b60 1173 inseg.len -= 1;
iva2k 0:e614f7875b60 1174 }
iva2k 0:e614f7875b60 1175 pbuf_realloc(inseg.p, inseg.len);
iva2k 0:e614f7875b60 1176 tcplen = TCP_TCPLEN(&inseg);
iva2k 0:e614f7875b60 1177 LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
iva2k 0:e614f7875b60 1178 (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
iva2k 0:e614f7875b60 1179 }
iva2k 0:e614f7875b60 1180 #if TCP_QUEUE_OOSEQ
iva2k 0:e614f7875b60 1181 /* Received in-sequence data, adjust ooseq data if:
iva2k 0:e614f7875b60 1182 - FIN has been received or
iva2k 0:e614f7875b60 1183 - inseq overlaps with ooseq */
iva2k 0:e614f7875b60 1184 if (pcb->ooseq != NULL) {
iva2k 0:e614f7875b60 1185 if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
iva2k 0:e614f7875b60 1186 LWIP_DEBUGF(TCP_INPUT_DEBUG,
iva2k 0:e614f7875b60 1187 ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
iva2k 0:e614f7875b60 1188 /* Received in-order FIN means anything that was received
iva2k 0:e614f7875b60 1189 * out of order must now have been received in-order, so
iva2k 0:e614f7875b60 1190 * bin the ooseq queue */
iva2k 0:e614f7875b60 1191 while (pcb->ooseq != NULL) {
iva2k 0:e614f7875b60 1192 struct tcp_seg *old_ooseq = pcb->ooseq;
iva2k 0:e614f7875b60 1193 pcb->ooseq = pcb->ooseq->next;
iva2k 0:e614f7875b60 1194 tcp_seg_free(old_ooseq);
iva2k 0:e614f7875b60 1195 }
iva2k 0:e614f7875b60 1196 }
iva2k 0:e614f7875b60 1197 else {
iva2k 0:e614f7875b60 1198 next = pcb->ooseq;
iva2k 0:e614f7875b60 1199 /* Remove all segments on ooseq that are covered by inseg already.
iva2k 0:e614f7875b60 1200 * FIN is copied from ooseq to inseg if present. */
iva2k 0:e614f7875b60 1201 while (next &&
iva2k 0:e614f7875b60 1202 TCP_SEQ_GEQ(seqno + tcplen,
iva2k 0:e614f7875b60 1203 next->tcphdr->seqno + next->len)) {
iva2k 0:e614f7875b60 1204 /* inseg cannot have FIN here (already processed above) */
iva2k 0:e614f7875b60 1205 if (TCPH_FLAGS(next->tcphdr) & TCP_FIN &&
iva2k 0:e614f7875b60 1206 (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
iva2k 0:e614f7875b60 1207 TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
iva2k 0:e614f7875b60 1208 tcplen = TCP_TCPLEN(&inseg);
iva2k 0:e614f7875b60 1209 }
iva2k 0:e614f7875b60 1210 prev = next;
iva2k 0:e614f7875b60 1211 next = next->next;
iva2k 0:e614f7875b60 1212 tcp_seg_free(prev);
iva2k 0:e614f7875b60 1213 }
iva2k 0:e614f7875b60 1214 /* Now trim right side of inseg if it overlaps with the first
iva2k 0:e614f7875b60 1215 * segment on ooseq */
iva2k 0:e614f7875b60 1216 if (next &&
iva2k 0:e614f7875b60 1217 TCP_SEQ_GT(seqno + tcplen,
iva2k 0:e614f7875b60 1218 next->tcphdr->seqno)) {
iva2k 0:e614f7875b60 1219 /* inseg cannot have FIN here (already processed above) */
iva2k 0:e614f7875b60 1220 inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
iva2k 0:e614f7875b60 1221 if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
iva2k 0:e614f7875b60 1222 inseg.len -= 1;
iva2k 0:e614f7875b60 1223 }
iva2k 0:e614f7875b60 1224 pbuf_realloc(inseg.p, inseg.len);
iva2k 0:e614f7875b60 1225 tcplen = TCP_TCPLEN(&inseg);
iva2k 0:e614f7875b60 1226 LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
iva2k 0:e614f7875b60 1227 (seqno + tcplen) == next->tcphdr->seqno);
iva2k 0:e614f7875b60 1228 }
iva2k 0:e614f7875b60 1229 pcb->ooseq = next;
iva2k 0:e614f7875b60 1230 }
iva2k 0:e614f7875b60 1231 }
iva2k 0:e614f7875b60 1232 #endif /* TCP_QUEUE_OOSEQ */
iva2k 0:e614f7875b60 1233
iva2k 0:e614f7875b60 1234 pcb->rcv_nxt = seqno + tcplen;
iva2k 0:e614f7875b60 1235
iva2k 0:e614f7875b60 1236 /* Update the receiver's (our) window. */
iva2k 0:e614f7875b60 1237 LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
iva2k 0:e614f7875b60 1238 pcb->rcv_wnd -= tcplen;
iva2k 0:e614f7875b60 1239
iva2k 0:e614f7875b60 1240 tcp_update_rcv_ann_wnd(pcb);
iva2k 0:e614f7875b60 1241
iva2k 0:e614f7875b60 1242 /* If there is data in the segment, we make preparations to
iva2k 0:e614f7875b60 1243 pass this up to the application. The ->recv_data variable
iva2k 0:e614f7875b60 1244 is used for holding the pbuf that goes to the
iva2k 0:e614f7875b60 1245 application. The code for reassembling out-of-sequence data
iva2k 0:e614f7875b60 1246 chains its data on this pbuf as well.
iva2k 0:e614f7875b60 1247
iva2k 0:e614f7875b60 1248 If the segment was a FIN, we set the TF_GOT_FIN flag that will
iva2k 0:e614f7875b60 1249 be used to indicate to the application that the remote side has
iva2k 0:e614f7875b60 1250 closed its end of the connection. */
iva2k 0:e614f7875b60 1251 if (inseg.p->tot_len > 0) {
iva2k 0:e614f7875b60 1252 recv_data = inseg.p;
iva2k 0:e614f7875b60 1253 /* Since this pbuf now is the responsibility of the
iva2k 0:e614f7875b60 1254 application, we delete our reference to it so that we won't
iva2k 0:e614f7875b60 1255 (mistakingly) deallocate it. */
iva2k 0:e614f7875b60 1256 inseg.p = NULL;
iva2k 0:e614f7875b60 1257 }
iva2k 0:e614f7875b60 1258 if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
iva2k 0:e614f7875b60 1259 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
iva2k 0:e614f7875b60 1260 recv_flags |= TF_GOT_FIN;
iva2k 0:e614f7875b60 1261 }
iva2k 0:e614f7875b60 1262
iva2k 0:e614f7875b60 1263 #if TCP_QUEUE_OOSEQ
iva2k 0:e614f7875b60 1264 /* We now check if we have segments on the ->ooseq queue that
iva2k 0:e614f7875b60 1265 are now in sequence. */
iva2k 0:e614f7875b60 1266 while (pcb->ooseq != NULL &&
iva2k 0:e614f7875b60 1267 pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
iva2k 0:e614f7875b60 1268
iva2k 0:e614f7875b60 1269 cseg = pcb->ooseq;
iva2k 0:e614f7875b60 1270 seqno = pcb->ooseq->tcphdr->seqno;
iva2k 0:e614f7875b60 1271
iva2k 0:e614f7875b60 1272 pcb->rcv_nxt += TCP_TCPLEN(cseg);
iva2k 0:e614f7875b60 1273 LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
iva2k 0:e614f7875b60 1274 pcb->rcv_wnd >= TCP_TCPLEN(cseg));
iva2k 0:e614f7875b60 1275 pcb->rcv_wnd -= TCP_TCPLEN(cseg);
iva2k 0:e614f7875b60 1276
iva2k 0:e614f7875b60 1277 tcp_update_rcv_ann_wnd(pcb);
iva2k 0:e614f7875b60 1278
iva2k 0:e614f7875b60 1279 if (cseg->p->tot_len > 0) {
iva2k 0:e614f7875b60 1280 /* Chain this pbuf onto the pbuf that we will pass to
iva2k 0:e614f7875b60 1281 the application. */
iva2k 0:e614f7875b60 1282 if (recv_data) {
iva2k 0:e614f7875b60 1283 pbuf_cat(recv_data, cseg->p);
iva2k 0:e614f7875b60 1284 } else {
iva2k 0:e614f7875b60 1285 recv_data = cseg->p;
iva2k 0:e614f7875b60 1286 }
iva2k 0:e614f7875b60 1287 cseg->p = NULL;
iva2k 0:e614f7875b60 1288 }
iva2k 0:e614f7875b60 1289 if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
iva2k 0:e614f7875b60 1290 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
iva2k 0:e614f7875b60 1291 recv_flags |= TF_GOT_FIN;
iva2k 0:e614f7875b60 1292 if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
iva2k 0:e614f7875b60 1293 pcb->state = CLOSE_WAIT;
iva2k 0:e614f7875b60 1294 }
iva2k 0:e614f7875b60 1295 }
iva2k 0:e614f7875b60 1296
iva2k 0:e614f7875b60 1297 pcb->ooseq = cseg->next;
iva2k 0:e614f7875b60 1298 tcp_seg_free(cseg);
iva2k 0:e614f7875b60 1299 }
iva2k 0:e614f7875b60 1300 #endif /* TCP_QUEUE_OOSEQ */
iva2k 0:e614f7875b60 1301
iva2k 0:e614f7875b60 1302
iva2k 0:e614f7875b60 1303 /* Acknowledge the segment(s). */
iva2k 0:e614f7875b60 1304 tcp_ack(pcb);
iva2k 0:e614f7875b60 1305
iva2k 0:e614f7875b60 1306 } else {
iva2k 0:e614f7875b60 1307 /* We get here if the incoming segment is out-of-sequence. */
iva2k 0:e614f7875b60 1308 tcp_send_empty_ack(pcb);
iva2k 0:e614f7875b60 1309 #if TCP_QUEUE_OOSEQ
iva2k 0:e614f7875b60 1310 /* We queue the segment on the ->ooseq queue. */
iva2k 0:e614f7875b60 1311 if (pcb->ooseq == NULL) {
iva2k 0:e614f7875b60 1312 pcb->ooseq = tcp_seg_copy(&inseg);
iva2k 0:e614f7875b60 1313 } else {
iva2k 0:e614f7875b60 1314 /* If the queue is not empty, we walk through the queue and
iva2k 0:e614f7875b60 1315 try to find a place where the sequence number of the
iva2k 0:e614f7875b60 1316 incoming segment is between the sequence numbers of the
iva2k 0:e614f7875b60 1317 previous and the next segment on the ->ooseq queue. That is
iva2k 0:e614f7875b60 1318 the place where we put the incoming segment. If needed, we
iva2k 0:e614f7875b60 1319 trim the second edges of the previous and the incoming
iva2k 0:e614f7875b60 1320 segment so that it will fit into the sequence.
iva2k 0:e614f7875b60 1321
iva2k 0:e614f7875b60 1322 If the incoming segment has the same sequence number as a
iva2k 0:e614f7875b60 1323 segment on the ->ooseq queue, we discard the segment that
iva2k 0:e614f7875b60 1324 contains less data. */
iva2k 0:e614f7875b60 1325
iva2k 0:e614f7875b60 1326 prev = NULL;
iva2k 0:e614f7875b60 1327 for(next = pcb->ooseq; next != NULL; next = next->next) {
iva2k 0:e614f7875b60 1328 if (seqno == next->tcphdr->seqno) {
iva2k 0:e614f7875b60 1329 /* The sequence number of the incoming segment is the
iva2k 0:e614f7875b60 1330 same as the sequence number of the segment on
iva2k 0:e614f7875b60 1331 ->ooseq. We check the lengths to see which one to
iva2k 0:e614f7875b60 1332 discard. */
iva2k 0:e614f7875b60 1333 if (inseg.len > next->len) {
iva2k 0:e614f7875b60 1334 /* The incoming segment is larger than the old
iva2k 0:e614f7875b60 1335 segment. We replace some segments with the new
iva2k 0:e614f7875b60 1336 one. */
iva2k 0:e614f7875b60 1337 cseg = tcp_seg_copy(&inseg);
iva2k 0:e614f7875b60 1338 if (cseg != NULL) {
iva2k 0:e614f7875b60 1339 if (prev != NULL) {
iva2k 0:e614f7875b60 1340 prev->next = cseg;
iva2k 0:e614f7875b60 1341 } else {
iva2k 0:e614f7875b60 1342 pcb->ooseq = cseg;
iva2k 0:e614f7875b60 1343 }
iva2k 0:e614f7875b60 1344 tcp_oos_insert_segment(cseg, next);
iva2k 0:e614f7875b60 1345 }
iva2k 0:e614f7875b60 1346 break;
iva2k 0:e614f7875b60 1347 } else {
iva2k 0:e614f7875b60 1348 /* Either the lenghts are the same or the incoming
iva2k 0:e614f7875b60 1349 segment was smaller than the old one; in either
iva2k 0:e614f7875b60 1350 case, we ditch the incoming segment. */
iva2k 0:e614f7875b60 1351 break;
iva2k 0:e614f7875b60 1352 }
iva2k 0:e614f7875b60 1353 } else {
iva2k 0:e614f7875b60 1354 if (prev == NULL) {
iva2k 0:e614f7875b60 1355 if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
iva2k 0:e614f7875b60 1356 /* The sequence number of the incoming segment is lower
iva2k 0:e614f7875b60 1357 than the sequence number of the first segment on the
iva2k 0:e614f7875b60 1358 queue. We put the incoming segment first on the
iva2k 0:e614f7875b60 1359 queue. */
iva2k 0:e614f7875b60 1360 cseg = tcp_seg_copy(&inseg);
iva2k 0:e614f7875b60 1361 if (cseg != NULL) {
iva2k 0:e614f7875b60 1362 pcb->ooseq = cseg;
iva2k 0:e614f7875b60 1363 tcp_oos_insert_segment(cseg, next);
iva2k 0:e614f7875b60 1364 }
iva2k 0:e614f7875b60 1365 break;
iva2k 0:e614f7875b60 1366 }
iva2k 0:e614f7875b60 1367 } else {
iva2k 0:e614f7875b60 1368 /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
iva2k 0:e614f7875b60 1369 TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
iva2k 0:e614f7875b60 1370 if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
iva2k 0:e614f7875b60 1371 /* The sequence number of the incoming segment is in
iva2k 0:e614f7875b60 1372 between the sequence numbers of the previous and
iva2k 0:e614f7875b60 1373 the next segment on ->ooseq. We trim trim the previous
iva2k 0:e614f7875b60 1374 segment, delete next segments that included in received segment
iva2k 0:e614f7875b60 1375 and trim received, if needed. */
iva2k 0:e614f7875b60 1376 cseg = tcp_seg_copy(&inseg);
iva2k 0:e614f7875b60 1377 if (cseg != NULL) {
iva2k 0:e614f7875b60 1378 if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
iva2k 0:e614f7875b60 1379 /* We need to trim the prev segment. */
iva2k 0:e614f7875b60 1380 prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
iva2k 0:e614f7875b60 1381 pbuf_realloc(prev->p, prev->len);
iva2k 0:e614f7875b60 1382 }
iva2k 0:e614f7875b60 1383 prev->next = cseg;
iva2k 0:e614f7875b60 1384 tcp_oos_insert_segment(cseg, next);
iva2k 0:e614f7875b60 1385 }
iva2k 0:e614f7875b60 1386 break;
iva2k 0:e614f7875b60 1387 }
iva2k 0:e614f7875b60 1388 }
iva2k 0:e614f7875b60 1389 /* If the "next" segment is the last segment on the
iva2k 0:e614f7875b60 1390 ooseq queue, we add the incoming segment to the end
iva2k 0:e614f7875b60 1391 of the list. */
iva2k 0:e614f7875b60 1392 if (next->next == NULL &&
iva2k 0:e614f7875b60 1393 TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
iva2k 0:e614f7875b60 1394 if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
iva2k 0:e614f7875b60 1395 /* segment "next" already contains all data */
iva2k 0:e614f7875b60 1396 break;
iva2k 0:e614f7875b60 1397 }
iva2k 0:e614f7875b60 1398 next->next = tcp_seg_copy(&inseg);
iva2k 0:e614f7875b60 1399 if (next->next != NULL) {
iva2k 0:e614f7875b60 1400 if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
iva2k 0:e614f7875b60 1401 /* We need to trim the last segment. */
iva2k 0:e614f7875b60 1402 next->len = (u16_t)(seqno - next->tcphdr->seqno);
iva2k 0:e614f7875b60 1403 pbuf_realloc(next->p, next->len);
iva2k 0:e614f7875b60 1404 }
iva2k 0:e614f7875b60 1405 }
iva2k 0:e614f7875b60 1406 break;
iva2k 0:e614f7875b60 1407 }
iva2k 0:e614f7875b60 1408 }
iva2k 0:e614f7875b60 1409 prev = next;
iva2k 0:e614f7875b60 1410 }
iva2k 0:e614f7875b60 1411 }
iva2k 0:e614f7875b60 1412 #endif /* TCP_QUEUE_OOSEQ */
iva2k 0:e614f7875b60 1413
iva2k 0:e614f7875b60 1414 }
iva2k 0:e614f7875b60 1415 } else {
iva2k 0:e614f7875b60 1416 /* The incoming segment is not withing the window. */
iva2k 0:e614f7875b60 1417 tcp_send_empty_ack(pcb);
iva2k 0:e614f7875b60 1418 }
iva2k 0:e614f7875b60 1419 } else {
iva2k 0:e614f7875b60 1420 /* Segments with length 0 is taken care of here. Segments that
iva2k 0:e614f7875b60 1421 fall out of the window are ACKed. */
iva2k 0:e614f7875b60 1422 /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) ||
iva2k 0:e614f7875b60 1423 TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
iva2k 0:e614f7875b60 1424 if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
iva2k 0:e614f7875b60 1425 tcp_ack_now(pcb);
iva2k 0:e614f7875b60 1426 }
iva2k 0:e614f7875b60 1427 }
iva2k 0:e614f7875b60 1428 }
iva2k 0:e614f7875b60 1429
iva2k 0:e614f7875b60 1430 /**
iva2k 0:e614f7875b60 1431 * Parses the options contained in the incoming segment.
iva2k 0:e614f7875b60 1432 *
iva2k 0:e614f7875b60 1433 * Called from tcp_listen_input() and tcp_process().
iva2k 0:e614f7875b60 1434 * Currently, only the MSS option is supported!
iva2k 0:e614f7875b60 1435 *
iva2k 0:e614f7875b60 1436 * @param pcb the tcp_pcb for which a segment arrived
iva2k 0:e614f7875b60 1437 */
iva2k 0:e614f7875b60 1438 static void
iva2k 0:e614f7875b60 1439 tcp_parseopt(struct tcp_pcb *pcb)
iva2k 0:e614f7875b60 1440 {
iva2k 0:e614f7875b60 1441 u16_t c, max_c;
iva2k 0:e614f7875b60 1442 u16_t mss;
iva2k 0:e614f7875b60 1443 u8_t *opts, opt;
iva2k 0:e614f7875b60 1444 #if LWIP_TCP_TIMESTAMPS
iva2k 0:e614f7875b60 1445 u32_t tsval;
iva2k 0:e614f7875b60 1446 #endif
iva2k 0:e614f7875b60 1447
iva2k 0:e614f7875b60 1448 opts = (u8_t *)tcphdr + TCP_HLEN;
iva2k 0:e614f7875b60 1449
iva2k 0:e614f7875b60 1450 /* Parse the TCP MSS option, if present. */
iva2k 0:e614f7875b60 1451 if(TCPH_HDRLEN(tcphdr) > 0x5) {
iva2k 0:e614f7875b60 1452 max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
iva2k 0:e614f7875b60 1453 for (c = 0; c < max_c; ) {
iva2k 0:e614f7875b60 1454 opt = opts[c];
iva2k 0:e614f7875b60 1455 switch (opt) {
iva2k 0:e614f7875b60 1456 case 0x00:
iva2k 0:e614f7875b60 1457 /* End of options. */
iva2k 0:e614f7875b60 1458 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
iva2k 0:e614f7875b60 1459 return;
iva2k 0:e614f7875b60 1460 case 0x01:
iva2k 0:e614f7875b60 1461 /* NOP option. */
iva2k 0:e614f7875b60 1462 ++c;
iva2k 0:e614f7875b60 1463 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
iva2k 0:e614f7875b60 1464 break;
iva2k 0:e614f7875b60 1465 case 0x02:
iva2k 0:e614f7875b60 1466 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
iva2k 0:e614f7875b60 1467 if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
iva2k 0:e614f7875b60 1468 /* Bad length */
iva2k 0:e614f7875b60 1469 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
iva2k 0:e614f7875b60 1470 return;
iva2k 0:e614f7875b60 1471 }
iva2k 0:e614f7875b60 1472 /* An MSS option with the right option length. */
iva2k 0:e614f7875b60 1473 mss = (opts[c + 2] << 8) | opts[c + 3];
iva2k 0:e614f7875b60 1474 /* Limit the mss to the configured TCP_MSS and prevent division by zero */
iva2k 0:e614f7875b60 1475 pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
iva2k 0:e614f7875b60 1476 /* Advance to next option */
iva2k 0:e614f7875b60 1477 c += 0x04;
iva2k 0:e614f7875b60 1478 break;
iva2k 0:e614f7875b60 1479 #if LWIP_TCP_TIMESTAMPS
iva2k 0:e614f7875b60 1480 case 0x08:
iva2k 0:e614f7875b60 1481 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
iva2k 0:e614f7875b60 1482 if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
iva2k 0:e614f7875b60 1483 /* Bad length */
iva2k 0:e614f7875b60 1484 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
iva2k 0:e614f7875b60 1485 return;
iva2k 0:e614f7875b60 1486 }
iva2k 0:e614f7875b60 1487 /* TCP timestamp option with valid length */
iva2k 0:e614f7875b60 1488 tsval = (opts[c+2]) | (opts[c+3] << 8) |
iva2k 0:e614f7875b60 1489 (opts[c+4] << 16) | (opts[c+5] << 24);
iva2k 0:e614f7875b60 1490 if (flags & TCP_SYN) {
iva2k 0:e614f7875b60 1491 pcb->ts_recent = ntohl(tsval);
iva2k 0:e614f7875b60 1492 pcb->flags |= TF_TIMESTAMP;
iva2k 0:e614f7875b60 1493 } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
iva2k 0:e614f7875b60 1494 pcb->ts_recent = ntohl(tsval);
iva2k 0:e614f7875b60 1495 }
iva2k 0:e614f7875b60 1496 /* Advance to next option */
iva2k 0:e614f7875b60 1497 c += 0x0A;
iva2k 0:e614f7875b60 1498 break;
iva2k 0:e614f7875b60 1499 #endif
iva2k 0:e614f7875b60 1500 default:
iva2k 0:e614f7875b60 1501 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
iva2k 0:e614f7875b60 1502 if (opts[c + 1] == 0) {
iva2k 0:e614f7875b60 1503 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
iva2k 0:e614f7875b60 1504 /* If the length field is zero, the options are malformed
iva2k 0:e614f7875b60 1505 and we don't process them further. */
iva2k 0:e614f7875b60 1506 return;
iva2k 0:e614f7875b60 1507 }
iva2k 0:e614f7875b60 1508 /* All other options have a length field, so that we easily
iva2k 0:e614f7875b60 1509 can skip past them. */
iva2k 0:e614f7875b60 1510 c += opts[c + 1];
iva2k 0:e614f7875b60 1511 }
iva2k 0:e614f7875b60 1512 }
iva2k 0:e614f7875b60 1513 }
iva2k 0:e614f7875b60 1514 }
iva2k 0:e614f7875b60 1515
iva2k 0:e614f7875b60 1516 #endif /* LWIP_TCP */