Move nic_common.h and nic_common.c to mainline
authorAndrew Waterman <waterman@cs.berkeley.edu>
Sat, 23 Mar 2013 23:50:53 +0000 (16:50 -0700)
committerAndrew Waterman <waterman@cs.berkeley.edu>
Sat, 23 Mar 2013 23:50:53 +0000 (16:50 -0700)
Happily, there was nothing x86-specific about them!

24 files changed:
kern/arch/i686/Makefrag
kern/arch/i686/e1000.c
kern/arch/i686/e1000.h
kern/arch/i686/frontend.c
kern/arch/i686/ne2k.h
kern/arch/i686/net/ip.c [new file with mode: 0644]
kern/arch/i686/net/nic_common.c [new file with mode: 0644]
kern/arch/i686/net/pbuf.c [new file with mode: 0644]
kern/arch/i686/net/tcp.c [new file with mode: 0644]
kern/arch/i686/net/tcp_in.c [new file with mode: 0644]
kern/arch/i686/net/tcp_out.c [new file with mode: 0644]
kern/arch/i686/net/tcpip.h [new file with mode: 0644]
kern/arch/i686/net/timers.c [new file with mode: 0644]
kern/arch/i686/net/udp.c [new file with mode: 0644]
kern/arch/i686/nic_common.c [deleted file]
kern/arch/i686/nic_common.h [deleted file]
kern/arch/i686/rl8168.h
kern/include/net/nic_common.h [new file with mode: 0644]
kern/src/Makefrag
kern/src/eth_audio.c
kern/src/net/ip.c
kern/src/net/nic_common.c [new file with mode: 0644]
kern/src/net/pbuf.c
kern/src/syscall.c

index 7a6b35d..d9118c4 100644 (file)
@@ -32,7 +32,6 @@ KERN_ARCH_SRCFILES := $(KERN_ARCH_SRC_DIR)/entry.S \
                       $(KERN_ARCH_SRC_DIR)/rl8168.c \
                       $(KERN_ARCH_SRC_DIR)/ne2k.c \
                       $(KERN_ARCH_SRC_DIR)/e1000.c \
-                      $(KERN_ARCH_SRC_DIR)/nic_common.c \
                       $(KERN_ARCH_SRC_DIR)/init.c \
                       $(KERN_ARCH_SRC_DIR)/env.c \
                       $(KERN_ARCH_SRC_DIR)/frontend.c \
index 526ef7a..0b04d9c 100644 (file)
@@ -19,7 +19,7 @@
 #include <arch/smp.h>
 #include <arch/apic.h>
 #include <arch/pci.h>
-#include <arch/nic_common.h>
+#include <net/nic_common.h>
 #include <arch/e1000.h>
 
 #include <ros/memlayout.h>
index 6771651..a1fb56c 100644 (file)
@@ -4,7 +4,7 @@
 #include <ros/common.h>
 #include <trap.h>
 #include <pmap.h>
-#include <arch/nic_common.h>
+#include <net/nic_common.h>
 #include <net/pbuf.h>
 #if 1
 #define e1000_debug(...)               printk(__VA_ARGS__)  
index 80167b3..bd4ef0f 100644 (file)
@@ -1,6 +1,6 @@
 #include <multiboot.h>
 #include <arch/frontend.h>
-#include <arch/nic_common.h>
+#include <net/nic_common.h>
 #include <kmalloc.h>
 
 #define debug(...) printk(__VA_ARGS__)
index 07e2b0f..c72d6db 100644 (file)
@@ -5,7 +5,7 @@
 #include <trap.h>
 #include <pmap.h>
 #include <arch/pci.h>
-#include <arch/nic_common.h>
+#include <net/nic_common.h>
 
 #define ne2k_debug(...)  //cprintf(__VA_ARGS__)  
 #define ne2k_interrupt_debug(...) //cprintf(__VA_ARGS__)  
diff --git a/kern/arch/i686/net/ip.c b/kern/arch/i686/net/ip.c
new file mode 100644 (file)
index 0000000..c8f5e45
--- /dev/null
@@ -0,0 +1,179 @@
+#include <ros/common.h>
+#include <assert.h>
+#include <socket.h>
+#include <bits/netinet.h>
+#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>
+
+/* statically configured next gateway */
+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;
+
+/* TODO: build arp table, and look up */
+int eth_send(struct pbuf *p, struct in_addr *dest) {
+       uint32_t bytes_sent; 
+       printk("size of pbuf_header movement %d\n", sizeof(struct ethernet_hdr));
+       if (pbuf_header(p, sizeof(struct ethernet_hdr)) != 0){
+               warn("eth_send buffer ran out");
+               /* unsuccessful, needs to allocate */   
+               return -ENOBUFS;
+       }
+
+       struct ethernet_hdr *ethhdr = (struct ethernet_hdr *)p->payload; 
+       // TODO: for now just forward to gateway
+       memcpy(ethhdr->dst_mac, GTWAY, 6);
+       memcpy(ethhdr->src_mac, device_mac, 6);
+       ethhdr->eth_type = htons(IP_ETH_TYPE);
+       /* The reason for not sending to send_nic for each pbuf in the chain
+        * is so that we can send from multi-buffer later.
+        */
+       if (send_pbuf){
+               bytes_sent = send_pbuf(p);
+               return bytes_sent;
+       }
+       else {
+               warn("no pbuf send function \n");
+               return -1;
+       }
+       /* is the address local , if no, search for MAC of the gateway and dest to gateway */
+       /* if address is local, use arp etc */
+
+}
+
+/* while it would be nice to write a generic send_pbuf it is impossible to do so in
+ * efficiently.
+ */
+/* Assume no ip options */
+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;
+       printk ("ip output reached\n");
+       /* TODO: Check for IP_HDRINCL */
+       if (dest->s_addr == IP_HDRINCL) {
+               /*send right away since */
+               warn("header included in the ip packets");
+               return -1;
+       }
+       if (pbuf_header(p, IP_HDR_SZ)) {
+               warn("buffer ran out");
+               /* unsuccessful, needs to allocate */   
+               return -ENOBUFS;
+       }
+       iphdr = (struct ip_hdr *) p->payload;
+
+       /* successful */
+       iphdr->version = IPPROTO_IPV4;
+       /* assume no IP options */
+       iphdr->hdr_len = IP_HDR_SZ >> 2;
+       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 = 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);
+       iphdr->dst_addr = (dest->s_addr);
+       /* force hardware checksum
+        * TODO: provide option to do both hardware/software checksum
+        */
+       /* Since the IP header is set already, we can compute the checksum. */
+       /* TODO: Use the card to calculate the checksum */
+       iphdr->checksum = 0;
+       iphdr->checksum = ip_checksum(iphdr); //7ab6
+       if (p->tot_len > DEFAULT_MTU) /*MAX MTU? header included */
+               return -1;//ip_frag(p, dest);
+       else
+               return eth_send(p, dest);
+}
+
+int ip_input(struct pbuf *p) {
+       uint32_t iphdr_hlen, iphdr_len;
+       struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+       //printk("start of ip %p \n", p->payload);
+       //print_pbuf(p);
+       /* use that info to build arp table */
+  if (iphdr->version != 4) {
+               warn("ip version not 4!\n");
+    pbuf_free(p);
+               return -1;
+       }
+       iphdr_hlen = iphdr->hdr_len * 4;
+       iphdr_len = ntohs(iphdr->packet_len);
+       // printk("ip input coming from %x of size %d", ntohs(iphdr->dst_addr), iphdr_len);
+  /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+  if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
+    if (iphdr_hlen > p->len) {
+        warn("IP header (len 0x%X) does not fit in first pbuf (len %X), IP packet dropped.\n",
+        iphdr_hlen, p->len);
+    }
+    if (iphdr_len > p->tot_len) {
+        warn("IP (len %X) is longer than pbuf (len %X), IP packet dropped.\n",
+        iphdr_len, p->tot_len);
+    }
+    /* free (drop) packet pbufs */
+    pbuf_free(p);
+    return -1;
+  }
+       if (ip_checksum(iphdr) != 0) {
+               warn("checksum failed \n");
+               pbuf_free(p);
+               return -1;
+       }
+
+       /* check if it is destined for me? */
+       /* XXX: IP address for the interface is IP_ANY */
+       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", LOCAL_IP_ADDR.s_addr);
+               warn("ip mismatch \n");
+               pbuf_free(p);
+               /* TODO:forward packets */
+               // ip_forward(p, iphdr, inp);
+       }
+
+       if ((ntohs(iphdr->flags_frags) & (IP_OFFMASK | IP_MF)) != 0){
+               panic ("ip fragment detected\n");
+               pbuf_free(p);
+       }
+
+       //printk ("loc head %p, loc protocol %p\n", iphdr, &iphdr->protocol);
+       /* currently a noop, compared to the memory wasted, cutting out ipheader is not really saving much */
+       // pbuf_realloc(p, iphdr_len);
+       switch (iphdr->protocol) {
+               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");
+       }
+       return -1;
+}
+
+void print_ipheader(struct ip_hdr* iph){
+
+       
+}
+
+
diff --git a/kern/arch/i686/net/nic_common.c b/kern/arch/i686/net/nic_common.c
new file mode 100644 (file)
index 0000000..71f2403
--- /dev/null
@@ -0,0 +1,36 @@
+/** @file
+ * @brief Common Nic Variables
+ *
+ * See Info below 
+ *
+ * @author Paul Pearce <pearce@eecs.berkeley.edu>
+ *
+ */
+
+#ifdef __SHARC__
+#pragma nosharc
+#endif
+
+#include <arch/nic_common.h>
+#include <kmalloc.h>
+#include <stdio.h>
+
+// Global send_frame function pointer
+// Means we can only have one network card per system right now...
+int (*send_frame)(const char *data, size_t len);
+int (*send_pbuf)(struct pbuf *p);
+struct pbuf*  (*recv_pbuf)(void);
+
+
+// Global variables for managing ethernet packets over a nic
+// Again, since these are global for all network cards we are 
+// limited to only one for now
+unsigned char device_mac[6];
+uint8_t eth_up = 0; 
+uint32_t num_packet_buffers = 0;
+char* packet_buffers[MAX_PACKET_BUFFERS];
+uint32_t packet_buffers_sizes[MAX_PACKET_BUFFERS];
+uint32_t packet_buffers_head = 0;
+uint32_t packet_buffers_tail = 0;
+spinlock_t packet_buffers_lock = SPINLOCK_INITIALIZER_IRQSAVE;
+
diff --git a/kern/arch/i686/net/pbuf.c b/kern/arch/i686/net/pbuf.c
new file mode 100644 (file)
index 0000000..2fd2fe7
--- /dev/null
@@ -0,0 +1,1031 @@
+#include <ros/common.h>
+#include <stdio.h>
+
+#include <string.h>
+#include <kmalloc.h>
+#include <slab.h>
+#include <assert.h>
+#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
+ *    could be dangerous if it runs on pbufs on a send/recv socket queue
+ * 3. Tot_len could be useless at some point, especially if the max len is only two...
+ * 4. pbuf_chain and pbuf_cat, pbuf_clen has no users yet
+ */
+#define SIZEOF_STRUCT_PBUF (ROUNDUP(sizeof(struct pbuf), ROS_MEM_ALIGN))
+#define MTU_PBUF_SIZE SIZEOF_STRUCT_PBUF + MAX_FRAME_SIZE + ETH_PAD_SIZE
+
+struct kmem_cache *pbuf_kcache;
+struct kmem_cache *mtupbuf_kcache;
+
+
+void pbuf_init(void){
+       printk("size of struct pbuf%d, %d \n", SIZEOF_STRUCT_PBUF, sizeof(struct pbuf));
+       printk("alignment %d\n", __alignof__(struct pbuf));
+       pbuf_kcache = kmem_cache_create("pbuf", sizeof(struct pbuf),
+                                                                       __alignof__(struct pbuf), 0, 0, 0);
+  mtupbuf_kcache = kmem_cache_create("mtupbuf_kcache", MTU_PBUF_SIZE, 
+                                                                               __alignof__(struct pbuf), 0, 0, 0);
+}
+
+static void pbuf_free_auto(struct kref *kref){
+    struct pbuf *p = container_of(kref, struct pbuf, bufref);
+               if (!p) return;
+               struct pbuf *q = STAILQ_NEXT(p, next);
+               printd("deleting p %p of type %d\n", p, p->type);
+    switch (p->type){
+        case PBUF_REF:
+            kmem_cache_free(pbuf_kcache, p);
+            break;
+        case PBUF_RAM:
+            kfree(p);
+            break;
+                               case PBUF_MTU:
+                                               kmem_cache_free(mtupbuf_kcache,p);
+                                               break;
+        default:
+            panic("Invalid pbuf type");
+    }
+               if (q != NULL) 
+                       pbuf_deref(q);
+}
+
+/**
+ * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_RAM: buffer memory for pbuf is allocated as one large
+ *             chunk. This includes protocol headers as well.
+ * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
+ *             protocol headers. Additional headers must be prepended
+ *             by allocating another pbuf and chain in to the front of
+ *             the ROM pbuf. It is assumed that the memory used is really
+ *             similar to ROM in that it is immutable and will not be
+ *             changed. Memory which is dynamic should generally not
+ *             be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
+ *             protocol headers. It is assumed that the pbuf is only
+ *             being used in a single thread. If the pbuf gets queued,
+ *             then pbuf_take should be called to copy the buffer.
+ * - PBUF_MTU: specific to ROS, additional type that comes out of a 
+ *             slab dedicated for the most common size (MTU sized) pbuf.
+ *
+ * @return the allocated pbuf. If multiple pbufs where allocated, this
+ * is the first pbuf of a pbuf chain.
+ */
+struct pbuf *pbuf_alloc(pbuf_layer layer, uint16_t length, pbuf_type type)
+{
+  struct pbuf *p, *q, *r;
+  uint16_t offset;
+       uint16_t buf_size; 
+  int rem_len; /* remaining length */
+
+  /* determine header offset */
+  offset = 0;
+  switch (layer) {
+  case PBUF_TRANSPORT:
+    /* add room for transport (often TCP) layer header */
+    offset += PBUF_TRANSPORT_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_IP:
+    /* add room for IP layer header */
+    offset += PBUF_IP_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_LINK:
+    /* add room for link layer header */
+    offset += PBUF_LINK_HLEN;
+    break;
+  case PBUF_RAW:
+    break;
+  default:
+    warn("pbuf_alloc: bad pbuf layer", 0);
+    return NULL;
+  }
+       
+  switch (type) {
+       case PBUF_MTU:
+               /* special case PBUFs that are of a common size, notice the length has to be 0 in this case */
+               assert(length==0); // TODO: reconsider this
+    /* only allocate memory for the pbuf structure */
+    p = (struct pbuf *)kmem_cache_alloc(mtupbuf_kcache, 0);
+    if (p == NULL) {
+      return NULL;
+    }
+    p->payload = (void *)((uint8_t *)p + SIZEOF_STRUCT_PBUF + offset);
+               STAILQ_NEXT(p, next) = NULL;
+               p->type = type;
+               p->alloc_len = MTU_PBUF_SIZE;
+               p->len = p->tot_len = 0;
+               break;
+
+       case PBUF_RAM:
+    /* If pbuf is to be allocated in RAM, allocate memory for it. */
+               buf_size =  (SIZEOF_STRUCT_PBUF + offset) + MEM_ALIGN_SIZE(length);
+    p = (struct pbuf*)kmalloc(buf_size, 0);
+
+    if (p == NULL) {
+      return NULL;
+    }
+    /* Set up internal structure of the pbuf. */
+    p->payload = (void *)((uint8_t *)p + SIZEOF_STRUCT_PBUF + offset);
+    p->alloc_len = p->len = p->tot_len = length;
+               STAILQ_NEXT(p, next) = NULL;
+    p->type = type;
+    break;
+  case PBUF_REF:
+    /* only allocate memory for the pbuf structure */
+    p = (struct pbuf *)kmem_cache_alloc(pbuf_kcache, 0);
+    if (p == NULL) {
+      return NULL;
+    }
+    p->payload = NULL;
+    p->alloc_len = p->len = p->tot_len = length;
+               STAILQ_NEXT(p, next) = NULL;
+    p->type = type;
+    break;
+       case PBUF_POOL:
+               warn("POOL type not supported!");       
+               return NULL;    
+  default:
+    warn("pbuf_alloc: wrong type", 0);
+    return NULL;
+  }
+  kref_init(&p->bufref, pbuf_free_auto, 1); // TODO: pbuf_free
+  /* set flags */
+  p->flags = 0;
+  return p;
+}
+
+
+/**
+ * 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);
+}
+
+/**
+ * true if the pbuf is deallocated as a result of pbuf_deref
+ * false means just a simple deref
+ */
+bool pbuf_deref(struct pbuf *p){
+       return kref_put(&p->bufref);
+}
+
+void attach_pbuf(struct pbuf *p, struct pbuf_head *ph){
+       spin_lock_irqsave(&ph->lock);
+       ph->qlen++;
+       pbuf_ref(p);
+       STAILQ_INSERT_TAIL(&ph->pbuf_fifo, p, next);
+       spin_unlock_irqsave(&ph->lock);
+}
+
+struct pbuf* detach_pbuf(struct pbuf_head *ph){
+       struct pbuf* buf = NULL;
+       if (ph->qlen == 0) return NULL;
+       spin_lock_irqsave(&ph->lock);
+       ph->qlen--;
+       buf = STAILQ_FIRST(&ph->pbuf_fifo);
+       STAILQ_REMOVE_HEAD(&ph->pbuf_fifo, next);
+       spin_unlock_irqsave(&ph->lock);
+       return buf;
+}
+
+/**
+ * Copy (part of) the contents of a packet buffer
+ * to an application supplied buffer.
+ *
+ * @param buf the pbuf from which to copy data
+ * @param dataptr the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more 
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+int pbuf_copy_out(struct pbuf *buf, void *dataptr, size_t len, uint16_t offset)
+{
+  struct pbuf *p;
+  uint16_t left;
+  uint16_t buf_copy_len;
+  uint16_t copied_total = 0;
+       
+       if (dataptr == NULL || buf == NULL){
+               warn("Copying a pbuf_copy to null pointer");
+               return 0;
+       }
+
+  left = 0;
+  for(p = buf; len != 0 && p != NULL; p = STAILQ_NEXT(p, next)) {
+    if ((offset != 0) && (offset >= p->len)) {
+      /* don't copy from this buffer -> on to the next */
+      offset -= p->len;
+    } else {
+                       /* offset is 0 now, start copying */
+      buf_copy_len = p->len - offset;
+      if (buf_copy_len > len)
+          buf_copy_len = len;
+      /* copy the necessary parts of the buffer */
+      memcpy(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
+      copied_total += buf_copy_len;
+      left += buf_copy_len;
+      len -= buf_copy_len;
+      offset = 0;
+    }
+  }
+  return copied_total;
+}
+
+/**
+ * Chain two pbufs (or pbuf chains) together.
+ * 
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ * 
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+  pbuf_cat(h, t);
+  /* t is now referenced by h */
+  pbuf_ref(t);
+}
+
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+  struct pbuf *p;
+  /* proceed to last pbuf of chain */
+  for (p = h; STAILQ_NEXT(p, next) != NULL; p = STAILQ_NEXT(p, next)) {
+    /* add total length of second chain to all totals of first chain */
+    p->tot_len += t->tot_len;
+  }
+  /* add total length of second chain to last pbuf total of first chain */
+  p->tot_len += t->tot_len;
+  /* chain last pbuf of head (p) with first of tail (t) */
+       STAILQ_NEXT(p,next) = t;
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * (dis)appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * (Using a negative value decreases the header size.)
+ * If hdr_size_inc is 0, this function does nothing and returns succesful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+
+/* TODO: when do we need to lock a pbuf?? */
+int pbuf_header(struct pbuf *p, int delta){ // increase header size
+       uint8_t type = p->type;
+       void *payload = p->payload;
+       if (p == NULL || delta == 0)
+               return 0;
+       // This assertion used to apply when len meant allocated space..
+       // assert(-delta < p->len);
+
+  /* pbuf types containing payloads? */
+  if (type == PBUF_RAM || type == PBUF_POOL || type == PBUF_MTU) {
+    /* set new payload pointer */
+    p->payload = (uint8_t *)p->payload - delta;
+    /* boundary check fails? */
+    if ((uint8_t *)p->payload < (uint8_t *)p + SIZEOF_STRUCT_PBUF) {
+      /* restore old payload pointer */
+      p->payload = payload;
+                       warn("boundary failed \n");
+      /* bail out unsuccesfully */
+      return 1;
+    }
+  /* pbuf types refering to external payloads? */
+  } else if (type == PBUF_REF || type == PBUF_ROM) {
+               /* header was embedded in the payload, we are extracting it */
+    if ((delta < 0) && ((-delta) <= p->len)) {
+      /* increase payload pointer */
+      p->payload = (uint8_t *)p->payload - delta;
+    } else {
+      /* cannot expand payload to front (yet!)
+       * bail out unsuccesfully */
+      return 1;
+    }
+  } else {
+    /* Unknown type */
+    assert("bad pbuf type");
+    return 1;
+  }
+  /* modify pbuf length fields */
+  p->len += delta;
+  p->tot_len += delta;
+
+  return 0;
+}
+
+void print_pbuf(struct pbuf *p) {
+       struct pbuf *next = p;
+       //basically while pbuf is not on the socket queue yet, we can't use STAILQ_NEXT 
+
+       /*XXX: this is wrong.. */
+       while (next != NULL) {
+               printk("pbuf start \n");
+               dumppacket(next->payload, next->len);
+               printk("\n");
+               next = STAILQ_NEXT(next, next);
+       }
+}
+
+
+/**
+ * Dereference a pbuf chain or queue and deallocate any no-longer-used
+ * pbufs at the head of this chain or queue.
+ *
+ * Decrements the pbuf reference count. If it reaches zero, the pbuf is
+ * deallocated.
+ *
+ * For a pbuf chain, this is repeated for each pbuf in the chain,
+ * up to the first pbuf which has a non-zero reference count after
+ * decrementing. So, when all reference counts are one, the whole
+ * chain is free'd.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ *
+ */
+bool pbuf_free(struct pbuf *p) {
+       return pbuf_deref(p);
+}
+
+/**
+ * Count number of pbufs in a chain
+ *
+ * @param p first pbuf of chain
+ * @return the number of pbufs in a chain
+ */
+
+uint8_t pbuf_clen(struct pbuf *p)
+{
+  uint8_t len;
+
+  len = 0;
+  while (p != NULL) {
+    ++len;
+    p = STAILQ_NEXT(p, next);
+  }
+  return len;
+}
+
+
+#if 0
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Initialize a custom pbuf (already allocated).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type type of the pbuf (only used to treat the pbuf accordingly, as
+ *        this function allocates no memory)
+ * @param p pointer to the custom pbuf to initialize (already allocated)
+ * @param payload_mem pointer to the buffer that is used for payload and headers,
+ *        must be at least big enough to hold 'length' plus the header size,
+ *        may be NULL if set later
+ * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
+ *        big enough to hold 'length' plus the header size
+ */
+struct pbuf*
+pbuf_alloced_custom(pbuf_layer l, uint16_t length, pbuf_type type, struct pbuf_custom *p,
+                    void *payload_mem, uint16_t payload_mem_len)
+{
+  uint16_t offset;
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
+
+  /* determine header offset */
+  offset = 0;
+  switch (l) {
+  case PBUF_TRANSPORT:
+    /* add room for transport (often TCP) layer header */
+    offset += PBUF_TRANSPORT_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_IP:
+    /* add room for IP layer header */
+    offset += PBUF_IP_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_LINK:
+    /* add room for link layer header */
+    offset += PBUF_LINK_HLEN;
+    break;
+  case PBUF_RAW:
+    break;
+  default:
+    LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
+    return NULL;
+  }
+
+  if (LWIP_MEM_ALIGN_SIZE(offset) + length < payload_mem_len) {
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
+    return NULL;
+  }
+
+  p->pbuf.next = NULL;
+  if (payload_mem != NULL) {
+    p->pbuf.payload = LWIP_MEM_ALIGN((void *)((uint8_t *)payload_mem + offset));
+  } else {
+    p->pbuf.payload = NULL;
+  }
+  p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
+  p->pbuf.len = p->pbuf.tot_len = length;
+  p->pbuf.type = type;
+  p->pbuf.ref = 1;
+  return &p->pbuf;
+}
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/**
+ * 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;
+
+  LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
+  LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
+              p->type == PBUF_ROM ||
+              p->type == PBUF_RAM ||
+              p->type == PBUF_REF);
+
+  /* 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 = 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 */
+
+  /* shrink allocated memory for PBUF_RAM */
+  /* (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, (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 */
+  q->len = rem_len;
+  q->tot_len = q->len;
+
+  /* any remaining pbufs in chain? */
+  if (q->next != NULL) {
+    /* free remaining pbufs in chain */
+    pbuf_free(q->next);
+  }
+  /* q is last packet in chain */
+  q->next = NULL;
+
+}
+
+
+
+/**
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+  SYS_ARCH_DECL_PROTECT(old_level);
+  /* pbuf given? */
+  if (p != NULL) {
+    SYS_ARCH_PROTECT(old_level);
+    ++(p->ref);
+    SYS_ARCH_UNPROTECT(old_level);
+  }
+}
+
+/**
+ * Concatenate two pbufs (each may be a pbuf chain) and take over
+ * the caller's reference of the tail pbuf.
+ * 
+ * @note The caller MAY NOT reference the tail pbuf afterwards.
+ * Use pbuf_chain() for that purpose.
+ * 
+ * @see pbuf_chain()
+ */
+
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+  struct pbuf *p;
+
+  LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
+             ((h != NULL) && (t != NULL)), return;);
+
+  /* proceed to last pbuf of chain */
+  for (p = h; p->next != NULL; p = p->next) {
+    /* add total length of second chain to all totals of first chain */
+    p->tot_len += t->tot_len;
+  }
+  /* { p is last pbuf of first h chain, p->next == NULL } */
+  LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
+  LWIP_ASSERT("p->next == NULL", p->next == NULL);
+  /* add total length of second chain to last pbuf total of first chain */
+  p->tot_len += t->tot_len;
+  /* chain last pbuf of head (p) with first of tail (t) */
+  p->next = t;
+  /* p->next now references t, but the caller will drop its reference to t,
+   * so netto there is no change to the reference count of t.
+   */
+}
+
+/**
+ * Chain two pbufs (or pbuf chains) together.
+ * 
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ * 
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+  pbuf_cat(h, t);
+  /* t is now referenced by h */
+  pbuf_ref(t);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/**
+ * Dechains the first pbuf from its succeeding pbufs in the chain.
+ *
+ * Makes p->tot_len field equal to p->len.
+ * @param p pbuf to dechain
+ * @return remainder of the pbuf chain, or NULL if it was de-allocated.
+ * @note May not be called on a packet queue.
+ */
+struct pbuf *
+pbuf_dechain(struct pbuf *p)
+{
+  struct pbuf *q;
+  uint8_t tail_gone = 1;
+  /* tail */
+  q = p->next;
+  /* pbuf has successor in chain? */
+  if (q != NULL) {
+    /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+    LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
+    /* enforce invariant if assertion is disabled */
+    q->tot_len = p->tot_len - p->len;
+    /* decouple pbuf from remainder */
+    p->next = NULL;
+    /* total length of pbuf p is its own length only */
+    p->tot_len = p->len;
+    /* q is no longer referenced by p, free it */
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+    tail_gone = pbuf_free(q);
+    if (tail_gone > 0) {
+      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
+                  ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
+    }
+    /* return remaining tail or NULL if deallocated */
+  }
+  /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+  LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
+  return ((tail_gone > 0) ? NULL : q);
+}
+
+/**
+ *
+ * Create PBUF_RAM copies of pbufs.
+ *
+ * Used to queue packets on behalf of the lwIP stack, such as
+ * ARP based queueing.
+ *
+ * @note You MUST explicitly use p = pbuf_take(p);
+ *
+ * @note Only one packet is copied, no packet queue!
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ *
+ * @return ERR_OK if pbuf was copied
+ *         ERR_ARG if one of the pbufs is NULL or p_to is not big
+ *                 enough to hold p_from
+ */
+err_t
+pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
+{
+  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));
+
+  /* is the target big enough to hold the source? */
+  LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
+             (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+
+  /* iterate through pbuf chain */
+  do
+  {
+    LWIP_ASSERT("p_to != NULL", p_to != NULL);
+    /* copy one part of the original chain */
+    if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
+      /* complete current p_from fits into current p_to */
+      len = p_from->len - offset_from;
+    } else {
+      /* current p_from does not fit into current p_to */
+      len = p_to->len - offset_to;
+    }
+    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);
+    if (offset_to == p_to->len) {
+      /* on to next p_to (if any) */
+      offset_to = 0;
+      p_to = p_to->next;
+    }
+    LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
+    if (offset_from >= p_from->len) {
+      /* on to next p_from (if any) */
+      offset_from = 0;
+      p_from = p_from->next;
+    }
+
+    if((p_from != NULL) && (p_from->len == p_from->tot_len)) {
+      /* don't copy more than one packet! */
+      LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+                 (p_from->next == NULL), return ERR_VAL;);
+    }
+    if((p_to != NULL) && (p_to->len == p_to->tot_len)) {
+      /* don't copy more than one packet! */
+      LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+                  (p_to->next == NULL), return ERR_VAL;);
+    }
+  } while (p_from);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+  return ERR_OK;
+}
+
+
+/**
+ * Copy application supplied data into a pbuf.
+ * This function can only be used to copy the equivalent of buf->tot_len data.
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take(struct pbuf *buf, const void *dataptr, uint16_t len)
+{
+  struct pbuf *p;
+  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;);
+
+  if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
+    return ERR_ARG;
+  }
+
+  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+  for(p = buf; total_copy_len != 0; p = p->next) {
+    LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
+    buf_copy_len = total_copy_len;
+    if (buf_copy_len > p->len) {
+      /* this pbuf cannot hold all remaining data */
+      buf_copy_len = p->len;
+    }
+    /* copy the necessary parts of the buffer */
+    MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);
+    total_copy_len -= buf_copy_len;
+    copied_total += buf_copy_len;
+  }
+  LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
+  return ERR_OK;
+}
+
+/**
+ * Creates a single pbuf out of a queue of pbufs.
+ *
+ * @remark: Either the source pbuf 'p' is freed by this function or the original
+ *          pbuf 'p' is returned, therefore the caller has to check the result!
+ *
+ * @param p the source pbuf
+ * @param layer pbuf_layer of the new pbuf
+ *
+ * @return a new, single pbuf (p->next is NULL)
+ *         or the old pbuf if allocation fails
+ */
+struct pbuf*
+pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
+{
+  struct pbuf *q;
+  err_t err;
+  if (p->next == NULL) {
+    return p;
+  }
+  q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
+  if (q == NULL) {
+    /* @todo: what do we do now? */
+    return p;
+  }
+  err = pbuf_copy(q, p);
+  LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
+  pbuf_free(p);
+  return q;
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/**
+ * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
+ * the checksum while copying
+ *
+ * @param p the pbuf to copy data into
+ * @param start_offset offset of p->payload where to copy the data to
+ * @param dataptr data to copy into the pbuf
+ * @param len length of data to copy into the pbuf
+ * @param chksum pointer to the checksum which is updated
+ * @return ERR_OK if successful, another error if the data does not fit
+ *         within the (first) pbuf (no pbuf queues!)
+ */
+err_t
+pbuf_fill_chksum(struct pbuf *p, uint16_t start_offset, const void *dataptr,
+                 uint16_t len, uint16_t *chksum)
+{
+  u32_t acc;
+  uint16_t copy_chksum;
+  char *dst_ptr;
+  LWIP_ASSERT("p != NULL", p != NULL);
+  LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
+  LWIP_ASSERT("chksum != NULL", chksum != NULL);
+  LWIP_ASSERT("len != 0", len != 0);
+
+  if ((start_offset >= p->len) || (start_offset + len > p->len)) {
+    return ERR_ARG;
+  }
+
+  dst_ptr = ((char*)p->payload) + start_offset;
+  copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
+  if ((start_offset & 1) != 0) {
+    copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
+  }
+  acc = *chksum;
+  acc += copy_chksum;
+  *chksum = FOLD_U32T(acc);
+  return ERR_OK;
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+ /** Get one byte from the specified position in a pbuf
+ * WARNING: returns zero for offset >= p->tot_len
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
+ */
+uint8_t
+pbuf_get_at(struct pbuf* p, uint16_t offset)
+{
+  uint16_t copy_from = offset;
+  struct pbuf* q = p;
+
+  /* get the correct pbuf */
+  while ((q != NULL) && (q->len <= copy_from)) {
+    copy_from -= q->len;
+    q = q->next;
+  }
+  /* return requested data if pbuf is OK */
+  if ((q != NULL) && (q->len > copy_from)) {
+    return ((uint8_t*)q->payload)[copy_from];
+  }
+  return 0;
+}
+
+/** Compare pbuf contents at specified offset with memory s2, both of length n
+ *
+ * @param p pbuf to compare
+ * @param offset offset into p at wich to start comparing
+ * @param s2 buffer to compare
+ * @param n length of buffer to compare
+ * @return zero if equal, nonzero otherwise
+ *         (0xffff if p is too short, diffoffset+1 otherwise)
+ */
+uint16_t
+pbuf_memcmp(struct pbuf* p, uint16_t offset, const void* s2, uint16_t n)
+{
+  uint16_t start = offset;
+  struct pbuf* q = p;
+
+  /* get the correct pbuf */
+  while ((q != NULL) && (q->len <= start)) {
+    start -= q->len;
+    q = q->next;
+  }
+  /* return requested data if pbuf is OK */
+  if ((q != NULL) && (q->len > start)) {
+    uint16_t i;
+    for(i = 0; i < n; i++) {
+      uint8_t a = pbuf_get_at(q, start + i);
+      uint8_t b = ((uint8_t*)s2)[i];
+      if (a != b) {
+        return i+1;
+      }
+    }
+    return 0;
+  }
+  return 0xffff;
+}
+
+/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+ * start_offset.
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ *        return value 'not found'
+ * @param mem search for the contents of this buffer
+ * @param mem_len length of 'mem'
+ * @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
+ */
+uint16_t
+pbuf_memfind(struct pbuf* p, const void* mem, uint16_t mem_len, uint16_t start_offset)
+{
+  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; ) {
+      uint16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+      if (plus == 0) {
+        return i;
+      } else {
+        i += plus;
+      }
+    }
+  }
+  return 0xFFFF;
+}
+
+/** Find occurrence of substr with length substr_len in pbuf p, start at offset
+ * start_offset
+ * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
+ * the pbuf/source string!
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ *        return value 'not found'
+ * @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
+ */
+uint16_t
+pbuf_strstr(struct pbuf* p, const char* substr)
+{
+  size_t substr_len;
+  if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
+    return 0xFFFF;
+  }
+  substr_len = strlen(substr);
+  if (substr_len >= 0xFFFF) {
+    return 0xFFFF;
+  }
+  return pbuf_memfind(p, substr, (uint16_t)substr_len, 0);
+}
+
+#endif /*EVERYTHING*/
diff --git a/kern/arch/i686/net/tcp.c b/kern/arch/i686/net/tcp.c
new file mode 100644 (file)
index 0000000..bc3ce94
--- /dev/null
@@ -0,0 +1,1616 @@
+/**
+ * @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);
+}
+
+/**
+ * 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) {
+       int8_t irq_state = 0;
+       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..
+               if (!sem_up_irqsave(&sock->sem, &irq_state)) {
+                       // wake up all waiters
+                       struct semaphore_entry *sentry, *sentry_tmp;
+                       spin_lock(&sock->waiter_lock);
+                       LIST_FOREACH_SAFE(sentry, &sock->waiters, link, sentry_tmp) {
+                               sem_up_irqsave(&sentry->sem, &irq_state);
+                               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 */  
+#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/arch/i686/net/tcp_in.c b/kern/arch/i686/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/arch/i686/net/tcp_out.c b/kern/arch/i686/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/arch/i686/net/tcpip.h b/kern/arch/i686/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/arch/i686/net/timers.c b/kern/arch/i686/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
diff --git a/kern/arch/i686/net/udp.c b/kern/arch/i686/net/udp.c
new file mode 100644 (file)
index 0000000..6f1e7ad
--- /dev/null
@@ -0,0 +1,314 @@
+/**
+ * Contains shamelessly stolen code from BSD & lwip, both have
+ * BSD-style licenses
+ *
+ */
+#include <ros/common.h>
+#include <string.h>
+#include <kmalloc.h>
+#include <socket.h>
+#include <net.h>
+#include <sys/queue.h>
+#include <atomic.h>
+
+#include <bits/netinet.h>
+#include <net/ip.h>
+#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;
+
+struct udp_pcb* udp_new(void){
+       struct udp_pcb *pcb = kmem_cache_alloc(udp_pcb_kcache, 0);
+    // if pcb is only tracking ttl, then no need!
+       if (pcb!= NULL){
+               pcb->ttl = UDP_TTL;
+       memset(pcb, 0, sizeof(struct udp_pcb));
+       }
+       return pcb;
+}
+
+int udp_send(struct udp_pcb *pcb, struct pbuf *p)
+{
+  /* send to the packet using remote ip and port stored in the pcb */
+  // rip and rport should be in socket not pcb?
+  return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
+}
+
+typedef unsigned char u16;
+typedef unsigned long u32;
+
+u16 udp_sum_calc(u16 len_udp, u16 src_addr[],u16 dest_addr[],  int padding, u16 buff[])
+{
+u16 prot_udp=17;
+u16 padd=0;
+u16 word16;
+u32 sum;
+int i;
+       
+       // Find out if the length of data is even or odd number. If odd,
+       // add a padding byte = 0 at the end of packet
+       if ((padding&1)==1){
+               padd=1;
+               buff[len_udp]=0;
+       }
+       
+       //initialize sum to zero
+       sum=0;
+       
+       // make 16 bit words out of every two adjacent 8 bit words and 
+       // calculate the sum of all 16 vit words
+       for (i=0;i<len_udp+padd;i=i+2){
+               word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF);
+               sum = sum + (unsigned long)word16;
+       }       
+       // add the UDP pseudo header which contains the IP source and destinationn addresses
+       for (i=0;i<4;i=i+2){
+               word16 =((src_addr[i]<<8)&0xFF00)+(src_addr[i+1]&0xFF);
+               sum=sum+word16; 
+       }
+       for (i=0;i<4;i=i+2){
+               word16 =((dest_addr[i]<<8)&0xFF00)+(dest_addr[i+1]&0xFF);
+               sum=sum+word16;         
+       }
+       // the protocol number and the length of the UDP packet
+       sum = sum + prot_udp + len_udp;
+
+       // keep only the last 16 bits of the 32 bit calculated sum and add the carries
+       while (sum>>16)
+               sum = (sum & 0xFFFF)+(sum >> 16);
+               
+       // Take the one's complement of sum
+       sum = ~sum;
+
+return ((u16) sum);
+}
+
+int udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+                    struct in_addr *dst_ip, uint16_t dst_port){
+    // we now have one netif to send to, otherwise we need to route
+    // ip_route();
+    struct udp_hdr *udphdr;
+    struct pbuf *q;
+               printd("udp_sendto ip %x, port %d\n", dst_ip->s_addr, dst_port); 
+    // broadcast?
+    if (pcb->local_port == 0) {
+                               /* if the PCB not bound to a port, bind it and give local ip */
+        if (udp_bind(pcb, &pcb->local_ip, pcb->local_port)!=0)
+                                       warn("udp binding failed \n");
+    }
+    if (pbuf_header(p, UDP_HLEN)){ // we could probably save this check for block.
+        // CHECK: should allocate enough for the other headers too
+        q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
+        if (q == NULL)
+           panic("out of memory");
+        // if the original packet is not empty
+        if (p->tot_len !=0) {
+            pbuf_chain(q,p);
+            // check if it is chained properly ..
+        }
+    } else {
+                               /* Successfully padded the header*/
+                               q = p;
+    }
+
+    udphdr = (struct udp_hdr *) q->payload;
+               printd("src port %d, dst port %d \n, length %d ", pcb->local_port, ntohs(dst_port), q->tot_len);
+    udphdr->src_port = htons(pcb->local_port);
+    udphdr->dst_port = (dst_port);
+    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", LOCAL_IP_ADDR.s_addr, (dst_ip->s_addr), 
+                                         q->tot_len);
+
+    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, &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, const struct in_addr *ip, uint16_t port){ 
+    int rebind = pcb->local_port;
+    struct udp_pcb *ipcb;
+               assert(pcb);
+               /* trying to assign port */
+    if (port != 0)
+        pcb->local_port = port;
+
+    /* no lock needed since we are just traversing/reading */
+    /* Check for double bind and rebind of the same pcb */
+    for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+        /* is this UDP PCB already on active list? */
+        if (pcb == ipcb) {
+            rebind = 1; //already on the list
+        } else if (ipcb->local_port == port){
+            warn("someone else is using the port %d\n" , port); 
+            return -1;
+        }
+    }
+    /* accept data for all interfaces */
+    if (ip == NULL || (ip->s_addr == INADDR_ANY.s_addr))
+               /* true right now */
+        pcb->local_ip = INADDR_ANY;
+    /* assign a port */
+    if (port == 0) {
+        port = SOCKET_PORT_START; 
+        ipcb = udp_pcbs;
+        while ((ipcb != NULL) && (port != SOCKET_PORT_END)) {
+            if (ipcb->local_port == port) {
+                /* port is already used by another udp_pcb */
+                port++;
+                /* restart scanning all udp pcbs */
+                ipcb = udp_pcbs;
+            } else {
+                /* go on with next udp pcb */
+                ipcb = ipcb->next;
+            }
+        }
+        if (ipcb != NULL){
+            warn("No more udp ports available!");
+        }
+    }
+    if (rebind == 0) {
+        /* place the PCB on the active list if not already there */
+                               pcb->next = udp_pcbs;
+                               udp_pcbs = pcb;
+    }
+               printk("local port bound to 0x%x \n", port);
+    pcb->local_port = port;
+    return 0;
+}
+
+/* port are in host order, ips are in network order */
+/* Think: a pcb is here, if someone is waiting for a connection or the udp conn
+ * has been established */
+static struct udp_pcb* find_pcb(struct udp_pcb* list, uint16_t src_port, uint16_t dst_port,
+                                                               uint16_t srcip, uint16_t dstip) {
+       struct udp_pcb* uncon_pcb = NULL;
+       struct udp_pcb* pcb = NULL;
+       uint8_t local_match = 0;
+
+       for (pcb = list; pcb != NULL; pcb = pcb->next) {
+               local_match = 0;
+               if ((pcb->local_port == dst_port) 
+                       && (pcb->local_ip.s_addr == dstip 
+                       || ip_addr_isany(&pcb->local_ip))){
+                               local_match = 1;
+        if ((uncon_pcb == NULL) && 
+            ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
+          /* the first unconnected matching PCB */
+          uncon_pcb = pcb;
+        }
+               }
+
+               if (local_match && (pcb->remote_port == src_port) &&
+                               (ip_addr_isany(&pcb->remote_ip) ||
+                                pcb->remote_ip.s_addr == srcip))
+                       /* perfect match */
+                       return pcb;
+       }
+       return uncon_pcb;
+}
+
+#if 0 // not working yet
+// need to have pbuf queue support
+int udp_attach(struct pbuf *p, struct sock *socket) {
+       // pretend the attaching of packet is succesful
+       /*
+       recv_q->last->next = p;
+       recv_q->last=p->last
+       */ 
+}
+
+#endif 
+
+/** Process an incoming UDP datagram. 
+ * Given an incoming UDP datagram, this function finds the right PCB
+ * which links to the right socket buffer, and attaches the datagram
+ * to the right socket. 
+ * If no appropriate PCB is found, the pbuf is freed.
+ */ 
+
+/** TODO: think about combining udp_input and ip_input together */
+// TODO: figure out if we even need a PCB? or just socket buff. 
+// TODO: test out looking up pcbs.. since matching function may fail
+
+int udp_input(struct pbuf *p){
+       struct udp_hdr *udphdr;
+
+       struct udp_pcb *pcb, uncon_pcb;
+       struct ip_hdr *iphdr;
+       uint16_t src, dst;
+       bool local_match = 0;
+       iphdr = (struct ip_hdr *)p->payload;
+       /* Move the header to where the udp header is */
+       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;
+       }
+       printk("start of udp %p\n", p->payload);
+       udphdr = (struct udp_hdr *)p->payload;
+       /* convert the src port and dst port to host order */
+       src = ntohs(udphdr->src_port);
+       dst = ntohs(udphdr->dst_port);
+       pcb = find_pcb(udp_pcbs, src, dst, iphdr->src_addr, iphdr->dst_addr);
+       /* TODO: Possibly adjust the pcb to the head of the queue? */
+       /* TODO: Linux uses a set of hashtables to lookup PCBs 
+        * Look at __udp4_lib_lookup function in Linux kernel 2.6.21.1
+        */
+       /* Anything that is not directed at this pcb should have been dropped */
+       if (pcb == NULL){
+               warn("udp_input: Did not find a matching PCB for a udp packet\n");
+               pbuf_free(p);
+               return -1;
+       }
+
+       /* checksum check */
+  if (udphdr->checksum != 0) {
+    if (inet_chksum_pseudo(p, (iphdr->src_addr), (iphdr->dst_addr), 
+                                IPPROTO_UDP, p->tot_len) != 0){
+                       warn("udp_input: UPD datagram discarded due to failed chksum!");
+                       pbuf_free(p);
+                       return -1;
+    }
+       }
+  /* ignore SO_REUSE */
+       if (pcb != NULL && pcb->pcbsock != NULL){
+               /* For each in the pbuf chain, disconnect from the chain and add it to the
+                * recv_buff of the correct socket 
+                */ 
+               struct socket *sock = pcb->pcbsock;
+               attach_pbuf(p, &sock->recv_buff);
+               struct kthread *kthread;
+               int8_t irq_state = 0;
+               /* 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..
+               /* TODO: consider a helper with this and tcp.c */
+               if (!sem_up_irqsave(&sock->sem, &irq_state)) {
+                       // 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
+                               sem_up_irqsave(&sentry->sem, &irq_state);
+                               LIST_REMOVE(sentry, link);
+                               /* do not need to free since all the sentry are stack-based vars
+                                * */
+                       }
+                       spin_unlock(&sock->waiter_lock);
+               }
+               // the attaching of pbuf should have increfed pbuf ref, so free is simply a decref
+               pbuf_free(p);
+       }
+       return 0;
+}
diff --git a/kern/arch/i686/nic_common.c b/kern/arch/i686/nic_common.c
deleted file mode 100644 (file)
index 71f2403..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/** @file
- * @brief Common Nic Variables
- *
- * See Info below 
- *
- * @author Paul Pearce <pearce@eecs.berkeley.edu>
- *
- */
-
-#ifdef __SHARC__
-#pragma nosharc
-#endif
-
-#include <arch/nic_common.h>
-#include <kmalloc.h>
-#include <stdio.h>
-
-// Global send_frame function pointer
-// Means we can only have one network card per system right now...
-int (*send_frame)(const char *data, size_t len);
-int (*send_pbuf)(struct pbuf *p);
-struct pbuf*  (*recv_pbuf)(void);
-
-
-// Global variables for managing ethernet packets over a nic
-// Again, since these are global for all network cards we are 
-// limited to only one for now
-unsigned char device_mac[6];
-uint8_t eth_up = 0; 
-uint32_t num_packet_buffers = 0;
-char* packet_buffers[MAX_PACKET_BUFFERS];
-uint32_t packet_buffers_sizes[MAX_PACKET_BUFFERS];
-uint32_t packet_buffers_head = 0;
-uint32_t packet_buffers_tail = 0;
-spinlock_t packet_buffers_lock = SPINLOCK_INITIALIZER_IRQSAVE;
-
diff --git a/kern/arch/i686/nic_common.h b/kern/arch/i686/nic_common.h
deleted file mode 100644 (file)
index 148bcb4..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef ROS_INC_NIC_COMMON_H
-#define ROS_INC_NIC_COMMON_H
-
-#include <ros/common.h>
-#include <trap.h>
-#include <net.h>
-#include <pmap.h>
-#include <net/pbuf.h>
-
-// Packet sizes
-#define MTU              1500
-/* 14 for the header, 4 for something else.  It's either the CRC at the end
- * (which seems to be 0'd), or for the optional 802.1q tag. */
-#define MAX_FRAME_SIZE   (MTU + 18)
-#define MIN_FRAME_SIZE   60 // See the spec...
-
-// Maximum packet buffers we can handle at any given time
-#define MAX_PACKET_BUFFERS    32 //1024
-// Global send_frame function pointer
-// Means we can only have one network card per system right now...
-extern int (*send_frame)(const char *data, size_t len);
-extern int (*send_pbuf)(struct pbuf *p);
-extern struct pbuf* (*recv_pbuf)(void);
-
-// Global variables for managing ethernet packets over a nic
-// Again, since these are global for all network cards we are 
-// limited to only one for now
-extern unsigned char device_mac[6];
-extern uint8_t eth_up;
-extern uint32_t num_packet_buffers;
-extern char* packet_buffers[MAX_PACKET_BUFFERS];
-extern uint32_t packet_buffers_sizes[MAX_PACKET_BUFFERS];
-extern uint32_t packet_buffers_head;
-extern uint32_t packet_buffers_tail;
-extern spinlock_t packet_buffers_lock; 
-
-// Creates a new ethernet packet and puts the header on it
-char* eth_wrap(const char* data, size_t len, char src_mac[6], 
-               char dest_mac[6], uint16_t eth_type);
-
-struct ETH_Header {
-       char dest_mac[6];
-       char source_mac[6];
-       uint16_t eth_type;
-};
-
-struct eth_frame {
-       struct ETH_Header eth_head;
-       char data[MTU];
-} __attribute__((packed));
-
-static inline void print_mac (uint8_t* mac_addr) {
-       printk("%02x:%02x:%02x:%02x:%02x:%02x\n", 0xFF & mac_addr[0], 0xFF & mac_addr[1],       
-                                                                0xFF & mac_addr[2], 0xFF & mac_addr[3],        
-                                                           0xFF & mac_addr[4], 0xFF & mac_addr[5]);
-}
-
-#endif /* !ROS_INC_NIC_COMMON_H */
index 4faaf49..c95f9bf 100644 (file)
@@ -4,7 +4,7 @@
 #include <ros/common.h>
 #include <trap.h>
 #include <pmap.h>
-#include <arch/nic_common.h>
+#include <net/nic_common.h>
 
 #define rl8168_debug(...) // cprintf(__VA_ARGS__)  
 #define rl8168_interrupt_debug(...) //cprintf(__VA_ARGS__)  
diff --git a/kern/include/net/nic_common.h b/kern/include/net/nic_common.h
new file mode 100644 (file)
index 0000000..148bcb4
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef ROS_INC_NIC_COMMON_H
+#define ROS_INC_NIC_COMMON_H
+
+#include <ros/common.h>
+#include <trap.h>
+#include <net.h>
+#include <pmap.h>
+#include <net/pbuf.h>
+
+// Packet sizes
+#define MTU              1500
+/* 14 for the header, 4 for something else.  It's either the CRC at the end
+ * (which seems to be 0'd), or for the optional 802.1q tag. */
+#define MAX_FRAME_SIZE   (MTU + 18)
+#define MIN_FRAME_SIZE   60 // See the spec...
+
+// Maximum packet buffers we can handle at any given time
+#define MAX_PACKET_BUFFERS    32 //1024
+// Global send_frame function pointer
+// Means we can only have one network card per system right now...
+extern int (*send_frame)(const char *data, size_t len);
+extern int (*send_pbuf)(struct pbuf *p);
+extern struct pbuf* (*recv_pbuf)(void);
+
+// Global variables for managing ethernet packets over a nic
+// Again, since these are global for all network cards we are 
+// limited to only one for now
+extern unsigned char device_mac[6];
+extern uint8_t eth_up;
+extern uint32_t num_packet_buffers;
+extern char* packet_buffers[MAX_PACKET_BUFFERS];
+extern uint32_t packet_buffers_sizes[MAX_PACKET_BUFFERS];
+extern uint32_t packet_buffers_head;
+extern uint32_t packet_buffers_tail;
+extern spinlock_t packet_buffers_lock; 
+
+// Creates a new ethernet packet and puts the header on it
+char* eth_wrap(const char* data, size_t len, char src_mac[6], 
+               char dest_mac[6], uint16_t eth_type);
+
+struct ETH_Header {
+       char dest_mac[6];
+       char source_mac[6];
+       uint16_t eth_type;
+};
+
+struct eth_frame {
+       struct ETH_Header eth_head;
+       char data[MTU];
+} __attribute__((packed));
+
+static inline void print_mac (uint8_t* mac_addr) {
+       printk("%02x:%02x:%02x:%02x:%02x:%02x\n", 0xFF & mac_addr[0], 0xFF & mac_addr[1],       
+                                                                0xFF & mac_addr[2], 0xFF & mac_addr[3],        
+                                                           0xFF & mac_addr[4], 0xFF & mac_addr[5]);
+}
+
+#endif /* !ROS_INC_NIC_COMMON_H */
index 1adf3f5..a74418c 100644 (file)
@@ -68,6 +68,7 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/net/tcp_out.c \
                  $(KERN_SRC_DIR)/net/tcp_in.c \
                  $(KERN_SRC_DIR)/net/timers.c \
+                 $(KERN_SRC_DIR)/net/nic_common.c \
                  $(KERN_SRC_DIR)/arsc.c
 
 # Only build files if they exist.
index f5d20db..2d59ecb 100644 (file)
@@ -11,7 +11,7 @@
 #include <devfs.h>
 #include <page_alloc.h>
 #include <pmap.h>
-#include <arch/nic_common.h>
+#include <net/nic_common.h>
 #include <process.h>
 #include <smp.h>
 
index c8f5e45..b2ade7d 100644 (file)
@@ -7,7 +7,7 @@
 #include <net/udp.h>
 #include <net/tcp_impl.h>
 #include <ros/errno.h>
-#include <arch/nic_common.h>
+#include <net/nic_common.h>
 
 /* statically configured next gateway */
 const uint8_t GTWAY[6] = {0xda, 0x76, 0xe7, 0x4c, 0xca, 0x7e};
diff --git a/kern/src/net/nic_common.c b/kern/src/net/nic_common.c
new file mode 100644 (file)
index 0000000..e854c9e
--- /dev/null
@@ -0,0 +1,36 @@
+/** @file
+ * @brief Common Nic Variables
+ *
+ * See Info below 
+ *
+ * @author Paul Pearce <pearce@eecs.berkeley.edu>
+ *
+ */
+
+#ifdef __SHARC__
+#pragma nosharc
+#endif
+
+#include <net/nic_common.h>
+#include <kmalloc.h>
+#include <stdio.h>
+
+// Global send_frame function pointer
+// Means we can only have one network card per system right now...
+int (*send_frame)(const char *data, size_t len);
+int (*send_pbuf)(struct pbuf *p);
+struct pbuf*  (*recv_pbuf)(void);
+
+
+// Global variables for managing ethernet packets over a nic
+// Again, since these are global for all network cards we are 
+// limited to only one for now
+unsigned char device_mac[6];
+uint8_t eth_up = 0; 
+uint32_t num_packet_buffers = 0;
+char* packet_buffers[MAX_PACKET_BUFFERS];
+uint32_t packet_buffers_sizes[MAX_PACKET_BUFFERS];
+uint32_t packet_buffers_head = 0;
+uint32_t packet_buffers_tail = 0;
+spinlock_t packet_buffers_lock = SPINLOCK_INITIALIZER_IRQSAVE;
+
index 2fd2fe7..b327899 100644 (file)
@@ -9,7 +9,7 @@
 #include <sys/queue.h>
 #include <net.h>
 #include <debug.h>
-#include <arch/nic_common.h>
+#include <net/nic_common.h>
 
 
 /* TODO: before running
index c2bd706..c35b026 100644 (file)
@@ -38,7 +38,7 @@
 
 
 #ifdef __CONFIG_NETWORKING__
-#include <arch/nic_common.h>
+#include <net/nic_common.h>
 extern int (*send_frame)(const char *CT(len) data, size_t len);
 extern unsigned char device_mac[6];
 #endif