initial tcp commit!
authorDavid Zhu <yuzhu@cs.berkeley.edu>
Sun, 13 May 2012 21:40:32 +0000 (14:40 -0700)
committerDavid Zhu <yuzhu@cs.berkeley.edu>
Wed, 20 Mar 2013 03:37:39 +0000 (20:37 -0700)
Things that may break, IP options, out of sequence packet handling, etc.

20 files changed:
kern/include/debug.h [new file with mode: 0644]
kern/include/net/ip.h
kern/include/net/pbuf.h
kern/include/net/tcp.h [new file with mode: 0644]
kern/include/net/tcp_impl.h [new file with mode: 0644]
kern/include/net/udp.h
kern/src/net/ip.c
kern/src/net/pbuf.c
kern/src/net/tcp.c [new file with mode: 0644]
kern/src/net/tcp_in.c [new file with mode: 0644]
kern/src/net/tcp_out.c [new file with mode: 0644]
kern/src/net/tcpip.h [new file with mode: 0644]
kern/src/net/timers.c [new file with mode: 0644]
kern/src/net/udp.c
tests/tcp_test.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/bind.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/connect.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/recv.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/send.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/socket.c

diff --git a/kern/include/debug.h b/kern/include/debug.h
new file mode 100644 (file)
index 0000000..1fc7c9b
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *  
+ *
+ */
+#ifndef __LWIP_DEBUG_H__
+#define __LWIP_DEBUG_H__
+
+#define LWIP_PLATFORM_ASSERT(x)  assert(x)
+
+/** lower two bits indicate debug level
+ * - 0 all
+ * - 1 warning
+ * - 2 serious
+ * - 3 severe
+ */
+#define LWIP_DBG_LEVEL_ALL     0x00
+#define LWIP_DBG_LEVEL_OFF     LWIP_DBG_LEVEL_ALL /* compatibility define only */
+#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */
+#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */
+#define LWIP_DBG_LEVEL_SEVERE  0x03
+#define LWIP_DBG_MASK_LEVEL    0x03
+
+/** flag for LWIP_DEBUGF to enable that debug message */
+#define LWIP_DBG_ON            0x80U
+/** flag for LWIP_DEBUGF to disable that debug message */
+#define LWIP_DBG_OFF           0x00U
+
+/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */
+#define LWIP_DBG_TRACE         0x40U
+/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */
+#define LWIP_DBG_STATE         0x20U
+/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */
+#define LWIP_DBG_FRESH         0x10U
+/** flag for LWIP_DEBUGF to halt after printing this debug message */
+#define LWIP_DBG_HALT          0x08U
+
+#ifndef LWIP_NOASSERT
+#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \
+  LWIP_PLATFORM_ASSERT(message); } while(0)
+#else  /* LWIP_NOASSERT */
+#define LWIP_ASSERT(message, assertion) 
+#endif /* LWIP_NOASSERT */
+
+/** if "expression" isn't true, then print "message" and execute "handler" expression */
+#ifndef LWIP_ERROR
+#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
+  LWIP_PLATFORM_ASSERT(message); handler;}} while(0)
+#endif /* LWIP_ERROR */
+
+#ifdef LWIP_DEBUG
+/** print debug message only if debug message type is enabled...
+ *  AND is of correct type AND is at least LWIP_DBG_LEVEL
+ */
+#define LWIP_DEBUGF(debug, message) do { \
+                               if ( \
+                                   ((debug) & LWIP_DBG_ON) && \
+                                   ((debug) & LWIP_DBG_TYPES_ON) && \
+                                   ((int16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \
+                                 if ((debug) & LWIP_DBG_HALT) { \
+                                   while(1); \
+                                 } \
+                               } \
+                             } while(0)
+
+#else  /* LWIP_DEBUG */
+#define LWIP_DEBUGF(debug, message) 
+#endif /* LWIP_DEBUG */
+
+#endif /* __LWIP_DEBUG_H__ */
+
index 14e103a..dea76e2 100644 (file)
@@ -6,9 +6,24 @@
 #define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->s_addr == IPADDR_ANY)
 #define ip_addr_cmp(addr1, addr2) ((addr1)->s_addr == (addr2)->s_addr)
 #define ip_match(addr1, addr2) (ip_addr_isany(addr1) || ip_addr_isany(addr2) || ip_addr_cmp(addr1, addr2))
+#define ip_addr_copy(addr1, addr2) ((addr1).s_addr = (addr2).s_addr)
 
-extern struct in_addr global_ip;
-int ip_output(struct pbuf *p, struct in_addr *src, struct in_addr *dest, uint8_t proto);
+struct in_addr {
+    uint32_t s_addr;
+};
+
+typedef struct in_addr ip_addr_t;
+
+#define IP_PCB \
+/* ips are in network byte order */ \
+struct in_addr local_ip; \
+struct in_addr remote_ip; \
+uint8_t so_options; \
+uint8_t tos; \
+uint8_t ttl; \
+uint8_t addr_hint;
+
+int ip_output(struct pbuf *p, struct in_addr *src, struct in_addr *dest, uint8_t ttl, uint8_t tos, uint8_t proto);
 int ip_input(struct pbuf *p);
 
 #endif // ROS_KERN_IP_H
index ae6977e..c43bc3d 100644 (file)
@@ -21,6 +21,14 @@ extern "C" {
 #define PBUF_IP_HLEN        20
 #define PBUF_LINK_HLEN      14 + ETH_PAD_SIZE // Padding
 
+/** indicates this packet's data should be immediately passed to the application */
+#define PBUF_FLAG_PUSH      0x01U
+/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a
+    a pbuf differently */
+#define PBUF_FLAG_IS_CUSTOM 0x02U
+/** indicates this pbuf is UDP multicast to be looped back */
+#define PBUF_FLAG_MCASTLOOP 0x04U
+
 typedef enum {
   PBUF_TRANSPORT,
   PBUF_IP,
@@ -96,10 +104,10 @@ bool pbuf_free(struct pbuf *p);
 
 void attach_pbuf(struct pbuf *p, struct pbuf_head *buf_head);
 struct pbuf* detach_pbuf(struct pbuf_head *buf_head);
+uint8_t pbuf_clen(struct pbuf *p);  
+void pbuf_realloc(struct pbuf *p, uint16_t size); 
 // end
 #if 0
-void pbuf_realloc(struct pbuf *p, u16_t size); 
-u8_t pbuf_clen(struct pbuf *p);  
 struct pbuf *pbuf_dechain(struct pbuf *p);
 err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from);
 err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
diff --git a/kern/include/net/tcp.h b/kern/include/net/tcp.h
new file mode 100644 (file)
index 0000000..792c6ea
--- /dev/null
@@ -0,0 +1,490 @@
+#ifndef ROS_KERN_TCP_H
+#define ROS_KERN_TCP_H
+#include <net/pbuf.h>
+#include <net/ip.h>
+#include <net.h>
+#include <error.h>
+#include <bits/netinet.h>
+#include <socket.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef TCP_LOCAL_PORT_RANGE_START
+#define TCP_LOCAL_PORT_RANGE_START 4096
+#define TCP_LOCAL_PORT_RANGE_END   0x7fff
+#endif
+
+#ifndef LWIP_CALLBACK_API
+#define LWIP_CALLBACK_API 1
+#endif 
+
+
+/**
+ * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT
+ * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set
+ * in seconds. (does not require sockets.c, and will affect tcp.c)
+ */
+#ifndef LWIP_TCP_KEEPALIVE
+#define LWIP_TCP_KEEPALIVE              0
+#endif
+
+/**
+ * TCP_TTL: Default Time-To-Live value.
+ */
+#ifndef TCP_TTL
+#define TCP_TTL                         (255)
+#endif
+
+/**
+ * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default,
+ * you might want to increase this.)
+ * For the receive side, this MSS is advertised to the remote side
+ * when opening a connection. For the transmit size, this MSS sets
+ * an upper limit on the MSS advertised by the remote host.
+ */
+#ifndef TCP_MSS
+#define TCP_MSS                         (536)
+#endif
+
+/**
+ * TCP_WND: The size of a TCP window.  This must be at least 
+ * (2 * TCP_MSS) for things to work well
+ */
+#ifndef TCP_WND
+#define TCP_WND                         (4 * TCP_MSS)
+#endif 
+
+/**
+ * TCP_MAXRTX: Maximum number of retransmissions of data segments.
+ */
+#ifndef TCP_MAXRTX
+#define TCP_MAXRTX                      12
+#endif
+
+/**
+ * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments.
+ */
+#ifndef TCP_SYNMAXRTX
+#define TCP_SYNMAXRTX                   6
+#endif
+
+/**
+ * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order.
+ * Define to 0 if your device is low on memory.
+ */
+#ifndef TCP_QUEUE_OOSEQ
+#define TCP_QUEUE_OOSEQ                 0
+#endif
+
+
+/**
+ * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really
+ * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which
+ * reflects the available reassembly buffer size at the remote host) and the
+ * largest size permitted by the IP layer" (RFC 1122)
+ * Setting this to 1 enables code that checks TCP_MSS against the MTU of the
+ * netif used for a connection and limits the MSS if it would be too big otherwise.
+ */
+#ifndef TCP_CALCULATE_EFF_SEND_MSS
+#define TCP_CALCULATE_EFF_SEND_MSS      1
+#endif
+
+
+/**
+ * TCP_SND_BUF: TCP sender buffer space (bytes). 
+ */
+#ifndef TCP_SND_BUF
+#define TCP_SND_BUF                     256
+#endif
+
+/**
+ * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work.
+ */
+#ifndef TCP_SND_QUEUELEN
+#define TCP_SND_QUEUELEN                ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS))
+#endif
+
+/**
+ * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than
+ * TCP_SND_BUF. It is the amount of space which must be available in the
+ * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT).
+ */
+#ifndef TCP_SNDLOWAT
+#define TCP_SNDLOWAT                    ((TCP_SND_BUF)/2)
+#endif
+
+/**
+ * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be grater
+ * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below
+ * this number, select returns writable (combined with TCP_SNDLOWAT).
+ */
+#ifndef TCP_SNDQUEUELOWAT
+#define TCP_SNDQUEUELOWAT               ((TCP_SND_QUEUELEN)/2)
+#endif
+
+/**
+ * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb.
+ */
+#ifndef TCP_LISTEN_BACKLOG
+#define TCP_LISTEN_BACKLOG              0
+#endif
+
+/**
+ * The maximum allowed backlog for TCP listen netconns.
+ * This backlog is used unless another is explicitly specified.
+ * 0xff is the maximum (u8_t).
+ */
+#ifndef TCP_DEFAULT_LISTEN_BACKLOG
+#define TCP_DEFAULT_LISTEN_BACKLOG      0xff
+#endif
+
+/**
+ * TCP_OVERSIZE: The maximum number of bytes that tcp_write may
+ * allocate ahead of time in an attempt to create shorter pbuf chains
+ * for transmission. The meaningful range is 0 to TCP_MSS. Some
+ * suggested values are:
+ *
+ * 0:         Disable oversized allocation. Each tcp_write() allocates a new
+              pbuf (old behaviour).
+ * 1:         Allocate size-aligned pbufs with minimal excess. Use this if your
+ *            scatter-gather DMA requires aligned fragments.
+ * 128:       Limit the pbuf/memory overhead to 20%.
+ * TCP_MSS:   Try to create unfragmented TCP packets.
+ * TCP_MSS/4: Try to create 4 fragments or less per TCP packet.
+ */
+#ifndef TCP_OVERSIZE
+#define TCP_OVERSIZE                    TCP_MSS
+#endif
+
+/**
+ * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option.
+ */
+#ifndef LWIP_TCP_TIMESTAMPS
+#define LWIP_TCP_TIMESTAMPS             0
+#endif
+
+/**
+ * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an
+ * explicit window update
+ */
+#ifndef TCP_WND_UPDATE_THRESHOLD
+#define TCP_WND_UPDATE_THRESHOLD   (TCP_WND / 4)
+#endif
+
+struct tcp_pcb;
+
+#define TCP_PRIO_MIN    1
+#define TCP_PRIO_NORMAL 64
+#define TCP_PRIO_MAX    127
+
+/** Function prototype for tcp accept callback functions. Called when a new
+ * connection can be accepted on a listening pcb.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param newpcb The new connection pcb
+ * @param err An error code if there has been an error accepting.
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef error_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, error_t err);
+
+/** Function prototype for tcp receive callback functions. Called when data has
+ * been received.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which received data
+ * @param p The received data (or NULL when the connection has been closed!)
+ * @param err An error code if there has been an error receiving
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef error_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb,
+                             struct pbuf *p, error_t err);
+
+/** Function prototype for tcp sent callback functions. Called when sent data has
+ * been acknowledged by the remote side. Use it to free corresponding resources.
+ * This also means that the pcb has now space available to send new data.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb for which data has been acknowledged
+ * @param len The amount of bytes acknowledged
+ * @return ERR_OK: try to send some data by calling tcp_output
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef error_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb,
+                              uint16_t len);
+
+/** Function prototype for tcp poll callback functions. Called periodically as
+ * specified by @see tcp_poll.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb tcp pcb
+ * @return ERR_OK: try to send some data by calling tcp_output
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef error_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb);
+
+/** Function prototype for tcp error callback functions. Called when the pcb
+ * receives a RST or is unexpectedly closed for any other reason.
+ *
+ * @note The corresponding pcb is already freed when this callback is called!
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param err Error code to indicate why the pcb has been closed
+ *            ERR_ABRT: aborted through tcp_abort or by a TCP timer
+ *            ERR_RST: the connection was reset by the remote host
+ */
+typedef void  (*tcp_err_fn)(void *arg, error_t err);
+
+/** Function prototype for tcp connected callback functions. Called when a pcb
+ * is connected to the remote side after initiating a connection attempt by
+ * calling tcp_connect().
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which is connected
+ * @param err An unused error code, always ERR_OK currently ;-) TODO!
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ *
+ * @note When a connection attempt fails, the error callback is currently called!
+ */
+typedef error_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, error_t err);
+
+enum tcp_state {
+  CLOSED      = 0,
+  LISTEN      = 1,
+  SYN_SENT    = 2,
+  SYN_RCVD    = 3,
+  ESTABLISHED = 4,
+  FIN_WAIT_1  = 5,
+  FIN_WAIT_2  = 6,
+  CLOSE_WAIT  = 7,
+  CLOSING     = 8,
+  LAST_ACK    = 9,
+  TIME_WAIT   = 10
+};
+
+/**
+ * members common to struct tcp_pcb and struct tcp_listen_pcb
+ */
+#define TCP_PCB_COMMON(type) \
+  type *next; /* for the linked list */ \
+  enum tcp_state state; /* TCP state */ \
+  uint8_t prio; \
+  void *callback_arg; \
+       tcp_accept_fn accept; \
+  /* ports are in host byte order */ \
+  uint16_t local_port
+
+
+/* the TCP protocol control block */
+struct tcp_pcb {
+       IP_PCB;
+
+/** protocol specific PCB members */
+  TCP_PCB_COMMON(struct tcp_pcb);
+       struct socket *pcbsock;
+
+  /* ports are in host byte order */
+  uint16_t remote_port;
+  
+  uint8_t flags;
+#define TF_ACK_DELAY   ((uint8_t)0x01U)   /* Delayed ACK. */
+#define TF_ACK_NOW     ((uint8_t)0x02U)   /* Immediate ACK. */
+#define TF_INFR        ((uint8_t)0x04U)   /* In fast recovery. */
+#define TF_TIMESTAMP   ((uint8_t)0x08U)   /* Timestamp option enabled */
+#define TF_RXCLOSED    ((uint8_t)0x10U)   /* rx closed by tcp_shutdown */
+#define TF_FIN         ((uint8_t)0x20U)   /* Connection was closed locally (FIN segment enqueued). */
+#define TF_NODELAY     ((uint8_t)0x40U)   /* Disable Nagle algorithm */
+#define TF_NAGLEMEMERR ((uint8_t)0x80U)   /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
+
+  /* the rest of the fields are in host byte order
+     as we have to do some math with them */
+  /* receiver variables */
+  uint32_t rcv_nxt;   /* next seqno expected */
+  uint16_t rcv_wnd;   /* receiver window available */
+  uint16_t rcv_ann_wnd; /* receiver window to announce */
+  uint32_t rcv_ann_right_edge; /* announced right edge of window */
+
+  /* Timers */
+  uint32_t tmr;
+  uint8_t polltmr, pollinterval;
+  
+  /* Retransmission timer. */
+  int16_t rtime;
+  
+  uint16_t mss;   /* maximum segment size */
+  
+  /* RTT (round trip time) estimation variables */
+  uint32_t rttest; /* RTT estimate in 500ms ticks */
+  uint32_t rtseq;  /* sequence number being timed */
+  int16_t sa, sv; /* @todo document this */
+
+  int16_t rto;    /* retransmission time-out */
+  uint8_t nrtx;    /* number of retransmissions */
+
+  /* fast retransmit/recovery */
+  uint32_t lastack; /* Highest acknowledged seqno. */
+  uint8_t dupacks;
+  
+  /* congestion avoidance/control variables */
+  uint16_t cwnd;  
+  uint16_t ssthresh;
+
+  /* sender variables */
+  uint32_t snd_nxt;   /* next new seqno to be sent */
+  uint16_t snd_wnd;   /* sender window */
+  uint32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last
+                             window update. */
+  uint32_t snd_lbb;       /* Sequence number of next byte to be buffered. */
+
+  uint16_t acked;
+  
+  uint16_t snd_buf;   /* Available buffer space for sending (in bytes). */
+#define TCP_SNDQUEUELEN_OVERFLOW (0xffff-3)
+  uint16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */
+
+#if TCP_OVERSIZE
+  /* Extra bytes available at the end of the last pbuf in unsent. */
+  uint16_t unsent_oversize;
+#endif /* TCP_OVERSIZE */ 
+
+  /* These are ordered by sequence number: */
+  struct tcp_seg *unsent;   /* Unsent (queued) segments. */
+  struct tcp_seg *unacked;  /* Sent but unacknowledged segments. */
+#if TCP_QUEUE_OOSEQ  
+  struct tcp_seg *ooseq;    /* Received out of sequence segments. */
+#endif /* TCP_QUEUE_OOSEQ */
+
+  struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */
+
+  /* Function to be called when more send buffer space is available. */
+  tcp_sent_fn sent;
+  /* Function to be called when (in-sequence) data has arrived. */
+  tcp_recv_fn recv;
+  /* Function to be called when a connection has been set up. */
+  tcp_connected_fn connected;
+  /* Function which is called periodically. */
+  tcp_poll_fn poll;
+  /* Function to be called whenever a fatal error occurs. */
+  tcp_err_fn errf;
+
+#if LWIP_TCP_TIMESTAMPS
+  uint32_t ts_lastacksent;
+  uint32_t ts_recent;
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+  /* idle time before KEEPALIVE is sent */
+  uint32_t keep_idle;
+#if LWIP_TCP_KEEPALIVE
+  uint32_t keep_intvl;
+  uint32_t keep_cnt;
+#endif /* LWIP_TCP_KEEPALIVE */
+  
+  /* Persist timer counter */
+  uint32_t persist_cnt;
+  /* Persist timer back-off */
+  uint8_t persist_backoff;
+
+  /* KEEPALIVE counter */
+  uint8_t keep_cnt_sent;
+};
+
+struct tcp_pcb_listen {  
+       IP_PCB;
+/* Protocol specific PCB members */
+  TCP_PCB_COMMON(struct tcp_pcb_listen);
+
+#if TCP_LISTEN_BACKLOG
+  uint8_t backlog;
+  uint8_t accepts_pending;
+#endif /* TCP_LISTEN_BACKLOG */
+};
+
+#if 0 //LWIP_EVENT_API
+
+enum lwip_event {
+  LWIP_EVENT_ACCEPT,
+  LWIP_EVENT_SENT,
+  LWIP_EVENT_RECV,
+  LWIP_EVENT_CONNECTED,
+  LWIP_EVENT_POLL,
+  LWIP_EVENT_ERR
+};
+
+error_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb,
+         enum lwip_event,
+         struct pbuf *p,
+         uint16_t size,
+         error_t err);
+
+#endif /* LWIP_EVENT_API */
+
+/* Application program's interface: */
+struct tcp_pcb * tcp_new (void);
+error_t tcp_bind(struct tcp_pcb *pcb, const struct in_addr *ipaddr, uint16_t port);
+void             tcp_abort (struct tcp_pcb *pcb);
+
+void             tcp_arg     (struct tcp_pcb *pcb, void *arg);
+void             tcp_accept  (struct tcp_pcb *pcb, tcp_accept_fn accept);
+void             tcp_recv    (struct tcp_pcb *pcb, tcp_recv_fn recv);
+void             tcp_sent    (struct tcp_pcb *pcb, tcp_sent_fn sent);
+void             tcp_poll    (struct tcp_pcb *pcb, tcp_poll_fn poll, uint8_t interval);
+void             tcp_err     (struct tcp_pcb *pcb, tcp_err_fn err);
+
+#define          tcp_mss(pcb)             (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12)  : (pcb)->mss)
+#define          tcp_sndbuf(pcb)          ((pcb)->snd_buf)
+#define          tcp_sndqueuelen(pcb)     ((pcb)->snd_queuelen)
+#define          tcp_nagle_disable(pcb)   ((pcb)->flags |= TF_NODELAY)
+#define          tcp_nagle_enable(pcb)    ((pcb)->flags &= ~TF_NODELAY)
+#define          tcp_nagle_disabled(pcb)  (((pcb)->flags & TF_NODELAY) != 0)
+
+#if TCP_LISTEN_BACKLOG
+#define          tcp_accepted(pcb) do { \
+  LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \
+  (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0)
+#else  /* TCP_LISTEN_BACKLOG */
+#define          tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \
+                                               pcb->state == LISTEN)
+#endif /* TCP_LISTEN_BACKLOG */
+
+void             tcp_recved  (struct tcp_pcb *pcb, uint16_t len);
+error_t            tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+                              uint16_t port, tcp_connected_fn connected);
+
+struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, uint8_t backlog);
+#define          tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
+
+error_t            tcp_close   (struct tcp_pcb *pcb);
+error_t            tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx);
+
+/* Flags for "apiflags" parameter in tcp_write */
+#define TCP_WRITE_FLAG_COPY 0x01
+#define TCP_WRITE_FLAG_MORE 0x02
+
+error_t            tcp_write   (struct tcp_pcb *pcb, const void *dataptr, uint16_t len,
+                              uint8_t apiflags);
+
+void             tcp_setprio (struct tcp_pcb *pcb, uint8_t prio);
+
+#define TCP_PRIO_MIN    1
+#define TCP_PRIO_NORMAL 64
+#define TCP_PRIO_MAX    127
+
+error_t            tcp_output  (struct tcp_pcb *pcb);
+
+
+const char* tcp_debug_state_str(enum tcp_state s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/kern/include/net/tcp_impl.h b/kern/include/net/tcp_impl.h
new file mode 100644 (file)
index 0000000..7758447
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_TCP_IMPL_H__
+#define __LWIP_TCP_IMPL_H__
+#include "error.h"
+#include "net.h"
+#include "socket.h"
+#include "net/pbuf.h"
+#include "net/ip.h"
+#include "bits/netinet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// akaros specific option
+
+#define LWIP_EVENT_API 0
+
+struct tcp_hdr;
+struct tcp_pcb* tcp_alloc(uint8_t prio);
+/* Functions for interfacing with TCP: */
+
+/* Lower layer interface to TCP: */
+#define tcp_init() /* Compatibility define, no init needed. */
+void             tcp_tmr     (void);  /* Must be called every
+                                         TCP_TMR_INTERVAL
+                                         ms. (Typically 250 ms). */
+/* It is also possible to call these two functions at the right
+   intervals (instead of calling tcp_tmr()). */
+void             tcp_slowtmr (void);
+void             tcp_fasttmr (void);
+
+
+/* Only used by IP to pass a TCP segment to TCP: */
+// Change: assumed on network interface
+void             tcp_input   (struct pbuf *p);
+/* Used within the TCP code only: */
+void             tcp_abandon (struct tcp_pcb *pcb, int reset);
+error_t            tcp_send_empty_ack(struct tcp_pcb *pcb);
+void             tcp_rexmit  (struct tcp_pcb *pcb);
+void             tcp_rexmit_rto  (struct tcp_pcb *pcb);
+void             tcp_rexmit_fast (struct tcp_pcb *pcb);
+uint32_t            tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
+
+/**
+ * This is the Nagle algorithm: try to combine user data to send as few TCP
+ * segments as possible. Only send if
+ * - no previously transmitted data on the connection remains unacknowledged or
+ * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or
+ * - the only unsent segment is at least pcb->mss bytes long (or there is more
+ *   than one unsent segment - with lwIP, this can happen although unsent->len < mss)
+ * - or if we are in fast-retransmit (TF_INFR)
+ */
+#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \
+                            ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \
+                            (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \
+                              ((tpcb)->unsent->len >= (tpcb)->mss))) \
+                            ) ? 1 : 0)
+#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ESUCCESS)
+
+
+#define TCP_SEQ_LT(a,b)     ((int32_t)((a)-(b)) < 0)
+#define TCP_SEQ_LEQ(a,b)    ((int32_t)((a)-(b)) <= 0)
+#define TCP_SEQ_GT(a,b)     ((int32_t)((a)-(b)) > 0)
+#define TCP_SEQ_GEQ(a,b)    ((int32_t)((a)-(b)) >= 0)
+#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c))
+#define TCP_FIN 0x01U
+#define TCP_SYN 0x02U
+#define TCP_RST 0x04U
+#define TCP_PSH 0x08U
+#define TCP_ACK 0x10U
+#define TCP_URG 0x20U
+#define TCP_ECE 0x40U
+#define TCP_CWR 0x80U
+
+#define TCP_FLAGS 0x3fU
+
+/* Length of the TCP header, excluding options. */
+#define TCP_HLEN 20
+
+#ifndef TCP_TMR_INTERVAL
+#define TCP_TMR_INTERVAL       250  /* The TCP timer interval in milliseconds. */
+#endif /* TCP_TMR_INTERVAL */
+
+#ifndef TCP_FAST_INTERVAL
+#define TCP_FAST_INTERVAL      TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */
+#endif /* TCP_FAST_INTERVAL */
+
+#ifndef TCP_SLOW_INTERVAL
+#define TCP_SLOW_INTERVAL      (2*TCP_TMR_INTERVAL)  /* the coarse grained timeout in milliseconds */
+#endif /* TCP_SLOW_INTERVAL */
+
+#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */
+#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */
+
+#define TCP_OOSEQ_TIMEOUT        6U /* x RTO */
+
+#ifndef TCP_MSL
+#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */
+#endif
+
+/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */
+#ifndef  TCP_KEEPIDLE_DEFAULT
+#define  TCP_KEEPIDLE_DEFAULT     7200000UL /* Default KEEPALIVE timer in milliseconds */
+#endif
+
+#ifndef  TCP_KEEPINTVL_DEFAULT
+#define  TCP_KEEPINTVL_DEFAULT    75000UL   /* Default Time between KEEPALIVE probes in milliseconds */
+#endif
+
+#ifndef  TCP_KEEPCNT_DEFAULT
+#define  TCP_KEEPCNT_DEFAULT      9U        /* Default Counter for KEEPALIVE probes */
+#endif
+
+#define  TCP_MAXIDLE              TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT  /* Maximum KEEPALIVE probe time */
+
+#define TCPH_OFFSET(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 8)
+#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12)
+#define TCPH_FLAGS(phdr)  (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS)
+
+#define TCPH_OFFSET_SET(phdr, offset) (phdr)->_hdrlen_rsvd_flags = htons(((offset) << 8) | TCPH_FLAGS(phdr))
+#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr))
+#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((uint16_t)(~(uint16_t)(TCP_FLAGS)))) | htons(flags))
+#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags))
+
+#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags))
+#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) )
+
+#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0))
+
+/** Flags used on input processing, not on pcb->flags
+*/
+#define TF_RESET     (uint8_t)0x08U   /* Connection was reset. */
+#define TF_CLOSED    (uint8_t)0x10U   /* Connection was sucessfully closed. */
+#define TF_GOT_FIN   (uint8_t)0x20U   /* Connection was closed by the remote end. */
+
+#if LWIP_EVENT_API
+
+#define TCP_EVENT_ACCEPT(pcb,err,ret)    ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_ACCEPT, NULL, 0, err)
+#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                   LWIP_EVENT_SENT, NULL, space, ESUCCESS)
+#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_RECV, (p), 0, (err))
+#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_RECV, NULL, 0, ESUCCESS)
+#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_CONNECTED, NULL, 0, (err))
+#define TCP_EVENT_POLL(pcb,ret)       ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_POLL, NULL, 0, ESUCCESS)
+#define TCP_EVENT_ERR(errf,arg,err)  lwip_tcp_event((arg), NULL, \
+                LWIP_EVENT_ERR, NULL, 0, (err))
+
+#else /* LWIP_EVENT_API */
+
+#define TCP_EVENT_ACCEPT(pcb,err,ret)                          \
+  do {                                                         \
+    if((pcb)->accept != NULL)                                  \
+      (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err));  \
+    else (ret) = EFAIL;                                      \
+  } while (0)
+
+#define TCP_EVENT_SENT(pcb,space,ret)                          \
+  do {                                                         \
+    if((pcb)->sent != NULL)                                    \
+      (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space));  \
+    else (ret) = ESUCCESS;                                       \
+  } while (0)
+
+#define TCP_EVENT_RECV(pcb,p,err,ret)                          \
+  do {                                                         \
+    if((pcb)->recv != NULL) {                                  \
+      (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\
+    } else {                                                   \
+      (ret) = tcp_recv_null(NULL, (pcb), (p), (err));          \
+    }                                                          \
+  } while (0)
+
+#define TCP_EVENT_CLOSED(pcb,ret)                                \
+  do {                                                           \
+    if(((pcb)->recv != NULL)) {                                  \
+      (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ESUCCESS);\
+    } else {                                                     \
+      (ret) = ESUCCESS;                                            \
+    }                                                            \
+  } while (0)
+
+#define TCP_EVENT_CONNECTED(pcb,err,ret)                         \
+  do {                                                           \
+    if((pcb)->connected != NULL)                                 \
+      (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \
+    else (ret) = ESUCCESS;                                         \
+  } while (0)
+
+#define TCP_EVENT_POLL(pcb,ret)                                \
+  do {                                                         \
+    if((pcb)->poll != NULL)                                    \
+      (ret) = (pcb)->poll((pcb)->callback_arg,(pcb));          \
+    else (ret) = ESUCCESS;                                       \
+  } while (0)
+
+#define TCP_EVENT_ERR(errf,arg,err)                            \
+  do {                                                         \
+    if((errf) != NULL)                                         \
+      (errf)((arg),(err));                                     \
+  } while (0)
+
+#endif /* LWIP_EVENT_API */
+
+/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */
+#if TCP_OVERSIZE && defined(LWIP_DEBUG)
+#define TCP_OVERSIZE_DBGCHECK 1
+#else
+#define TCP_OVERSIZE_DBGCHECK 0
+#endif
+
+/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */
+#define TCP_CHECKSUM_ON_COPY  (CHECKSUM_GEN_TCP)
+
+/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */
+struct tcp_seg {
+  struct tcp_seg *next;    /* used when putting segements on a queue */
+  struct pbuf *p;          /* buffer containing data + TCP header */
+  void *dataptr;           /* pointer to the TCP data in the pbuf */
+  uint16_t len;               /* the TCP length of this segment */
+#if TCP_OVERSIZE_DBGCHECK
+  uint16_t oversize_left;     /* Extra bytes available at the end of the last
+                              pbuf in unsent (used for asserting vs.
+                              tcp_pcb.unsent_oversized only) */
+#endif /* TCP_OVERSIZE_DBGCHECK */ 
+#if TCP_CHECKSUM_ON_COPY
+  uint16_t chksum;
+  uint8_t  chksum_swapped;
+#endif /* TCP_CHECKSUM_ON_COPY */
+  uint8_t  flags;
+#define TF_SEG_OPTS_MSS         (uint8_t)0x01U /* Include MSS option. */
+#define TF_SEG_OPTS_TS          (uint8_t)0x02U /* Include timestamp option. */
+#define TF_SEG_DATA_CHECKSUMMED (uint8_t)0x04U /* ALL data (not the header) is
+                                               checksummed into 'chksum' */
+  struct tcp_hdr *tcphdr;  /* the TCP header */
+};
+
+#define LWIP_TCP_OPT_LENGTH(flags)              \
+  (flags & TF_SEG_OPTS_MSS ? 4  : 0) +          \
+  (flags & TF_SEG_OPTS_TS  ? 12 : 0)
+
+/** This returns a TCP header option for MSS in an uint32_t */
+#define TCP_BUILD_MSS_OPTION(x) (x) = PP_HTONL(((uint32_t)2 << 24) |          \
+                                               ((uint32_t)4 << 16) |          \
+                                               (((uint32_t)TCP_MSS / 256) << 8) | \
+                                               (TCP_MSS & 255))
+
+/* Global variables: */
+extern struct tcp_pcb *tcp_input_pcb;
+extern uint32_t tcp_ticks;
+
+/* The TCP PCB lists. */
+union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */
+  struct tcp_pcb_listen *listen_pcbs; 
+  struct tcp_pcb *pcbs;
+};
+extern struct tcp_pcb *tcp_bound_pcbs;
+extern union tcp_listen_pcbs_t tcp_listen_pcbs;
+extern struct tcp_pcb *tcp_active_pcbs;  /* List of all TCP PCBs that are in a
+              state in which they accept or send
+              data. */
+extern struct tcp_pcb *tcp_tw_pcbs;      /* List of all TCP PCBs in TIME-WAIT. */
+
+extern struct tcp_pcb *tcp_tmp_pcb;      /* Only used for temporary storage. */
+
+/* Axioms about the above lists:   
+   1) Every TCP PCB that is not CLOSED is in one of the lists.
+   2) A PCB is only in one of the lists.
+   3) All PCBs in the tcp_listen_pcbs list is in LISTEN state.
+   4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state.
+*/
+/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB
+   with a PCB list or removes a PCB from a list, respectively. */
+#ifndef TCP_DEBUG_PCB_LISTS
+#define TCP_DEBUG_PCB_LISTS 0
+#endif
+#if TCP_DEBUG_PCB_LISTS
+#define TCP_REG(pcbs, npcb) do {\
+                            LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \
+                            for(tcp_tmp_pcb = *(pcbs); \
+          tcp_tmp_pcb != NULL; \
+        tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+                                LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \
+                            } \
+                            LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \
+                            (npcb)->next = *(pcbs); \
+                            LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \
+                            *(pcbs) = (npcb); \
+                            LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+              tcp_timer_needed(); \
+                            } while(0)
+#define TCP_RMV(pcbs, npcb) do { \
+                            LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \
+                            LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \
+                            if(*(pcbs) == (npcb)) { \
+                               *(pcbs) = (*pcbs)->next; \
+                            } else for(tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+                               if(tcp_tmp_pcb->next == (npcb)) { \
+                                  tcp_tmp_pcb->next = (npcb)->next; \
+                                  break; \
+                               } \
+                            } \
+                            (npcb)->next = NULL; \
+                            LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+                            LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \
+                            } while(0)
+
+#else /* LWIP_DEBUG */
+
+#define TCP_REG(pcbs, npcb)                        \
+  do {                                             \
+    (npcb)->next = *pcbs;                          \
+    *(pcbs) = (npcb);                              \
+    tcp_timer_needed();                            \
+  } while (0)
+
+#define TCP_RMV(pcbs, npcb)                        \
+  do {                                             \
+    if(*(pcbs) == (npcb)) {                        \
+      (*(pcbs)) = (*pcbs)->next;                   \
+    }                                              \
+    else {                                         \
+      for(tcp_tmp_pcb = *pcbs;                     \
+          tcp_tmp_pcb != NULL;                     \
+          tcp_tmp_pcb = tcp_tmp_pcb->next) {       \
+        if(tcp_tmp_pcb->next == (npcb)) {          \
+          tcp_tmp_pcb->next = (npcb)->next;        \
+          break;                                   \
+        }                                          \
+      }                                            \
+    }                                              \
+    (npcb)->next = NULL;                           \
+  } while(0)
+
+#endif /* LWIP_DEBUG */
+
+
+/* Internal functions: */
+struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb);
+void tcp_pcb_purge(struct tcp_pcb *pcb);
+void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb);
+
+void tcp_segs_free(struct tcp_seg *seg);
+void tcp_seg_free(struct tcp_seg *seg);
+struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg);
+
+#define tcp_ack(pcb)                               \
+  do {                                             \
+    if((pcb)->flags & TF_ACK_DELAY) {              \
+      (pcb)->flags &= ~TF_ACK_DELAY;               \
+      (pcb)->flags |= TF_ACK_NOW;                  \
+    }                                              \
+    else {                                         \
+      (pcb)->flags |= TF_ACK_DELAY;                \
+    }                                              \
+  } while (0)
+
+#define tcp_ack_now(pcb)                           \
+  do {                                             \
+    (pcb)->flags |= TF_ACK_NOW;                    \
+  } while (0)
+
+error_t tcp_send_fin(struct tcp_pcb *pcb);
+error_t tcp_enqueue_flags(struct tcp_pcb *pcb, uint8_t flags);
+
+void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg);
+
+void tcp_rst(uint32_t seqno, uint32_t ackno,
+       ip_addr_t *local_ip, ip_addr_t *remote_ip,
+       uint16_t local_port, uint16_t remote_port);
+
+uint32_t tcp_next_iss(void);
+
+void tcp_keepalive(struct tcp_pcb *pcb);
+void tcp_zero_window_probe(struct tcp_pcb *pcb);
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+uint16_t tcp_eff_send_mss(uint16_t sendmss, ip_addr_t *addr);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+#if LWIP_CALLBACK_API
+error_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, error_t err);
+#endif /* LWIP_CALLBACK_API */
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+void tcp_debug_print(struct tcp_hdr *tcphdr);
+void tcp_debug_print_flags(uint8_t flags);
+void tcp_debug_print_state(enum tcp_state s);
+void tcp_debug_print_pcbs(void);
+s16_t tcp_pcbs_sane(void);
+#else
+#  define tcp_debug_print(tcphdr)
+#  define tcp_debug_print_flags(flags)
+#  define tcp_debug_print_state(s)
+#  define tcp_debug_print_pcbs()
+#  define tcp_pcbs_sane() 1
+#endif /* TCP_DEBUG */
+
+/** External function (implemented in timers.c), called when TCP detects
+ * that a timer is needed (i.e. active- or time-wait-pcb found). */
+void tcp_timer_needed(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_TCP_H__ */
index 5b9b3ef..6e9f274 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef ROS_KERN_UDP_H
 #define ROS_KERN_UDP_H
 #include <net/pbuf.h>
+#include <net/ip.h>
 #include <net.h>
 #include <bits/netinet.h>
 #include <socket.h>
@@ -8,14 +9,13 @@
 #define UDP_HLEN 8
 #define UDP_TTL 255
 
+
 struct udp_pcb {
-               /* ips are in network byte order */
-    struct in_addr local_ip;
-    struct in_addr remote_ip;
+               IP_PCB;
     /** ports are in host byte order */
     uint16_t local_port, remote_port;
-    uint8_t ttl;
     uint8_t flags;
+               uint8_t pad2;
     /* Protocol specific PCB members */
     struct udp_pcb *next;
                struct socket *pcbsock;
@@ -27,7 +27,7 @@ struct udp_pcb * udp_new(void);
 int udp_send(struct udp_pcb *pcb, struct pbuf *p);
 int udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
                     struct in_addr *dst_ip, uint16_t dst_port);
-int udp_bind(struct udp_pcb *pcb, struct in_addr *ip, uint16_t port);
+int udp_bind(struct udp_pcb *pcb, const struct in_addr *ip, uint16_t port);
 int udp_input(struct pbuf *p);
 
 #define UDP_FLAGS_NOCHKSUM       0x01U
@@ -35,77 +35,4 @@ int udp_input(struct pbuf *p);
 #define UDP_FLAGS_CONNECTED      0x04U
 #define UDP_FLAGS_MULTICAST_LOOP 0x08U
 
-#if 0
-
-/** Function prototype for udp pcb receive callback functions
- * addr and port are in same byte order as in the pcb
- * The callback is responsible for freeing the pbuf
- * if it's not used any more.
- *
- * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf
- *            makes 'addr' invalid, too.
- *
- * @param arg user supplied argument (udp_pcb.recv_arg)
- * @param pcb the udp_pcb which received data
- * @param p the packet buffer that was received
- * @param addr the remote IP address from which the packet was received
- * @param port the remote port from which the packet was received
- */
-typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,
-    ip_addr_t *addr, u16_t port);
-
-
-/* udp_pcbs export for exernal reference (e.g. SNMP agent) */
-extern struct udp_pcb *udp_pcbs;
-
-/* The following functions is the application layer interface to the
-   UDP code. */
-void             udp_remove     (struct udp_pcb *pcb);
-err_t            udp_bind       (struct udp_pcb *pcb, ip_addr_t *ipaddr,
-                                 u16_t port);
-err_t            udp_connect    (struct udp_pcb *pcb, ip_addr_t *ipaddr,
-                                 u16_t port);
-void             udp_disconnect (struct udp_pcb *pcb);
-void             udp_recv       (struct udp_pcb *pcb, udp_recv_fn recv,
-                                 void *recv_arg);
-err_t            udp_sendto_if  (struct udp_pcb *pcb, struct pbuf *p,
-                                 ip_addr_t *dst_ip, u16_t dst_port,
-                                 struct netif *netif);
-err_t            udp_sendto     (struct udp_pcb *pcb, struct pbuf *p,
-                                 ip_addr_t *dst_ip, u16_t dst_port);
-err_t            udp_send       (struct udp_pcb *pcb, struct pbuf *p);
-
-#if LWIP_CHECKSUM_ON_COPY
-err_t            udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p,
-                                 ip_addr_t *dst_ip, u16_t dst_port,
-                                 struct netif *netif, u8_t have_chksum,
-                                 u16_t chksum);
-err_t            udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p,
-                                 ip_addr_t *dst_ip, u16_t dst_port,
-                                 u8_t have_chksum, u16_t chksum);
-err_t            udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
-                                 u8_t have_chksum, u16_t chksum);
-#endif /* LWIP_CHECKSUM_ON_COPY */
-
-#define          udp_flags(pcb) ((pcb)->flags)
-#define          udp_setflags(pcb, f)  ((pcb)->flags = (f))
-
-/* The following functions are the lower layer interface to UDP. */
-void             udp_input      (struct pbuf *p, struct netif *inp);
-
-#define udp_init() /* Compatibility define, not init needed. */
-
-#if UDP_DEBUG
-void udp_debug_print(struct udp_hdr *udphdr);
-#else
-#define udp_debug_print(udphdr)
 #endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_UDP */
-
-//#endif /* if 0 , comment out */
-#endif /* __LWIP_UDP_H__ */
index 86aed6e..d292836 100644 (file)
@@ -5,6 +5,7 @@
 #include <net.h>
 #include <net/ip.h>
 #include <net/udp.h>
+#include <net/tcp_impl.h>
 #include <ros/errno.h>
 #include <arch/nic_common.h>
 
@@ -14,7 +15,6 @@ const uint8_t GTWAY[6] = {0xda, 0x76, 0xe7, 0x4c, 0xca, 0x7e};
 /* TODO: ip id unique for all ip packets? or is it unique for a flow? */
 // can do atomic increment at a minimum
 static uint16_t ip_id = 0;
-struct in_addr global_ip = {IP_ADDR};
 
 /* TODO: build arp table, and look up */
 int eth_send(struct pbuf *p, struct in_addr *dest) {
@@ -51,7 +51,8 @@ int eth_send(struct pbuf *p, struct in_addr *dest) {
  * efficiently.
  */
 /* Assume no ip options */
-int ip_output(struct pbuf *p, struct in_addr *src, struct in_addr *dest, uint8_t proto) {
+int ip_output(struct pbuf *p, struct in_addr *src, struct in_addr *dest, 
+                                                       uint8_t ttl, uint8_t tos, uint8_t proto) {
        struct pbuf *q;
        struct ip_hdr *iphdr;   
        /* TODO: Check for IP_HDRINCL */
@@ -71,14 +72,19 @@ int ip_output(struct pbuf *p, struct in_addr *src, struct in_addr *dest, uint8_t
        iphdr->version = IPPROTO_IPV4;
        /* assume no IP options */
        iphdr->hdr_len = IP_HDR_SZ >> 2;
-       iphdr->tos = 0;
+       if (tos != 0) {
+               iphdr->tos = htons(tos);
+       }
+       else {
+               iphdr->tos = 0;
+       }
        iphdr->packet_len = htons(p->tot_len);
        // TODO: NET_LOCK
        iphdr->id = htons (ip_id); // 1
        ip_id++;
        iphdr->flags_frags = htons(0); // 4000  may fragment
        iphdr->protocol = proto;
-       iphdr->ttl = DEFAULT_TTL;
+       iphdr->ttl = ttl; //DEFAULT_TTL;
        /* Eventually if we support more than one device this may change */
        printk("src ip %x, dest ip %x \n", src->s_addr, dest->s_addr);
        iphdr->src_addr = htonl(src->s_addr);
@@ -132,9 +138,9 @@ int ip_input(struct pbuf *p) {
 
        /* check if it is destined for me? */
        /* XXX: IP address for the interface is IP_ANY */
-       if (ntohl(iphdr->dst_addr) != global_ip.s_addr){
+       if (ntohl(iphdr->dst_addr) != LOCAL_IP_ADDR.s_addr){
                printk("dest ip in network order%x\n", ntohl(iphdr->dst_addr));
-               printk("dest ip in network order%x\n", global_ip.s_addr);
+               printk("dest ip in network order%x\n", LOCAL_IP_ADDR.s_addr);
                warn("ip mismatch \n");
                pbuf_free(p);
                /* TODO:forward packets */
@@ -153,6 +159,9 @@ int ip_input(struct pbuf *p) {
                case IPPROTO_UDP:
                        return udp_input(p);
                case IPPROTO_TCP:
+                       tcp_input(p);
+                       // XXX: error handling for tcp
+                       return 0;
                default:
                        printk("IP protocol type %02x\n", iphdr->protocol);
                        warn("protocol not supported! \n");
index 88a3689..2fd2fe7 100644 (file)
@@ -8,8 +8,10 @@
 #include <net/pbuf.h>
 #include <sys/queue.h>
 #include <net.h>
+#include <debug.h>
 #include <arch/nic_common.h>
 
+
 /* TODO: before running
  * 1. pbuf_free_auto currently decrefs the next one on the chain or queue
  * 2. copy_out currently copies out the chain following the next pointer
@@ -170,6 +172,68 @@ struct pbuf *pbuf_alloc(pbuf_layer layer, uint16_t length, pbuf_type type)
 }
 
 
+/**
+ * Shrink a pbuf chain to a desired length.
+ *
+ * @param p pbuf to shrink.
+ * @param new_len desired new length of pbuf chain
+ *
+ * Depending on the desired length, the first few pbufs in a chain might
+ * be skipped and left unchanged. The new last pbuf in the chain will be
+ * resized, and any remaining pbufs will be freed.
+ *
+ * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
+ * @note May not be called on a packet queue.
+ *
+ * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
+ */
+void
+pbuf_realloc(struct pbuf *p, uint16_t new_len)
+{
+  struct pbuf *q;
+  uint16_t rem_len; /* remaining length */
+  int32_t grow;
+
+  /* desired length larger than current length? */
+  if (new_len >= p->tot_len) {
+    /* enlarging not yet supported */
+    return;
+  }
+
+  /* the pbuf chain grows by (new_len - p->tot_len) bytes
+   * (which may be negative in case of shrinking) */
+  grow = new_len - p->tot_len;
+
+  /* first, step over any pbufs that should remain in the chain */
+  rem_len = new_len;
+  q = p;
+  /* should this pbuf be kept? */
+  while (rem_len > q->len) {
+    /* decrease remaining length by pbuf length */
+    rem_len -= q->len;
+    /* decrease total length indicator */
+    LWIP_ASSERT("grow < max_uint16_t", grow < 0xffff);
+    q->tot_len += (uint16_t)grow;
+    /* proceed to next pbuf in chain */
+               q = STAILQ_NEXT(q, next);
+    LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
+  }
+  /* we have now reached the new last pbuf (in q) */
+  /* rem_len == desired length for pbuf q */
+
+  /* adjust length fields for new last pbuf */
+  q->len = rem_len;
+  q->tot_len = q->len;
+
+  /* any remaining pbufs in chain? */
+  if (STAILQ_NEXT(q, next) != NULL) {
+    /* free remaining pbufs in chain */
+               pbuf_free(STAILQ_NEXT(q, next));
+  }
+  /* q is last packet in chain */
+       STAILQ_NEXT(q, next) = NULL;
+}
+
 void pbuf_ref(struct pbuf *p){
        kref_get(&p->bufref, 1);
 }
@@ -422,10 +486,10 @@ uint8_t pbuf_clen(struct pbuf *p)
  *        big enough to hold 'length' plus the header size
  */
 struct pbuf*
-pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
-                    void *payload_mem, u16_t payload_mem_len)
+pbuf_alloced_custom(pbuf_layer l, uint16_t length, pbuf_type type, struct pbuf_custom *p,
+                    void *payload_mem, uint16_t payload_mem_len)
 {
-  u16_t offset;
+  uint16_t offset;
   LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
 
   /* determine header offset */
@@ -457,7 +521,7 @@ pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_cust
 
   p->pbuf.next = NULL;
   if (payload_mem != NULL) {
-    p->pbuf.payload = LWIP_MEM_ALIGN((void *)((u8_t *)payload_mem + offset));
+    p->pbuf.payload = LWIP_MEM_ALIGN((void *)((uint8_t *)payload_mem + offset));
   } else {
     p->pbuf.payload = NULL;
   }
@@ -485,11 +549,11 @@ pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_cust
  * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
  */
 void
-pbuf_realloc(struct pbuf *p, u16_t new_len)
+pbuf_realloc(struct pbuf *p, uint16_t new_len)
 {
   struct pbuf *q;
-  u16_t rem_len; /* remaining length */
-  s32_t grow;
+  uint16_t rem_len; /* remaining length */
+  int32_t grow;
 
   LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
   LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
@@ -515,8 +579,8 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
     /* decrease remaining length by pbuf length */
     rem_len -= q->len;
     /* decrease total length indicator */
-    LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
-    q->tot_len += (u16_t)grow;
+    LWIP_ASSERT("grow < max_uint16_t", grow < 0xffff);
+    q->tot_len += (uint16_t)grow;
     /* proceed to next pbuf in chain */
     q = q->next;
     LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
@@ -528,7 +592,7 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
   /* (other types merely adjust their length fields */
   if ((q->type == PBUF_RAM) && (rem_len != q->len)) {
     /* reallocate and adjust the length of the pbuf that will be split */
-    q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
+    q = (struct pbuf *)mem_trim(q, (uint16_t)((uint8_t *)q->payload - (uint8_t *)q) + rem_len);
     LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
   }
   /* adjust length fields for new last pbuf */
@@ -637,7 +701,7 @@ struct pbuf *
 pbuf_dechain(struct pbuf *p)
 {
   struct pbuf *q;
-  u8_t tail_gone = 1;
+  uint8_t tail_gone = 1;
   /* tail */
   q = p->next;
   /* pbuf has successor in chain? */
@@ -685,7 +749,7 @@ pbuf_dechain(struct pbuf *p)
 err_t
 pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
 {
-  u16_t offset_to=0, offset_from=0, len;
+  uint16_t offset_to=0, offset_from=0, len;
 
   LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
     (void*)p_to, (void*)p_from));
@@ -706,7 +770,7 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
       /* current p_from does not fit into current p_to */
       len = p_to->len - offset_to;
     }
-    MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
+    MEMCPY((uint8_t*)p_to->payload + offset_to, (uint8_t*)p_from->payload + offset_from, len);
     offset_to += len;
     offset_from += len;
     LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
@@ -749,12 +813,12 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
  * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
  */
 err_t
-pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
+pbuf_take(struct pbuf *buf, const void *dataptr, uint16_t len)
 {
   struct pbuf *p;
-  u16_t buf_copy_len;
-  u16_t total_copy_len = len;
-  u16_t copied_total = 0;
+  uint16_t buf_copy_len;
+  uint16_t total_copy_len = len;
+  uint16_t copied_total = 0;
 
   LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;);
   LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;);
@@ -825,11 +889,11 @@ pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
  *         within the (first) pbuf (no pbuf queues!)
  */
 err_t
-pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
-                 u16_t len, u16_t *chksum)
+pbuf_fill_chksum(struct pbuf *p, uint16_t start_offset, const void *dataptr,
+                 uint16_t len, uint16_t *chksum)
 {
   u32_t acc;
-  u16_t copy_chksum;
+  uint16_t copy_chksum;
   char *dst_ptr;
   LWIP_ASSERT("p != NULL", p != NULL);
   LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
@@ -859,10 +923,10 @@ pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
  * @param offset offset into p of the byte to return
  * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
  */
-u8_t
-pbuf_get_at(struct pbuf* p, u16_t offset)
+uint8_t
+pbuf_get_at(struct pbuf* p, uint16_t offset)
 {
-  u16_t copy_from = offset;
+  uint16_t copy_from = offset;
   struct pbuf* q = p;
 
   /* get the correct pbuf */
@@ -872,7 +936,7 @@ pbuf_get_at(struct pbuf* p, u16_t offset)
   }
   /* return requested data if pbuf is OK */
   if ((q != NULL) && (q->len > copy_from)) {
-    return ((u8_t*)q->payload)[copy_from];
+    return ((uint8_t*)q->payload)[copy_from];
   }
   return 0;
 }
@@ -886,10 +950,10 @@ pbuf_get_at(struct pbuf* p, u16_t offset)
  * @return zero if equal, nonzero otherwise
  *         (0xffff if p is too short, diffoffset+1 otherwise)
  */
-u16_t
-pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
+uint16_t
+pbuf_memcmp(struct pbuf* p, uint16_t offset, const void* s2, uint16_t n)
 {
-  u16_t start = offset;
+  uint16_t start = offset;
   struct pbuf* q = p;
 
   /* get the correct pbuf */
@@ -899,10 +963,10 @@ pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
   }
   /* return requested data if pbuf is OK */
   if ((q != NULL) && (q->len > start)) {
-    u16_t i;
+    uint16_t i;
     for(i = 0; i < n; i++) {
-      u8_t a = pbuf_get_at(q, start + i);
-      u8_t b = ((u8_t*)s2)[i];
+      uint8_t a = pbuf_get_at(q, start + i);
+      uint8_t b = ((uint8_t*)s2)[i];
       if (a != b) {
         return i+1;
       }
@@ -922,14 +986,14 @@ pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
  * @param start_offset offset into p at which to start searching
  * @return 0xFFFF if substr was not found in p or the index where it was found
  */
-u16_t
-pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
+uint16_t
+pbuf_memfind(struct pbuf* p, const void* mem, uint16_t mem_len, uint16_t start_offset)
 {
-  u16_t i;
-  u16_t max = p->tot_len - mem_len;
+  uint16_t i;
+  uint16_t max = p->tot_len - mem_len;
   if (p->tot_len >= mem_len + start_offset) {
     for(i = start_offset; i <= max; ) {
-      u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+      uint16_t plus = pbuf_memcmp(p, i, mem, mem_len);
       if (plus == 0) {
         return i;
       } else {
@@ -950,7 +1014,7 @@ pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
  * @param substr string to search for in p, maximum length is 0xFFFE
  * @return 0xFFFF if substr was not found in p or the index where it was found
  */
-u16_t
+uint16_t
 pbuf_strstr(struct pbuf* p, const char* substr)
 {
   size_t substr_len;
@@ -961,7 +1025,7 @@ pbuf_strstr(struct pbuf* p, const char* substr)
   if (substr_len >= 0xFFFF) {
     return 0xFFFF;
   }
-  return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
+  return pbuf_memfind(p, substr, (uint16_t)substr_len, 0);
 }
 
 #endif /*EVERYTHING*/
diff --git a/kern/src/net/tcp.c b/kern/src/net/tcp.c
new file mode 100644 (file)
index 0000000..455a19b
--- /dev/null
@@ -0,0 +1,1629 @@
+/**
+ * @file
+ * Transmission Control Protocol for IP
+ *
+ * This file contains common functions for the TCP implementation, such as functinos
+ * for manipulating the data structures and the TCP timer functions. TCP functions
+ * related to input and output is found in tcp_in.c and tcp_out.c respectively.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ * Modified by David Zhu <yuzhu@cs.berkeley.edu> to be used for Akaros
+ *
+ */
+
+#include <ros/common.h>
+#include <string.h>
+#include <kmalloc.h>
+#include <net.h>
+#include <sys/queue.h>
+#include <atomic.h>
+
+#include <bits/netinet.h>
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <net/tcp_impl.h>
+#include <slab.h>
+#include <socket.h>
+#include <string.h>
+#include <debug.h>
+
+/* String array used to display different TCP states */
+const char * const tcp_state_str[] = {
+  "CLOSED",      
+  "LISTEN",      
+  "SYN_SENT",    
+  "SYN_RCVD",    
+  "ESTABLISHED", 
+  "FIN_WAIT_1",  
+  "FIN_WAIT_2",  
+  "CLOSE_WAIT",  
+  "CLOSING",     
+  "LAST_ACK",    
+  "TIME_WAIT"   
+};
+
+const uint8_t tcp_backoff[13] =
+    { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
+ /* Times per slowtmr hits */
+const uint8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
+
+struct tcp_pcb *tcp_pcbs;
+
+/** List of all TCP PCBs bound but not yet (connected || listening) */
+struct tcp_pcb *tcp_bound_pcbs;
+/** List of all TCP PCBs in LISTEN state */
+union tcp_listen_pcbs_t tcp_listen_pcbs;
+/** List of all TCP PCBs that are in a state in which
+ * they accept or send data. */
+struct tcp_pcb *tcp_active_pcbs;
+/** List of all TCP PCBs in TIME-WAIT state */
+struct tcp_pcb *tcp_tw_pcbs;
+
+#define NUM_TCP_PCB_LISTS               4
+#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT  3
+/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
+struct tcp_pcb **tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
+  &tcp_active_pcbs, &tcp_tw_pcbs};
+
+/** Timer counter to handle calling slow-timer from tcp_tmr() */ 
+static uint8_t tcp_timer;
+static uint16_t tcp_new_port(void);
+
+/** Only used for temporary storage. */
+struct tcp_pcb *tcp_tmp_pcb;
+
+/* Incremented every coarse grained timer shot (typically every 500 ms). */
+uint32_t tcp_ticks;
+uint16_t tcp_port_num = SOCKET_PORT_START;
+
+static uint16_t tcp_new_port(void);
+/**
+ * Abandons a connection and optionally sends a RST to the remote
+ * host.  Deletes the local protocol control block. This is done when
+ * a connection is killed because of shortage of memory.
+ *
+ * @param pcb the tcp_pcb to abort
+ * @param reset boolean to indicate whether a reset should be sent
+ */
+void
+tcp_abandon(struct tcp_pcb *pcb, int reset)
+{
+  uint32_t seqno, ackno;
+  uint16_t remote_port, local_port;
+  ip_addr_t remote_ip, local_ip;
+#if LWIP_CALLBACK_API  
+  tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+  void *errf_arg;
+
+  /* pcb->state LISTEN not allowed here */
+  LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
+    pcb->state != LISTEN);
+  /* Figure out on which TCP PCB list we are, and remove us. If we
+     are in an active state, call the receive function associated with
+     the PCB with a NULL argument, and send an RST to the remote end. */
+  if (pcb->state == TIME_WAIT) {
+    tcp_pcb_remove(&tcp_tw_pcbs, pcb);
+               kmem_cache_free(tcp_pcb_kcache, (void*)pcb);
+  } else {
+    seqno = pcb->snd_nxt;
+    ackno = pcb->rcv_nxt;
+    ip_addr_copy(local_ip, pcb->local_ip);
+    ip_addr_copy(remote_ip, pcb->remote_ip);
+    local_port = pcb->local_port;
+    remote_port = pcb->remote_port;
+#if LWIP_CALLBACK_API
+    errf = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+    errf_arg = pcb->callback_arg;
+    tcp_pcb_remove(&tcp_active_pcbs, pcb);
+    if (pcb->unacked != NULL) {
+      tcp_segs_free(pcb->unacked);
+    }
+    if (pcb->unsent != NULL) {
+      tcp_segs_free(pcb->unsent);
+    }
+#if TCP_QUEUE_OOSEQ    
+    if (pcb->ooseq != NULL) {
+      tcp_segs_free(pcb->ooseq);
+    }
+#endif /* TCP_QUEUE_OOSEQ */
+               kmem_cache_free(tcp_pcb_kcache, (void*)pcb);
+    TCP_EVENT_ERR(errf, errf_arg, ECONNABORTED);
+    if (reset) {
+      LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
+      tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
+    }
+  }
+}
+
+/**
+ * Aborts the connection by sending a RST (reset) segment to the remote
+ * host. The pcb is deallocated. This function never fails.
+ *
+ * ATTENTION: When calling this from one of the TCP callbacks, make
+ * sure you always return ECONNABORTED (and never return ECONNABORTED otherwise
+ * or you will risk accessing deallocated memory or memory leaks!
+ *
+ * @param pcb the tcp pcb to abort
+ */
+void
+tcp_abort(struct tcp_pcb *pcb)
+{
+  tcp_abandon(pcb, 1);
+}
+
+
+/** 
+ * Update the state that tracks the available window space to advertise.
+ *
+ * Returns how much extra window would be advertised if we sent an
+ * update now.
+ */
+uint32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
+{
+  uint32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
+
+  if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + MIN((TCP_WND / 2), pcb->mss))) {
+    /* we can advertise more window */
+    pcb->rcv_ann_wnd = pcb->rcv_wnd;
+    return new_right_edge - pcb->rcv_ann_right_edge;
+  } else {
+    if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
+      /* Can happen due to other end sending out of advertised window,
+       * but within actual available (but not yet advertised) window */
+      pcb->rcv_ann_wnd = 0;
+    } else {
+      /* keep the right edge of window constant */
+      uint32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
+      pcb->rcv_ann_wnd = (uint16_t)new_rcv_ann_wnd;
+    }
+    return 0;
+  }
+}
+
+/**
+ * Kills the oldest connection that is in TIME_WAIT state.
+ * Called from tcp_alloc() if no more connections are available.
+ */
+static void
+tcp_kill_timewait(void)
+{
+  struct tcp_pcb *pcb, *inactive;
+  uint32_t inactivity;
+
+  inactivity = 0;
+  inactive = NULL;
+  /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
+  for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    if ((uint32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+      inactivity = tcp_ticks - pcb->tmr;
+      inactive = pcb;
+    }
+  }
+  if (inactive != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
+           (void *)inactive, inactivity));
+    tcp_abort(inactive);
+  }
+}
+
+/**
+ * Kills the oldest active connection that has lower priority than prio.
+ *
+ * @param prio minimum priority
+ */
+static void
+tcp_kill_prio(uint8_t prio)
+{
+  struct tcp_pcb *pcb, *inactive;
+  uint32_t inactivity;
+  uint8_t mprio;
+
+
+  mprio = TCP_PRIO_MAX;
+  
+  /* We kill the oldest active connection that has lower priority than prio. */
+  inactivity = 0;
+  inactive = NULL;
+  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->prio <= prio &&
+       pcb->prio <= mprio &&
+       (uint32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+      inactivity = tcp_ticks - pcb->tmr;
+      inactive = pcb;
+      mprio = pcb->prio;
+    }
+  }
+  if (inactive != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
+           (void *)inactive, inactivity));
+    tcp_abort(inactive);
+  }
+}
+/**
+ * This function should be called by the application when it has
+ * processed the data. The purpose is to advertise a larger window
+ * when the data has been processed.
+ *
+ * @param pcb the tcp_pcb for which data is read
+ * @param len the amount of bytes that have been read by the application
+ */
+void
+tcp_recved(struct tcp_pcb *pcb, uint16_t len)
+{
+  int wnd_inflation;
+
+  check(len <= 0xffff - pcb->rcv_wnd);
+
+  pcb->rcv_wnd += len;
+  if (pcb->rcv_wnd > TCP_WND) {
+    pcb->rcv_wnd = TCP_WND;
+  }
+
+  wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
+
+  /* If the change in the right edge of window is significant (default
+   * watermark is TCP_WND/4), then send an explicit update now.
+   * Otherwise wait for a packet to be sent in the normal course of
+   * events (or more window to be available later) */
+  if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
+    tcp_ack_now(pcb);
+    //XXX: tcp_output(pcb);
+  }
+
+  printk("tcp_recved: received %d  bytes, wnd %d (%d).\n",
+         len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd);
+}
+
+static void wrap_restart_kthread(struct trapframe *tf, uint32_t srcid,
+                                       long a0, long a1, long a2){
+       restart_kthread((struct kthread*) a0);
+}
+
+/**
+ * Default receive callback that is called if the user didn't register
+ * a recv callback for the pcb.
+ */
+error_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, error_t err) {
+       if (pcb == NULL || pcb->pcbsock == NULL) {
+               pbuf_free(p);
+               return -1;
+       }
+  if (p != NULL && pcb != NULL) {
+               // notify that we have recved and increase the recv window
+               // attach it to socket
+               struct socket *sock = pcb->pcbsock;
+               // TODO: attach_pbuf needs to return stuff that can not fit in the buffer right now
+               attach_pbuf(p, &sock->recv_buff);
+               struct kthread *kthread;
+               /* First notify any blocking recv calls,
+                * then notify anyone who might be waiting in a select
+                */ 
+               // multiple people might be waiting on the socket here..
+               kthread = __up_sem(&(sock->sem), FALSE);
+               if (kthread) {
+                        send_kernel_message(core_id(), (amr_t)wrap_restart_kthread, (long)kthread, 0, 0,
+                                                                                                 KMSG_ROUTINE);
+               } else {
+                       // wake up all waiters
+                       struct semaphore_entry *sentry, *sentry_tmp;
+                       spin_lock(&sock->waiter_lock);
+                 LIST_FOREACH_SAFE(sentry, &(sock->waiters), link, sentry_tmp){
+                               //should only wake up one waiter
+                               kthread = __up_sem(&sentry->sem, true);
+                               if (kthread){
+                               send_kernel_message(core_id(), (amr_t)wrap_restart_kthread, (long)kthread, 0, 0,
+                                                                                                 KMSG_ROUTINE);
+                               }
+                               LIST_REMOVE(sentry, link);
+                               /* do not need to free since all the sentry are stack-based vars */
+                       }
+                       spin_unlock(&sock->waiter_lock);
+               }
+       }
+       printk ("received total length tcp %d\n", p->tot_len);
+       tcp_recved(pcb, p->tot_len);
+       // decref
+       pbuf_free(p);
+  return ESUCCESS;
+}
+
+
+/**
+ * Creates a new TCP protocol control block but doesn't place it on
+ * any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ *
+ * @internal: Maybe there should be a idle TCP PCB list where these
+ * PCBs are put on. Port reservation using tcp_bind() is implemented but
+ * allocated pcbs that are not bound can't be killed automatically if wanting
+ * to allocate a pcb with higher prio (@see tcp_kill_prio())
+ *
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb* tcp_new(void) {
+  return tcp_alloc(TCP_PRIO_NORMAL);
+}
+
+/**
+ * Calculates a new initial sequence number for new connections.
+ * TODO: Consider use a secure pseduo ISN
+ *
+ * @return uint32_t pseudo random sequence number
+ */
+uint32_t tcp_next_iss(void)
+{
+  static uint32_t iss = 6510;
+  
+  iss += tcp_ticks;       /* XXX */
+  return iss;
+}
+
+/**
+ * Allocate a new tcp_pcb structure.
+ *
+ * @param prio priority for the new pcb
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb* tcp_alloc(uint8_t prio) {
+  struct tcp_pcb *pcb;
+  uint32_t iss;
+  pcb = kmem_cache_alloc(tcp_pcb_kcache, 0);
+  if (pcb == NULL) {
+               /* Try killing oldest connection in TIME-WAIT. */
+               printd("tcp_alloc: killing off oldest TIME-WAIT connection\n");
+               tcp_kill_timewait();
+               /* Try to allocate a tcp_pcb again. */
+               pcb = (struct tcp_pcb *)kmem_cache_alloc(tcp_pcb_kcache, 0);
+               if (pcb == NULL) {
+                       /* Try killing active connections with lower priority than the new one. */
+                       printd("tcp_alloc: killing connection with prio lower than %d\n", prio);
+                       tcp_kill_prio(prio);
+                       /* Try to allocate a tcp_pcb again. */
+                       pcb = (struct tcp_pcb *)kmem_cache_alloc(tcp_pcb_kcache, 0);
+               }
+       }
+  if (pcb != NULL) {
+    memset(pcb, 0, sizeof(struct tcp_pcb));
+    pcb->prio = prio;
+    pcb->snd_buf = TCP_SND_BUF;
+    pcb->snd_queuelen = 0;
+    pcb->rcv_wnd = TCP_WND;
+    pcb->rcv_ann_wnd = TCP_WND;
+    pcb->tos = 0;
+    pcb->ttl = TCP_TTL;
+    /* As initial send MSS, we use TCP_MSS but limit it to 536.
+       The send MSS is updated when an MSS option is received. */
+    pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
+    pcb->rto = 3000 / TCP_SLOW_INTERVAL;
+    pcb->sa = 0;
+    pcb->sv = 3000 / TCP_SLOW_INTERVAL;
+    pcb->rtime = -1;
+    pcb->cwnd = 1;
+    iss = tcp_next_iss();
+    pcb->snd_wl2 = iss;
+    pcb->snd_nxt = iss;
+    pcb->lastack = iss;
+    pcb->snd_lbb = iss;   
+    pcb->tmr = tcp_ticks;
+
+    pcb->polltmr = 0;
+
+/* Basically we need to use the callback api because then we can switch
+ * handlers based on the state that the pcb is in. 
+ */
+
+    pcb->recv = tcp_recv_null;
+    
+    /* Init KEEPALIVE timer */
+    pcb->keep_idle  = TCP_KEEPIDLE_DEFAULT;
+    
+#if LWIP_TCP_KEEPALIVE
+    pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
+    pcb->keep_cnt   = TCP_KEEPCNT_DEFAULT;
+#endif /* LWIP_TCP_KEEPALIVE */
+
+    pcb->keep_cnt_sent = 0;
+  }
+  return pcb;
+}
+
+/**
+ * A nastly hack featuring 'goto' statements that allocates a
+ * new TCP local port.
+ *
+ * @return a new (free) local TCP port number
+ */
+static uint16_t tcp_new_port(void) {
+  int i;
+  struct tcp_pcb *pcb;
+  static uint16_t port = TCP_LOCAL_PORT_RANGE_START;
+  
+ again:
+  if (++port > TCP_LOCAL_PORT_RANGE_END) {
+    port = TCP_LOCAL_PORT_RANGE_START;
+  }
+  /* Check all PCB lists. */
+  for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {  
+    for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
+      if (pcb->local_port == port) {
+        goto again;
+      }
+    }
+  }
+  return port;
+}
+
+
+/**
+ * Binds the connection to a local portnumber and IP address. If the
+ * IP address is not given (i.e., ipaddr == NULL), the IP address of
+ * the outgoing network interface is used instead.
+ *
+ * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
+ *        already bound!)
+ * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind
+ *        to any local address
+ * @param port the local port to bind to
+ * @return ERR_USE if the port is already in use
+ *         ESUCCESS if bound
+ */
+error_t tcp_bind(struct tcp_pcb *pcb, const struct in_addr *ipaddr, uint16_t port) {
+  int i;
+  int max_pcb_list = NUM_TCP_PCB_LISTS;
+  struct tcp_pcb *cpcb;
+
+  LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return -EISCONN);
+
+#if SO_REUSE
+  /* Unless the REUSEADDR flag is set,
+     we have to check the pcbs in TIME-WAIT state, also.
+     We do not dump TIME_WAIT pcb's; they can still be matched by incoming
+     packets using both local and remote IP addresses and ports to distinguish.
+   */
+  if ((pcb->so_options & SO_REUSEADDR) != 0) {
+    max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
+  }
+#endif /* SO_REUSE */
+
+  if (port == 0) {
+    port = tcp_new_port();
+  }
+
+  /* Check if the address already is in use (on all lists) */
+  for (i = 0; i < max_pcb_list; i++) {
+    for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+      if (cpcb->local_port == port) {
+#if SO_REUSE
+        /* Omit checking for the same port if both pcbs have REUSEADDR set.
+           For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
+           tcp_connect. */
+        if (((pcb->so_options & SO_REUSEADDR) == 0) ||
+          ((cpcb->so_options & SO_REUSEADDR) == 0))
+#endif /* SO_REUSE */
+        {
+          if (ip_addr_isany(&(cpcb->local_ip)) ||
+              ip_addr_isany(ipaddr) ||
+              ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
+            return EADDRINUSE;
+          }
+        }
+      }
+    }
+  }
+
+  if (!ip_addr_isany(ipaddr)) {
+    pcb->local_ip = *ipaddr;
+  }
+  pcb->local_port = port;
+  TCP_REG(&tcp_bound_pcbs, pcb);
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
+  return 0;
+}
+
+/**
+ * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
+ * "refused" by upper layer (application) and sends delayed ACKs.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void tcp_fasttmr(void) {
+  struct tcp_pcb *pcb = tcp_active_pcbs;
+
+  while(pcb != NULL) {
+    struct tcp_pcb *next = pcb->next;
+    /* If there is data which was previously "refused" by upper layer */
+    if (pcb->refused_data != NULL) {
+      /* Notify again application with data previously received. */
+      error_t err;
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n"));
+      TCP_EVENT_RECV(pcb, pcb->refused_data, ESUCCESS, err);
+      if (err == ESUCCESS) {
+        pcb->refused_data = NULL;
+      } else if (err == ECONNABORTED) {
+        /* if err == ECONNABORTED, 'pcb' is already deallocated */
+        pcb = NULL;
+      }
+    }
+
+    /* send delayed ACKs */
+    if (pcb && (pcb->flags & TF_ACK_DELAY)) {
+      printd("tcp_fasttmr: delayed ACK\n");
+      tcp_ack_now(pcb);
+      // XXX: tcp_output(pcb);
+      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+    }
+
+    pcb = next;
+  }
+}
+
+/**
+ * Called periodically to dispatch TCP timers.
+ *
+ */
+void tcp_tmr(void) {
+       /* Call tcp_fasttmr() every 250 ms */
+  tcp_fasttmr();
+
+  if (++tcp_timer & 1) {
+    /* Call tcp_tmr() every 500 ms, i.e., every other timer
+       tcp_tmr() is called. */
+    tcp_slowtmr();
+  }
+}
+
+/**
+ * Closes the TX side of a connection held by the PCB.
+ * For tcp_close(), a RST is sent if the application didn't receive all data
+ * (tcp_recved() not called for all data passed to recv callback).
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it.
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ESUCCESS if connection has been closed
+ *         another error_t if closing failed and pcb is not freed
+ */
+static error_t
+tcp_close_shutdown(struct tcp_pcb *pcb, uint8_t rst_on_unacked_data)
+{
+  error_t err;
+
+  if (rst_on_unacked_data && (pcb->state != LISTEN)) {
+    if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) {
+      /* Not all data received by application, send RST to tell the remote
+         side about this. */
+      LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
+
+      /* don't call tcp_abort here: we must not deallocate the pcb since
+         that might not be expected when calling tcp_close */
+      tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+        pcb->local_port, pcb->remote_port);
+
+      tcp_pcb_purge(pcb);
+
+      /* TODO: to which state do we move now? */
+
+      /* move to TIME_WAIT since we close actively */
+      TCP_RMV(&tcp_active_pcbs, pcb);
+      pcb->state = TIME_WAIT;
+      TCP_REG(&tcp_tw_pcbs, pcb);
+
+      return ESUCCESS;
+    }
+  }
+
+  switch (pcb->state) {
+  case CLOSED:
+    /* Closing a pcb in the CLOSED state might seem erroneous,
+     * however, it is in this state once allocated and as yet unused
+     * and the user needs some way to free it should the need arise.
+     * Calling tcp_close() with a pcb that has already been closed, (i.e. twice)
+     * or for a pcb that has been used and then entered the CLOSED state 
+     * is erroneous, but this should never happen as the pcb has in those cases
+     * been freed, and so any remaining handles are bogus. */
+    err = ESUCCESS;
+    TCP_RMV(&tcp_bound_pcbs, pcb);
+               kmem_cache_free(tcp_pcb_kcache, (void*)pcb);
+    pcb = NULL;
+    break;
+  case LISTEN:
+    err = ESUCCESS;
+    tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
+               kmem_cache_free(tcp_pcb_kcache, (void*)pcb);
+    pcb = NULL;
+    break;
+  case SYN_SENT:
+    err = ESUCCESS;
+    tcp_pcb_remove(&tcp_active_pcbs, pcb);
+               kmem_cache_free(tcp_pcb_kcache, (void*)pcb);
+    pcb = NULL;
+    break;
+  case SYN_RCVD:
+    err = tcp_send_fin(pcb);
+    if (err == ESUCCESS) {
+      pcb->state = FIN_WAIT_1;
+    }
+    break;
+  case ESTABLISHED:
+    err = tcp_send_fin(pcb);
+    if (err == ESUCCESS) {
+      pcb->state = FIN_WAIT_1;
+    }
+    break;
+  case CLOSE_WAIT:
+    err = tcp_send_fin(pcb);
+    if (err == ESUCCESS) {
+      pcb->state = LAST_ACK;
+    }
+    break;
+  default:
+    /* Has already been closed, do nothing. */
+    err = ESUCCESS;
+    pcb = NULL;
+    break;
+  }
+
+  if (pcb != NULL && err == ESUCCESS) {
+    /* To ensure all data has been sent when tcp_close returns, we have
+       to make sure tcp_output doesn't fail.
+       Since we don't really have to ensure all data has been sent when tcp_close
+       returns (unsent data is sent from tcp timer functions, also), we don't care
+       for the return value of tcp_output for now. */
+    /* @todo: When implementing SO_LINGER, this must be changed somehow:
+       If SOF_LINGER is set, the data should be sent and acked before close returns.
+       This can only be valid for sequential APIs, not for the raw API. */
+    tcp_output(pcb);
+  }
+  return err;
+}
+
+/**
+ * Closes the connection held by the PCB.
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it (unless an error is returned).
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ESUCCESS if connection has been closed
+ *         another error_t if closing failed and pcb is not freed
+ */
+error_t
+tcp_close(struct tcp_pcb *pcb)
+{
+#if TCP_DEBUG
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
+  tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+
+  if (pcb->state != LISTEN) {
+    /* Set a flag not to receive any more data... */
+    pcb->flags |= TF_RXCLOSED;
+  }
+  /* ... and close */
+  return tcp_close_shutdown(pcb, 1);
+}
+
+/**
+ * Causes all or part of a full-duplex connection of this PCB to be shut down.
+ * This doesn't deallocate the PCB!
+ *
+ * @param pcb PCB to shutdown
+ * @param shut_rx shut down receive side if this is != 0
+ * @param shut_tx shut down send side if this is != 0
+ * @return ESUCCESS if shutdown succeeded (or the PCB has already been shut down)
+ *         another error_t on error.
+ */
+error_t
+tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
+{
+  if (pcb->state == LISTEN) {
+    return ENOTCONN;
+  }
+  if (shut_rx) {
+    /* shut down the receive side: free buffered data... */
+    if (pcb->refused_data != NULL) {
+      pbuf_free(pcb->refused_data);
+      pcb->refused_data = NULL;
+    }
+    /* ... and set a flag not to receive any more data */
+    pcb->flags |= TF_RXCLOSED;
+  }
+  if (shut_tx) {
+    /* This can't happen twice since if it succeeds, the pcb's state is changed.
+       Only close in these states as the others directly deallocate the PCB */
+    switch (pcb->state) {
+  case SYN_RCVD:
+  case ESTABLISHED:
+  case CLOSE_WAIT:
+    return tcp_close_shutdown(pcb, 0);
+  default:
+    /* don't shut down other states */
+    break;
+    }
+  }
+  /* @todo: return another error_t if not in correct state or already shut? */
+  return ESUCCESS;
+}
+
+/**
+ * Default accept callback if no accept callback is specified by the user.
+ */
+static error_t
+tcp_accept_null(void *arg, struct tcp_pcb *pcb, error_t err)
+{
+       //XXX: IMPLEMENT ACCEPT
+
+  return ECONNABORTED;
+}
+
+/**
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ *       called like this:
+ *             tpcb = tcp_listen(tpcb);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog(struct tcp_pcb *pcb, uint8_t backlog)
+{
+  struct tcp_pcb_listen *lpcb;
+
+  LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL);
+
+  /* already listening? */
+  if (pcb->state == LISTEN) {
+    return pcb;
+  }
+#if SO_REUSE
+  if ((pcb->so_options & SO_REUSEADDR) != 0) {
+    /* Since SO_REUSEADDR allows reusing a local address before the pcb's usage
+       is declared (listen-/connection-pcb), we have to make sure now that
+       this port is only used once for every local IP. */
+    for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+      if (lpcb->local_port == pcb->local_port) {
+        if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
+          /* this address/port is already used */
+          return NULL;
+        }
+      }
+    }
+  }
+#endif /* SO_REUSE */
+       lpcb = kmem_cache_alloc(tcp_pcb_listen_kcache, 0);
+  if (lpcb == NULL) {
+    return NULL;
+  }
+  lpcb->callback_arg = pcb->callback_arg;
+  lpcb->local_port = pcb->local_port;
+  lpcb->state = LISTEN;
+  lpcb->prio = pcb->prio;
+  lpcb->so_options = pcb->so_options;
+  lpcb->so_options |= SO_ACCEPTCONN;
+  lpcb->ttl = pcb->ttl;
+  lpcb->tos = pcb->tos;
+  ip_addr_copy(lpcb->local_ip, pcb->local_ip);
+  TCP_RMV(&tcp_bound_pcbs, pcb);
+       kmem_cache_free(tcp_pcb_kcache, (void*)pcb);
+#if LWIP_CALLBACK_API
+  lpcb->accept = tcp_accept_null;
+#endif /* LWIP_CALLBACK_API */
+#if TCP_LISTEN_BACKLOG
+  lpcb->accepts_pending = 0;
+  lpcb->backlog = (backlog ? backlog : 1);
+#endif /* TCP_LISTEN_BACKLOG */
+  TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
+  return (struct tcp_pcb *)lpcb;
+}
+
+
+/**
+ * Connects to another host. The function given as the "connected"
+ * argument will be called when the connection has been established.
+ *
+ * @param pcb the tcp_pcb used to establish the connection
+ * @param ipaddr the remote ip address to connect to
+ * @param port the remote tcp port to connect to
+ * @param connected callback function to call when connected (or on error)
+ * @return ERR_VAL if invalid arguments are given
+ *         ESUCCESS if connect request has been sent
+ *         other error_t values if connect request couldn't be sent
+ */
+error_t
+tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, uint16_t port,
+      tcp_connected_fn connected)
+{
+  error_t ret;
+  uint32_t iss;
+
+  LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return EISCONN);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
+  if (ipaddr != NULL) {
+    pcb->remote_ip = *ipaddr;
+  } else {
+    return ENETUNREACH;
+  }
+  pcb->remote_port = port;
+
+  /* check if we have a route to the remote host */
+  if (ip_addr_isany(&(pcb->local_ip))) {
+               // assume we have a route anywhere..
+
+    /* no local IP address set, yet. */
+    // struct netif *netif = ip_route(&(pcb->remote_ip));
+    /* Use the netif's IP address as local address. */
+               pcb->local_ip = LOCAL_IP_ADDR;
+  }
+
+  if (pcb->local_port == 0) {
+    pcb->local_port = tcp_new_port();
+  }
+#if SO_REUSE
+  if ((pcb->so_options & SO_REUSEADDR) != 0) {
+    /* Since SO_REUSEADDR allows reusing a local address, we have to make sure
+       now that the 5-tuple is unique. */
+    struct tcp_pcb *cpcb;
+    int i;
+    /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
+    for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
+      for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+        if ((cpcb->local_port == pcb->local_port) &&
+            (cpcb->remote_port == port) &&
+            ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
+            ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
+          /* linux returns EISCONN here, but ERR_USE should be OK for us */
+          return ERR_USE;
+        }
+      }
+    }
+  }
+#endif /* SO_REUSE */
+  iss = tcp_next_iss();
+  pcb->rcv_nxt = 0;
+  pcb->snd_nxt = iss;
+  pcb->lastack = iss - 1;
+  pcb->snd_lbb = iss - 1;
+  pcb->rcv_wnd = TCP_WND;
+  pcb->rcv_ann_wnd = TCP_WND;
+  pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+  pcb->snd_wnd = TCP_WND;
+  /* As initial send MSS, we use TCP_MSS but limit it to 536.
+     The send MSS is updated when an MSS option is received. */
+  pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
+#if TCP_CALCULATE_EFF_SEND_MSS
+  pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+  pcb->cwnd = 1;
+  pcb->ssthresh = pcb->mss * 10;
+#if LWIP_CALLBACK_API
+  pcb->connected = connected;
+#else /* LWIP_CALLBACK_API */  
+  LWIP_UNUSED_ARG(connected);
+#endif /* LWIP_CALLBACK_API */
+
+  /* Send a SYN together with the MSS option. */
+  ret = tcp_enqueue_flags(pcb, TCP_SYN);
+  if (ret == ESUCCESS) {
+    /* SYN segment was enqueued, changed the pcbs state now */
+    pcb->state = SYN_SENT;
+    TCP_RMV(&tcp_bound_pcbs, pcb);
+    TCP_REG(&tcp_active_pcbs, pcb);
+    //snmp_inc_tcpactiveopens();
+
+    tcp_output(pcb);
+  }
+  return ret;
+}
+
+/**
+ * Called every 500 ms and implements the retransmission timer and the timer that
+ * removes PCBs that have been in TIME-WAIT for enough time. It also increments
+ * various timers such as the inactivity timer in each PCB.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_slowtmr(void)
+{
+  struct tcp_pcb *pcb, *prev;
+  uint16_t eff_wnd;
+  uint8_t pcb_remove;      /* flag if a PCB should be removed */
+  uint8_t pcb_reset;       /* flag if a RST should be sent when removing */
+  error_t err;
+
+  err = ESUCCESS;
+
+  ++tcp_ticks;
+
+  /* Steps through all of the active PCBs. */
+  prev = NULL;
+  pcb = tcp_active_pcbs;
+  if (pcb == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
+  }
+  while (pcb != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
+    LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED);
+    LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);
+    LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);
+
+    pcb_remove = 0;
+    pcb_reset = 0;
+
+    if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
+      ++pcb_remove;
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
+    }
+    else if (pcb->nrtx == TCP_MAXRTX) {
+      ++pcb_remove;
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
+    } else {
+      if (pcb->persist_backoff > 0) {
+        /* If snd_wnd is zero, use persist timer to send 1 byte probes
+         * instead of using the standard retransmission mechanism. */
+        pcb->persist_cnt++;
+        if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) {
+          pcb->persist_cnt = 0;
+          if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
+            pcb->persist_backoff++;
+          }
+          tcp_zero_window_probe(pcb);
+        }
+      } else {
+        /* Increase the retransmission timer if it is running */
+        if(pcb->rtime >= 0)
+          ++pcb->rtime;
+
+        if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
+          /* Time for a retransmission. */
+          LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
+                                      " pcb->rto %"S16_F"\n",
+                                      pcb->rtime, pcb->rto));
+
+          /* Double retransmission time-out unless we are trying to
+           * connect to somebody (i.e., we are in SYN_SENT). */
+          if (pcb->state != SYN_SENT) {
+            pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
+          }
+
+          /* Reset the retransmission timer. */
+          pcb->rtime = 0;
+
+          /* Reduce congestion window and ssthresh. */
+          eff_wnd = MIN(pcb->cwnd, pcb->snd_wnd);
+          pcb->ssthresh = eff_wnd >> 1;
+          if (pcb->ssthresh < (pcb->mss << 1)) {
+            pcb->ssthresh = (pcb->mss << 1);
+          }
+          pcb->cwnd = pcb->mss;
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F
+                                       " ssthresh %"U16_F"\n",
+                                       pcb->cwnd, pcb->ssthresh));
+          /* The following needs to be called AFTER cwnd is set to one
+             mss - STJ */
+          tcp_rexmit_rto(pcb);
+        }
+      }
+    }
+    /* Check if this PCB has stayed too long in FIN-WAIT-2 */
+    if (pcb->state == FIN_WAIT_2) {
+      if ((uint32_t)(tcp_ticks - pcb->tmr) >
+          TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
+        ++pcb_remove;
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
+      }
+    }
+
+    /* Check if KEEPALIVE should be sent */
+    if((pcb->so_options & SO_KEEPALIVE) &&
+       ((pcb->state == ESTABLISHED) ||
+        (pcb->state == CLOSE_WAIT))) {
+#if LWIP_TCP_KEEPALIVE
+      if((uint32_t)(tcp_ticks - pcb->tmr) >
+         (pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl))
+         / TCP_SLOW_INTERVAL)
+#else      
+      if((uint32_t)(tcp_ticks - pcb->tmr) >
+         (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL)
+#endif /* LWIP_TCP_KEEPALIVE */
+      {
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n",
+                                ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+                                ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
+        
+        ++pcb_remove;
+        ++pcb_reset;
+      }
+#if LWIP_TCP_KEEPALIVE
+      else if((uint32_t)(tcp_ticks - pcb->tmr) > 
+              (pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl)
+              / TCP_SLOW_INTERVAL)
+#else
+      else if((uint32_t)(tcp_ticks - pcb->tmr) > 
+              (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT) 
+              / TCP_SLOW_INTERVAL)
+#endif /* LWIP_TCP_KEEPALIVE */
+      {
+        tcp_keepalive(pcb);
+        pcb->keep_cnt_sent++;
+      }
+    }
+
+    /* If this PCB has queued out of sequence data, but has been
+       inactive for too long, will drop the data (it will eventually
+       be retransmitted). */
+#if TCP_QUEUE_OOSEQ
+    if (pcb->ooseq != NULL &&
+        (uint32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) {
+      tcp_segs_free(pcb->ooseq);
+      pcb->ooseq = NULL;
+      LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
+    }
+#endif /* TCP_QUEUE_OOSEQ */
+
+    /* Check if this PCB has stayed too long in SYN-RCVD */
+    if (pcb->state == SYN_RCVD) {
+      if ((uint32_t)(tcp_ticks - pcb->tmr) >
+          TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) {
+        ++pcb_remove;
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n"));
+      }
+    }
+
+    /* Check if this PCB has stayed too long in LAST-ACK */
+    if (pcb->state == LAST_ACK) {
+      if ((uint32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+        ++pcb_remove;
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n"));
+      }
+    }
+
+    /* If the PCB should be removed, do it. */
+    if (pcb_remove) {
+      struct tcp_pcb *pcb2;
+      tcp_pcb_purge(pcb);
+      /* Remove PCB from tcp_active_pcbs list. */
+      if (prev != NULL) {
+        LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
+        prev->next = pcb->next;
+      } else {
+        /* This PCB was the first. */
+        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
+        tcp_active_pcbs = pcb->next;
+      }
+
+      TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ECONNABORTED);
+      if (pcb_reset) {
+        tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+          pcb->local_port, pcb->remote_port);
+      }
+
+      pcb2 = pcb;
+      pcb = pcb->next;
+                       kmem_cache_free(tcp_pcb_kcache, (void*)pcb2);
+    } else {
+      /* get the 'next' element now and work with 'prev' below (in case of abort) */
+      prev = pcb;
+      pcb = pcb->next;
+
+      /* We check if we should poll the connection. */
+      ++prev->polltmr;
+      if (prev->polltmr >= prev->pollinterval) {
+        prev->polltmr = 0;
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
+        TCP_EVENT_POLL(prev, err);
+        /* if err == ECONNABORTED, 'prev' is already deallocated */
+        if (err == ESUCCESS) {
+          tcp_output(prev);
+        }
+      }
+    }
+  }
+
+  
+  /* Steps through all of the TIME-WAIT PCBs. */
+  prev = NULL;
+  pcb = tcp_tw_pcbs;
+  while (pcb != NULL) {
+    LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+    pcb_remove = 0;
+
+    /* Check if this PCB has stayed long enough in TIME-WAIT */
+    if ((uint32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+      ++pcb_remove;
+    }
+    
+
+
+    /* If the PCB should be removed, do it. */
+    if (pcb_remove) {
+      struct tcp_pcb *pcb2;
+      tcp_pcb_purge(pcb);
+      /* Remove PCB from tcp_tw_pcbs list. */
+      if (prev != NULL) {
+        LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
+        prev->next = pcb->next;
+      } else {
+        /* This PCB was the first. */
+        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
+        tcp_tw_pcbs = pcb->next;
+      }
+      pcb2 = pcb;
+      pcb = pcb->next;
+                       kmem_cache_free(tcp_pcb_kcache, (void*)pcb2);
+    } else {
+      prev = pcb;
+      pcb = pcb->next;
+    }
+  }
+}
+
+
+/**
+ * Deallocates a list of TCP segments (tcp_seg structures).
+ *
+ * @param seg tcp_seg list of TCP segments to free
+ */
+void
+tcp_segs_free(struct tcp_seg *seg)
+{
+  while (seg != NULL) {
+    struct tcp_seg *next = seg->next;
+    tcp_seg_free(seg);
+    seg = next;
+  }
+}
+
+/**
+ * Frees a TCP segment (tcp_seg structure).
+ *
+ * @param seg single tcp_seg to free
+ */
+void
+tcp_seg_free(struct tcp_seg *seg)
+{
+  if (seg != NULL) {
+    if (seg->p != NULL) {
+      pbuf_free(seg->p);
+#if TCP_DEBUG
+      seg->p = NULL;
+#endif /* TCP_DEBUG */
+    }
+               kmem_cache_free(tcp_segment_kcache, seg);
+  }
+}
+
+/**
+ * Sets the priority of a connection.
+ *
+ * @param pcb the tcp_pcb to manipulate
+ * @param prio new priority
+ */
+void
+tcp_setprio(struct tcp_pcb *pcb, uint8_t prio)
+{
+  pcb->prio = prio;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Returns a copy of the given TCP segment.
+ * The pbuf and data are not copied, only the pointers
+ *
+ * @param seg the old tcp_seg
+ * @return a copy of seg
+ */ 
+struct tcp_seg *
+tcp_seg_copy(struct tcp_seg *seg)
+{
+  struct tcp_seg *cseg;
+
+  cseg = (struct tcp_seg *)kmem_cache_alloc(tcp_segment_kcache, 0);
+  if (cseg == NULL) {
+    return NULL;
+  }
+  memcpy((uint8_t *)cseg, (const uint8_t *)seg, sizeof(struct tcp_seg)); 
+  pbuf_ref(cseg->p);
+  return cseg;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+
+/**
+ * Used to specify the argument that should be passed callback
+ * functions.
+ *
+ * @param pcb tcp_pcb to set the callback argument
+ * @param arg void pointer argument to pass to callback functions
+ */ 
+void
+tcp_arg(struct tcp_pcb *pcb, void *arg)
+{  
+  pcb->callback_arg = arg;
+}
+#if LWIP_CALLBACK_API
+
+/**
+ * Used to specify the function that should be called when a TCP
+ * connection receives data.
+ *
+ * @param pcb tcp_pcb to set the recv callback
+ * @param recv callback function to call for this pcb when data is received
+ */ 
+void
+tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
+{
+  pcb->recv = recv;
+}
+
+/**
+ * Used to specify the function that should be called when TCP data
+ * has been successfully delivered to the remote host.
+ *
+ * @param pcb tcp_pcb to set the sent callback
+ * @param sent callback function to call for this pcb when data is successfully sent
+ */ 
+void
+tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
+{
+  pcb->sent = sent;
+}
+
+/**
+ * Used to specify the function that should be called when a fatal error
+ * has occured on the connection.
+ *
+ * @param pcb tcp_pcb to set the err callback
+ * @param err callback function to call for this pcb when a fatal error
+ *        has occured on the connection
+ */ 
+void
+tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
+{
+  pcb->errf = err;
+}
+
+/**
+ * Used for specifying the function that should be called when a
+ * LISTENing connection has been connected to another host.
+ *
+ * @param pcb tcp_pcb to set the accept callback
+ * @param accept callback function to call for this pcb when LISTENing
+ *        connection has been connected to another host
+ */ 
+void
+tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
+{
+  pcb->accept = accept;
+}
+#endif /* LWIP_CALLBACK_API */
+
+
+/**
+ * Used to specify the function that should be called periodically
+ * from TCP. The interval is specified in terms of the TCP coarse
+ * timer interval, which is called twice a second.
+ *
+ */ 
+void
+tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, uint8_t interval)
+{
+#if LWIP_CALLBACK_API
+  pcb->poll = poll;
+#else /* LWIP_CALLBACK_API */  
+  LWIP_UNUSED_ARG(poll);
+#endif /* LWIP_CALLBACK_API */  
+  pcb->pollinterval = interval;
+}
+
+/**
+ * Purges a TCP PCB. Removes any buffered data and frees the buffer memory
+ * (pcb->ooseq, pcb->unsent and pcb->unacked are freed).
+ *
+ * @param pcb tcp_pcb to purge. The pcb itself is not deallocated!
+ */
+void
+tcp_pcb_purge(struct tcp_pcb *pcb)
+{
+  if (pcb->state != CLOSED &&
+     pcb->state != TIME_WAIT &&
+     pcb->state != LISTEN) {
+
+    printd("tcp_pcb_purge\n");
+
+#if TCP_LISTEN_BACKLOG
+    if (pcb->state == SYN_RCVD) {
+      /* Need to find the corresponding listen_pcb and decrease its accepts_pending */
+      struct tcp_pcb_listen *lpcb;
+      LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL",
+        tcp_listen_pcbs.listen_pcbs != NULL);
+      for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+        if ((lpcb->local_port == pcb->local_port) &&
+            (ip_addr_isany(&lpcb->local_ip) ||
+             ip_addr_cmp(&pcb->local_ip, &lpcb->local_ip))) {
+            /* port and address of the listen pcb match the timed-out pcb */
+            LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending",
+              lpcb->accepts_pending > 0);
+            lpcb->accepts_pending--;
+            break;
+          }
+      }
+    }
+#endif /* TCP_LISTEN_BACKLOG */
+
+
+    if (pcb->refused_data != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
+      pbuf_free(pcb->refused_data);
+      pcb->refused_data = NULL;
+    }
+    if (pcb->unsent != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
+    }
+    if (pcb->unacked != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
+    }
+#if TCP_QUEUE_OOSEQ
+    if (pcb->ooseq != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
+    }
+    tcp_segs_free(pcb->ooseq);
+    pcb->ooseq = NULL;
+#endif /* TCP_QUEUE_OOSEQ */
+
+    /* Stop the retransmission timer as it will expect data on unacked
+       queue if it fires */
+    pcb->rtime = -1;
+
+    tcp_segs_free(pcb->unsent);
+    tcp_segs_free(pcb->unacked);
+    pcb->unacked = pcb->unsent = NULL;
+#if TCP_OVERSIZE
+    pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+  }
+}
+
+/**
+ * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
+ *
+ * @param pcblist PCB list to purge.
+ * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated!
+ */
+void
+tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
+{
+  TCP_RMV(pcblist, pcb);
+
+  tcp_pcb_purge(pcb);
+  
+  /* if there is an outstanding delayed ACKs, send it */
+  if (pcb->state != TIME_WAIT &&
+     pcb->state != LISTEN &&
+     pcb->flags & TF_ACK_DELAY) {
+    pcb->flags |= TF_ACK_NOW;
+    tcp_output(pcb);
+  }
+
+  if (pcb->state != LISTEN) {
+    LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
+    LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
+#if TCP_QUEUE_OOSEQ
+    LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
+#endif /* TCP_QUEUE_OOSEQ */
+  }
+
+  pcb->state = CLOSED;
+
+  LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+/**
+ * Calcluates the effective send mss that can be used for a specific IP address
+ * by using ip_route to determin the netif used to send to the address and
+ * calculating the minimum of TCP_MSS and that netif's mtu (if set).
+ */
+uint16_t
+tcp_eff_send_mss(uint16_t sendmss, ip_addr_t *addr)
+{
+  uint16_t mss_s;
+  struct netif *outif;
+
+  //outif = ip_route(addr);
+    mss_s = DEFAULT_MTU - IP_HDR_SZ - TCP_HLEN;
+    /* RFC 1122, chap 4.2.2.6:
+     * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
+     * We correct for TCP options in tcp_write(), and don't support IP options.
+     */
+    sendmss = MIN(sendmss, mss_s);
+  return sendmss;
+}
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+const char*
+tcp_debug_state_str(enum tcp_state s)
+{
+  return tcp_state_str[s];
+}
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+/**
+ * Print a tcp header for debugging purposes.
+ *
+ * @param tcphdr pointer to a struct tcp_hdr
+ */
+void
+tcp_debug_print(struct tcp_hdr *tcphdr)
+{
+  LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|    %5"U16_F"      |    %5"U16_F"      | (src port, dest port)\n",
+         ntohs(tcphdr->src), ntohs(tcphdr->dest)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|           %010"U32_F"          | (seq no)\n",
+          ntohl(tcphdr->seqno)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|           %010"U32_F"          | (ack no)\n",
+         ntohl(tcphdr->ackno)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" |   |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"|     %5"U16_F"     | (hdrlen, flags (",
+       TCPH_HDRLEN(tcphdr),
+         TCPH_FLAGS(tcphdr) >> 5 & 1,
+         TCPH_FLAGS(tcphdr) >> 4 & 1,
+         TCPH_FLAGS(tcphdr) >> 3 & 1,
+         TCPH_FLAGS(tcphdr) >> 2 & 1,
+         TCPH_FLAGS(tcphdr) >> 1 & 1,
+         TCPH_FLAGS(tcphdr) & 1,
+         ntohs(tcphdr->wnd)));
+  tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+  LWIP_DEBUGF(TCP_DEBUG, ("), win)\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|    0x%04"X16_F"     |     %5"U16_F"     | (chksum, urgp)\n",
+         ntohs(tcphdr->chksum), ntohs(tcphdr->urgp)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+}
+
+/**
+ * Print a tcp state for debugging purposes.
+ *
+ * @param s enum tcp_state to print
+ */
+void
+tcp_debug_print_state(enum tcp_state s)
+{
+  LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s]));
+}
+
+/**
+ * Print tcp flags for debugging purposes.
+ *
+ * @param flags tcp flags, all active flags are printed
+ */
+void
+tcp_debug_print_flags(uint8_t flags)
+{
+  if (flags & TCP_FIN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
+  }
+  if (flags & TCP_SYN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
+  }
+  if (flags & TCP_RST) {
+    LWIP_DEBUGF(TCP_DEBUG, ("RST "));
+  }
+  if (flags & TCP_PSH) {
+    LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
+  }
+  if (flags & TCP_ACK) {
+    LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
+  }
+  if (flags & TCP_URG) {
+    LWIP_DEBUGF(TCP_DEBUG, ("URG "));
+  }
+  if (flags & TCP_ECE) {
+    LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
+  }
+  if (flags & TCP_CWR) {
+    LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
+  }
+  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+}
+
+/**
+ * Print all tcp_pcbs in every list for debugging purposes.
+ */
+void
+tcp_debug_print_pcbs(void)
+{
+  struct tcp_pcb *pcb;
+  LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n"));
+  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+                       pcb->local_port, pcb->remote_port,
+                       pcb->snd_nxt, pcb->rcv_nxt));
+    tcp_debug_print_state(pcb->state);
+  }    
+  LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
+  for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+                       pcb->local_port, pcb->remote_port,
+                       pcb->snd_nxt, pcb->rcv_nxt));
+    tcp_debug_print_state(pcb->state);
+  }    
+  LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n"));
+  for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+                       pcb->local_port, pcb->remote_port,
+                       pcb->snd_nxt, pcb->rcv_nxt));
+    tcp_debug_print_state(pcb->state);
+  }    
+}
+
+/**
+ * Check state consistency of the tcp_pcb lists.
+ */
+s16_t tcp_pcbs_sane(void)
+{
+  struct tcp_pcb *pcb;
+  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
+    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
+    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+  }
+  for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+  }
+  return 1;
+}
+#endif /* TCP_DEBUG */
diff --git a/kern/src/net/tcp_in.c b/kern/src/net/tcp_in.c
new file mode 100644 (file)
index 0000000..ce08f3b
--- /dev/null
@@ -0,0 +1,1587 @@
+/**
+ * @file
+ * Transmission Control Protocol, incoming traffic
+ *
+ * The input processing functions of the TCP layer.
+ *
+ * These functions are generally called in the order (ip_input() ->)
+ * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
+ * 
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "net/tcp.h"
+#include "net/tcp_impl.h"
+#include "bits/netinet.h"
+#include "net/pbuf.h"
+#include "debug.h"
+#include "error.h"
+#include <string.h>
+
+/* These variables are global to all functions involved in the input
+   processing of TCP segments. They are set by the tcp_input()
+   function. */
+// static struct tcp_hdr *tcphdr;
+// static struct tcp_seg inseg;
+
+//static uint32_t seqno, ackno;
+//static uint8_t flags;
+//static uint16_t tcplen;
+
+static uint8_t recv_flags;
+static struct pbuf *recv_data;
+
+struct tcp_pcb *tcp_input_pcb;
+
+/* Forward declarations. */
+static error_t tcp_process(struct tcp_pcb *pcb, struct tcp_seg *insegp, struct ip_hdr *iphdr, uint16_t tcplen);
+static void tcp_receive(struct tcp_pcb *pcb, struct tcp_seg *insegp, uint16_t tcplen);
+static void tcp_parseopt(struct tcp_pcb *pcb, struct tcp_hdr *tcphdr);
+
+static error_t tcp_listen_input(struct tcp_pcb_listen *pcb, struct tcp_seg *inseg, struct ip_hdr *iphdr, uint16_t tcplen);
+static error_t tcp_timewait_input(struct tcp_pcb *pcb, struct tcp_seg *inseg, struct ip_hdr *iphdr, uint16_t tcplen);
+
+/**
+ * The initial input processing of TCP. It verifies the TCP header, demultiplexes
+ * the segment between the PCBs and passes it on to tcp_process(), which implements
+ * the TCP finite state machine. This function is called by the IP layer (in
+ * ip_input()).
+ *
+ * @param p received TCP segment to process (p->payload pointing to the IP header)
+ * @param inp network interface on which this segment was received
+ */
+void
+tcp_input(struct pbuf *p)
+{
+  struct tcp_pcb *pcb, *prev;
+  struct tcp_pcb_listen *lpcb;
+#if SO_REUSE
+  struct tcp_pcb *lpcb_prev = NULL;
+  struct tcp_pcb_listen *lpcb_any = NULL;
+#endif /* SO_REUSE */
+  uint8_t hdrlen;
+  error_t err;
+
+  // TCP_STATS_INC(tcp.recv);
+  // snmp_inc_tcpinsegs();
+
+       struct tcp_seg inseg;
+  struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+  struct tcp_hdr *tcphdr = (struct tcp_hdr *)((uint8_t *)p->payload + (iphdr->hdr_len) * 4);
+       ip_addr_t current_iphdr_src = {iphdr->src_addr};
+       ip_addr_t current_iphdr_dest = {iphdr->dst_addr};
+
+#if TCP_INPUT_DEBUG
+  tcp_debug_print(tcphdr);
+#endif
+
+  /* remove header from payload */
+  if (pbuf_header(p, -((int16_t)((iphdr->hdr_len) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) {
+    /* drop short packets */
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
+    //TCP_STATS_INC(tcp.lenerr);
+    //TCP_STATS_INC(tcp.drop);
+    //snmp_inc_tcpinerrs();
+    pbuf_free(p);
+    return;
+  }
+
+  /* Don't even process incoming broadcasts/multicasts. 
+  if (ip_addr_isbroadcast(&current_iphdr_dest, inp) ||
+      ip_addr_ismulticast(&current_iphdr_dest)) {
+    TCP_STATS_INC(tcp.proterr);
+    TCP_STATS_INC(tcp.drop);
+    snmp_inc_tcpinerrs();
+    pbuf_free(p);
+    return;
+  } */
+
+#if CHECKSUM_CHECK_TCP
+  /* Verify TCP checksum. */
+  if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+      IP_PROTO_TCP, p->tot_len) != 0) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
+        inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+      IP_PROTO_TCP, p->tot_len)));
+#if TCP_DEBUG
+    tcp_debug_print(tcphdr);
+#endif /* TCP_DEBUG */
+    TCP_STATS_INC(tcp.chkerr);
+    TCP_STATS_INC(tcp.drop);
+    // snmp_inc_tcpinerrs();
+    pbuf_free(p);
+    return;
+  }
+#endif
+
+  /* Move the payload pointer in the pbuf so that it points to the
+     TCP data instead of the TCP header. */
+  hdrlen = TCPH_HDRLEN(tcphdr);
+  if(pbuf_header(p, -(hdrlen * 4))){
+    /* drop short packets */
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
+    //TCP_STATS_INC(tcp.lenerr);
+    //TCP_STATS_INC(tcp.drop);
+    //snmp_inc_tcpinerrs();
+    pbuf_free(p);
+    return;
+  }
+
+  /* Convert fields in TCP header to host byte order. */
+  tcphdr->src = ntohs(tcphdr->src);
+  tcphdr->dest = ntohs(tcphdr->dest);
+  uint32_t seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
+  uint32_t ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
+  tcphdr->wnd = ntohs(tcphdr->wnd);
+
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+       // if the packet is a FIN or a SYN, add 1?
+  uint16_t tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);
+
+  /* Demultiplex an incoming segment. First, we check if it is destined
+     for an active connection. */
+  prev = NULL;
+
+  // XXX: find_pcb? 
+  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
+    LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+    LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
+    if (pcb->remote_port == tcphdr->src &&
+       pcb->local_port == tcphdr->dest &&
+       ip_addr_cmp(&(pcb->remote_ip), &current_iphdr_src) &&
+       ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest)) {
+
+      /* Move this PCB to the front of the list so that subsequent
+         lookups will be faster (we exploit locality in TCP segment
+         arrivals). */
+      LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
+      if (prev != NULL) {
+        prev->next = pcb->next;
+        pcb->next = tcp_active_pcbs;
+        tcp_active_pcbs = pcb;
+      }
+      LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
+      break;
+    }
+    prev = pcb;
+  }
+
+  if (pcb == NULL) {
+    /* If it did not go to an active connection, we check the connections
+       in the TIME-WAIT state. */
+    for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+      LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+      if (pcb->remote_port == tcphdr->src &&
+         pcb->local_port == tcphdr->dest &&
+         ip_addr_cmp(&(pcb->remote_ip), &current_iphdr_src) &&
+         ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest)) {
+        /* We don't really care enough to move this PCB to the front
+           of the list since we are not very likely to receive that
+           many segments for connections in TIME-WAIT. */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
+        tcp_timewait_input(pcb, &inseg, iphdr, tcplen);
+        pbuf_free(p);
+        return;
+      }
+    }
+
+    /* Finally, if we still did not get a match, we check all PCBs that
+       are LISTENing for incoming connections. */
+    prev = NULL;
+    for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+      if (lpcb->local_port == tcphdr->dest) {
+#if SO_REUSE
+        if (ip_addr_cmp(&(lpcb->local_ip), &current_iphdr_dest)) {
+          /* found an exact match */
+          break;
+        } else if(ip_addr_isany(&(lpcb->local_ip))) {
+          /* found an ANY-match */
+          lpcb_any = lpcb;
+          lpcb_prev = prev;
+        }
+#else /* SO_REUSE */
+        if (ip_addr_cmp(&(lpcb->local_ip), &current_iphdr_dest) ||
+            ip_addr_isany(&(lpcb->local_ip))) {
+          /* found a match */
+          break;
+        }
+#endif /* SO_REUSE */
+      }
+      prev = (struct tcp_pcb *)lpcb;
+    }
+#if SO_REUSE
+    /* first try specific local IP */
+    if (lpcb == NULL) {
+      /* only pass to ANY if no specific local IP has been found */
+      lpcb = lpcb_any;
+      prev = lpcb_prev;
+    }
+#endif /* SO_REUSE */
+    if (lpcb != NULL) {
+      /* Move this PCB to the front of the list so that subsequent
+         lookups will be faster (we exploit locality in TCP segment
+         arrivals). */
+      if (prev != NULL) {
+        ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
+              /* our successor is the remainder of the listening list */
+        lpcb->next = tcp_listen_pcbs.listen_pcbs;
+              /* put this listening pcb at the head of the listening list */
+        tcp_listen_pcbs.listen_pcbs = lpcb;
+      }
+    
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
+      tcp_listen_input(lpcb, &inseg, iphdr, tcplen);
+      pbuf_free(p);
+      return;
+    }
+  }
+
+#if TCP_INPUT_DEBUG
+  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
+  tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
+#endif /* TCP_INPUT_DEBUG */
+
+
+  if (pcb != NULL) {
+    /* The incoming segment belongs to a connection. */
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+    tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+
+    /* Set up a tcp_seg structure. */
+    inseg.next = NULL;
+    inseg.len = p->tot_len;
+    inseg.dataptr = p->payload;
+    inseg.p = p;
+    inseg.tcphdr = tcphdr;
+
+    recv_data = NULL;
+    recv_flags = 0;
+
+    /* If there is data which was previously "refused" by upper layer */
+    if (pcb->refused_data != NULL) {
+      /* Notify again application with data previously received. */
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
+      TCP_EVENT_RECV(pcb, pcb->refused_data, ESUCCESS, err);
+                       // XXX: err is a global, need better indication of whether the socket receive was successful
+      if (err == ESUCCESS) {
+        pcb->refused_data = NULL;
+      } else if ((err == -ECONNABORTED) || (tcplen > 0)) {
+        /* if err == -ECONNABORTED, 'pcb' is already deallocated */
+        /* Drop incoming packets because pcb is "full" (only if the incoming
+           segment contains data). */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
+        //TCP_STATS_INC(tcp.drop);
+        //snmp_inc_tcpinerrs();
+        pbuf_free(p);
+        return;
+      }
+    }
+    tcp_input_pcb = pcb;
+    err = tcp_process(pcb, &inseg, iphdr, tcplen);
+    /* A return value of -ECONNABORTED means that tcp_abort() was called
+       and that the pcb has been freed. If so, we don't do anything. */
+    if (err != -ECONNABORTED) {
+      if (recv_flags & TF_RESET) {
+        /* TF_RESET means that the connection was reset by the other
+           end. We then call the error callback to inform the
+           application that the connection is dead before we
+           deallocate the PCB. */
+        // XXX: worry about connection reset later
+                               //TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
+        tcp_pcb_remove(&tcp_active_pcbs, pcb);
+                               kmem_cache_free(tcp_pcb_kcache, pcb);
+      } else if (recv_flags & TF_CLOSED) {
+        /* The connection has been closed and we will deallocate the
+           PCB. */
+        tcp_pcb_remove(&tcp_active_pcbs, pcb);
+                               kmem_cache_free(tcp_pcb_kcache, pcb);
+      } else {
+        err = ESUCCESS;
+        /* If the application has registered a "sent" function to be
+           called when new send buffer space is available, we call it
+           now. */
+        if (pcb->acked > 0) {
+          TCP_EVENT_SENT(pcb, pcb->acked, err);
+          if (err == -ECONNABORTED) {
+            goto aborted;
+          }
+        }
+
+        if (recv_data != NULL) {
+          LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
+          if (pcb->flags & TF_RXCLOSED) {
+            /* received data although already closed -> abort (send RST) to
+               notify the remote host that not all data has been processed */
+            pbuf_free(recv_data);
+            tcp_abort(pcb);
+            goto aborted;
+          }
+          if (flags & TCP_PSH) {
+            recv_data->flags |= PBUF_FLAG_PUSH;
+          }
+
+          /* Notify application that data has been received. */
+          TCP_EVENT_RECV(pcb, recv_data, ESUCCESS, err);
+          if (err == -ECONNABORTED) {
+            goto aborted;
+          }
+
+          /* If the upper layer can't receive this data, store it */
+          if (err != ESUCCESS) {
+            pcb->refused_data = recv_data;
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
+          }
+        }
+
+        /* If a FIN segment was received, we call the callback
+           function with a NULL buffer to indicate EOF. */
+        if (recv_flags & TF_GOT_FIN) {
+          /* correct rcv_wnd as the application won't call tcp_recved()
+             for the FIN's seqno */
+          if (pcb->rcv_wnd != TCP_WND) {
+            pcb->rcv_wnd++;
+          }
+          TCP_EVENT_CLOSED(pcb, err);
+          if (err == -ECONNABORTED) {
+            goto aborted;
+          }
+        }
+
+        tcp_input_pcb = NULL;
+        /* Try to send something out. */
+        tcp_output(pcb);
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+        tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+      }
+    }
+    /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
+       Below this line, 'pcb' may not be dereferenced! */
+aborted:
+    tcp_input_pcb = NULL;
+    recv_data = NULL;
+
+    /* give up our reference to inseg.p */
+    if (inseg.p != NULL)
+    {
+      pbuf_free(inseg.p);
+      inseg.p = NULL;
+    }
+  } else {
+
+    /* If no matching PCB was found, send a TCP RST (reset) to the
+       sender. */
+    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
+    if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
+      //TCP_STATS_INC(tcp.proterr);
+      //TCP_STATS_INC(tcp.drop);
+      tcp_rst(ackno, seqno + tcplen,
+        &current_iphdr_dest, &current_iphdr_src,
+        tcphdr->dest, tcphdr->src);
+    }
+    pbuf_free(p);
+  }
+
+  LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a listening
+ * connection (from tcp_input()).
+ *
+ * @param pcb the tcp_pcb_listen for which a segment arrived
+ * @return ESUCCESS if the segment was processed
+ *         another error_t on error
+ *
+ * @note the return value is not (yet?) used in tcp_input()
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static error_t
+tcp_listen_input(struct tcp_pcb_listen *pcb, struct tcp_seg *insegp, struct ip_hdr *iphdr, uint16_t tcplen)
+{
+  struct tcp_pcb *npcb;
+       struct tcp_hdr *tcphdr = insegp->tcphdr;
+
+  error_t rc;
+
+       ip_addr_t current_iphdr_src = {iphdr->src_addr};
+       ip_addr_t current_iphdr_dest = {iphdr->dst_addr};
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+  uint32_t ackno = tcphdr->ackno;
+  uint32_t seqno = tcphdr->seqno;
+  /* In the LISTEN state, we check for incoming SYN segments,
+     creates a new PCB, and responds with a SYN|ACK. */
+  if (flags & TCP_ACK) {
+    /* For incoming segments with the ACK flag set, respond with a
+       RST. */
+    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
+    tcp_rst(ackno + 1, seqno + tcplen,
+      &current_iphdr_dest, &current_iphdr_src,
+      tcphdr->dest, tcphdr->src);
+  } else if (flags & TCP_SYN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
+#if TCP_LISTEN_BACKLOG
+    if (pcb->accepts_pending >= pcb->backlog) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
+      return -ECONNABORTED;
+    }
+#endif /* TCP_LISTEN_BACKLOG */
+    npcb = tcp_alloc(pcb->prio);
+    /* If a new PCB could not be created (probably due to lack of memory),
+       we don't do anything, but rely on the sender will retransmit the
+       SYN at a time when we have more memory available. */
+    if (npcb == NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
+      //TCP_STATS_INC(tcp.memerr);
+      return -ENOMEM;
+    }
+#if TCP_LISTEN_BACKLOG
+    pcb->accepts_pending++;
+#endif /* TCP_LISTEN_BACKLOG */
+    /* Set up the new PCB. */
+    ip_addr_copy(npcb->local_ip, current_iphdr_dest);
+    npcb->local_port = pcb->local_port;
+    ip_addr_copy(npcb->remote_ip, current_iphdr_src);
+    npcb->remote_port = tcphdr->src;
+    npcb->state = SYN_RCVD;
+    npcb->rcv_nxt = seqno + 1;
+    npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+    npcb->snd_wnd = tcphdr->wnd;
+    npcb->ssthresh = npcb->snd_wnd;
+    npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+    npcb->callback_arg = pcb->callback_arg;
+#if LWIP_CALLBACK_API
+    npcb->accept = pcb->accept;
+#endif /* LWIP_CALLBACK_API */
+    /* inherit socket options */
+    npcb->so_options = pcb->so_options & SO_INHERITED;
+    /* Register the new PCB so that we can begin receiving segments
+       for it. */
+    TCP_REG(&tcp_active_pcbs, npcb);
+
+    /* Parse any options in the SYN. */
+    tcp_parseopt(npcb, tcphdr);
+#if TCP_CALCULATE_EFF_SEND_MSS
+    npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+    // snmp_inc_tcppassiveopens();
+
+    /* Send a SYN|ACK together with the MSS option. */
+    rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
+    if (rc != ESUCCESS) {
+      tcp_abandon(npcb, 0);
+      return rc;
+    }
+    return tcp_output(npcb);
+  }
+  return ESUCCESS;
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a connection in
+ * TIME_WAIT.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static error_t tcp_timewait_input(struct tcp_pcb *pcb, struct tcp_seg *insegp, struct ip_hdr *iphdr, uint16_t tcplen)
+{
+  /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
+  /* RFC 793 3.9 Event Processing - Segment Arrives:
+   * - first check sequence number - we skip that one in TIME_WAIT (always
+   *   acceptable since we only send ACKs)
+   * - second check the RST bit (... return) */
+       struct tcp_hdr *tcphdr = insegp->tcphdr;
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+       ip_addr_t current_iphdr_src = {iphdr->src_addr};
+       ip_addr_t current_iphdr_dest = {iphdr->dst_addr};
+  uint32_t ackno = tcphdr->ackno;
+  uint32_t seqno = tcphdr->seqno;
+  if (flags & TCP_RST)  {
+    return ESUCCESS;
+  }
+  /* - fourth, check the SYN bit, */
+  if (flags & TCP_SYN) {
+    /* If an incoming segment is not acceptable, an acknowledgment
+       should be sent in reply */
+    if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) {
+      /* If the SYN is in the window it is an error, send a reset */
+      tcp_rst(ackno, seqno + tcplen, &current_iphdr_dest, &current_iphdr_src,
+        tcphdr->dest, tcphdr->src);
+      return ESUCCESS;
+    }
+  } else if (flags & TCP_FIN) {
+    /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
+         Restart the 2 MSL time-wait timeout.*/
+    pcb->tmr = tcp_ticks;
+  }
+
+  if ((tcplen > 0))  {
+    /* Acknowledge data, FIN or out-of-window SYN */
+    pcb->flags |= TF_ACK_NOW;
+    return tcp_output(pcb);
+  }
+  return ESUCCESS;
+}
+
+/**
+ * Implements the TCP state machine. Called by tcp_input. In some
+ * states tcp_receive() is called to receive data. The tcp_seg
+ * argument will be freed by the caller (tcp_input()) unless the
+ * recv_data pointer in the pcb is set.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static error_t
+tcp_process(struct tcp_pcb *pcb, struct tcp_seg *insegp, struct ip_hdr *iphdr, uint16_t tcplen)
+{
+       struct tcp_hdr *tcphdr = insegp->tcphdr;
+  struct tcp_seg *rseg;
+  uint8_t acceptable = 0;
+  error_t err;
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+  uint32_t ackno = tcphdr->ackno;
+  uint32_t seqno = tcphdr->seqno;
+
+       ip_addr_t current_iphdr_src = {iphdr->src_addr};
+       ip_addr_t current_iphdr_dest = {iphdr->dst_addr};
+  err = ESUCCESS;
+
+  /* Process incoming RST segments. */
+  if (flags & TCP_RST) {
+    /* First, determine if the reset is acceptable. */
+    if (pcb->state == SYN_SENT) {
+      if (ackno == pcb->snd_nxt) {
+        acceptable = 1;
+      }
+    } else {
+      if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, 
+                          pcb->rcv_nxt+pcb->rcv_wnd)) {
+        acceptable = 1;
+      }
+    }
+
+    if (acceptable) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
+      LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
+      recv_flags |= TF_RESET;
+      pcb->flags &= ~TF_ACK_DELAY;
+      return -ENETRESET;
+    } else {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+       seqno, pcb->rcv_nxt));
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+       seqno, pcb->rcv_nxt));
+      return ESUCCESS;
+    }
+  }
+
+  if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { 
+    /* Cope with new connection attempt after remote end crashed */
+    tcp_ack_now(pcb);
+    return ESUCCESS;
+  }
+  
+  if ((pcb->flags & TF_RXCLOSED) == 0) {
+    /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
+    pcb->tmr = tcp_ticks;
+  }
+  pcb->keep_cnt_sent = 0;
+
+  tcp_parseopt(pcb, tcphdr);
+
+  /* Do different things depending on the TCP state. */
+  switch (pcb->state) {
+  case SYN_SENT:
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
+     pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
+    /* received SYN ACK with expected sequence number? */
+    if ((flags & TCP_ACK) && (flags & TCP_SYN)
+        && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
+      pcb->snd_buf++;
+      pcb->rcv_nxt = seqno + 1;
+      pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+      pcb->lastack = ackno;
+      pcb->snd_wnd = tcphdr->wnd;
+      pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
+      pcb->state = ESTABLISHED;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+      pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+      /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
+       * but for the default value of pcb->mss) */
+      pcb->ssthresh = pcb->mss * 10;
+
+      pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+      LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
+      --pcb->snd_queuelen;
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (uint16_t)pcb->snd_queuelen));
+      rseg = pcb->unacked;
+      pcb->unacked = rseg->next;
+
+      /* If there's nothing left to acknowledge, stop the retransmit
+         timer, otherwise reset it to start again */
+      if(pcb->unacked == NULL)
+        pcb->rtime = -1;
+      else {
+        pcb->rtime = 0;
+        pcb->nrtx = 0;
+      }
+
+      tcp_seg_free(rseg);
+
+      /* Call the user specified function to call when sucessfully
+       * connected. */
+      TCP_EVENT_CONNECTED(pcb, ESUCCESS, err);
+      if (err == -ECONNABORTED) {
+        return -ECONNABORTED;
+      }
+      tcp_ack_now(pcb);
+    }
+    /* received ACK? possibly a half-open connection */
+    else if (flags & TCP_ACK) {
+      /* send a RST to bring the other side in a non-synchronized state. */
+      tcp_rst(ackno, seqno + tcplen, &current_iphdr_dest, &current_iphdr_src,
+        tcphdr->dest, tcphdr->src);
+    }
+    break;
+  case SYN_RCVD:
+    if (flags & TCP_ACK) {
+      /* expected ACK number? */
+      if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
+        uint16_t old_cwnd;
+        pcb->state = ESTABLISHED;
+        LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+#if LWIP_CALLBACK_API
+        LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
+#endif
+        /* Call the accept function. */
+        TCP_EVENT_ACCEPT(pcb, ESUCCESS, err);
+        if (err != ESUCCESS) {
+          /* If the accept function returns with an error, we abort
+           * the connection. */
+          /* Already aborted? */
+          if (err != -ECONNABORTED) {
+            tcp_abort(pcb);
+          }
+          return -ECONNABORTED;
+        }
+        old_cwnd = pcb->cwnd;
+        /* If there was any data contained within this ACK,
+         * we'd better pass it on to the application as well. */
+        tcp_receive(pcb, insegp, tcplen);
+
+        /* Prevent ACK for SYN to generate a sent event */
+        if (pcb->acked != 0) {
+          pcb->acked--;
+        }
+
+        pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+
+        if (recv_flags & TF_GOT_FIN) {
+          tcp_ack_now(pcb);
+          pcb->state = CLOSE_WAIT;
+        }
+      } else {
+        /* incorrect ACK number, send RST */
+       tcp_rst(ackno, seqno + tcplen, &current_iphdr_dest, &current_iphdr_src,
+               tcphdr->dest, tcphdr->src);
+      }
+    } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
+      /* Looks like another copy of the SYN - retransmit our SYN-ACK */
+      tcp_rexmit(pcb);
+    }
+    break;
+  case CLOSE_WAIT:
+    /* FALLTHROUGH */
+  case ESTABLISHED:
+               tcp_receive(pcb, insegp, tcplen);
+    if (recv_flags & TF_GOT_FIN) { /* passive close */
+      tcp_ack_now(pcb);
+      pcb->state = CLOSE_WAIT;
+    }
+    break;
+  case FIN_WAIT_1:
+    tcp_receive(pcb, insegp, tcplen);
+    if (recv_flags & TF_GOT_FIN) {
+      if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
+        LWIP_DEBUGF(TCP_DEBUG,
+          ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+        tcp_ack_now(pcb);
+        tcp_pcb_purge(pcb);
+        TCP_RMV(&tcp_active_pcbs, pcb);
+        pcb->state = TIME_WAIT;
+        TCP_REG(&tcp_tw_pcbs, pcb);
+      } else {
+        tcp_ack_now(pcb);
+        pcb->state = CLOSING;
+      }
+    } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
+      pcb->state = FIN_WAIT_2;
+    }
+    break;
+  case FIN_WAIT_2:
+    tcp_receive(pcb, insegp, tcplen);
+    if (recv_flags & TF_GOT_FIN) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      tcp_ack_now(pcb);
+      tcp_pcb_purge(pcb);
+      TCP_RMV(&tcp_active_pcbs, pcb);
+      pcb->state = TIME_WAIT;
+      TCP_REG(&tcp_tw_pcbs, pcb);
+    }
+    break;
+  case CLOSING:
+    tcp_receive(pcb, insegp, tcplen);
+    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      tcp_pcb_purge(pcb);
+      TCP_RMV(&tcp_active_pcbs, pcb);
+      pcb->state = TIME_WAIT;
+      TCP_REG(&tcp_tw_pcbs, pcb);
+    }
+    break;
+  case LAST_ACK:
+    tcp_receive(pcb, insegp, tcplen);
+    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
+      recv_flags |= TF_CLOSED;
+    }
+    break;
+  default:
+    break;
+  }
+  return ESUCCESS;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Insert segment into the list (segments covered with new one will be deleted)
+ *
+ * Called from tcp_receive()
+ */
+static void
+tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
+{
+  struct tcp_seg *old_seg;
+
+  if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+    /* received segment overlaps all following segments */
+    tcp_segs_free(next);
+    next = NULL;
+  }
+  else {
+    /* delete some following segments
+       oos queue may have segments with FIN flag */
+    while (next &&
+           TCP_SEQ_GEQ((seqno + cseg->len),
+                      (next->tcphdr->seqno + next->len))) {
+      /* cseg with FIN already processed */
+      if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+        TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
+      }
+      old_seg = next;
+      next = next->next;
+      tcp_seg_free(old_seg);
+    }
+    if (next &&
+        TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
+      /* We need to trim the incoming segment. */
+      cseg->len = (uint16_t)(next->tcphdr->seqno - seqno);
+      pbuf_realloc(cseg->p, cseg->len);
+    }
+  }
+  cseg->next = next;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+/**
+ * Called by tcp_process. Checks if the given segment is an ACK for outstanding
+ * data, and if so frees the memory of the buffered data. Next, is places the
+ * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
+ * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
+ * i it has been removed from the buffer.
+ *
+ * If the incoming segment constitutes an ACK for a segment that was used for RTT
+ * estimation, the RTT is estimated here as well.
+ *
+ * Called from tcp_process().
+ */
+static void
+tcp_receive(struct tcp_pcb *pcb, struct tcp_seg *insegp, uint16_t tcplen) {
+  struct tcp_seg *next;
+#if TCP_QUEUE_OOSEQ
+  struct tcp_seg *prev, *cseg;
+#endif /* TCP_QUEUE_OOSEQ */
+       struct tcp_hdr *tcphdr = insegp->tcphdr;
+  struct pbuf *p;
+  int32_t off;
+  int16_t m;
+  uint32_t right_wnd_edge;
+  uint16_t new_tot_len;
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+  uint32_t ackno = tcphdr->ackno;
+  uint32_t seqno = tcphdr->seqno;
+  int found_dupack = 0;
+
+  if (flags & TCP_ACK) {
+    right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
+
+    /* Update window. */
+    if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
+       (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
+       (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
+      pcb->snd_wnd = tcphdr->wnd;
+      pcb->snd_wl1 = seqno;
+      pcb->snd_wl2 = ackno;
+      if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) {
+          pcb->persist_backoff = 0;
+      }
+      LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd));
+#if TCP_WND_DEBUG
+    } else {
+      if (pcb->snd_wnd != tcphdr->wnd) {
+        LWIP_DEBUGF(TCP_WND_DEBUG, 
+                    ("tcp_receive: no window update lastack %"U32_F" ackno %"
+                     U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
+                     pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
+      }
+#endif /* TCP_WND_DEBUG */
+    }
+
+    /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
+     * duplicate ack if:
+     * 1) It doesn't ACK new data 
+     * 2) length of received packet is zero (i.e. no payload) 
+     * 3) the advertised window hasn't changed 
+     * 4) There is outstanding unacknowledged data (retransmission timer running)
+     * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
+     * 
+     * If it passes all five, should process as a dupack: 
+     * a) dupacks < 3: do nothing 
+     * b) dupacks == 3: fast retransmit 
+     * c) dupacks > 3: increase cwnd 
+     * 
+     * If it only passes 1-3, should reset dupack counter (and add to
+     * stats, which we don't do in lwIP)
+     *
+     * If it only passes 1, should reset dupack counter
+     *
+     */
+
+    /* Clause 1 */
+    if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
+      pcb->acked = 0;
+      /* Clause 2 */
+      if (tcplen == 0) {
+        /* Clause 3 */
+        if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
+          /* Clause 4 */
+          if (pcb->rtime >= 0) {
+            /* Clause 5 */
+            if (pcb->lastack == ackno) {
+              found_dupack = 1;
+              if (pcb->dupacks + 1 > pcb->dupacks)
+                ++pcb->dupacks;
+              if (pcb->dupacks > 3) {
+                /* Inflate the congestion window, but not if it means that
+                   the value overflows. */
+                if ((uint16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+                  pcb->cwnd += pcb->mss;
+                }
+              } else if (pcb->dupacks == 3) {
+                /* Do fast retransmit */
+                tcp_rexmit_fast(pcb);
+              }
+            }
+          }
+        }
+      }
+      /* If Clause (1) or more is true, but not a duplicate ack, reset
+       * count of consecutive duplicate acks */
+      if (!found_dupack) {
+        pcb->dupacks = 0;
+      }
+    } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){
+      /* We come here when the ACK acknowledges new data. */
+
+      /* Reset the "IN Fast Retransmit" flag, since we are no longer
+         in fast retransmit. Also reset the congestion window to the
+         slow start threshold. */
+      if (pcb->flags & TF_INFR) {
+        pcb->flags &= ~TF_INFR;
+        pcb->cwnd = pcb->ssthresh;
+      }
+
+      /* Reset the number of retransmissions. */
+      pcb->nrtx = 0;
+
+      /* Reset the retransmission time-out. */
+      pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+      /* Update the send buffer space. Diff between the two can never exceed 64K? */
+      pcb->acked = (uint16_t)(ackno - pcb->lastack);
+
+      pcb->snd_buf += pcb->acked;
+
+      /* Reset the fast retransmit variables. */
+      pcb->dupacks = 0;
+      pcb->lastack = ackno;
+
+      /* Update the congestion control variables (cwnd and
+         ssthresh). */
+      if (pcb->state >= ESTABLISHED) {
+        if (pcb->cwnd < pcb->ssthresh) {
+          if ((uint16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+            pcb->cwnd += pcb->mss;
+          }
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));
+        } else {
+          uint16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
+          if (new_cwnd > pcb->cwnd) {
+            pcb->cwnd = new_cwnd;
+          }
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd));
+        }
+      }
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
+                                    ackno,
+                                    pcb->unacked != NULL?
+                                    ntohl(pcb->unacked->tcphdr->seqno): 0,
+                                    pcb->unacked != NULL?
+                                    ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
+
+      /* Remove segment from the unacknowledged list if the incoming
+         ACK acknowlegdes them. */
+      while (pcb->unacked != NULL &&
+             TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
+                         TCP_TCPLEN(pcb->unacked), ackno)) {
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
+                                      ntohl(pcb->unacked->tcphdr->seqno),
+                                      ntohl(pcb->unacked->tcphdr->seqno) +
+                                      TCP_TCPLEN(pcb->unacked)));
+
+        next = pcb->unacked;
+        pcb->unacked = pcb->unacked->next;
+
+        LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (uint16_t)pcb->snd_queuelen));
+        LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+        /* Prevent ACK for FIN to generate a sent event */
+        if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+          pcb->acked--;
+        }
+
+        pcb->snd_queuelen -= pbuf_clen(next->p);
+        tcp_seg_free(next);
+
+        LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (uint16_t)pcb->snd_queuelen));
+        if (pcb->snd_queuelen != 0) {
+          LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
+                      pcb->unsent != NULL);
+        }
+      }
+
+      /* If there's nothing left to acknowledge, stop the retransmit
+         timer, otherwise reset it to start again */
+      if(pcb->unacked == NULL)
+        pcb->rtime = -1;
+      else
+        pcb->rtime = 0;
+
+      pcb->polltmr = 0;
+    } else {
+      /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
+      pcb->acked = 0;
+    }
+
+    /* We go through the ->unsent list to see if any of the segments
+       on the list are acknowledged by the ACK. This may seem
+       strange since an "unsent" segment shouldn't be acked. The
+       rationale is that lwIP puts all outstanding segments on the
+       ->unsent list after a retransmission, so these segments may
+       in fact have been sent once. */
+    while (pcb->unsent != NULL &&
+           TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + 
+                           TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
+                                    ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) +
+                                    TCP_TCPLEN(pcb->unsent)));
+
+      next = pcb->unsent;
+      pcb->unsent = pcb->unsent->next;
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (uint16_t)pcb->snd_queuelen));
+      LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+      /* Prevent ACK for FIN to generate a sent event */
+      if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+        pcb->acked--;
+      }
+      pcb->snd_queuelen -= pbuf_clen(next->p);
+      tcp_seg_free(next);
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (uint16_t)pcb->snd_queuelen));
+      if (pcb->snd_queuelen != 0) {
+        LWIP_ASSERT("tcp_receive: valid queue length",
+          pcb->unacked != NULL || pcb->unsent != NULL);
+      }
+    }
+    /* End of ACK for new data processing. */
+
+    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
+                                pcb->rttest, pcb->rtseq, ackno));
+
+    /* RTT estimation calculations. This is done by checking if the
+       incoming segment acknowledges the segment we use to take a
+       round-trip time measurement. */
+    if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
+      /* diff between this shouldn't exceed 32K since this are tcp timer ticks
+         and a round-trip shouldn't be that long... */
+      m = (int16_t)(tcp_ticks - pcb->rttest);
+
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
+                                  m, m * TCP_SLOW_INTERVAL));
+
+      /* This is taken directly from VJs original code in his paper */
+      m = m - (pcb->sa >> 3);
+      pcb->sa += m;
+      if (m < 0) {
+        m = -m;
+      }
+      m = m - (pcb->sv >> 2);
+      pcb->sv += m;
+      pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
+                                  pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
+
+      pcb->rttest = 0;
+    }
+  }
+
+  /* If the incoming segment contains data, we must process it
+     further. */
+  if (tcplen > 0) {
+    /* This code basically does three things:
+
+    +) If the incoming segment contains data that is the next
+    in-sequence data, this data is passed to the application. This
+    might involve trimming the first edge of the data. The rcv_nxt
+    variable and the advertised window are adjusted.
+
+    +) If the incoming segment has data that is above the next
+    sequence number expected (->rcv_nxt), the segment is placed on
+    the ->ooseq queue. This is done by finding the appropriate
+    place in the ->ooseq queue (which is ordered by sequence
+    number) and trim the segment in both ends if needed. An
+    immediate ACK is sent to indicate that we received an
+    out-of-sequence segment.
+
+    +) Finally, we check if the first segment on the ->ooseq queue
+    now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
+    rcv_nxt > ooseq->seqno, we must trim the first edge of the
+    segment on ->ooseq before we adjust rcv_nxt. The data in the
+    segments that are now on sequence are chained onto the
+    incoming segment so that we only need to call the application
+    once.
+    */
+
+    /* First, we check if we must trim the first edge. We have to do
+       this if the sequence number of the incoming segment is less
+       than rcv_nxt, and the sequence number plus the length of the
+       segment is larger than rcv_nxt. */
+    /*    if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
+          if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
+    if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){
+      /* Trimming the first edge is done by pushing the payload
+         pointer in the pbuf downwards. This is somewhat tricky since
+         we do not want to discard the full contents of the pbuf up to
+         the new starting point of the data since we have to keep the
+         TCP header which is present in the first pbuf in the chain.
+
+         What is done is really quite a nasty hack: the first pbuf in
+         the pbuf chain is pointed to by inseg.p. Since we need to be
+         able to deallocate the whole pbuf, we cannot change this
+         inseg.p pointer to point to any of the later pbufs in the
+         chain. Instead, we point the ->payload pointer in the first
+         pbuf to data in one of the later pbufs. We also set the
+         inseg.data pointer to point to the right place. This way, the
+         ->p pointer will still point to the first pbuf, but the
+         ->p->payload pointer will point to data in another pbuf.
+
+         After we are done with adjusting the pbuf pointers we must
+         adjust the ->data pointer in the seg and the segment
+         length.*/
+
+      off = pcb->rcv_nxt - seqno;
+      p = insegp->p;
+      LWIP_ASSERT("inseg.p != NULL", insegp->p);
+      LWIP_ASSERT("insane offset!", (off < 0x7fff));
+      if (insegp->p->len < off) {
+        LWIP_ASSERT("pbuf too short!", (((int32_t)insegp->p->tot_len) >= off));
+        new_tot_len = (uint16_t)(insegp->p->tot_len - off);
+        while (p->len < off) {
+          off -= p->len;
+          /* KJM following line changed (with addition of new_tot_len var)
+             to fix bug #9076
+             inseg.p->tot_len -= p->len; */
+          p->tot_len = new_tot_len;
+          p->len = 0;
+          p = STAILQ_NEXT(p, next);
+        }
+        if(pbuf_header(p, (int16_t)-off)) {
+          /* Do we need to cope with this failing?  Assert for now */
+          LWIP_ASSERT("pbuf_header failed", 0);
+        }
+      } else {
+        if(pbuf_header(insegp->p, (int16_t)-off)) {
+          /* Do we need to cope with this failing?  Assert for now */
+          LWIP_ASSERT("pbuf_header failed", 0);
+        }
+      }
+      /* KJM following line changed to use p->payload rather than inseg->p->payload
+         to fix bug #9076 */
+      insegp->dataptr = p->payload;
+      insegp->len -= (uint16_t)(pcb->rcv_nxt - seqno);
+      insegp->tcphdr->seqno = seqno = pcb->rcv_nxt;
+    }
+    else {
+      if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
+        /* the whole segment is < rcv_nxt */
+        /* must be a duplicate of a packet that has already been correctly handled */
+
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
+        tcp_ack_now(pcb);
+      }
+    }
+
+    /* The sequence number must be within the window (above rcv_nxt
+       and below rcv_nxt + rcv_wnd) in order to be further
+       processed. */
+    if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, 
+                        pcb->rcv_nxt + pcb->rcv_wnd - 1)){
+      if (pcb->rcv_nxt == seqno) {
+        /* The incoming segment is the next in sequence. We check if
+           we have to trim the end of the segment and update rcv_nxt
+           and pass the data to the application. */
+        tcplen = TCP_TCPLEN(insegp);
+
+        if (tcplen > pcb->rcv_wnd) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, 
+                      ("tcp_receive: other end overran receive window"
+                       "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+                       seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+          if (TCPH_FLAGS(insegp->tcphdr) & TCP_FIN) {
+            /* Must remove the FIN from the header as we're trimming 
+             * that byte of sequence-space from the packet */
+            TCPH_FLAGS_SET(insegp->tcphdr, TCPH_FLAGS(insegp->tcphdr) &~ TCP_FIN);
+          }
+          /* Adjust length of segment to fit in the window. */
+          insegp->len = pcb->rcv_wnd;
+          if (TCPH_FLAGS(insegp->tcphdr) & TCP_SYN) {
+            insegp->len -= 1;
+          }
+          pbuf_realloc(insegp->p, insegp->len);
+          tcplen = TCP_TCPLEN(insegp);
+          LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+                      (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+        }
+#if TCP_QUEUE_OOSEQ
+        /* Received in-sequence data, adjust ooseq data if:
+           - FIN has been received or
+           - inseq overlaps with ooseq */
+        if (pcb->ooseq != NULL) {
+          if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, 
+                        ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
+            /* Received in-order FIN means anything that was received
+             * out of order must now have been received in-order, so
+             * bin the ooseq queue */
+            while (pcb->ooseq != NULL) {
+              struct tcp_seg *old_ooseq = pcb->ooseq;
+              pcb->ooseq = pcb->ooseq->next;
+              tcp_seg_free(old_ooseq);
+            }
+          }
+          else {
+            next = pcb->ooseq;
+            /* Remove all segments on ooseq that are covered by inseg already.
+             * FIN is copied from ooseq to inseg if present. */
+            while (next &&
+                   TCP_SEQ_GEQ(seqno + tcplen,
+                               next->tcphdr->seqno + next->len)) {
+              /* inseg cannot have FIN here (already processed above) */
+              if (TCPH_FLAGS(next->tcphdr) & TCP_FIN &&
+                  (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
+                TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
+                tcplen = TCP_TCPLEN(&inseg);
+              }
+              prev = next;
+              next = next->next;
+              tcp_seg_free(prev);
+            }
+            /* Now trim right side of inseg if it overlaps with the first
+             * segment on ooseq */
+            if (next &&
+                TCP_SEQ_GT(seqno + tcplen,
+                           next->tcphdr->seqno)) {
+              /* inseg cannot have FIN here (already processed above) */
+              inseg.len = (uint16_t)(next->tcphdr->seqno - seqno);
+              if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+                inseg.len -= 1;
+              }
+              pbuf_realloc(inseg.p, inseg.len);
+              tcplen = TCP_TCPLEN(&inseg);
+              LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
+                          (seqno + tcplen) == next->tcphdr->seqno);
+            }
+            pcb->ooseq = next;
+          }
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+        pcb->rcv_nxt = seqno + tcplen;
+
+        /* Update the receiver's (our) window. */
+        LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
+        pcb->rcv_wnd -= tcplen;
+
+        tcp_update_rcv_ann_wnd(pcb);
+
+        /* If there is data in the segment, we make preparations to
+           pass this up to the application. The ->recv_data variable
+           is used for holding the pbuf that goes to the
+           application. The code for reassembling out-of-sequence data
+           chains its data on this pbuf as well.
+
+           If the segment was a FIN, we set the TF_GOT_FIN flag that will
+           be used to indicate to the application that the remote side has
+           closed its end of the connection. */
+        if (insegp->p->tot_len > 0) {
+          recv_data = insegp->p;
+          /* Since this pbuf now is the responsibility of the
+             application, we delete our reference to it so that we won't
+             (mistakingly) deallocate it. */
+          insegp->p = NULL;
+        }
+        if (TCPH_FLAGS(insegp->tcphdr) & TCP_FIN) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
+          recv_flags |= TF_GOT_FIN;
+        }
+
+#if TCP_QUEUE_OOSEQ
+        /* We now check if we have segments on the ->ooseq queue that
+           are now in sequence. */
+        while (pcb->ooseq != NULL &&
+               pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
+
+          cseg = pcb->ooseq;
+          seqno = pcb->ooseq->tcphdr->seqno;
+
+          pcb->rcv_nxt += TCP_TCPLEN(cseg);
+          LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
+                      pcb->rcv_wnd >= TCP_TCPLEN(cseg));
+          pcb->rcv_wnd -= TCP_TCPLEN(cseg);
+
+          tcp_update_rcv_ann_wnd(pcb);
+
+          if (cseg->p->tot_len > 0) {
+            /* Chain this pbuf onto the pbuf that we will pass to
+               the application. */
+            if (recv_data) {
+              pbuf_cat(recv_data, cseg->p);
+            } else {
+              recv_data = cseg->p;
+            }
+            cseg->p = NULL;
+          }
+          if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
+            recv_flags |= TF_GOT_FIN;
+            if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
+              pcb->state = CLOSE_WAIT;
+            } 
+          }
+
+          pcb->ooseq = cseg->next;
+          tcp_seg_free(cseg);
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+        /* Acknowledge the segment(s). */
+        tcp_ack(pcb);
+
+      } else {
+        /* We get here if the incoming segment is out-of-sequence. */
+        tcp_send_empty_ack(pcb);
+#if TCP_QUEUE_OOSEQ
+        /* We queue the segment on the ->ooseq queue. */
+        if (pcb->ooseq == NULL) {
+          pcb->ooseq = tcp_seg_copy(&inseg);
+        } else {
+          /* If the queue is not empty, we walk through the queue and
+             try to find a place where the sequence number of the
+             incoming segment is between the sequence numbers of the
+             previous and the next segment on the ->ooseq queue. That is
+             the place where we put the incoming segment. If needed, we
+             trim the second edges of the previous and the incoming
+             segment so that it will fit into the sequence.
+
+             If the incoming segment has the same sequence number as a
+             segment on the ->ooseq queue, we discard the segment that
+             contains less data. */
+
+          prev = NULL;
+          for(next = pcb->ooseq; next != NULL; next = next->next) {
+            if (seqno == next->tcphdr->seqno) {
+              /* The sequence number of the incoming segment is the
+                 same as the sequence number of the segment on
+                 ->ooseq. We check the lengths to see which one to
+                 discard. */
+              if (inseg.len > next->len) {
+                /* The incoming segment is larger than the old
+                   segment. We replace some segments with the new
+                   one. */
+                cseg = tcp_seg_copy(&inseg);
+                if (cseg != NULL) {
+                  if (prev != NULL) {
+                    prev->next = cseg;
+                  } else {
+                    pcb->ooseq = cseg;
+                  }
+                  tcp_oos_insert_segment(cseg, next);
+                }
+                break;
+              } else {
+                /* Either the lenghts are the same or the incoming
+                   segment was smaller than the old one; in either
+                   case, we ditch the incoming segment. */
+                break;
+              }
+            } else {
+              if (prev == NULL) {
+                if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
+                  /* The sequence number of the incoming segment is lower
+                     than the sequence number of the first segment on the
+                     queue. We put the incoming segment first on the
+                     queue. */
+                  cseg = tcp_seg_copy(&inseg);
+                  if (cseg != NULL) {
+                    pcb->ooseq = cseg;
+                    tcp_oos_insert_segment(cseg, next);
+                  }
+                  break;
+                }
+              } else {
+                /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
+                  TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
+                if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
+                  /* The sequence number of the incoming segment is in
+                     between the sequence numbers of the previous and
+                     the next segment on ->ooseq. We trim trim the previous
+                     segment, delete next segments that included in received segment
+                     and trim received, if needed. */
+                  cseg = tcp_seg_copy(&inseg);
+                  if (cseg != NULL) {
+                    if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
+                      /* We need to trim the prev segment. */
+                      prev->len = (uint16_t)(seqno - prev->tcphdr->seqno);
+                      pbuf_realloc(prev->p, prev->len);
+                    }
+                    prev->next = cseg;
+                    tcp_oos_insert_segment(cseg, next);
+                  }
+                  break;
+                }
+              }
+              /* If the "next" segment is the last segment on the
+                 ooseq queue, we add the incoming segment to the end
+                 of the list. */
+              if (next->next == NULL &&
+                  TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
+                if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+                  /* segment "next" already contains all data */
+                  break;
+                }
+                next->next = tcp_seg_copy(&inseg);
+                if (next->next != NULL) {
+                  if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
+                    /* We need to trim the last segment. */
+                    next->len = (uint16_t)(seqno - next->tcphdr->seqno);
+                    pbuf_realloc(next->p, next->len);
+                  }
+                  /* check if the remote side overruns our receive window */
+                  if ((uint32_t)tcplen + seqno > pcb->rcv_nxt + (uint32_t)pcb->rcv_wnd) {
+                    LWIP_DEBUGF(TCP_INPUT_DEBUG, 
+                                ("tcp_receive: other end overran receive window"
+                                 "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+                                 seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+                    if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
+                      /* Must remove the FIN from the header as we're trimming 
+                       * that byte of sequence-space from the packet */
+                      TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN);
+                    }
+                    /* Adjust length of segment to fit in the window. */
+                    next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno;
+                    pbuf_realloc(next->next->p, next->next->len);
+                    tcplen = TCP_TCPLEN(next->next);
+                    LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+                                (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+                  }
+                }
+                break;
+              }
+            }
+            prev = next;
+          }
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+      }
+    } else {
+      /* The incoming segment is not withing the window. */
+      tcp_send_empty_ack(pcb);
+    }
+  } else {
+    /* Segments with length 0 is taken care of here. Segments that
+       fall out of the window are ACKed. */
+    /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) ||
+      TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
+    if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
+      tcp_ack_now(pcb);
+    }
+  }
+}
+
+/**
+ * Parses the options contained in the incoming segment. 
+ *
+ * Called from tcp_listen_input() and tcp_process().
+ * Currently, only the MSS option is supported!
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ */
+static void
+tcp_parseopt(struct tcp_pcb *pcb, struct tcp_hdr *tcphdr)
+{
+  uint16_t c, max_c;
+  uint16_t mss;
+  uint8_t *opts, opt;
+#if LWIP_TCP_TIMESTAMPS
+  uint32_t tsval;
+#endif
+
+  opts = (uint8_t *)tcphdr + TCP_HLEN;
+
+  /* Parse the TCP MSS option, if present. */
+  if(TCPH_HDRLEN(tcphdr) > 0x5) {
+    max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
+    for (c = 0; c < max_c; ) {
+      opt = opts[c];
+      switch (opt) {
+      case 0x00:
+        /* End of options. */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
+        return;
+      case 0x01:
+        /* NOP option. */
+        ++c;
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
+        break;
+      case 0x02:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
+        if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
+          /* Bad length */
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          return;
+        }
+        /* An MSS option with the right option length. */
+        mss = (opts[c + 2] << 8) | opts[c + 3];
+        /* Limit the mss to the configured TCP_MSS and prevent division by zero */
+        pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
+        /* Advance to next option */
+        c += 0x04;
+        break;
+#if LWIP_TCP_TIMESTAMPS
+      case 0x08:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
+        if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
+          /* Bad length */
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          return;
+        }
+        /* TCP timestamp option with valid length */
+        tsval = (opts[c+2]) | (opts[c+3] << 8) | 
+          (opts[c+4] << 16) | (opts[c+5] << 24);
+        if (flags & TCP_SYN) {
+          pcb->ts_recent = ntohl(tsval);
+          pcb->flags |= TF_TIMESTAMP;
+        } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
+          pcb->ts_recent = ntohl(tsval);
+        }
+        /* Advance to next option */
+        c += 0x0A;
+        break;
+#endif
+      default:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
+        if (opts[c + 1] == 0) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          /* If the length field is zero, the options are malformed
+             and we don't process them further. */
+          return;
+        }
+        /* All other options have a length field, so that we easily
+           can skip past them. */
+        c += opts[c + 1];
+      }
+    }
+  }
+}
+
diff --git a/kern/src/net/tcp_out.c b/kern/src/net/tcp_out.c
new file mode 100644 (file)
index 0000000..87afdb7
--- /dev/null
@@ -0,0 +1,1463 @@
+/**
+ * @file
+ * Transmission Control Protocol, outgoing traffic
+ *
+ * The output functions of TCP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Adapted by David Zhu for Akaros <yuzhu@cs.berkeley.edu>
+ *
+ */
+#include "net/tcp.h"
+#include "net/tcp_impl.h"
+#include "bits/netinet.h"
+#include "net/pbuf.h"
+#include "debug.h"
+#include "error.h"
+#include <string.h>
+
+/* Define some copy-macros for checksum-on-copy so that the code looks
+   nicer by preventing too many ifdef's. */
+#if TCP_CHECKSUM_ON_COPY
+#define TCP_DATA_COPY(dst, src, len, seg) do { \
+  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
+                     len, &seg->chksum, &seg->chksum_swapped); \
+  seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped)  \
+  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
+#else /* TCP_CHECKSUM_ON_COPY*/
+#define TCP_DATA_COPY(dst, src, len, seg)                     memcpy(dst, src, len)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) memcpy(dst, src, len)
+#endif /* TCP_CHECKSUM_ON_COPY*/
+
+/** Define this to 1 for an extra check that the output checksum is valid
+ * (usefule when the checksum is generated by the application, not the stack) */
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK   0
+#endif
+
+/* Forward declarations.*/
+static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
+
+/** Allocate a pbuf and create a tcphdr at p->payload, used for output
+ * functions other than the default tcp_output -> tcp_output_segment
+ * (e.g. tcp_send_empty_ack, etc.)
+ *
+ * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
+ * @param optlen length of header-options
+ * @param datalen length of tcp data to reserve in pbuf
+ * @param seqno_be seqno in network byte order (big-endian)
+ * @return pbuf with p->payload being the tcp_hdr
+ */
+static struct pbuf *
+tcp_output_alloc_header(struct tcp_pcb *pcb, uint16_t optlen, uint16_t datalen,
+                      uint32_t seqno_be /* already in network byte order */)
+{
+  struct tcp_hdr *tcphdr;
+  struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
+  if (p != NULL) {
+    LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+                 (p->len >= TCP_HLEN + optlen));
+    tcphdr = (struct tcp_hdr *)p->payload;
+    tcphdr->src = htons(pcb->local_port);
+    tcphdr->dest = htons(pcb->remote_port);
+    tcphdr->seqno = seqno_be;
+    tcphdr->ackno = htonl(pcb->rcv_nxt);
+    TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
+    tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+    tcphdr->chksum = 0;
+    tcphdr->urgp = 0;
+
+    /* If we're sending a packet, update the announced right window edge */
+    pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+  }
+  return p;
+}
+
+/**
+ * Called by tcp_close() to send a segment including FIN flag but not data.
+ *
+ * @param pcb the tcp_pcb over which to send a segment
+ * @return ESUCCESS if sent, another error_t otherwise
+ */
+error_t
+tcp_send_fin(struct tcp_pcb *pcb)
+{
+  /* first, try to add the fin to the last unsent segment */
+  if (pcb->unsent != NULL) {
+    struct tcp_seg *last_unsent;
+    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+         last_unsent = last_unsent->next);
+
+    if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
+      /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
+      TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
+      return ESUCCESS;
+    }
+  }
+  /* no data, no length, flags, copy=1, no optdata */
+  return tcp_enqueue_flags(pcb, TCP_FIN);
+}
+
+/**
+ * Create a TCP segment with prefilled header.
+ *
+ * Called by tcp_write and tcp_enqueue_flags.
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param p pbuf that is used to hold the TCP header.
+ * @param flags TCP flags for header.
+ * @param seqno TCP sequence number of this packet
+ * @param optflags options to include in TCP header
+ * @return a new tcp_seg pointing to p, or NULL.
+ * The TCP header is filled in except ackno and wnd.
+ * p is freed on failure.
+ */
+static struct tcp_seg *
+tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, uint8_t flags, uint32_t seqno, uint8_t optflags)
+{
+  struct tcp_seg *seg;
+  uint8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+  if ((seg = (struct tcp_seg *)kmem_cache_alloc(tcp_segment_kcache, 0)) == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));
+    pbuf_free(p);
+    return NULL;
+  }
+  seg->flags = optflags;
+  seg->next = NULL;
+  seg->p = p;
+  seg->dataptr = p->payload;
+  seg->len = p->tot_len - optlen;
+#if TCP_OVERSIZE_DBGCHECK
+  seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+  seg->chksum = 0;
+  seg->chksum_swapped = 0;
+  /* check optflags */
+  LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
+              (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  /* build TCP header */
+  if (pbuf_header(p, TCP_HLEN)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
+    //TCP_STATS_INC(tcp.err);
+    tcp_seg_free(seg);
+    return NULL;
+  }
+  seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
+  seg->tcphdr->src = htons(pcb->local_port);
+  seg->tcphdr->dest = htons(pcb->remote_port);
+  seg->tcphdr->seqno = htonl(seqno);
+  /* ackno is set in tcp_output */
+  TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);
+  /* wnd and chksum are set in tcp_output */
+  seg->tcphdr->urgp = 0;
+  return seg;
+} 
+
+/**
+ * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
+ *
+ * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
+ * there may be extra bytes available at the end.
+ *
+ * @param layer flag to define header size.
+ * @param length size of the pbuf's payload.
+ * @param max_length maximum usable size of payload+oversize.
+ * @param oversize pointer to a uint16_t that will receive the number of usable tail bytes.
+ * @param pcb The TCP connection that willo enqueue the pbuf.
+ * @param apiflags API flags given to tcp_write.
+ * @param first_seg true when this pbuf will be used in the first enqueued segment.
+ * @param 
+ */
+#if TCP_OVERSIZE
+static struct pbuf *
+tcp_pbuf_prealloc(pbuf_layer layer, uint16_t length, uint16_t max_length,
+                  uint16_t *oversize, struct tcp_pcb *pcb, uint8_t apiflags,
+                  uint8_t first_seg)
+{
+  struct pbuf *p;
+  uint16_t alloc = length;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  LWIP_UNUSED_ARG(max_length);
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(apiflags);
+  LWIP_UNUSED_ARG(first_seg);
+  /* always create MSS-sized pbufs */
+  alloc = TCP_MSS;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+  if (length < max_length) {
+    /* Should we allocate an oversized pbuf, or just the minimum
+     * length required? If tcp_write is going to be called again
+     * before this segment is transmitted, we want the oversized
+     * buffer. If the segment will be transmitted immediately, we can
+     * save memory by allocating only length. We use a simple
+     * heuristic based on the following information:
+     *
+     * Did the user set TCP_WRITE_FLAG_MORE?
+     *
+     * Will the Nagle algorithm defer transmission of this segment?
+     */
+    if ((apiflags & TCP_WRITE_FLAG_MORE) ||
+        (!(pcb->flags & TF_NODELAY) &&
+         (!first_seg ||
+          pcb->unsent != NULL ||
+          pcb->unacked != NULL))) {
+      alloc = MIN(max_length, MEM_ALIGN_SIZE(length + TCP_OVERSIZE));
+    }
+  }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+  p = pbuf_alloc(layer, alloc, PBUF_RAM);
+  if (p == NULL) {
+    return NULL;
+  }
+  LWIP_ASSERT("need unchained pbuf", (STAILQ_NEXT(p, next) == NULL));
+  *oversize = p->len - length;
+  /* trim p->len to the currently used size */
+  p->len = p->tot_len = length;
+  return p;
+}
+#else /* TCP_OVERSIZE */
+#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
+#endif /* TCP_OVERSIZE */
+
+#if TCP_CHECKSUM_ON_COPY
+/** Add a checksum of newly added data to the segment */
+static void
+tcp_seg_add_chksum(uint16_t chksum, uint16_t len, uint16_t *seg_chksum,
+                   uint8_t *seg_chksum_swapped)
+{
+  uint32_t helper;
+  /* add chksum to old chksum and fold to uint16_t */
+  helper = chksum + *seg_chksum;
+  chksum = FOLD_U32T(helper);
+  if ((len & 1) != 0) {
+    *seg_chksum_swapped = 1 - *seg_chksum_swapped;
+    chksum = SWAP_BYTES_IN_WORD(chksum);
+  }
+  *seg_chksum = chksum;
+}
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
+ *
+ * @param pcb the tcp pcb to check for
+ * @param len length of data to send (checked agains snd_buf)
+ * @return ESUCCESS if tcp_write is allowed to proceed, another error_t otherwise
+ */
+static error_t
+tcp_write_checks(struct tcp_pcb *pcb, uint16_t len)
+{
+  /* connection is in invalid state for data transmission? */
+  if ((pcb->state != ESTABLISHED) &&
+      (pcb->state != CLOSE_WAIT) &&
+      (pcb->state != SYN_SENT) &&
+      (pcb->state != SYN_RCVD)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
+    return -ENOTCONN;
+  } else if (len == 0) {
+    return ESUCCESS;
+  }
+
+  /* fail on too much data */
+  if (len > pcb->snd_buf) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",
+      len, pcb->snd_buf));
+    pcb->flags |= TF_NAGLEMEMERR;
+    return -ENOMEM;
+  }
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (uint16_t)pcb->snd_queuelen));
+
+  /* If total number of pbufs on the unsent/unacked queues exceeds the
+   * configured maximum, return an error */
+  /* check for configured max queuelen and possible overflow */
+  if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
+      pcb->snd_queuelen, TCP_SND_QUEUELEN));
+    //TCP_STATS_INC(tcp.memerr);
+    pcb->flags |= TF_NAGLEMEMERR;
+    return -ENOMEM;
+  }
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
+      pcb->unacked != NULL || pcb->unsent != NULL);
+  } else {
+    LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
+      pcb->unacked == NULL && pcb->unsent == NULL);
+  }
+  return ESUCCESS;
+}
+
+/**
+ * Write data for sending (but does not send it immediately).
+ *
+ * It waits in the expectation of more data being sent soon (as
+ * it can send them more efficiently by combining them together).
+ * To prompt the system to send data now, call tcp_output() after
+ * calling tcp_write().
+ *
+ * @param pcb Protocol control block for the TCP connection to enqueue data for.
+ * @param arg Pointer to the data to be enqueued for sending.
+ * @param len Data length in bytes
+ * @param apiflags combination of following flags :
+ * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
+ * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
+ * @return ESUCCESS if enqueued, another error_t on error
+ */
+error_t
+tcp_write(struct tcp_pcb *pcb, const void *arg, uint16_t len, uint8_t apiflags)
+{
+  struct pbuf *concat_p = NULL;
+  struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
+  uint16_t pos = 0; /* position in 'arg' data */
+  uint16_t queuelen;
+  uint8_t optlen = 0;
+  uint8_t optflags = 0;
+#if TCP_OVERSIZE
+  uint16_t oversize = 0;
+  uint16_t oversize_used = 0;
+#endif /* TCP_OVERSIZE */
+#if TCP_CHECKSUM_ON_COPY
+  uint16_t concat_chksum = 0;
+  uint8_t concat_chksum_swapped = 0;
+  uint16_t concat_chksummed = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+  error_t err;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  /* Always copy to try to create single pbufs for TX */
+  apiflags |= TCP_WRITE_FLAG_COPY;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
+    (void *)pcb, arg, len, (uint16_t)apiflags));
+  LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", 
+             arg != NULL, return -EFAIL;);
+
+  err = tcp_write_checks(pcb, len);
+  if (err != ESUCCESS) {
+    return err;
+  }
+  queuelen = pcb->snd_queuelen;
+
+#if LWIP_TCP_TIMESTAMPS
+  if ((pcb->flags & TF_TIMESTAMP)) {
+    optflags = TF_SEG_OPTS_TS;
+    optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+  }
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+
+  /*
+   * TCP segmentation is done in three phases with increasing complexity:
+   *
+   * 1. Copy data directly into an oversized pbuf.
+   * 2. Chain a new pbuf to the end of pcb->unsent.
+   * 3. Create new segments.
+   *
+   * We may run out of memory at any point. In that case we must
+   * return -ENOMEM and not change anything in pcb. Therefore, all
+   * changes are recorded in local variables and committed at the end
+   * of the function. Some pcb fields are maintained in local copies:
+   *
+   * queuelen = pcb->snd_queuelen
+   * oversize = pcb->unsent_oversize
+   *
+   * These variables are set consistently by the phases:
+   *
+   * seg points to the last segment tampered with.
+   *
+   * pos records progress as data is segmented.
+   */
+
+  /* Find the tail of the unsent queue. */
+  if (pcb->unsent != NULL) {
+    uint16_t space;
+    uint16_t unsent_optlen;
+
+    /* @todo: this could be sped up by keeping last_unsent in the pcb */
+    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+         last_unsent = last_unsent->next);
+
+    /* Usable space at the end of the last unsent segment */
+    unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
+    space = pcb->mss - (last_unsent->len + unsent_optlen);
+
+    /*
+     * Phase 1: Copy data directly into an oversized pbuf.
+     *
+     * The number of bytes copied is recorded in the oversize_used
+     * variable. The actual copying is done at the bottom of the
+     * function.
+     */
+#if TCP_OVERSIZE
+#if TCP_OVERSIZE_DBGCHECK
+    /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */
+    LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
+                pcb->unsent_oversize == last_unsent->oversize_left);
+#endif /* TCP_OVERSIZE_DBGCHECK */
+    oversize = pcb->unsent_oversize;
+    if (oversize > 0) {
+      LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);
+      seg = last_unsent;
+      oversize_used = oversize < len ? oversize : len;
+      pos += oversize_used;
+      oversize -= oversize_used;
+      space -= oversize_used;
+    }
+    /* now we are either finished or oversize is zero */
+    LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len));
+#endif /* TCP_OVERSIZE */
+
+    /*
+     * Phase 2: Chain a new pbuf to the end of pcb->unsent.
+     *
+     * We don't extend segments containing SYN/FIN flags or options
+     * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
+     * the end.
+     */
+    if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
+      uint16_t seglen = space < len - pos ? space : len - pos;
+      seg = last_unsent;
+
+      /* Create a pbuf with a copy or reference to seglen bytes. We
+       * can use PBUF_RAW here since the data appears in the middle of
+       * a segment. A header will never be prepended. */
+      if (apiflags & TCP_WRITE_FLAG_COPY) {
+        /* Data is copied */
+        if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
+          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+                      ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
+                       seglen));
+          goto memerr;
+        }
+#if TCP_OVERSIZE_DBGCHECK
+        last_unsent->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+        TCP_DATA_COPY2(concat_p->payload, (uint8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
+#if TCP_CHECKSUM_ON_COPY
+        concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+      } else {
+        /* Data is not copied */
+        if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+                      ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+          goto memerr;
+        }
+#if TCP_CHECKSUM_ON_COPY
+        /* calculate the checksum of nocopy-data */
+        tcp_seg_add_chksum(~inet_chksum((uint8_t*)arg + pos, seglen), seglen,
+          &concat_chksum, &concat_chksum_swapped);
+        concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+        /* reference the non-volatile payload data */
+        concat_p->payload = (uint8_t*)arg + pos;
+      }
+
+      pos += seglen;
+      queuelen += pbuf_clen(concat_p);
+    }
+  } else {
+#if TCP_OVERSIZE
+    LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
+                pcb->unsent_oversize == 0);
+#endif /* TCP_OVERSIZE */
+  }
+
+  /*
+   * Phase 3: Create new segments.
+   *
+   * The new segments are chained together in the local 'queue'
+   * variable, ready to be appended to pcb->unsent.
+   */
+  while (pos < len) {
+    struct pbuf *p;
+    uint16_t left = len - pos;
+    uint16_t max_len = pcb->mss - optlen;
+    uint16_t seglen = left > max_len ? max_len : left;
+#if TCP_CHECKSUM_ON_COPY
+    uint16_t chksum = 0;
+    uint8_t chksum_swapped = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+    if (apiflags & TCP_WRITE_FLAG_COPY) {
+      /* If copy is set, memory should be allocated and data copied
+       * into pbuf */
+      if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+        goto memerr;
+      }
+      LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
+                  (p->len >= seglen));
+      TCP_DATA_COPY2((char *)p->payload + optlen, (uint8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
+    } else {
+      /* Copy is not set: First allocate a pbuf for holding the data.
+       * Since the referenced data is available at least until it is
+       * sent out on the link (as it has to be ACKed by the remote
+       * party) we can safely use PBUF_ROM instead of PBUF_REF here.
+       */
+      struct pbuf *p2;
+#if TCP_OVERSIZE
+      LWIP_ASSERT("oversize == 0", oversize == 0);
+#endif /* TCP_OVERSIZE */
+      if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+        goto memerr;
+      }
+#if TCP_CHECKSUM_ON_COPY
+      /* calculate the checksum of nocopy-data */
+      chksum = ~inet_chksum((uint8_t*)arg + pos, seglen);
+#endif /* TCP_CHECKSUM_ON_COPY */
+      /* reference the non-volatile payload data */
+      p2->payload = (uint8_t*)arg + pos;
+
+      /* Second, allocate a pbuf for the headers. */
+      if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+        /* If allocation fails, we have to deallocate the data pbuf as
+         * well. */
+        pbuf_free(p2);
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n"));
+        goto memerr;
+      }
+      /* Concatenate the headers and data pbufs together. */
+      pbuf_cat(p/*header*/, p2/*data*/);
+    }
+
+    queuelen += pbuf_clen(p);
+
+    /* Now that there are more segments queued, we check again if the
+     * length of the queue exceeds the configured maximum or
+     * overflows. */
+    if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
+      pbuf_free(p);
+      goto memerr;
+    }
+
+    if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
+      goto memerr;
+    }
+#if TCP_OVERSIZE_DBGCHECK
+    seg->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+    seg->chksum = chksum;
+    seg->chksum_swapped = chksum_swapped;
+    seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+    /* Fix dataptr for the nocopy case */
+    if ((apiflags & TCP_WRITE_FLAG_COPY) == 0) {
+      seg->dataptr = (uint8_t*)arg + pos;
+    }
+
+    /* first segment of to-be-queued data? */
+    if (queue == NULL) {
+      queue = seg;
+    } else {
+      /* Attach the segment to the end of the queued segments */
+      LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
+      prev_seg->next = seg;
+    }
+    /* remember last segment of to-be-queued data for next iteration */
+    prev_seg = seg;
+
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
+      ntohl(seg->tcphdr->seqno),
+      ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
+
+    pos += seglen;
+  }
+
+  /*
+   * All three segmentation phases were successful. We can commit the
+   * transaction.
+   */
+
+  /*
+   * Phase 1: If data has been added to the preallocated tail of
+   * last_unsent, we update the length fields of the pbuf chain.
+   */
+#if TCP_OVERSIZE
+  if (oversize_used > 0) {
+    struct pbuf *p;
+    /* Bump tot_len of whole chain, len of tail */
+    for (p = last_unsent->p; p; p = STAILQ_NEXT(p, next)) {
+      p->tot_len += oversize_used;
+      if (STAILQ_NEXT(p, next) == NULL) {
+        TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
+        p->len += oversize_used;
+      }
+    }
+    last_unsent->len += oversize_used;
+#if TCP_OVERSIZE_DBGCHECK
+    last_unsent->oversize_left -= oversize_used;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+  }
+  pcb->unsent_oversize = oversize;
+#endif /* TCP_OVERSIZE */
+
+  /*
+   * Phase 2: concat_p can be concatenated onto last_unsent->p
+   */
+  if (concat_p != NULL) {
+    LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
+      (last_unsent != NULL));
+    pbuf_cat(last_unsent->p, concat_p);
+    last_unsent->len += concat_p->tot_len;
+#if TCP_CHECKSUM_ON_COPY
+    if (concat_chksummed) {
+      tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+        &last_unsent->chksum_swapped);
+      last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+    }
+#endif /* TCP_CHECKSUM_ON_COPY */
+  }
+
+  /*
+   * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
+   * is harmless
+   */
+  if (last_unsent == NULL) {
+    pcb->unsent = queue;
+  } else {
+    last_unsent->next = queue;
+  }
+
+  /*
+   * Finally update the pcb state.
+   */
+  pcb->snd_lbb += len;
+  pcb->snd_buf -= len;
+  pcb->snd_queuelen = queuelen;
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
+    pcb->snd_queuelen));
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: valid queue length",
+                pcb->unacked != NULL || pcb->unsent != NULL);
+  }
+
+  /* Set the PSH flag in the last segment that we enqueued. */
+  if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
+    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
+  }
+
+  return ESUCCESS;
+memerr:
+  pcb->flags |= TF_NAGLEMEMERR;
+  // TCP_STATS_INC(tcp.memerr);
+
+  if (concat_p != NULL) {
+    pbuf_free(concat_p);
+  }
+  if (queue != NULL) {
+    tcp_segs_free(queue);
+  }
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
+      pcb->unsent != NULL);
+  }
+  LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
+  return -ENOMEM;
+}
+
+/**
+ * Enqueue TCP options for transmission.
+ *
+ * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param flags TCP header flags to set in the outgoing segment.
+ * @param optdata pointer to TCP options, or NULL.
+ * @param optlen length of TCP options in bytes.
+ */
+error_t
+tcp_enqueue_flags(struct tcp_pcb *pcb, uint8_t flags)
+{
+  struct pbuf *p;
+  struct tcp_seg *seg;
+  uint8_t optflags = 0;
+  uint8_t optlen = 0;
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (uint16_t)pcb->snd_queuelen));
+
+  LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
+              (flags & (TCP_SYN | TCP_FIN)) != 0);
+
+  /* check for configured max queuelen and possible overflow */
+  if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
+                                       pcb->snd_queuelen, TCP_SND_QUEUELEN));
+    // TCP_STATS_INC(tcp.memerr);
+    pcb->flags |= TF_NAGLEMEMERR;
+    return -ENOMEM;
+  }
+
+  if (flags & TCP_SYN) {
+    optflags = TF_SEG_OPTS_MSS;
+  }
+#if LWIP_TCP_TIMESTAMPS
+  if ((pcb->flags & TF_TIMESTAMP)) {
+    optflags |= TF_SEG_OPTS_TS;
+  }
+#endif /* LWIP_TCP_TIMESTAMPS */
+  optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+  /* tcp_enqueue_flags is always called with either SYN or FIN in flags.
+   * We need one available snd_buf byte to do that.
+   * This means we can't send FIN while snd_buf==0. A better fix would be to
+   * not include SYN and FIN sequence numbers in the snd_buf count. */
+  if (pcb->snd_buf == 0) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n"));
+    // TCP_STATS_INC(tcp.memerr);
+    return -ENOMEM;
+  }
+
+  /* Allocate pbuf with room for TCP header + options */
+  if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+    pcb->flags |= TF_NAGLEMEMERR;
+    // TCP_STATS_INC(tcp.memerr);
+    return -ENOMEM;
+  }
+  LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
+              (p->len >= optlen));
+
+  /* Allocate memory for tcp_seg, and fill in fields. */
+  if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
+    pcb->flags |= TF_NAGLEMEMERR;
+    //TCP_STATS_INC(tcp.memerr);
+    return -ENOMEM;
+  }
+  LWIP_ASSERT("seg->tcphdr not aligned", ((uint32_t)seg->tcphdr % 4) == 0);
+  LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
+              ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
+               ntohl(seg->tcphdr->seqno),
+               ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
+               (uint16_t)flags));
+
+  /* Now append seg to pcb->unsent queue */
+  if (pcb->unsent == NULL) {
+    pcb->unsent = seg;
+  } else {
+    struct tcp_seg *useg;
+    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
+    useg->next = seg;
+  }
+#if TCP_OVERSIZE
+  /* The new unsent tail has no space */
+  pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+
+  /* SYN and FIN bump the sequence number */
+  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
+    pcb->snd_lbb++;
+    /* optlen does not influence snd_buf */
+    pcb->snd_buf--;
+  }
+  if (flags & TCP_FIN) {
+    pcb->flags |= TF_FIN;
+  }
+
+  /* update number of segments on the queues */
+  pcb->snd_queuelen += pbuf_clen(seg->p);
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
+      pcb->unacked != NULL || pcb->unsent != NULL);
+  }
+
+  return ESUCCESS;
+}
+
+#if LWIP_TCP_TIMESTAMPS
+/* Build a timestamp option (12 bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the timestamp option
+ */
+static void
+tcp_build_timestamp_option(struct tcp_pcb *pcb, uint32_t *opts)
+{
+  /* Pad with two NOP options to make everything nicely aligned */
+  opts[0] = PP_HTONL(0x0101080A);
+  opts[1] = htonl(sys_now());
+  opts[2] = htonl(pcb->ts_recent);
+}
+#endif
+
+/** Send an ACK without data.
+ *
+ * @param pcb Protocol control block for the TCP connection to send the ACK
+ */
+error_t
+tcp_send_empty_ack(struct tcp_pcb *pcb)
+{
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+  uint8_t optlen = 0;
+
+#if LWIP_TCP_TIMESTAMPS
+  if (pcb->flags & TF_TIMESTAMP) {
+    optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+  }
+#endif
+
+  p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
+  if (p == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+    return -ENOMEM;
+  }
+  tcphdr = (struct tcp_hdr *)p->payload;
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, 
+              ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
+  /* remove ACK flags from the PCB, as we send an empty ACK now */
+  pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+
+  /* NB. MSS option is only sent on SYNs, so ignore it here */
+#if LWIP_TCP_TIMESTAMPS
+  pcb->ts_lastacksent = pcb->rcv_nxt;
+
+  if (pcb->flags & TF_TIMESTAMP) {
+    tcp_build_timestamp_option(pcb, (uint32_t *)(tcphdr + 1));
+  }
+#endif 
+
+#if CHECKSUM_GEN_TCP
+  tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
+        IP_PROTO_TCP, p->tot_len);
+#endif
+#if LWIP_NETIF_HWADDRHINT
+   ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+      IPPROTO_TCP, &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+   ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+      IPPROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+  pbuf_free(p);
+
+  return ESUCCESS;
+}
+
+/**
+ * Find out what we can send and send it
+ *
+ * @param pcb Protocol control block for the TCP connection to send data
+ * @return ESUCCESS if data has been sent or nothing to send
+ *         another error_t on error
+ */
+error_t
+tcp_output(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg, *useg;
+  uint32_t wnd, snd_nxt;
+#if TCP_CWND_DEBUG
+  s16_t i = 0;
+#endif /* TCP_CWND_DEBUG */
+
+  /* First, check if we are invoked by the TCP input processing
+     code. If so, we do not output anything. Instead, we rely on the
+     input processing code to call us when input processing is done
+     with. */
+  if (tcp_input_pcb == pcb) {
+    return ESUCCESS;
+  }
+
+  wnd = MIN(pcb->snd_wnd, pcb->cwnd);
+
+  seg = pcb->unsent;
+
+  /* If the TF_ACK_NOW flag is set and no data will be sent (either
+   * because the ->unsent queue is empty or because the window does
+   * not allow it), construct an empty ACK segment and send it.
+   *
+   * If data is to be sent, we will just piggyback the ACK (see below).
+   */
+  if (pcb->flags & TF_ACK_NOW &&
+     (seg == NULL ||
+      ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
+     return tcp_send_empty_ack(pcb);
+  }
+
+  /* useg should point to last segment on unacked queue */
+  useg = pcb->unacked;
+  if (useg != NULL) {
+    for (; useg->next != NULL; useg = useg->next);
+  }
+
+#if TCP_OUTPUT_DEBUG
+  if (seg == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
+                                   (void*)pcb->unsent));
+  }
+#endif /* TCP_OUTPUT_DEBUG */
+#if TCP_CWND_DEBUG
+  if (seg == NULL) {
+    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
+                                 ", cwnd %"U16_F", wnd %"U32_F
+                                 ", seg == NULL, ack %"U32_F"\n",
+                                 pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
+  } else {
+    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"\n",
+                 pcb->snd_wnd, pcb->cwnd, wnd,
+                 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+                 ntohl(seg->tcphdr->seqno), pcb->lastack));
+  }
+#endif /* TCP_CWND_DEBUG */
+  /* data available and window allows it to be sent? */
+  while (seg != NULL &&
+         ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
+    LWIP_ASSERT("RST not expected here!", 
+                (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
+    /* Stop sending if the nagle algorithm would prevent it
+     * Don't stop:
+     * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
+     * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
+     *   either seg->next != NULL or pcb->unacked == NULL;
+     *   RST is no sent using tcp_write/tcp_output.
+     */
+    if((tcp_do_output_nagle(pcb) == 0) &&
+      ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){
+      break;
+    }
+#if TCP_CWND_DEBUG
+    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",
+                            pcb->snd_wnd, pcb->cwnd, wnd,
+                            ntohl(seg->tcphdr->seqno) + seg->len -
+                            pcb->lastack,
+                            ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+    ++i;
+#endif /* TCP_CWND_DEBUG */
+
+    pcb->unsent = seg->next;
+
+    if (pcb->state != SYN_SENT) {
+      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
+      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+    }
+
+    tcp_output_segment(seg, pcb);
+    snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+    if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+      pcb->snd_nxt = snd_nxt;
+    }
+    /* put segment on unacknowledged list if length > 0 */
+    if (TCP_TCPLEN(seg) > 0) {
+      seg->next = NULL;
+      /* unacked list is empty? */
+      if (pcb->unacked == NULL) {
+        pcb->unacked = seg;
+        useg = seg;
+      /* unacked list is not empty? */
+      } else {
+        /* In the case of fast retransmit, the packet should not go to the tail
+         * of the unacked queue, but rather somewhere before it. We need to check for
+         * this case. -STJ Jul 27, 2004 */
+        if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) {
+          /* add segment to before tail of unacked list, keeping the list sorted */
+          struct tcp_seg **cur_seg = &(pcb->unacked);
+          while (*cur_seg &&
+            TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
+              cur_seg = &((*cur_seg)->next );
+          }
+          seg->next = (*cur_seg);
+          (*cur_seg) = seg;
+        } else {
+          /* add segment to tail of unacked list */
+          useg->next = seg;
+          useg = useg->next;
+        }
+      }
+    /* do not queue empty segments on the unacked list */
+    } else {
+      tcp_seg_free(seg);
+    }
+    seg = pcb->unsent;
+  }
+#if TCP_OVERSIZE
+  if (pcb->unsent == NULL) {
+    /* last unsent has been removed, reset unsent_oversize */
+    pcb->unsent_oversize = 0;
+  }
+#endif /* TCP_OVERSIZE */
+
+  if (seg != NULL && pcb->persist_backoff == 0 && 
+      ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) {
+    /* prepare for persist timer */
+    pcb->persist_cnt = 0;
+    pcb->persist_backoff = 1;
+  }
+
+  pcb->flags &= ~TF_NAGLEMEMERR;
+  return ESUCCESS;
+}
+
+/**
+ * Called by tcp_output() to actually send a TCP segment over IP.
+ *
+ * @param seg the tcp_seg to send
+ * @param pcb the tcp_pcb for the TCP connection used to send the segment
+ */
+static void
+tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
+{
+  uint16_t len;
+  struct netif *netif;
+  uint32_t *opts;
+
+  /** @bug Exclude retransmitted segments from this count. */
+  //snmp_inc_tcpoutsegs();
+
+  /* The TCP header has already been constructed, but the ackno and
+   wnd fields remain. */
+  seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
+
+  /* advertise our receive window size in this TCP segment */
+  seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+
+  pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+
+  /* Add any requested options.  NB MSS option is only set on SYN
+     packets, so ignore it here */
+  LWIP_ASSERT("seg->tcphdr not aligned", ((uint32_t)seg->tcphdr % 4) == 0);
+  opts = (uint32_t *)(void *)(seg->tcphdr + 1);
+  if (seg->flags & TF_SEG_OPTS_MSS) {
+    TCP_BUILD_MSS_OPTION(*opts);
+    opts += 1;
+  }
+#if LWIP_TCP_TIMESTAMPS
+  pcb->ts_lastacksent = pcb->rcv_nxt;
+
+  if (seg->flags & TF_SEG_OPTS_TS) {
+    tcp_build_timestamp_option(pcb, opts);
+    opts += 3;
+  }
+#endif
+
+  /* If we don't have a local IP address, we get one by
+     calling ip_route(). */
+  if (ip_addr_isany(&(pcb->local_ip))) {
+               pcb->local_ip = LOCAL_IP_ADDR;
+               /*
+    netif = ip_route(&(pcb->remote_ip));
+    if (netif == NULL) {
+      return;
+    }
+    ip_addr_copy(pcb->local_ip, netif->ip_addr);
+               */
+  }
+
+  /* Set retransmission timer running if it is not currently enabled */
+  if(pcb->rtime == -1) {
+    pcb->rtime = 0;
+  }
+
+  if (pcb->rttest == 0) {
+    pcb->rttest = tcp_ticks;
+    pcb->rtseq = ntohl(seg->tcphdr->seqno);
+
+    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
+  }
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
+          htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
+          seg->len));
+
+  len = (uint16_t)((uint8_t *)seg->tcphdr - (uint8_t *)seg->p->payload);
+
+  seg->p->len -= len;
+  seg->p->tot_len -= len;
+
+  seg->p->payload = seg->tcphdr;
+
+  seg->tcphdr->chksum = 0;
+#if CHECKSUM_GEN_TCP
+#if TCP_CHECKSUM_ON_COPY
+  {
+    uint32_t acc;
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+    uint16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
+           &(pcb->remote_ip),
+           IP_PROTO_TCP, seg->p->tot_len);
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+    if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
+      LWIP_ASSERT("data included but not checksummed",
+        seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4));
+    }
+
+    /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
+    acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip),
+             &(pcb->remote_ip),
+             IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4);
+    /* add payload checksum */
+    if (seg->chksum_swapped) {
+      seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
+      seg->chksum_swapped = 0;
+    }
+    acc += (uint16_t)~(seg->chksum);
+    seg->tcphdr->chksum = FOLD_U32T(acc);
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+    if (chksum_slow != seg->tcphdr->chksum) {
+      LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING,
+                  ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
+                  seg->tcphdr->chksum, chksum_slow));
+      seg->tcphdr->chksum = chksum_slow;
+    }
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+  }
+#else /* TCP_CHECKSUM_ON_COPY */
+  seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
+         &(pcb->remote_ip),
+         IP_PROTO_TCP, seg->p->tot_len);
+#endif /* TCP_CHECKSUM_ON_COPY */
+#endif /* CHECKSUM_GEN_TCP */
+  //TCP_STATS_INC(tcp.xmit);
+
+#if LWIP_NETIF_HWADDRHINT
+  ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+      IPPROTO_TCP, &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+  ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+      IPPROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+}
+
+/**
+ * Send a TCP RESET packet (empty segment with RST flag set) either to
+ * abort a connection or to show that there is no matching local connection
+ * for a received segment.
+ *
+ * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
+ * matching local pcb was found), tcp_listen_input() (if incoming segment
+ * has ACK flag set) and tcp_process() (received segment in the wrong state)
+ *
+ * Since a RST segment is in most cases not sent for an active connection,
+ * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
+ * most other segment output functions.
+ *
+ * @param seqno the sequence number to use for the outgoing segment
+ * @param ackno the acknowledge number to use for the outgoing segment
+ * @param local_ip the local IP address to send the segment from
+ * @param remote_ip the remote IP address to send the segment to
+ * @param local_port the local TCP port to send the segment from
+ * @param remote_port the remote TCP port to send the segment to
+ */
+void
+tcp_rst(uint32_t seqno, uint32_t ackno,
+  ip_addr_t *local_ip, ip_addr_t *remote_ip,
+  uint16_t local_port, uint16_t remote_port)
+{
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+  p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
+  if (p == NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
+      return;
+  }
+  LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+              (p->len >= sizeof(struct tcp_hdr)));
+
+  tcphdr = (struct tcp_hdr *)p->payload;
+  tcphdr->src = htons(local_port);
+  tcphdr->dest = htons(remote_port);
+  tcphdr->seqno = htonl(seqno);
+  tcphdr->ackno = htonl(ackno);
+  TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
+  tcphdr->wnd = PP_HTONS(TCP_WND);
+  tcphdr->chksum = 0;
+  tcphdr->urgp = 0;
+
+#if CHECKSUM_GEN_TCP
+  tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip,
+              IP_PROTO_TCP, p->tot_len);
+#endif
+  //TCP_STATS_INC(tcp.xmit);
+  // snmp_inc_tcpoutrsts();
+   /* Send output with hardcoded TTL since we have no access to the pcb */
+  ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IPPROTO_TCP);
+  pbuf_free(p);
+  LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
+}
+
+/**
+ * Requeue all unacked segments for retransmission
+ *
+ * Called by tcp_slowtmr() for slow retransmission.
+ *
+ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
+ */
+void
+tcp_rexmit_rto(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg;
+
+  if (pcb->unacked == NULL) {
+    return;
+  }
+
+  /* Move all unacked segments to the head of the unsent queue */
+  for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
+  /* concatenate unsent queue after unacked queue */
+  seg->next = pcb->unsent;
+  /* unsent queue is the concatenated queue (of unacked, unsent) */
+  pcb->unsent = pcb->unacked;
+  /* unacked queue is now empty */
+  pcb->unacked = NULL;
+
+  /* increment number of retransmissions */
+  ++pcb->nrtx;
+
+  /* Don't take any RTT measurements after retransmitting. */
+  pcb->rttest = 0;
+
+  /* Do the actual retransmission */
+  tcp_output(pcb);
+}
+
+/**
+ * Requeue the first unacked segment for retransmission
+ *
+ * Called by tcp_receive() for fast retramsmit.
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg;
+  struct tcp_seg **cur_seg;
+
+  if (pcb->unacked == NULL) {
+    return;
+  }
+
+  /* Move the first unacked segment to the unsent queue */
+  /* Keep the unsent queue sorted. */
+  seg = pcb->unacked;
+  pcb->unacked = seg->next;
+
+  cur_seg = &(pcb->unsent);
+  while (*cur_seg &&
+    TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
+      cur_seg = &((*cur_seg)->next );
+  }
+  seg->next = *cur_seg;
+  *cur_seg = seg;
+
+  ++pcb->nrtx;
+
+  /* Don't take any rtt measurements after retransmitting. */
+  pcb->rttest = 0;
+
+  /* Do the actual retransmission. */
+  //snmp_inc_tcpretranssegs();
+  /* No need to call tcp_output: we are always called from tcp_input()
+     and thus tcp_output directly returns. */
+}
+
+
+/**
+ * Handle retransmission after three dupacks received
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void 
+tcp_rexmit_fast(struct tcp_pcb *pcb)
+{
+  if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
+    /* This is fast retransmit. Retransmit the first unacked segment. */
+    LWIP_DEBUGF(TCP_FR_DEBUG, 
+                ("tcp_receive: dupacks %"U16_F" (%"U32_F
+                 "), fast retransmit %"U32_F"\n",
+                 (uint16_t)pcb->dupacks, pcb->lastack,
+                 ntohl(pcb->unacked->tcphdr->seqno)));
+    tcp_rexmit(pcb);
+
+    /* Set ssthresh to half of the minimum of the current
+     * cwnd and the advertised window */
+    if (pcb->cwnd > pcb->snd_wnd) {
+      pcb->ssthresh = pcb->snd_wnd / 2;
+    } else {
+      pcb->ssthresh = pcb->cwnd / 2;
+    }
+    
+    /* The minimum value for ssthresh should be 2 MSS */
+    if (pcb->ssthresh < 2*pcb->mss) {
+      LWIP_DEBUGF(TCP_FR_DEBUG, 
+                  ("tcp_receive: The minimum value for ssthresh %"U16_F
+                   " should be min 2 mss %"U16_F"...\n",
+                   pcb->ssthresh, 2*pcb->mss));
+      pcb->ssthresh = 2*pcb->mss;
+    }
+    
+    pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
+    pcb->flags |= TF_INFR;
+  } 
+}
+
+
+/**
+ * Send keepalive packets to keep a connection active although
+ * no data is sent over it.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a keepalive packet
+ */
+void
+tcp_keepalive(struct tcp_pcb *pcb)
+{
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                          ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+                          ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F"   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", 
+                          tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
+   
+  p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
+  if(p == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, 
+                ("tcp_keepalive: could not allocate memory for pbuf\n"));
+    return;
+  }
+  tcphdr = (struct tcp_hdr *)p->payload;
+
+#if CHECKSUM_GEN_TCP
+  tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
+                                      IP_PROTO_TCP, p->tot_len);
+#endif
+  //TCP_STATS_INC(tcp.xmit);
+
+  /* Send output to IP */
+#if LWIP_NETIF_HWADDRHINT
+  ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
+    &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+  ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IPPROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+  pbuf_free(p);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
+                          pcb->snd_nxt - 1, pcb->rcv_nxt));
+}
+
+
+/**
+ * Send persist timer zero-window probes to keep a connection active
+ * when a window update is lost.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a zero-window probe packet
+ */
+void
+tcp_zero_window_probe(struct tcp_pcb *pcb)
+{
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+  struct tcp_seg *seg;
+  uint16_t len;
+  uint8_t is_fin;
+
+  LWIP_DEBUGF(TCP_DEBUG, 
+              ("tcp_zero_window_probe: sending ZERO WINDOW probe to %"
+               U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+               ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+               ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
+
+  LWIP_DEBUGF(TCP_DEBUG, 
+              ("tcp_zero_window_probe: tcp_ticks %"U32_F
+               "   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", 
+               tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
+
+  seg = pcb->unacked;
+
+  if(seg == NULL) {
+    seg = pcb->unsent;
+  }
+  if(seg == NULL) {
+    return;
+  }
+
+  is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
+  /* we want to send one seqno: either FIN or data (no options) */
+  len = is_fin ? 0 : 1;
+
+  p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
+  if(p == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
+    return;
+  }
+  tcphdr = (struct tcp_hdr *)p->payload;
+
+  if (is_fin) {
+    /* FIN segment, no data */
+    TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
+  } else {
+    /* Data segment, copy in one byte from the head of the unacked queue */
+    *((char *)p->payload + TCP_HLEN) = *(char *)seg->dataptr;
+  }
+
+#if CHECKSUM_GEN_TCP
+  tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
+                                      IP_PROTO_TCP, p->tot_len);
+#endif
+  //TCP_STATS_INC(tcp.xmit);
+
+  /* Send output to IP */
+#if LWIP_NETIF_HWADDRHINT
+  ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
+    &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+  ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IPPROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+  pbuf_free(p);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
+                          " ackno %"U32_F".\n",
+                          pcb->snd_nxt - 1, pcb->rcv_nxt));
+}
diff --git a/kern/src/net/tcpip.h b/kern/src/net/tcpip.h
new file mode 100644 (file)
index 0000000..995ba8a
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_TCPIP_H__
+#define __LWIP_TCPIP_H__
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api_msg.h"
+#include "lwip/netifapi.h"
+#include "lwip/pbuf.h"
+#include "lwip/api.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Define this to something that triggers a watchdog. This is called from
+ * tcpip_thread after processing a message. */
+#ifndef LWIP_TCPIP_THREAD_ALIVE
+#define LWIP_TCPIP_THREAD_ALIVE()
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+extern sys_mutex_t lock_tcpip_core;
+#define LOCK_TCPIP_CORE()     sys_mutex_lock(&lock_tcpip_core)
+#define UNLOCK_TCPIP_CORE()   sys_mutex_unlock(&lock_tcpip_core)
+#define TCPIP_APIMSG(m)       tcpip_apimsg_lock(m)
+#define TCPIP_APIMSG_ACK(m)
+#define TCPIP_NETIFAPI(m)     tcpip_netifapi_lock(m)
+#define TCPIP_NETIFAPI_ACK(m)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define LOCK_TCPIP_CORE()
+#define UNLOCK_TCPIP_CORE()
+#define TCPIP_APIMSG(m)       tcpip_apimsg(m)
+#define TCPIP_APIMSG_ACK(m)   sys_sem_signal(&m->conn->op_completed)
+#define TCPIP_NETIFAPI(m)     tcpip_netifapi(m)
+#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem)
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+/** Function prototype for the init_done function passed to tcpip_init */
+typedef void (*tcpip_init_done_fn)(void *arg);
+/** Function prototype for functions passed to tcpip_callback() */
+typedef void (*tcpip_callback_fn)(void *ctx);
+
+void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg);
+
+#if LWIP_NETCONN
+err_t tcpip_apimsg(struct api_msg *apimsg);
+#if LWIP_TCPIP_CORE_LOCKING
+err_t tcpip_apimsg_lock(struct api_msg *apimsg);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETCONN */
+
+err_t tcpip_input(struct pbuf *p, struct netif *inp);
+
+#if LWIP_NETIF_API
+err_t tcpip_netifapi(struct netifapi_msg *netifapimsg);
+#if LWIP_TCPIP_CORE_LOCKING
+err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETIF_API */
+
+err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block);
+#define tcpip_callback(f, ctx)              tcpip_callback_with_block(f, ctx, 1)
+
+/* free pbufs or heap memory from another context without blocking */
+err_t pbuf_free_callback(struct pbuf *p);
+err_t mem_free_callback(void *m);
+
+#if LWIP_TCPIP_TIMEOUT
+err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg);
+err_t tcpip_untimeout(sys_timeout_handler h, void *arg);
+#endif /* LWIP_TCPIP_TIMEOUT */
+
+enum tcpip_msg_type {
+#if LWIP_NETCONN
+  TCPIP_MSG_API,
+#endif /* LWIP_NETCONN */
+  TCPIP_MSG_INPKT,
+#if LWIP_NETIF_API
+  TCPIP_MSG_NETIFAPI,
+#endif /* LWIP_NETIF_API */
+#if LWIP_TCPIP_TIMEOUT
+  TCPIP_MSG_TIMEOUT,
+  TCPIP_MSG_UNTIMEOUT,
+#endif /* LWIP_TCPIP_TIMEOUT */
+  TCPIP_MSG_CALLBACK
+};
+
+struct tcpip_msg {
+  enum tcpip_msg_type type;
+  sys_sem_t *sem;
+  union {
+#if LWIP_NETCONN
+    struct api_msg *apimsg;
+#endif /* LWIP_NETCONN */
+#if LWIP_NETIF_API
+    struct netifapi_msg *netifapimsg;
+#endif /* LWIP_NETIF_API */
+    struct {
+      struct pbuf *p;
+      struct netif *netif;
+    } inp;
+    struct {
+      tcpip_callback_fn function;
+      void *ctx;
+    } cb;
+#if LWIP_TCPIP_TIMEOUT
+    struct {
+      u32_t msecs;
+      sys_timeout_handler h;
+      void *arg;
+    } tmo;
+#endif /* LWIP_TCPIP_TIMEOUT */
+  } msg;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !NO_SYS */
+
+#endif /* __LWIP_TCPIP_H__ */
diff --git a/kern/src/net/timers.c b/kern/src/net/timers.c
new file mode 100644 (file)
index 0000000..0d8a4ec
--- /dev/null
@@ -0,0 +1,398 @@
+/**
+ * @file
+ * Stack-internal timers implementation.
+ * This file includes timer callbacks for stack-internal timers as well as
+ * functions to set up or stop timers and check for expired timers.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *         Simon Goldschmidt
+ *
+ */
+#include "smp.h"
+#include "net/timers.h"
+#include "net/tcp_impl.h"
+#include "net/tcp.h"
+
+/** The one and only timeout list */
+static struct sys_timeo *next_timeout;
+
+/** global variable that shows if the tcp timer is currently scheduled or not */
+static bool tcpip_tcp_timer_active = false;  // default to not active
+static struct alarm_waiter *tcp_waiter = NULL; // initialized to empty waiter
+
+/**
+ * Timer callback function that calls tcp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void tcpip_tcp_timer(struct alarm_waiter *aw) {
+  /* call TCP timer handler */
+  tcp_tmr();
+       if (aw == NULL) {
+               // need to allocate a fresh waiter
+       }
+  /* timer still needed? */
+  if (tcp_active_pcbs || tcp_tw_pcbs) {
+               struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
+               set_awaiter_rel(aw, TCP_TMR_INTERVAL);
+               set_alarm(tchain, aw);
+               tcpip_tcp_timer_active = true;
+  } else {
+    /* disable timer */
+    tcpip_tcp_timer_active = false;
+  }
+}
+
+/**
+ * Called from TCP_REG when registering a new PCB:
+ * the reason is to have the TCP timer only running when
+ * there are active (or time-wait) PCBs.
+ */
+void tcp_timer_needed(void) {
+  /* timer is off but needed again? */
+  if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
+    /* enable and start timer */
+    tcpip_tcp_timer_active = true;
+               tcp_waiter = sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, tcp_waiter);
+       } else {
+               if (tcp_waiter != NULL) {
+                       struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
+                       unset_alarm(tchain, tcp_waiter);
+               }
+       }
+}
+
+#if IP_REASSEMBLY
+/**
+ * Timer callback function that calls ip_reass_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+ip_reass_timer(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n"));
+  ip_reass_tmr();
+  sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
+}
+#endif /* IP_REASSEMBLY */
+
+#if LWIP_ARP
+/**
+ * Timer callback function that calls etharp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+arp_timer(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n"));
+  etharp_tmr();
+  sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
+}
+#endif /* LWIP_ARP */
+
+#if LWIP_DHCP
+/**
+ * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dhcp_timer_coarse(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n"));
+  dhcp_coarse_tmr();
+  sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
+}
+
+/**
+ * Timer callback function that calls dhcp_fine_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dhcp_timer_fine(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n"));
+  dhcp_fine_tmr();
+  sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
+}
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+/**
+ * Timer callback function that calls autoip_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+autoip_timer(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n"));
+  autoip_tmr();
+  sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
+}
+#endif /* LWIP_AUTOIP */
+
+#if LWIP_IGMP
+/**
+ * Timer callback function that calls igmp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+igmp_timer(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n"));
+  igmp_tmr();
+  sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Timer callback function that calls dns_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dns_timer(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n"));
+  dns_tmr();
+  sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
+}
+#endif /* LWIP_DNS */
+
+/** Initialize this module */
+void sys_timeouts_init(void)
+{
+
+       //we don't support ipreassembly
+  // ip_reass_waiter = sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, ip_reass_waiter);
+#if 0 
+#if LWIP_ARP
+  sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
+#endif /* LWIP_ARP */
+#if LWIP_DHCP
+  sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
+  sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+  sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
+#endif /* LWIP_AUTOIP */
+#if LWIP_IGMP
+  sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+  sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
+#endif /* LWIP_DNS */
+       */
+#endif
+}
+
+/**
+ * Create a one-shot timer (aka timeout). Timeouts are processed in the
+ * following cases:
+ * @param msecs time in milliseconds after that the timer should expire
+ * @param handler callback function to call when msecs have elapsed, this handler takes no arguments
+ */
+struct alarm_waiter* sys_timeout(uint32_t msecs, sys_timeout_handler func, struct alarm_waiter* waiter) {
+       if (waiter == NULL) {
+               waiter = kmalloc(sizeof(struct alarm_waiter), 0);
+       }
+       struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
+       // initialize the waiter
+       init_awaiter(waiter, func);
+       // explicitly setting the waiter data to be null, since we do not need it in our handler
+       waiter->data = NULL;
+       // set waiting time
+       set_awaiter_rel(waiter, 1000 * msecs);
+       // attach the waiter to a chain to wait
+       set_alarm(tchain, waiter);
+
+       // XXX: when to destroy the waiter, handlers responsibility?
+       return waiter;
+}
+
+/**
+ * Go through timeout list (for this task only) and remove the first matching
+ * entry, even though the timeout has not triggered yet.
+ *
+ * @note This function only works as expected if there is only one timeout
+ * calling 'handler' in the list of timeouts.
+ *
+ * @param handler callback function that would be called by the timeout
+ * @param arg callback argument that would be passed to handler
+*/
+void
+sys_untimeout(alarm_handler handler, void *arg)
+{
+}
+
+#if 0
+
+/** Handle timeouts for NO_SYS==1 (i.e. without using
+ * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
+ * handler functions when timeouts expire.
+ *
+ * Must be called periodically from your main loop.
+ */
+void
+sys_check_timeouts(void)
+{
+  struct sys_timeo *tmptimeout;
+  uint32_t diff;
+  sys_timeout_handler handler;
+  void *arg;
+  int had_one;
+  uint32_t now;
+
+  now = sys_now();
+  if (next_timeout) {
+    /* this cares for wraparounds */
+    diff = LWIP_U32_DIFF(now, timeouts_last_time);
+    do
+    {
+      had_one = 0;
+      tmptimeout = next_timeout;
+      if (tmptimeout->time <= diff) {
+        /* timeout has expired */
+        had_one = 1;
+        timeouts_last_time = now;
+        diff -= tmptimeout->time;
+        next_timeout = tmptimeout->next;
+        handler = tmptimeout->h;
+        arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+        if (handler != NULL) {
+          LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n",
+            tmptimeout->handler_name, arg));
+        }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+        memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+        if (handler != NULL) {
+          handler(arg);
+        }
+      }
+    /* repeat until all expired timers have been called */
+    }while(had_one);
+  }
+}
+
+/** Set back the timestamp of the last call to sys_check_timeouts()
+ * This is necessary if sys_check_timeouts() hasn't been called for a long
+ * time (e.g. while saving energy) to prevent all timer functions of that
+ * period being called.
+ */
+void
+sys_restart_timeouts(void)
+{
+  timeouts_last_time = sys_now();
+}
+
+// #else /* NO_SYS */
+
+/**
+ * Wait (forever) for a message to arrive in an mbox.
+ * While waiting, timeouts are processed.
+ *
+ * @param mbox the mbox to fetch the message from
+ * @param msg the place to store the message
+ */
+void
+sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
+{
+  uint32_t time_needed;
+  struct sys_timeo *tmptimeout;
+  sys_timeout_handler handler;
+  void *arg;
+
+ again:
+  if (!next_timeout) {
+    time_needed = sys_arch_mbox_fetch(mbox, msg, 0);
+  } else {
+    if (next_timeout->time > 0) {
+      time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time);
+    } else {
+      time_needed = SYS_ARCH_TIMEOUT;
+    }
+
+    if (time_needed == SYS_ARCH_TIMEOUT) {
+      /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
+         could be fetched. We should now call the timeout handler and
+         deallocate the memory allocated for the timeout. */
+      tmptimeout = next_timeout;
+      next_timeout = tmptimeout->next;
+      handler = tmptimeout->h;
+      arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+      if (handler != NULL) {
+        LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n",
+          tmptimeout->handler_name, arg));
+      }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+      memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+      if (handler != NULL) {
+        /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the
+           timeout handler function. */
+        LOCK_TCPIP_CORE();
+        handler(arg);
+        UNLOCK_TCPIP_CORE();
+      }
+      LWIP_TCPIP_THREAD_ALIVE();
+
+      /* We try again to fetch a message from the mbox. */
+      goto again;
+    } else {
+      /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
+         occured. The time variable is set to the number of
+         milliseconds we waited for the message. */
+      if (time_needed < next_timeout->time) {
+        next_timeout->time -= time_needed;
+      } else {
+        next_timeout->time = 0;
+      }
+    }
+  }
+}
+
+#endif
index ca398f9..1b9e535 100644 (file)
@@ -16,6 +16,7 @@
 #include <net/udp.h>
 #include <slab.h>
 #include <socket.h>
+#include <debug.h>
 
 struct udp_pcb *udp_pcbs;
 uint16_t udp_port_num = SOCKET_PORT_START;
@@ -121,19 +122,19 @@ int udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
     udphdr->length = htons(q->tot_len); 
                udphdr->checksum = 0; // just to be sure.
                // printd("checksum inet_chksum %x \n", udphdr->checksum);
-               printd("params src addr %x, dst addr %x, length %x \n", global_ip.s_addr, (dst_ip->s_addr), 
+               printd("params src addr %x, dst addr %x, length %x \n", LOCAL_IP_ADDR.s_addr, (dst_ip->s_addr), 
                                          q->tot_len);
 
-    udphdr->checksum = inet_chksum_pseudo(q, htonl(global_ip.s_addr), dst_ip->s_addr,
+    udphdr->checksum = inet_chksum_pseudo(q, htonl(LOCAL_IP_ADDR.s_addr), dst_ip->s_addr,
                                                                                         IPPROTO_UDP, q->tot_len);
                printd ("method ours %x\n", udphdr->checksum);
                // 0x0000; //either use brho's checksum or use cards' capabilities
-               // ip_output(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP);
-               ip_output(q, &global_ip, dst_ip, IPPROTO_UDP);
+               ip_output(q, &LOCAL_IP_ADDR, dst_ip, pcb->ttl, pcb->tos, IPPROTO_UDP);
+               // ip_output(q, &global_ip, dst_ip, IPPROTO_UDP);
     return 0;
 }
 /* TODO: use the real queues we have implemented... */
-int udp_bind(struct udp_pcb *pcb, struct in_addr *ip, uint16_t port){ 
+int udp_bind(struct udp_pcb *pcb, const struct in_addr *ip, uint16_t port){ 
     int rebind = pcb->local_port;
     struct udp_pcb *ipcb;
                assert(pcb);
@@ -239,7 +240,7 @@ int udp_attach(struct pbuf *p, struct sock *socket) {
 // TODO: figure out if we even need a PCB? or just socket buff. 
 // TODO: test out looking up pcbs.. since matching function may fail
 
-void wrap_restart_kthread(struct trapframe *tf, uint32_t srcid,
+static void wrap_restart_kthread(struct trapframe *tf, uint32_t srcid,
                                        long a0, long a1, long a2){
        restart_kthread((struct kthread*) a0);
 }
@@ -253,7 +254,7 @@ int udp_input(struct pbuf *p){
        bool local_match = 0;
        iphdr = (struct ip_hdr *)p->payload;
        /* Move the header to where the udp header is */
-       if (pbuf_header(p, - PBUF_IP_HLEN)){
+       if (pbuf_header(p, -((int16_t)((iphdr->hdr_len) * 4)))) {
                warn("udp_input: Did not find a matching PCB for a udp packet\n");
                pbuf_free(p);
                return -1;
diff --git a/tests/tcp_test.c b/tests/tcp_test.c
new file mode 100644 (file)
index 0000000..d545543
--- /dev/null
@@ -0,0 +1,108 @@
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#define BUF_SIZE 16
+#define LARGE_BUFFER_SIZE 2048
+
+/* Test program
+ *
+ * Pings the server at argv1: argv2
+ * gets a response and prints it
+ * Running in Qemu 
+ * tcp_test server_port
+ * tcp_test server_addr server_port
+ */
+
+int main(int argc, char* argv[]) {
+       struct sockaddr_in server;
+       char buf[BUF_SIZE] = "hello world";
+       char bulkdata[LARGE_BUFFER_SIZE] = "testme";
+       char recv_buf[BUF_SIZE];
+       int sockfd, n, inqemu;
+       struct hostent* host;
+
+       // ignore the host for now
+       if (argc == 2){
+               printf("in qemu client\n");
+               inqemu = 1;
+       }
+       else if (argc == 3){
+               printf("linux client\n");
+               inqemu = 0;
+       } else 
+       {
+               printf("incorrect number of parameters\n");
+       }
+       if (!inqemu){
+               host = gethostbyname(argv[1]); //hostname
+       }
+       bzero(&server, sizeof(server));
+       server.sin_family = AF_INET;
+       if (inqemu)
+               server.sin_port = htons(atoi(argv[1]));
+       else
+               server.sin_port = htons(atoi(argv[2]));
+
+
+       if (inqemu)
+               server.sin_addr.s_addr = inet_addr("10.0.0.1"); //hardcoded server 
+       else
+               // linux client, use the argument as the server address
+               memcpy(&server.sin_addr.s_addr, host->h_addr, host->h_length);
+       
+       char* printbuf = (char*)&server.sin_addr.s_addr;
+       int size = sizeof(server.sin_addr.s_addr);      
+       int i;
+       for (i=0; i<size;i++) {
+               printf("connecting to %x \n", ((char*)printbuf)[i]); 
+       }
+
+       sockfd = socket(AF_INET, SOCK_STREAM, 0);       
+       if (sockfd==-1) {
+               printf("socket error\n");
+               return -1;
+       }
+
+       printf ("tcp_test: sockfd %d \n", sockfd);
+       int socklen = sizeof(server);
+
+       // Set up a connection with the server
+       int rc;
+
+       if((rc = connect(sockfd, (struct sockaddr *)&server, sizeof(server))) < 0)
+       {
+               // testing closing a socket file
+               close(sockfd);
+               exit(-1);
+       }
+       // sending large chunk of data of 2K, more than one frame
+       // int sendsize =  sendto(sockfd, bulkdata, LARGE_BUFFER_SIZE, 0, (struct sockaddr*) &server, socklen);
+
+       fd_set readset;
+       int sendsize = send(sockfd, buf, strlen(buf), 0);
+       if (sendsize != strlen(buf)) 
+               printf("send operation failed error code %d \n", sendsize);
+       int j=0;
+       int result;
+       for (j=0; j<10; j++){
+               strcpy(recv_buf, "DEADBEEFDEADBEE");
+               // in udp_test, we can recv_from with a size of 5, because it discards the rest of the packet. In the TCP
+               // version, we need to be precise about the size of the recv, because the left over data will be displayed in the next
+               // packet.
+               if ((n = recv(sockfd, recv_buf, 14 , 0))< 0){ 
+                       printf("recv failed\n");
+               }
+               // recv_buf[n-1] = 0; //null terminate
+               printf("[OUTPUT] recv %d with length %d from result %s\n", j,n,  recv_buf);
+       }
+       while(1){;}
+       close(sockfd);
+}
diff --git a/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/bind.c b/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/bind.c
new file mode 100644 (file)
index 0000000..eafa3da
--- /dev/null
@@ -0,0 +1,13 @@
+#include <sysdep.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <ros/syscall.h>
+
+int __bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+       return ros_syscall(SYS_bind, sockfd, addr, addrlen, 0, 0, 0);
+}
+
+weak_alias (__bind, bind)
diff --git a/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/connect.c b/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/connect.c
new file mode 100644 (file)
index 0000000..c52928c
--- /dev/null
@@ -0,0 +1,14 @@
+#include <sysdep.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <ros/syscall.h>
+#undef __connect
+int __connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+       return ros_syscall(SYS_connect, sockfd, addr, addrlen, 0, 0, 0);
+}
+
+INTDEF(__connect)
+weak_alias (__connect, connect)
diff --git a/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/recv.c b/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/recv.c
new file mode 100644 (file)
index 0000000..fbccc3a
--- /dev/null
@@ -0,0 +1,12 @@
+#include <sysdep.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <ros/syscall.h>
+int __recv(int s, void *buf, size_t len, int flags) {
+       return ros_syscall(SYS_recv, s, buf, len, flags, 0, 0);
+}
+
+//libc_hidden_def (__recv)
+weak_alias (__recv, recv)
diff --git a/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/send.c b/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/send.c
new file mode 100644 (file)
index 0000000..611e927
--- /dev/null
@@ -0,0 +1,14 @@
+#include <sysdep.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <ros/syscall.h>
+
+ssize_t
+__send(int s, const void *buf, size_t len, int flags) {
+       return ros_syscall(SYS_send, s, buf, len, flags, 0, 0);
+}
+
+libc_hidden_def (__send)
+weak_alias (__send, send)
index 509df81..800fcfb 100644 (file)
@@ -4,11 +4,8 @@
 #include <stddef.h>
 #include <ros/syscall.h>
 
-int
-__socket(int socket_family, int socket_type, int protocol) {
-       int a =  ros_syscall(SYS_socket, socket_family, socket_type, protocol, 0,0,0);
-       printf("socket call result %d\n", a);
-       return a;
+int __socket(int socket_family, int socket_type, int protocol) {
+       return ros_syscall(SYS_socket, socket_family, socket_type, protocol, 0,0,0);
 }
 
 //libc_hidden_def (__socket)