Merge origin/netpush (networking code) (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 22 Mar 2013 00:04:19 +0000 (17:04 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 22 Mar 2013 04:12:23 +0000 (21:12 -0700)
Had some compilation issues (missing mbuf.h, etc), but compiles now.

Other than basic conflicts, this also was using the old semaphore style,
as well as having a few other issues in semaphore usage, so please check
that out.

Rebuild your cross compiler.  There are a bunch of new files in the -ros
folder, so either copy them over, or just make clean.  Here are the new
glibc files:

glibc-2.14.1-ros/sysdeps/ros/accept.c
glibc-2.14.1-ros/sysdeps/ros/bind.c
glibc-2.14.1-ros/sysdeps/ros/connect.c
glibc-2.14.1-ros/sysdeps/ros/listen.c
glibc-2.14.1-ros/sysdeps/ros/recv.c
glibc-2.14.1-ros/sysdeps/ros/recvfrom.c
glibc-2.14.1-ros/sysdeps/ros/select.c
glibc-2.14.1-ros/sysdeps/ros/send.c
glibc-2.14.1-ros/sysdeps/ros/sendto.c
glibc-2.14.1-ros/sysdeps/ros/socket.c

Conflicts:
Makeconfig
kern/include/kthread.h
kern/include/time.h
kern/include/vfs.h
kern/src/Makefrag
kern/src/kthread.c
kern/src/syscall.c

58 files changed:
Makeconfig
kern/arch/i686/e1000.c
kern/arch/i686/e1000.h
kern/arch/i686/e1000_hw.h
kern/arch/i686/nic_common.c
kern/arch/i686/nic_common.h
kern/arch/i686/rl8168.c
kern/arch/i686/rl8168.h
kern/arch/i686/types.h
kern/arch/i686/x86.h
kern/include/alarm.h
kern/include/assert.h
kern/include/bits/netinet.h
kern/include/debug.h [new file with mode: 0644]
kern/include/endian.h
kern/include/kthread.h
kern/include/net.h
kern/include/net/dev.h [new file with mode: 0644]
kern/include/net/ip.h [new file with mode: 0644]
kern/include/net/pbuf.h [new file with mode: 0644]
kern/include/net/tcp.h [new file with mode: 0644]
kern/include/net/tcp_impl.h [new file with mode: 0644]
kern/include/net/timers.h [new file with mode: 0644]
kern/include/net/udp.h [new file with mode: 0644]
kern/include/ros/bits/syscall.h
kern/include/ros/common.h
kern/include/ros/syscall.h
kern/include/socket.h [new file with mode: 0644]
kern/include/sys/uio.h [new file with mode: 0644]
kern/include/time.h
kern/include/vfs.h
kern/src/Makefrag
kern/src/eth_audio.c
kern/src/init.c
kern/src/net.c
kern/src/net/ip.c [new file with mode: 0644]
kern/src/net/pbuf.c [new file with mode: 0644]
kern/src/net/tcp.c [new file with mode: 0644]
kern/src/net/tcp_in.c [new file with mode: 0644]
kern/src/net/tcp_out.c [new file with mode: 0644]
kern/src/net/tcpip.h [new file with mode: 0644]
kern/src/net/timers.c [new file with mode: 0644]
kern/src/net/udp.c [new file with mode: 0644]
kern/src/socket.c [new file with mode: 0644]
kern/src/syscall.c
kern/src/vfs.c
tests/tcp_test.c [new file with mode: 0644]
tests/udp_test.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/accept.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/bind.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/connect.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/listen.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/recv.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/recvfrom.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/select.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/send.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/sendto.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/socket.c [new file with mode: 0644]

index 740d087..1dd07c4 100644 (file)
@@ -35,6 +35,7 @@ CONFIG_ETH_AUDIO:=                 -D__CONFIG_ETH_AUDIO__
 CONFIG_KB_CORE0_ONLY:=             -D__CONFIG_KB_CORE0_ONLY__
 CONFIG_KTHREAD_POISON:=            -D__CONFIG_KTHREAD_POISON__
 CONFIG_PRINTK_NO_BACKSPACE:=       -D__CONFIG_PRINTK_NO_BACKSPACE__
+CONFIG_SOCKET:=                    -D__CONFIG_SOCKET__
 
 # Userspace configuration parameters
 # By default, each of these options will be turned off
index 62802b4..526ef7a 100644 (file)
@@ -6,6 +6,7 @@
  * See Info below 
  *
  * @author Paul Pearce <pearce@eecs.berkeley.edu>
+ * @author David Zhu <yuzhu@cs.berkeley.edu>
  *
  */
 
@@ -18,6 +19,7 @@
 #include <arch/smp.h>
 #include <arch/apic.h>
 #include <arch/pci.h>
+#include <arch/nic_common.h>
 #include <arch/e1000.h>
 
 #include <ros/memlayout.h>
@@ -31,8 +33,8 @@
 #include <pmap.h>
 #include <frontend.h>
 #include <arch/frontend.h>
-
 #include <eth_audio.h>
+#include <net/ip.h>
 
 #define NUM_TX_DESCRIPTORS E1000_NUM_TX_DESCRIPTORS
 #define NUM_RX_DESCRIPTORS E1000_NUM_RX_DESCRIPTORS
@@ -48,8 +50,10 @@ uint32_t e1000_addr_size = 0;
 unsigned char device_mac[6];
 
 // Vars relating to the receive descriptor ring
+// pointer to receive descriptors
 struct e1000_rx_desc *rx_des_kva;
 unsigned long rx_des_pa;
+// current rx index
 uint32_t e1000_rx_index = 0;
 
 
@@ -73,9 +77,13 @@ extern uint32_t packet_buffers_head;
 extern uint32_t packet_buffers_tail;
 spinlock_t packet_buffers_lock;
 
-// Allow us to register our send_frame as the global send_frameuint16_t device_id;
+// Allow us to register our send_frame as the global send_frame
+uint16_t device_id;
 extern int (*send_frame)(const char *CT(len) data, size_t len);
 
+// compat defines that make transitioning easier
+#define E1000_RX_DESC(x) rx_des_kva[x]
+
 void e1000_dump_rx() {
 
        for (int i = 0; i < 10; i++) {
@@ -93,7 +101,7 @@ void e1000_dump_stats() {
        while (offset <= 0x040FC) {
                if ((offset % 16) == 0)
                        printk("\n");
-                printk("%x:%d ", offset,e1000_rr32(offset));
+               printk("%x:%d ", offset,e1000_rr32(offset));
 
                offset = offset + 4;
        }
@@ -118,6 +126,8 @@ void e1000_init() {
 
        // "Register" our send_frame with the global system
        send_frame = &e1000_send_frame;
+       send_pbuf = &e1000_send_pbuf;
+       recv_pbuf = &e1000_recv_pbuf;
 
        // sudo /sbin/ifconfig eth0 up
        eth_up = 1;
@@ -191,9 +201,13 @@ int e1000_scan_pci() {
                e1000_irq = pcidev->irqline;
                e1000_debug("-->IRQ: %u\n", e1000_irq);
                /* Loop over the BARs */
+               /* TODO: pci layer should scan these things and put them in a pci_dev struct */
+               /* SelectBars based on the IORESOURCE_MEM */
                for (int k = 0; k <= 5; k++) {
                        int reg = 4 + k;
-               result = pcidev_read32(pcidev, reg << 2);       // SHAME!
+           // resource len?
+                                       result = pcidev_read32(pcidev, reg << 2);       // SHAME! why? 
+
                        if (result == 0) // (0 denotes no valid data)
                                continue;
                        // Read the bottom bit of the BAR. 
@@ -254,7 +268,6 @@ void e1000_wr32(uint32_t offset, uint32_t val) {
        }
 }
 
-
 /* E1000 Read From EEPROM
  * Read a 16 bit value from the EEPROM at the given offset 
  * in the EEPROM.
@@ -292,7 +305,7 @@ uint16_t e1000_read_eeprom(uint32_t offset) {
 void e1000_setup_mac() {
 
        uint16_t eeprom_data = 0;
-        uint32_t mmio_data = 0;
+       uint32_t mmio_data = 0;
 
        /* TODO: WARNING - EXTREMELY GHETTO */
        e1000_debug("-->Setting up MAC addr\n");
@@ -387,8 +400,8 @@ void e1000_setup_descriptors() {
        // Must be 16 byte aligned
 
        // How many pages do we need?
-        uint32_t num_rx_pages = ROUNDUP(NUM_RX_DESCRIPTORS * sizeof(struct e1000_rx_desc), PGSIZE) / PGSIZE;
-        uint32_t num_tx_pages = ROUNDUP(NUM_TX_DESCRIPTORS * sizeof(struct e1000_tx_desc), PGSIZE) / PGSIZE;
+  uint32_t num_rx_pages = ROUNDUP(NUM_RX_DESCRIPTORS * sizeof(struct e1000_rx_desc), PGSIZE) / PGSIZE;
+  uint32_t num_tx_pages = ROUNDUP(NUM_TX_DESCRIPTORS * sizeof(struct e1000_tx_desc), PGSIZE) / PGSIZE;
        
        // Get the pages
        rx_des_kva = get_cont_pages(LOG2_UP(num_rx_pages), 0);
@@ -472,8 +485,9 @@ void e1000_configure() {
 
        e1000_debug("-->Configuring Device.\n");
        
-       // Clear interrupts
+       // Clear Interrupts
        e1000_wr32(E1000_IMC, E1000_IMC_ALL);
+       E1000_WRITE_FLUSH();
 
        // Disable receiver and transmitter
        e1000_wr32(E1000_RCTL, 0x00);
@@ -602,12 +616,10 @@ void e1000_reset() {
        return;
 }
 
-void enable_e1000_irq(struct trapframe *tf, uint32_t src_id, 
-                                void* a0, void* a1, void* a2)
-{
-       pic_unmask_irq(e1000_irq);
-       unmask_lapic_lvt(LAPIC_LVT_LINT0);
-       enable_irq();
+void e1000_irq_enable() {
+       printk("e1000 enabled\n");
+       e1000_wr32(E1000_IMS, IMS_ENABLE_MASK);
+       E1000_WRITE_FLUSH();
 }
 
 // Configure and enable interrupts
@@ -622,23 +634,28 @@ void e1000_setup_interrupts() {
        
        // Clear interrupts
        e1000_wr32(E1000_IMS, 0xFFFFFFFF);
-       e1000_wr32(E1000_IMC, 0xFFFFFFFF);
+       e1000_wr32(E1000_IMC, E1000_IMC_ALL);
        
        // Set interrupts
-       // TODO: Make this only enable stuff we want
-       e1000_wr32(E1000_IMS, 0xFFFFFFFF); 
+       e1000_irq_enable();
 
        // Kernel based interrupt stuff
        register_interrupt_handler(interrupt_handlers, KERNEL_IRQ_OFFSET + e1000_irq, e1000_interrupt_handler, 0);
 
        // Enable irqs for the e1000
+       // TODO: figure out where the interrupts are actually going..
 #ifdef __CONFIG_ENABLE_MPTABLES__
        /* TODO: this should be for any IOAPIC EOI, not just MPTABLES */
        ioapic_route_irq(e1000_irq, E1000_IRQ_CPU);     
+       printk("ioapic rout\n");
+
 #else 
        // This will route the interrupts automatically to CORE 0
        // Call send_kernel_message if you want to route them somewhere else
-       enable_e1000_irq(NULL,0,0,0,0);
+       pic_unmask_irq(e1000_irq);
+       unmask_lapic_lvt(LAPIC_LVT_LINT0);
+       enable_irq();
+       printk("picroute\n");
 #endif
 
        return;
@@ -646,58 +663,81 @@ void e1000_setup_interrupts() {
 
 // Code that is executed when an interrupt comes in on IRQ e1000_irq
 void e1000_interrupt_handler(trapframe_t *tf, void* data) {
-
-//     printk("About to spam to mac addr: 00:14:4F:D1:EC:6C\n");
-//     while(1) {
-//             appserver_packet_t p;
-//             p.header.dst_mac[0] = 0x00;
-//             p.header.dst_mac[1] = 0x14;
-//             p.header.dst_mac[2] = 0x4f;
-//             p.header.dst_mac[3] = 0xd1;
-//             p.header.dst_mac[4] = 0xec;
-//             p.header.dst_mac[5] = 0x6c;
-//             p.header.src_mac[0] = 0x00;
-//             p.header.src_mac[1] = 0x23;
-//             p.header.src_mac[2] = 0x8b;
-//             p.header.src_mac[3] = 0x42;
-//             p.header.src_mac[4] = 0x80;
-//             p.header.src_mac[5] = 0xb8;
-//             p.header.ethertype = 0x8888;
-//             send_frame((char*)&p,0);
-//     }
-
        e1000_interrupt_debug("\nNic interrupt on core %u!\n", lapic_get_id());
-                               
+
        // Read the offending interrupt(s)
        // Note: Reading clears the interrupts
-       uint32_t interrupt_status = e1000_rr32(E1000_ICR);
-
-       // Loop to deal with TOCTOU 
-       while (interrupt_status != 0x0000) {
+       uint32_t icr = e1000_rr32(E1000_ICR);
 
-               //printk("Interrupt status: %x\n", interrupt_status);
+       
+       /* Interrupt did not come from our card.., handle one interrupt per isr */
+       if (!icr) return; 
+       /* disable interrupts, this may not be necessary as AUTOMASK of interrupts
+        * is enabled on some cards
+        * but we do it anyways to be safe..
+        */
+       e1000_wr32(E1000_IMC, ~0);
+       E1000_WRITE_FLUSH();
+
+       //printk("Interrupt status: %x\n", icr);
+
+       if ((icr & E1000_ICR_INT_ASSERTED) && (icr & E1000_ICR_RXT0)){
+               e1000_interrupt_debug("---->Packet Received\n");
+#ifdef __CONFIG_SOCKET__
+//#if 0
+               e1000_clean_rx_irq();
+               // e1000_recv_pbuf(); // really it is now performing the function of rx_clean
+#else
+               e1000_handle_rx_packet();
+#endif
+       }       
+       e1000_irq_enable();
+}
 
-               // Check to see if the interrupt was packet based.
-               if ((interrupt_status & E1000_ICR_INT_ASSERTED) && (interrupt_status & E1000_ICR_RXT0)) {
-                       e1000_interrupt_debug("---->Packet Received\n");
-                       e1000_handle_rx_packet();
-               }       
-               // Clear interrupts     
-               interrupt_status = e1000_rr32(E1000_ICR);
+void process_pbuf(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2){
+       if (srcid != core_id())
+               warn("pbuf came from a different core\n");
+       /* assume it is an ip packet */
+       struct pbuf* pb = (struct pbuf*) a0;
+       //printk("processing pbuf \n");
+       /*TODO: check checksum and drop */
+       /*check packet type*/
+       struct ethernet_hdr *ethhdr = (struct ethernet_hdr *) pb->payload;
+       //printk("start of eth %p \n", pb->payload);
+       //print_pbuf(pb);
+       if (memcmp(ethhdr->dst_mac, device_mac, 6)){
+               e1000_debug("mac address do not match, pbuf freed \n");
+               pbuf_free(pb);
        }
-       
-       // In the event that we got really unlucky and more data arrived after we set 
-       //  set the bit last, try one more check
-       e1000_handle_rx_packet();
+       switch(htons(ethhdr->eth_type)){
+               case ETHTYPE_IP:
+                       if (!pbuf_header(pb, -(ETH_HDR_SZ)))
+                               ip_input(pb);
+                       else
+                               warn("moving ethernet header in pbuf failed..\n");
+                       break;
+               case ETHTYPE_ARP:
+                       break;
+               default:
+                       //warn("packet type unknown");
+                       pbuf_free(pb);
+       }
+}
 
+static void schedule_pb(struct pbuf* pb) {
+       /* routine kernel message is kind of heavy weight, because it records src/dst etc */
+       /* TODO: consider a core-local chain of pbufs */
+       // using core 3 for network stuff..XXX
+       send_kernel_message(3, (amr_t) process_pbuf, (long)pb, 0, 0, KMSG_ROUTINE);
+       // send_kernel_message(core_id(), (amr_t) process_pbuf, (long)pb, 0, 0, KMSG_ROUTINE);
        return;
 }
-
 // Check to see if a packet arrived, and process the packet.
 void e1000_handle_rx_packet() {
        
        uint16_t packet_size;
        uint32_t status;
+       // find rx descriptor head
        uint32_t head = e1000_rr32(E1000_RDH);
 
        //printk("Current head is: %x\n", e1000_rr32(E1000_RDH));
@@ -781,6 +821,7 @@ void e1000_handle_rx_packet() {
 
        } while ((status & E1000_RXD_STAT_EOP) == 0); // Check to see if we are at the final fragment
 
+
 #ifdef __CONFIG_APPSERVER__
        // Treat as a syscall frontend response packet if eth_type says so
        // Will eventually go away, so not too worried about elegance here...
@@ -841,6 +882,7 @@ void e1000_handle_rx_packet() {
        
        // Bump the tail pointer. It should be 1 behind where we start reading from.
        e1000_wr32(E1000_RDT, (e1000_rx_index -1) % NUM_RX_DESCRIPTORS);
+       dumppacket((unsigned char *)rx_buffer, frame_size);
                                
        // Chew on the frame data. Command bits should be the same for all frags.
        //e1000_process_frame(rx_buffer, frame_size, current_command);
@@ -848,6 +890,375 @@ void e1000_handle_rx_packet() {
        return;
 }
 
+static void e1000_clean_rx_irq() {
+       // e1000_rx_index is the last one that we have processed
+       uint32_t i= e1000_rx_index;
+       // E1000 RDH is the last descriptor written by the hardware
+       uint32_t head = e1000_rr32(E1000_RDH);
+       uint32_t length = 0;
+       struct e1000_rx_desc *rx_desc =  &(E1000_RX_DESC(i));
+
+       // what happens when i go around the ring? 
+       while (rx_desc->status & E1000_RXD_STAT_DD){
+               struct pbuf* pb;
+               uint8_t status;
+               rx_desc = &rx_des_kva[i];
+               // buffer_info = &rx_des_kva[i];
+               status = rx_desc->status;
+               pb = pbuf_alloc(PBUF_RAW, 0 , PBUF_MTU);
+#if ETH_PAD_SIZE
+               pbuf_header(pb, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+               // fragment size
+               length = le16_to_cpu(rx_desc->length);
+
+               length -= 4;
+
+               memcpy(pb->payload, KADDR(E1000_RX_DESC(i).buffer_addr), length);
+               // skb_put(skb, length);
+               pb->len = length;
+               pb->tot_len = length;
+               schedule_pb(pb);
+               // do all the error handling 
+next_desc:
+               // this replaces e1000_set_rx_descriptor
+               rx_desc->status = 0;
+               if (++i == NUM_RX_DESCRIPTORS) i = 0;
+               rx_desc = &(E1000_RX_DESC(i)); 
+       }
+       //setting e1000_RDH?
+               printk ("cleaned index %d to %d \n", e1000_rx_index, i-1);
+               e1000_rx_index = i;
+}
+
+struct pbuf* e1000_recv_pbuf(void) {
+       uint16_t packet_size;
+       uint32_t status;
+       // recv head
+       uint32_t head = e1000_rr32(E1000_RDH);
+
+       printk("Current head is: %x\n", e1000_rr32(E1000_RDH));
+       printk("Current tail is: %x\n", e1000_rr32(E1000_RDT));
+       // e1000_rx_index = cleaned
+       // If the HEAD is where we last processed, no new packets.
+       if (head == e1000_rx_index) {
+               e1000_frame_debug("-->Nothing to process. Returning.");
+               return NULL;
+       }
+       // Set our current descriptor to where we last left off.
+       uint32_t rx_des_loop_cur = e1000_rx_index;
+       uint32_t frame_size = 0;
+       uint32_t fragment_size = 0;
+       uint32_t num_frags = 0;
+
+       uint32_t top_fragment = rx_des_loop_cur; 
+       struct pbuf* pb = pbuf_alloc(PBUF_RAW, 0, PBUF_MTU);
+       if (!pb){
+               warn("pbuf allocation failed, packet dropped\n");
+               return NULL;
+       }
+
+       uint32_t copied = 0;
+#if ETH_PAD_SIZE
+       pbuf_header(pb, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+       // pblen is way too big? it is not an indication of the size but the allocation
+       printk("pb loc %p , pb len %d \n", pb, pb->len);
+       void* rx_buffer = pb->payload;
+
+       /* The following loop generates 1 and only 1 pbuf out of 1(likely) 
+        * or more fragments. 
+        * TODO: convert this loop to clean rx irq style which is capable of 
+        * handling multiple packet / pbuf receptions
+        */
+
+       do {
+               // Get the descriptor status
+               status = rx_des_kva[rx_des_loop_cur].status;
+
+               // If the status is 0x00, it means we are somehow trying to process 
+               // a packet that hasnt been written by the NIC yet.
+               if (status & E1000_RXD_STAT_DD) {
+                       warn("ERROR: E1000: Packet owned by hardware has 0 status value\n");
+                       /* It's possible we are processing a packet that is a fragment
+                        * before the entire packet arrives.  The code currently assumes
+                        * that all of the packets fragments are there, so it assumes the
+                        * next one is ready.  We'll spin until it shows up...  This could
+                        * deadlock, and sucks in general, but will help us diagnose the
+                        * driver's issues.  TODO: determine root cause and fix this shit.*/
+                       while(rx_des_kva[rx_des_loop_cur].status == 0x0)
+                               cpu_relax();
+                       status = rx_des_kva[rx_des_loop_cur].status;
+               }
+               printk ("got out of the dead loop \n");
+       
+               // See how big this fragment is.
+               fragment_size = rx_des_kva[rx_des_loop_cur].length;
+               printk("fragment size %d\n",fragment_size);
+               
+               // If we've looped through the entire ring and not found a terminating packet, bad nic state.
+               // Panic or clear all descriptors? This is a nic hardware error. 
+               if (num_frags && (rx_des_loop_cur == head)) {
+                       e1000_frame_debug("-->ERR: No ending segment found in RX buffer.\n");
+                       panic("RX Descriptor Ring out of sync.");
+               }
+               // Denote that we have at least 1 fragment.
+               num_frags++;
+               if (num_frags > 1) warn ("we have fragments in the network \n");
+               // Make sure ownership is correct. Packet owned by the NIC (ready for kernel reading)
+               // is denoted by a 1. Packet owned by the kernel (ready for NIC use) is denoted by 0.
+               if ((status & E1000_RXD_STAT_DD) == 0x0) {
+                       e1000_frame_debug("-->ERR: Current RX descriptor not owned by software. Panic!");
+                       warn("RX Descriptor Ring OWN out of sync");
+               }
+               
+               // Deal with packets too large
+               if ((frame_size + fragment_size) > MAX_FRAME_SIZE) {
+                       e1000_frame_debug("-->ERR: Nic sent %u byte packet. Max is %u\n", frame_size, MAX_FRAME_SIZE);
+                       warn("NIC Sent packets larger than configured.");
+               }
+               
+               memcpy(rx_buffer, KADDR(rx_des_kva[rx_des_loop_cur].buffer_addr), fragment_size);
+               copied += fragment_size;
+               printk("fragment size %d \n", fragment_size);
+               rx_buffer += fragment_size;
+               
+
+               // Copy into pbuf allocated for this     
+               // TODO: reuse the pbuf later
+               // TODO:real driver uses a pbuf allocated (MTU sized) per descriptor and recycles that
+               // TODO:real driver also does not handle fragments.. simply drops them
+
+               // Reset the descriptor. Reuse current buffer (False means don't realloc).
+               e1000_set_rx_descriptor(rx_des_loop_cur, FALSE);
+               
+               // Note: We mask out fragment sizes at 0x3FFFF. There can be at most 2048 of them.
+               // This can not overflow the uint32_t we allocated for frame size, so
+               // we dont need to worry about mallocing too little then overflowing when we read.
+               frame_size = frame_size + fragment_size;
+               
+               /*Advance to the next descriptor*/
+               rx_des_loop_cur = (rx_des_loop_cur + 1) % NUM_RX_DESCRIPTORS;
+
+       } while ((status & E1000_RXD_STAT_EOP) == 0); // Check to see if we are at the final fragment
+
+       // rx_des_loop_cur has gone past the top_fragment
+       // printk("Copied %d bytes of data \n", copied);
+       // ethernet crc performed in hardware
+       copied -= 4;
+
+       pb->len = copied;
+       pb->tot_len = copied;
+       schedule_pb(pb);
+       return pb;
+}
+
+#if 0
+
+int e1000_clean_rx(){
+       struct net_device *netdev = adapter->netdev;
+       struct pci_dev *pdev = adapter->pdev;
+       struct e1000_rx_desc *rx_desc, *next_rxd;
+       struct e1000_buffer *buffer_info, *next_buffer;
+       unsigned long flags;
+       uint32_t length;
+       uint8_t last_byte;
+       unsigned int i;
+       int cleaned_count = 0;
+       boolean_t cleaned = FALSE;
+       unsigned int total_rx_bytes=0, total_rx_packets=0;
+
+       i = rx_ring->next_to_clean;
+       // rx_desc is the same as rx_des_kva[rx_des_loop_cur]
+       rx_desc = E1000_RX_DESC(*rx_ring, i);
+       buffer_info = &rx_ring->buffer_info[i];
+
+       while (rx_desc->status & E1000_RXD_STAT_DD) {
+               struct sk_buff *skb;
+               u8 status;
+
+#ifdef CONFIG_E1000_NAPI
+               if (*work_done >= work_to_do)
+                       break;
+               (*work_done)++;
+#endif
+               status = rx_desc->status;
+               skb = buffer_info->skb;
+               buffer_info->skb = NULL;
+
+               prefetch(skb->data - NET_IP_ALIGN);
+
+               if (++i == rx_ring->count) i = 0;
+               next_rxd = E1000_RX_DESC(*rx_ring, i);
+               prefetch(next_rxd);
+
+               next_buffer = &rx_ring->buffer_info[i];
+
+               cleaned = TRUE;
+               cleaned_count++;
+               pci_unmap_single(pdev,
+                                buffer_info->dma,
+                                buffer_info->length,
+                                PCI_DMA_FROMDEVICE);
+
+               length = le16_to_cpu(rx_desc->length);
+
+               if (unlikely(!(status & E1000_RXD_STAT_EOP))) {
+                       /* All receives must fit into a single buffer */
+                       E1000_DBG("%s: Receive packet consumed multiple"
+                                 " buffers\n", netdev->name);
+                       /* recycle */
+                       buffer_info->skb = skb;
+                       goto next_desc;
+               }
+
+               if (unlikely(rx_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK)) {
+                       last_byte = *(skb->data + length - 1);
+                       if (TBI_ACCEPT(&adapter->hw, status,
+                                     rx_desc->errors, length, last_byte)) {
+                               spin_lock_irqsave(&adapter->stats_lock, flags);
+                               e1000_tbi_adjust_stats(&adapter->hw,
+                                                      &adapter->stats,
+                                                      length, skb->data);
+                               spin_unlock_irqrestore(&adapter->stats_lock,
+                                                      flags);
+                               length--;
+                       } else {
+                               /* recycle */
+                               buffer_info->skb = skb;
+                               goto next_desc;
+                       }
+               }
+
+               /* adjust length to remove Ethernet CRC, this must be
+                * done after the TBI_ACCEPT workaround above */
+               length -= 4;
+
+               /* probably a little skewed due to removing CRC */
+               total_rx_bytes += length;
+               total_rx_packets++;
+
+               /* code added for copybreak, this should improve
+                * performance for small packets with large amounts
+                * of reassembly being done in the stack */
+               if (length < copybreak) {
+                       struct sk_buff *new_skb =
+                           netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
+                       if (new_skb) {
+                               skb_reserve(new_skb, NET_IP_ALIGN);
+                               memcpy(new_skb->data - NET_IP_ALIGN,
+                                      skb->data - NET_IP_ALIGN,
+                                      length + NET_IP_ALIGN);
+                               /* save the skb in buffer_info as good */
+                               buffer_info->skb = skb;
+                               skb = new_skb;
+                       }
+                       /* else just continue with the old one */
+               }
+               /* end copybreak code */
+               skb_put(skb, length);
+
+               /* Receive Checksum Offload */
+               e1000_rx_checksum(adapter,
+                                 (uint32_t)(status) |
+                                 ((uint32_t)(rx_desc->errors) << 24),
+                                 le16_to_cpu(rx_desc->csum), skb);
+
+               skb->protocol = eth_type_trans(skb, netdev);
+#ifdef CONFIG_E1000_NAPI
+               if (unlikely(adapter->vlgrp &&
+                           (status & E1000_RXD_STAT_VP))) {
+                       vlan_hwaccel_receive_skb(skb, adapter->vlgrp,
+                                                le16_to_cpu(rx_desc->special) &
+                                                E1000_RXD_SPC_VLAN_MASK);
+               } else {
+                       netif_receive_skb(skb);
+               }
+#else /* CONFIG_E1000_NAPI */
+               if (unlikely(adapter->vlgrp &&
+                           (status & E1000_RXD_STAT_VP))) {
+                       vlan_hwaccel_rx(skb, adapter->vlgrp,
+                                       le16_to_cpu(rx_desc->special) &
+                                       E1000_RXD_SPC_VLAN_MASK);
+               } else {
+                       netif_rx(skb);
+               }
+#endif /* CONFIG_E1000_NAPI */
+               netdev->last_rx = jiffies;
+
+next_desc:
+               rx_desc->status = 0;
+
+               /* return some buffers to hardware, one at a time is too slow */
+               if (unlikely(cleaned_count >= E1000_RX_BUFFER_WRITE)) {
+                       adapter->alloc_rx_buf(adapter, rx_ring, cleaned_count);
+                       cleaned_count = 0;
+               }
+
+               /* use prefetched values */
+               rx_desc = next_rxd;
+               buffer_info = next_buffer;
+       }
+       rx_ring->next_to_clean = i;
+
+       cleaned_count = E1000_DESC_UNUSED(rx_ring);
+       if (cleaned_count)
+               adapter->alloc_rx_buf(adapter, rx_ring, cleaned_count);
+
+       adapter->total_rx_packets += total_rx_packets;
+       adapter->total_rx_bytes += total_rx_bytes;
+       return cleaned;
+}
+}
+
+#endif
+
+int e1000_send_pbuf(struct pbuf *p) {
+       int len = p->tot_len;
+       // print_pbuf(p);
+       if (p == NULL) 
+               return -1;
+       if (len == 0)
+               return 0;
+       
+       // Find where we want to write
+       uint32_t head = e1000_rr32(E1000_TDH);
+
+       
+       // Fail if we are out of space
+       if (((e1000_tx_index + 1) % NUM_TX_DESCRIPTORS) == head) {
+               e1000_frame_debug("-->TX Ring Buffer Full!\n");
+               return -1;
+       }
+       
+       // Fail if we are too large
+       if (len > MAX_FRAME_SIZE) {
+               e1000_frame_debug("-->Frame Too Large!\n");
+               return -1;
+       }
+       
+       // Move the data
+       int cplen = pbuf_copy_out(p, KADDR(tx_des_kva[e1000_tx_index].buffer_addr), len, 0);
+
+       for(int i = 0; i< cplen; i++){
+               printd("%x", ((uint8_t*)KADDR(tx_des_kva[e1000_tx_index].buffer_addr))[i]);
+       }
+       // Set the length
+       tx_des_kva[e1000_tx_index].lower.flags.length = len;
+       
+       // Magic that means send 1 fragment and report.
+       tx_des_kva[e1000_tx_index].lower.flags.cmd = 0x0B;
+
+       // Track our location
+       e1000_tx_index = (e1000_tx_index + 1) % NUM_TX_DESCRIPTORS;
+       
+       // Bump the tail.
+       e1000_wr32(E1000_TDT, e1000_tx_index);
+
+       e1000_frame_debug("-->Sent packet.\n");
+       
+       return len;
+}
 // Main routine to send a frame. Just sends it and goes.
 // Card supports sending across multiple fragments, we don't.
 // Would we want to write a function that takes a larger packet and generates fragments?
index d3cb25d..6771651 100644 (file)
@@ -5,15 +5,25 @@
 #include <trap.h>
 #include <pmap.h>
 #include <arch/nic_common.h>
-
-#define e1000_debug(...)               //printk(__VA_ARGS__)  
-#define e1000_interrupt_debug(...)     //printk(__VA_ARGS__)  
-#define e1000_frame_debug(...)         //printk(__VA_ARGS__)  
+#include <net/pbuf.h>
+#if 1
+#define e1000_debug(...)               printk(__VA_ARGS__)  
+#define e1000_interrupt_debug(...)     printk(__VA_ARGS__)  
+#define e1000_frame_debug(...)         printk(__VA_ARGS__)  
+#else
+#define e1000_debug(...)
+#define e1000_interrupt_debug(...)
+#define e1000_frame_debug(...)
+#endif
 
 #define E1000_IRQ_CPU          0
 
 #define INTEL_VENDOR_ID                0x8086
-#define INTEL_DEV_ID0          0x100E  // Real E1000
+#define INTEL_DEV_ID0          0x100E  // Real E1000 QEMU emulates this. Linux device driver e1000
+
+/* 
+ * These two are technically supported by the igb driver in linux, since they support more advanced feature set 
+ */
 #define INTEL_DEV_ID1          0x10c9  // 82576
 #define INTEL_DEV_ID2          0x150a  // 82576 NS
 
 // This should be in line with the setting of BSIZE in RCTL
 #define E1000_RX_MAX_BUFFER_SIZE 2048
 #define E1000_TX_MAX_BUFFER_SIZE 2048
+#if 0
+struct e1000_tx_ring {
+       /* pointer to the descriptor ring memory */
+       void *desc;
+       /* physical address of the descriptor ring */
+       dma_addr_t dma;
+       /* length of descriptor ring in bytes */
+       unsigned int size;
+       /* number of descriptors in the ring */
+       unsigned int count;
+       /* next descriptor to associate a buffer with */
+       unsigned int next_to_use;
+       /* next descriptor to check for DD status bit */
+       unsigned int next_to_clean;
+       /* array of buffer information structs */
+       struct e1000_buffer *buffer_info;
+
+       spinlock_t tx_lock;
+       uint16_t tdh;
+       uint16_t tdt;
+       boolean_t last_tx_tso;
+};
+
+struct e1000_rx_ring {
+       /* pointer to the descriptor ring memory */
+       void *desc;
+       /* physical address of the descriptor ring */
+       dma_addr_t dma;
+       /* length of descriptor ring in bytes */
+       unsigned int size;
+       /* number of descriptors in the ring */
+       unsigned int count;
+       /* next descriptor to associate a buffer with */
+       unsigned int next_to_use;
+       /* next descriptor to check for DD status bit */
+       unsigned int next_to_clean;
+       /* array of buffer information structs */
+       struct e1000_buffer *buffer_info;
+       /* arrays of page information for packet split */
+       struct e1000_ps_page *ps_page;
+       struct e1000_ps_page_dma *ps_page_dma;
+
+       /* cpu for rx queue */
+       int cpu;
 
-uint32_t e1000_rr32(uint32_t offset);
-void e1000_wr32(uint32_t offset, uint32_t val);
+       uint16_t rdh;
+       uint16_t rdt;
+};
+struct e1000_adaptor{
+       struct e1000_tx_ring tx_ring;
+       struct e1000_rx_ring rx_ring;
+
+
+}
+#endif
+
+/* driver private functions */
+static uint32_t e1000_rr32(uint32_t offset);
+static void e1000_wr32(uint32_t offset, uint32_t val);
+#define E1000_WRITE_FLUSH() e1000_rr32(E1000_STATUS)
 
 void e1000_init(void);
 void e1000_reset(void);
@@ -47,5 +114,10 @@ void e1000_handle_rx_packet(void);
 void e1000_set_rx_descriptor(uint32_t des_num, uint8_t reset_buffer);
 void e1000_set_tx_descriptor(uint32_t des_num);
 int  e1000_send_frame(const char* data, size_t len);
-
+int e1000_send_pbuf(struct pbuf *p);
+static void e1000_clean_rx_irq();
+/* returns a chain of pbuf from the driver */
+struct pbuf* e1000_recv_pbuf();
+void process_pbuf(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2);
+static void schedule_pb(struct pbuf* pb);
 #endif /* !ROS_INC_E1000_H */
index 5219212..c0c8e1c 100644 (file)
@@ -944,4 +944,19 @@ struct e1000_data_desc {
 /* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */
 #define EEPROM_SUM 0xBABA
 
+/* This defines the bits that are set in the Interrupt Mask
+ * Set/Read Register.  Each bit is documented below:
+ *   o RXT0   = Receiver Timer Interrupt (ring 0)
+ *   o TXDW   = Transmit Descriptor Written Back
+ *   o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0)
+ *   o RXSEQ  = Receive Sequence Error
+ *   o LSC    = Link Status Change
+ */
+#define IMS_ENABLE_MASK ( \
+    E1000_IMS_RXT0   |    \
+    E1000_IMS_TXDW   |    \
+    E1000_IMS_RXDMT0 |    \
+    E1000_IMS_RXSEQ  |    \
+    E1000_IMS_LSC)
+
 #endif /* _E1000_HW_H_ */
index 75115fe..71f2403 100644 (file)
@@ -18,6 +18,9 @@
 // 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 
index 9fcc17f..148bcb4 100644 (file)
@@ -5,6 +5,7 @@
 #include <trap.h>
 #include <net.h>
 #include <pmap.h>
+#include <net/pbuf.h>
 
 // Packet sizes
 #define MTU              1500
@@ -19,6 +20,8 @@
 // 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 
@@ -47,4 +50,10 @@ struct eth_frame {
        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 9b5450c..2bdfe9d 100644 (file)
@@ -31,6 +31,8 @@
 #include <pmap.h>
 
 #include <eth_audio.h>
+#include <net/pbuf.h>
+
 
 /** @file
  * @brief Realtek RL8168 Driver
@@ -108,6 +110,7 @@ void rl8168_init() {
        rl8168_configure();
        rl8168_setup_interrupts();
        send_frame = &rl8168_send_frame;
+       send_pbuf = &rl8168_send_pbuf;
 
        eth_up = 1;
        
@@ -179,9 +182,8 @@ void rl8168_setup_descriptors() {
        // Allocate room for the buffers. 
        // Buffers need to be on 256 byte boundries.
        // Note: We use get_cont_pages to force page alignment, and thus 256 byte aligned
-
-        uint32_t num_rx_pages = ROUNDUP(NUM_RX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE;
-        uint32_t num_tx_pages = ROUNDUP(NUM_TX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE;
+       uint32_t num_rx_pages = ROUNDUP(NUM_RX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE;
+       uint32_t num_tx_pages = ROUNDUP(NUM_TX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE;
        
        rx_des_kva = get_cont_pages(LOG2_UP(num_rx_pages), 0);
        tx_des_kva = get_cont_pages(LOG2_UP(num_tx_pages), 0);
@@ -600,6 +602,41 @@ void rl8168_process_frame(char *frame_buffer, uint32_t frame_size, uint32_t comm
        return;
 }
 
+/* Look into how bsd does send mbuf ? */
+int rl8168_send_pbuf(struct pbuf *p) {
+       int len = p->tot_len;
+       if (p == NULL)
+               return -1;
+       if (len == 0)
+               return 0;
+
+       if (tx_des_kva[tx_des_cur].command & DES_OWN_MASK) {
+               rl8168_frame_debug("-->TX Ring Buffer Full!\n");
+               return -1;
+       }
+       if (len > MAX_FRAME_SIZE){
+               return -1;
+       }
+       /* Copy everything out of pbuf to network buffer to be sent */
+       /* One copy! */
+       pbuf_copy_out(p, KADDR(tx_des_kva[tx_des_cur].low_buf), len, 0);
+       tx_des_kva[tx_des_cur].command = tx_des_kva[tx_des_cur].command | len | DES_OWN_MASK | DES_FS_MASK | DES_LS_MASK;
+       
+       tx_des_kva[tx_des_cur].vlan = 0;
+
+
+       tx_des_cur = (tx_des_cur + 1) % NUM_TX_DESCRIPTORS;
+       
+       rl8168_frame_debug("--> Sending Packet\n");
+       for(int i=0; i<len; i++)
+               rl8168_frame_debug("%x ", (unsigned int)(unsigned char)(data[i]));
+       rl8168_frame_debug("\n");
+       rl8168_frame_debug("--> Sent packet.\n");
+       
+       outb(rl8168_io_base_addr + RL_TX_CTRL_REG, RL_TX_SEND_MASK);
+       
+       return len;
+}
 // Main routine to send a frame. Just sends it and goes.
 // Card supports sending across multiple fragments.
 // Would we want to write a function that takes a larger packet and generates fragments?
index f5222a6..4faaf49 100644 (file)
 
 // v----- Evil line ------v
 
-char *CT(PACKET_HEADER_SIZE + len)
-rl8168_packet_wrap(const char *CT(len) data, size_t len);
+//char *CT(PACKET_HEADER_SIZE + len)
+//rl8168_packet_wrap(const char *CT(len) data, size_t len);
 
 // ^----- Evil line ------^
 
@@ -128,5 +128,6 @@ void rl8168_set_tx_descriptor(uint32_t des_num);
 void rl8168_process_frame(char *CT(frame_size) frame_buffer,
                           uint32_t frame_size, uint32_t command);
 int rl8168_send_frame(const char *CT(len) data, size_t len);
+int rl8168_send_pbuf(struct pbuf *p);
 
 #endif /* !ROS_INC_REALTEK_H */
index 67bc626..4b49e0c 100644 (file)
@@ -2,8 +2,9 @@
 #define ROS_INC_TYPES_H
 
 #include <stddef.h>
-
+#ifndef LITTLE_ENDIAN
 #define LITTLE_ENDIAN
+#endif /* !LITTLE_ENDIAN */
 
 // Represents true-or-false values
 
index 493df85..0632e9c 100644 (file)
@@ -340,4 +340,8 @@ __cpu_relax(void)
        asm volatile("pause" : : : "memory");
 }
 
+#ifndef UNUSED_ARG
+#define UNUSED_ARG(x) (void)x
+#endif /* This prevents compiler warnings for UNUSED_ARG */ 
+
 #endif /* !ROS_INC_X86_H */
index 4386f34..1bf4446 100644 (file)
@@ -62,6 +62,8 @@ struct alarm_waiter {
 };
 TAILQ_HEAD(awaiters_tailq, alarm_waiter);              /* ideally not a LL */
 
+typedef void (*alarm_handler)(struct alarm_waiter *waiter);
+
 /* One of these per alarm source, such as a per-core timer.  Based on the
  * source, you may need a lock (such as for a global timer).  set_interrupt() is
  * a method for setting the interrupt source. */
index de94831..b765dbe 100644 (file)
@@ -10,6 +10,9 @@ void ( _panic)(const char* NTS, int, const char* NTS, ...)
 #define warn(...) _warn(__FILE__, __LINE__, __VA_ARGS__)
 #define panic(...) _panic(__FILE__, __LINE__, __VA_ARGS__)
 
+#define check(x)               \
+       do { if (!(x)) warn("warning failed: %s", #x); } while (0)
+
 #define assert(x)              \
        do { if (!(x)) panic("assertion failed: %s", #x); } while (0)
 
index b6e669e..0bb8219 100644 (file)
 #define        IPPROTO_TCP                     6               /* tcp */
 #define        IPPROTO_UDP                     17              /* user datagram protocol */
 
-#define        INADDR_ANY                      (uint32_t)0x00000000
-#define        INADDR_BROADCAST        (uint32_t)0xffffffff    /* must be masked */
+#define IP_HDR_SZ 20
+
+
+/* modified to be consistent with linux and our user space tool chain 
+ * we need to make our mind up about bsd/linux structs
+ */
+#define        INADDR_ANY                      (struct in_addr) {0x00000000}
+#define        INADDR_BROADCAST        (struct in_addr) {0xffffffff}   /* must be masked */
+
+/** 0.0.0.0 */
+#define IPADDR_ANY          ((uint32_t)0x00000000UL)
+/** 255.255.255.255 */
+#define IPADDR_BROADCAST    ((uint32_t)0xffffffffUL)
 
 #define        htonl(x) cpu_to_be32(x)
 #define        htons(x) cpu_to_be16(x)
diff --git a/kern/include/debug.h b/kern/include/debug.h
new file mode 100644 (file)
index 0000000..1fc7c9b
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *  
+ *
+ */
+#ifndef __LWIP_DEBUG_H__
+#define __LWIP_DEBUG_H__
+
+#define LWIP_PLATFORM_ASSERT(x)  assert(x)
+
+/** lower two bits indicate debug level
+ * - 0 all
+ * - 1 warning
+ * - 2 serious
+ * - 3 severe
+ */
+#define LWIP_DBG_LEVEL_ALL     0x00
+#define LWIP_DBG_LEVEL_OFF     LWIP_DBG_LEVEL_ALL /* compatibility define only */
+#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */
+#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */
+#define LWIP_DBG_LEVEL_SEVERE  0x03
+#define LWIP_DBG_MASK_LEVEL    0x03
+
+/** flag for LWIP_DEBUGF to enable that debug message */
+#define LWIP_DBG_ON            0x80U
+/** flag for LWIP_DEBUGF to disable that debug message */
+#define LWIP_DBG_OFF           0x00U
+
+/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */
+#define LWIP_DBG_TRACE         0x40U
+/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */
+#define LWIP_DBG_STATE         0x20U
+/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */
+#define LWIP_DBG_FRESH         0x10U
+/** flag for LWIP_DEBUGF to halt after printing this debug message */
+#define LWIP_DBG_HALT          0x08U
+
+#ifndef LWIP_NOASSERT
+#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \
+  LWIP_PLATFORM_ASSERT(message); } while(0)
+#else  /* LWIP_NOASSERT */
+#define LWIP_ASSERT(message, assertion) 
+#endif /* LWIP_NOASSERT */
+
+/** if "expression" isn't true, then print "message" and execute "handler" expression */
+#ifndef LWIP_ERROR
+#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
+  LWIP_PLATFORM_ASSERT(message); handler;}} while(0)
+#endif /* LWIP_ERROR */
+
+#ifdef LWIP_DEBUG
+/** print debug message only if debug message type is enabled...
+ *  AND is of correct type AND is at least LWIP_DBG_LEVEL
+ */
+#define LWIP_DEBUGF(debug, message) do { \
+                               if ( \
+                                   ((debug) & LWIP_DBG_ON) && \
+                                   ((debug) & LWIP_DBG_TYPES_ON) && \
+                                   ((int16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \
+                                 if ((debug) & LWIP_DBG_HALT) { \
+                                   while(1); \
+                                 } \
+                               } \
+                             } while(0)
+
+#else  /* LWIP_DEBUG */
+#define LWIP_DEBUGF(debug, message) 
+#endif /* LWIP_DEBUG */
+
+#endif /* __LWIP_DEBUG_H__ */
+
index be7ddfb..42c6027 100644 (file)
 #define be32_to_cpu(x) byte_swap32((x))
 #define be64_to_cpu(x) byte_swap64((x))
 
+#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
+#define PP_NTOHS(x) PP_HTONS(x)
+#define PP_HTONL(x) ((((x) & 0xff) << 24) | \
+                     (((x) & 0xff00) << 8) | \
+                     (((x) & 0xff0000UL) >> 8) | \
+                     (((x) & 0xff000000UL) >> 24))
+#define PP_NTOHL(x) PP_HTONL(x)
+
 #else /* big endian */
 
 # ifndef BIG_ENDIAN
 #define be32_to_cpu(x) ((uint32_t)(x))
 #define be64_to_cpu(x) ((uint64_t)(x))
 
+#define PP_HTONS(x) (x)
+#define PP_NTOHS(x) (x)
+#define PP_HTONL(x) (x)
+#define PP_NTOHL(x) (x)
+
 #endif /* endian */
 
 #endif /* ROS_KERN_ENDIAN_H */
index b179548..98e96a7 100644 (file)
 
 struct proc;
 struct kthread;
+struct semaphore;
+struct semaphore_entry;
 TAILQ_HEAD(kthread_tailq, kthread);
+LIST_HEAD(semaphore_list, semaphore_entry);
+
 
 /* This captures the essence of a kernel context that we want to suspend.  When
  * a kthread is running, we make sure its stacktop is the default kernel stack,
@@ -45,6 +49,13 @@ struct cond_var {
        bool                                            irq_okay;
 };
 
+/* TODO: consider building this into struct semaphore */
+struct semaphore_entry {
+       struct semaphore sem;
+       int fd;
+       LIST_ENTRY(semaphore_entry) link;
+};
+
 void kthread_init(void);
 void restart_kthread(struct kthread *kthread);
 void kthread_runnable(struct kthread *kthread);
index 605af84..8524065 100644 (file)
@@ -1,5 +1,6 @@
 /* Copyright (c) 2010 The Regents of the University of California
  * Barret Rhoden <brho@cs.berkeley.edu>
+ * David Zhu <yuzhu@cs.berkeley.edu>
  * See LICENSE for details.
  *
  * Arch independent networking infrastructure */
@@ -8,34 +9,58 @@
 #define ROS_KERN_NET_H
 
 #include <bits/netinet.h>
+#include <net/pbuf.h>
 #include <stdio.h>
 
+
 /* A few other useful standard defines.  Note the IP header can change size. */
-#define ETH_HDR_SZ 14
+#define ETH_HDR_SZ 14 // without padding, 16 with padding
+
+#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE)  
+
 #define UDP_HDR_SZ 8
 #define IP_ETH_TYPE 0x0800
+#define ETHTYPE_IP IP_ETH_TYPE
+#define ETHTYPE_ARP 0x0806
 
-/* ROS defaults: */
+#define IP_HDR_SZ 20
+/* ROS defaults: They really should be netif specific*/
 #define DEFAULT_TTL 64
+#define DEFAULT_MTU 1500
+// is this network order already?
+#define LOCAL_IP_ADDR (struct in_addr) {0x0A000002}  //lookout for address order
+
 
 /* Don't forget the bytes are in network order */
 struct ethernet_hdr {
+       /* might want to pad to increase access speed? */
        uint8_t                                         dst_mac[6];
        uint8_t                                         src_mac[6];
        uint16_t                                        eth_type;
        /* might be an optional 802.1q tag here */
-};
+} __attribute__((packed));
+
+#define IP_RF 0x8000        /* reserved fragment flag */
+#define IP_DF 0x4000        /* dont fragment flag */
+#define IP_MF 0x2000        /* more fragments flag */
+#define IP_OFFMASK 0x1fff   /* mask for fragmenting bits */
+
+#define PACK_STRUCT_FIELD(x) x __attribute__((packed))
 
 /* For the bit-enumerated fields, note that you need to read "backwards" through
  * the byte (first bits in memory are the "LSB" of the byte).  Can't seem to be
  * able to do it with flags/fragments (3/13 bits each...). */
 struct ip_hdr {
+       /* TODO: Are these accesses slower? */
        unsigned                                        hdr_len : 4;
        unsigned                                        version : 4;
        uint8_t                                         tos;
        uint16_t                                        packet_len;
-       uint16_t                                        id;
+       /* ip header id is used for fragmentation reassembly */
+       uint16_t                                        id;  // 1 index this?
+       /* flags controlling fragmentation(do not fragment etc) */
        uint16_t                                        flags_frags;
+       /* statically set to a constatnt right now */
        uint8_t                                         ttl;
        uint8_t                                         protocol;
        uint16_t                                        checksum;
@@ -49,8 +74,21 @@ struct udp_hdr {
        uint16_t                                        dst_port;
        uint16_t                                        length;
        uint16_t                                        checksum;
-};
+} __attribute__((packed));
+
+struct tcp_hdr {
+  PACK_STRUCT_FIELD(uint16_t src);
+  PACK_STRUCT_FIELD(uint16_t dest);
+  PACK_STRUCT_FIELD(uint32_t seqno);
+  PACK_STRUCT_FIELD(uint32_t ackno);
+  PACK_STRUCT_FIELD(uint16_t _hdrlen_rsvd_flags);
+  PACK_STRUCT_FIELD(uint16_t wnd);
+  PACK_STRUCT_FIELD(uint16_t chksum);
+  PACK_STRUCT_FIELD(uint16_t urgp);
+} __attribute__((packed));
 
+/* src and dst are in network order*/
+uint16_t inet_chksum_pseudo(struct pbuf *p, uint32_t src, uint32_t dest, uint8_t proto, uint16_t proto_len);
 uint16_t __ip_checksum(void *buf, unsigned int len, uint32_t sum);
 uint16_t ip_checksum(struct ip_hdr *ip_hdr);
 uint16_t udp_checksum(struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr);
diff --git a/kern/include/net/dev.h b/kern/include/net/dev.h
new file mode 100644 (file)
index 0000000..609cf94
--- /dev/null
@@ -0,0 +1,32 @@
+/* Copyright (c) 2010 The Regents of the University of California
+ * David Zhu <yuzhu@cs.berkeley.edu> 
+ * See LICENSE for details.
+ *
+ * Simplified network device interface */
+#ifndef ROS_KERN_NET_DEV_H
+#define ROS_KERN_NET_DEV_H
+
+#include <bits/netinet.h>
+#include <stdio.h>
+#include <socket.h>
+#include <ros/common.h>
+
+struct net_device_ops {
+       int (*init)(struct netif *netif);
+       int (*send_frame) (struct netif *netif, const char data, size_t len) ;
+       int (*send_pbuf) (struct netif *netif, const struct pbuf* pb);
+       int (*recv_pbuf) (struct netif *netif, const struct pbuf* pb);
+}
+
+struct netif {
+       /* TODO: next netif so we can build a list of them*/
+       struct in_addr ip_addr;
+       struct in_addr gw;
+       uint16_t mtu;
+       uint8_t mac[6];
+       struct net_device_ops ops;
+       uint8_t eth_up;   // status 
+}; 
+
+
+#endif //ROS_KERN_NET_DEV_H
diff --git a/kern/include/net/ip.h b/kern/include/net/ip.h
new file mode 100644 (file)
index 0000000..dea76e2
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef ROS_KERN_IP_H
+#define ROS_KERN_IP_H
+#include <net/pbuf.h>
+#include <net.h>
+#include <bits/netinet.h>
+#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->s_addr == IPADDR_ANY)
+#define ip_addr_cmp(addr1, addr2) ((addr1)->s_addr == (addr2)->s_addr)
+#define ip_match(addr1, addr2) (ip_addr_isany(addr1) || ip_addr_isany(addr2) || ip_addr_cmp(addr1, addr2))
+#define ip_addr_copy(addr1, addr2) ((addr1).s_addr = (addr2).s_addr)
+
+struct in_addr {
+    uint32_t s_addr;
+};
+
+typedef struct in_addr ip_addr_t;
+
+#define IP_PCB \
+/* ips are in network byte order */ \
+struct in_addr local_ip; \
+struct in_addr remote_ip; \
+uint8_t so_options; \
+uint8_t tos; \
+uint8_t ttl; \
+uint8_t addr_hint;
+
+int ip_output(struct pbuf *p, struct in_addr *src, struct in_addr *dest, uint8_t ttl, uint8_t tos, uint8_t proto);
+int ip_input(struct pbuf *p);
+
+#endif // ROS_KERN_IP_H
diff --git a/kern/include/net/pbuf.h b/kern/include/net/pbuf.h
new file mode 100644 (file)
index 0000000..c43bc3d
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef _ROS_PBUF_H_
+#define _ROS_PBUF_H_
+#include <kmalloc.h>
+#include <slab.h>
+#include <kref.h>
+#include <sys/queue.h>
+#include <atomic.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Currently, the pbuf_custom code is only needed for one specific configuration
+ * of IP_FRAG */
+#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF)
+/* Ensure IP address are 32-bit aligned on 32bit systems, and thus improving the speed of processing
+ * for regularly accessed fields such as IP addresses
+ */
+#define ETH_PAD_SIZE 2      // padding to ensure ip packet is longword aligned.
+#define PBUF_TRANSPORT_HLEN 20
+#define PBUF_IP_HLEN        20
+#define PBUF_LINK_HLEN      14 + ETH_PAD_SIZE // Padding
+
+/** indicates this packet's data should be immediately passed to the application */
+#define PBUF_FLAG_PUSH      0x01U
+/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a
+    a pbuf differently */
+#define PBUF_FLAG_IS_CUSTOM 0x02U
+/** indicates this pbuf is UDP multicast to be looped back */
+#define PBUF_FLAG_MCASTLOOP 0x04U
+
+typedef enum {
+  PBUF_TRANSPORT,
+  PBUF_IP,
+  PBUF_LINK,
+  PBUF_RAW
+} pbuf_layer;
+
+typedef enum {
+  PBUF_RAM, /* pbuf data is stored in RAM */
+  PBUF_ROM, /* pbuf data is stored in ROM */
+  PBUF_REF, /* pbuf comes from the pbuf pool */
+  PBUF_POOL, /* pbuf payload refers to RAM */
+       PBUF_MTU  /* pbuf with a fixed MTU size */
+} pbuf_type;
+
+
+/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a
+    a pbuf differently */
+#define PBUF_FLAG_IS_CUSTOM 0x02U
+struct pbuf;
+STAILQ_HEAD(pbuf_tailq, pbuf);
+
+struct pbuf {
+       /* Several reasons to roll own version of STAIL queue here 
+        * pbuf chain exists without a queue
+        * also pbuf chain need to be moved entirely onto a socket queue
+        */
+       STAILQ_ENTRY(pbuf) next;
+  // struct pbuf *next;
+
+  /** pointer to the actual data in the buffer */
+  void *payload;
+
+  uint16_t tot_len;
+
+  /** length of this buffer */
+  uint16_t len;
+
+       uint16_t alloc_len;
+
+  /** pbuf_type as u8_t instead of enum to save space */
+  uint8_t type;
+
+  /** misc flags */
+  uint8_t flags;
+
+  struct kref bufref;
+};
+
+struct pbuf_head {
+       struct pbuf_tailq pbuf_fifo;
+       uint32_t qlen;
+       spinlock_t lock; 
+};
+
+static inline void pbuf_head_init(struct pbuf_head *ph) {
+       STAILQ_INIT(&ph->pbuf_fifo);
+       ph->qlen = 0;
+       spinlock_init(&ph->lock);
+}
+extern struct kmem_cache *pbuf_kcache;
+/* Initializes the pbuf module. This call is empty for now, but may not be in future. */
+void pbuf_init(void);
+void pbuf_cat(struct pbuf *head, struct pbuf *tail);
+void pbuf_chain(struct pbuf *head, struct pbuf *tail);
+void pbuf_ref(struct pbuf *p);
+bool pbuf_deref(struct pbuf *p);
+int pbuf_header(struct pbuf *p, int header_size);
+struct pbuf *pbuf_alloc(pbuf_layer layer, uint16_t length, pbuf_type type);
+int pbuf_copy_out(struct pbuf *buf, void *dataptr, size_t len, uint16_t offset);
+void print_pbuf(struct pbuf *p);
+bool pbuf_free(struct pbuf *p);
+
+void attach_pbuf(struct pbuf *p, struct pbuf_head *buf_head);
+struct pbuf* detach_pbuf(struct pbuf_head *buf_head);
+uint8_t pbuf_clen(struct pbuf *p);  
+void pbuf_realloc(struct pbuf *p, uint16_t size); 
+// end
+#if 0
+struct pbuf *pbuf_dechain(struct pbuf *p);
+err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from);
+err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
+struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer);
+#if LWIP_CHECKSUM_ON_COPY
+err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+                       u16_t len, u16_t *chksum);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+u8_t pbuf_get_at(struct pbuf* p, u16_t offset);
+u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n);
+u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset);
+u16_t pbuf_strstr(struct pbuf* p, const char* substr);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_PBUF_H__ */
diff --git a/kern/include/net/tcp.h b/kern/include/net/tcp.h
new file mode 100644 (file)
index 0000000..2a5ddbf
--- /dev/null
@@ -0,0 +1,490 @@
+#ifndef ROS_KERN_TCP_H
+#define ROS_KERN_TCP_H
+#include <net/pbuf.h>
+#include <net/ip.h>
+#include <net.h>
+#include <error.h>
+#include <bits/netinet.h>
+#include <socket.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef TCP_LOCAL_PORT_RANGE_START
+#define TCP_LOCAL_PORT_RANGE_START 4096
+#define TCP_LOCAL_PORT_RANGE_END   0x7fff
+#endif
+
+#ifndef LWIP_CALLBACK_API
+#define LWIP_CALLBACK_API 1
+#endif 
+
+
+/**
+ * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT
+ * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set
+ * in seconds. (does not require sockets.c, and will affect tcp.c)
+ */
+#ifndef LWIP_TCP_KEEPALIVE
+#define LWIP_TCP_KEEPALIVE              0
+#endif
+
+/**
+ * TCP_TTL: Default Time-To-Live value.
+ */
+#ifndef TCP_TTL
+#define TCP_TTL                         (255)
+#endif
+
+/**
+ * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default,
+ * you might want to increase this.)
+ * For the receive side, this MSS is advertised to the remote side
+ * when opening a connection. For the transmit size, this MSS sets
+ * an upper limit on the MSS advertised by the remote host.
+ */
+#ifndef TCP_MSS
+#define TCP_MSS                         (512)
+#endif
+
+/**
+ * TCP_WND: The size of a TCP window.  This must be at least 
+ * (2 * TCP_MSS) for things to work well
+ */
+#ifndef TCP_WND
+#define TCP_WND                         (4 * TCP_MSS)
+#endif 
+
+/**
+ * TCP_MAXRTX: Maximum number of retransmissions of data segments.
+ */
+#ifndef TCP_MAXRTX
+#define TCP_MAXRTX                      12
+#endif
+
+/**
+ * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments.
+ */
+#ifndef TCP_SYNMAXRTX
+#define TCP_SYNMAXRTX                   6
+#endif
+
+/**
+ * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order.
+ * Define to 0 if your device is low on memory.
+ */
+#ifndef TCP_QUEUE_OOSEQ
+#define TCP_QUEUE_OOSEQ                 0
+#endif
+
+
+/**
+ * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really
+ * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which
+ * reflects the available reassembly buffer size at the remote host) and the
+ * largest size permitted by the IP layer" (RFC 1122)
+ * Setting this to 1 enables code that checks TCP_MSS against the MTU of the
+ * netif used for a connection and limits the MSS if it would be too big otherwise.
+ */
+#ifndef TCP_CALCULATE_EFF_SEND_MSS
+#define TCP_CALCULATE_EFF_SEND_MSS      1
+#endif
+
+
+/**
+ * TCP_SND_BUF: TCP sender buffer space (bytes). 
+ */
+#ifndef TCP_SND_BUF
+#define TCP_SND_BUF                     256
+#endif
+
+/**
+ * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work.
+ */
+#ifndef TCP_SND_QUEUELEN
+#define TCP_SND_QUEUELEN                ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS))
+#endif
+
+/**
+ * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than
+ * TCP_SND_BUF. It is the amount of space which must be available in the
+ * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT).
+ */
+#ifndef TCP_SNDLOWAT
+#define TCP_SNDLOWAT                    ((TCP_SND_BUF)/2)
+#endif
+
+/**
+ * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be grater
+ * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below
+ * this number, select returns writable (combined with TCP_SNDLOWAT).
+ */
+#ifndef TCP_SNDQUEUELOWAT
+#define TCP_SNDQUEUELOWAT               ((TCP_SND_QUEUELEN)/2)
+#endif
+
+/**
+ * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb.
+ */
+#ifndef TCP_LISTEN_BACKLOG
+#define TCP_LISTEN_BACKLOG              0
+#endif
+
+/**
+ * The maximum allowed backlog for TCP listen netconns.
+ * This backlog is used unless another is explicitly specified.
+ * 0xff is the maximum (u8_t).
+ */
+#ifndef TCP_DEFAULT_LISTEN_BACKLOG
+#define TCP_DEFAULT_LISTEN_BACKLOG      0xff
+#endif
+
+/**
+ * TCP_OVERSIZE: The maximum number of bytes that tcp_write may
+ * allocate ahead of time in an attempt to create shorter pbuf chains
+ * for transmission. The meaningful range is 0 to TCP_MSS. Some
+ * suggested values are:
+ *
+ * 0:         Disable oversized allocation. Each tcp_write() allocates a new
+              pbuf (old behaviour).
+ * 1:         Allocate size-aligned pbufs with minimal excess. Use this if your
+ *            scatter-gather DMA requires aligned fragments.
+ * 128:       Limit the pbuf/memory overhead to 20%.
+ * TCP_MSS:   Try to create unfragmented TCP packets.
+ * TCP_MSS/4: Try to create 4 fragments or less per TCP packet.
+ */
+#ifndef TCP_OVERSIZE
+#define TCP_OVERSIZE                    TCP_MSS
+#endif
+
+/**
+ * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option.
+ */
+#ifndef LWIP_TCP_TIMESTAMPS
+#define LWIP_TCP_TIMESTAMPS             0
+#endif
+
+/**
+ * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an
+ * explicit window update
+ */
+#ifndef TCP_WND_UPDATE_THRESHOLD
+#define TCP_WND_UPDATE_THRESHOLD   (TCP_WND / 4)
+#endif
+
+struct tcp_pcb;
+
+#define TCP_PRIO_MIN    1
+#define TCP_PRIO_NORMAL 64
+#define TCP_PRIO_MAX    127
+
+/** Function prototype for tcp accept callback functions. Called when a new
+ * connection can be accepted on a listening pcb.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param newpcb The new connection pcb
+ * @param err An error code if there has been an error accepting.
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef error_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, error_t err);
+
+/** Function prototype for tcp receive callback functions. Called when data has
+ * been received.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which received data
+ * @param p The received data (or NULL when the connection has been closed!)
+ * @param err An error code if there has been an error receiving
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef error_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb,
+                             struct pbuf *p, error_t err);
+
+/** Function prototype for tcp sent callback functions. Called when sent data has
+ * been acknowledged by the remote side. Use it to free corresponding resources.
+ * This also means that the pcb has now space available to send new data.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb for which data has been acknowledged
+ * @param len The amount of bytes acknowledged
+ * @return ERR_OK: try to send some data by calling tcp_output
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef error_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb,
+                              uint16_t len);
+
+/** Function prototype for tcp poll callback functions. Called periodically as
+ * specified by @see tcp_poll.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb tcp pcb
+ * @return ERR_OK: try to send some data by calling tcp_output
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef error_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb);
+
+/** Function prototype for tcp error callback functions. Called when the pcb
+ * receives a RST or is unexpectedly closed for any other reason.
+ *
+ * @note The corresponding pcb is already freed when this callback is called!
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param err Error code to indicate why the pcb has been closed
+ *            ERR_ABRT: aborted through tcp_abort or by a TCP timer
+ *            ERR_RST: the connection was reset by the remote host
+ */
+typedef void  (*tcp_err_fn)(void *arg, error_t err);
+
+/** Function prototype for tcp connected callback functions. Called when a pcb
+ * is connected to the remote side after initiating a connection attempt by
+ * calling tcp_connect().
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which is connected
+ * @param err An unused error code, always ERR_OK currently ;-) TODO!
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ *
+ * @note When a connection attempt fails, the error callback is currently called!
+ */
+typedef error_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, error_t err);
+
+enum tcp_state {
+  CLOSED      = 0,
+  LISTEN      = 1,
+  SYN_SENT    = 2,
+  SYN_RCVD    = 3,
+  ESTABLISHED = 4,
+  FIN_WAIT_1  = 5,
+  FIN_WAIT_2  = 6,
+  CLOSE_WAIT  = 7,
+  CLOSING     = 8,
+  LAST_ACK    = 9,
+  TIME_WAIT   = 10
+};
+
+/**
+ * members common to struct tcp_pcb and struct tcp_listen_pcb
+ */
+#define TCP_PCB_COMMON(type) \
+  type *next; /* for the linked list */ \
+  enum tcp_state state; /* TCP state */ \
+  uint8_t prio; \
+  void *callback_arg; \
+       tcp_accept_fn accept; \
+  /* ports are in host byte order */ \
+  uint16_t local_port
+
+
+/* the TCP protocol control block */
+struct tcp_pcb {
+       IP_PCB;
+
+/** protocol specific PCB members */
+  TCP_PCB_COMMON(struct tcp_pcb);
+       struct socket *pcbsock;
+
+  /* ports are in host byte order */
+  uint16_t remote_port;
+  
+  uint8_t flags;
+#define TF_ACK_DELAY   ((uint8_t)0x01U)   /* Delayed ACK. */
+#define TF_ACK_NOW     ((uint8_t)0x02U)   /* Immediate ACK. */
+#define TF_INFR        ((uint8_t)0x04U)   /* In fast recovery. */
+#define TF_TIMESTAMP   ((uint8_t)0x08U)   /* Timestamp option enabled */
+#define TF_RXCLOSED    ((uint8_t)0x10U)   /* rx closed by tcp_shutdown */
+#define TF_FIN         ((uint8_t)0x20U)   /* Connection was closed locally (FIN segment enqueued). */
+#define TF_NODELAY     ((uint8_t)0x40U)   /* Disable Nagle algorithm */
+#define TF_NAGLEMEMERR ((uint8_t)0x80U)   /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
+
+  /* the rest of the fields are in host byte order
+     as we have to do some math with them */
+  /* receiver variables */
+  uint32_t rcv_nxt;   /* next seqno expected */
+  uint16_t rcv_wnd;   /* receiver window available */
+  uint16_t rcv_ann_wnd; /* receiver window to announce */
+  uint32_t rcv_ann_right_edge; /* announced right edge of window */
+
+  /* Timers */
+  uint32_t tmr;
+  uint8_t polltmr, pollinterval;
+  
+  /* Retransmission timer. */
+  int16_t rtime;
+  
+  uint16_t mss;   /* maximum segment size */
+  
+  /* RTT (round trip time) estimation variables */
+  uint32_t rttest; /* RTT estimate in 500ms ticks */
+  uint32_t rtseq;  /* sequence number being timed */
+  int16_t sa, sv; /* @todo document this */
+
+  int16_t rto;    /* retransmission time-out */
+  uint8_t nrtx;    /* number of retransmissions */
+
+  /* fast retransmit/recovery */
+  uint32_t lastack; /* Highest acknowledged seqno. */
+  uint8_t dupacks;
+  
+  /* congestion avoidance/control variables */
+  uint16_t cwnd;  
+  uint16_t ssthresh;
+
+  /* sender variables */
+  uint32_t snd_nxt;   /* next new seqno to be sent */
+  uint16_t snd_wnd;   /* sender window */
+  uint32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last
+                             window update. */
+  uint32_t snd_lbb;       /* Sequence number of next byte to be buffered. */
+
+  uint16_t acked;
+  
+  uint16_t snd_buf;   /* Available buffer space for sending (in bytes). */
+#define TCP_SNDQUEUELEN_OVERFLOW (0xffff-3)
+  uint16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */
+
+#if TCP_OVERSIZE
+  /* Extra bytes available at the end of the last pbuf in unsent. */
+  uint16_t unsent_oversize;
+#endif /* TCP_OVERSIZE */ 
+
+  /* These are ordered by sequence number: */
+  struct tcp_seg *unsent;   /* Unsent (queued) segments. */
+  struct tcp_seg *unacked;  /* Sent but unacknowledged segments. */
+#if TCP_QUEUE_OOSEQ  
+  struct tcp_seg *ooseq;    /* Received out of sequence segments. */
+#endif /* TCP_QUEUE_OOSEQ */
+
+  struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */
+
+  /* Function to be called when more send buffer space is available. */
+  tcp_sent_fn sent;
+  /* Function to be called when (in-sequence) data has arrived. */
+  tcp_recv_fn recv;
+  /* Function to be called when a connection has been set up. */
+  tcp_connected_fn connected;
+  /* Function which is called periodically. */
+  tcp_poll_fn poll;
+  /* Function to be called whenever a fatal error occurs. */
+  tcp_err_fn errf;
+
+#if LWIP_TCP_TIMESTAMPS
+  uint32_t ts_lastacksent;
+  uint32_t ts_recent;
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+  /* idle time before KEEPALIVE is sent */
+  uint32_t keep_idle;
+#if LWIP_TCP_KEEPALIVE
+  uint32_t keep_intvl;
+  uint32_t keep_cnt;
+#endif /* LWIP_TCP_KEEPALIVE */
+  
+  /* Persist timer counter */
+  uint32_t persist_cnt;
+  /* Persist timer back-off */
+  uint8_t persist_backoff;
+
+  /* KEEPALIVE counter */
+  uint8_t keep_cnt_sent;
+};
+
+struct tcp_pcb_listen {  
+       IP_PCB;
+/* Protocol specific PCB members */
+  TCP_PCB_COMMON(struct tcp_pcb_listen);
+
+#if TCP_LISTEN_BACKLOG
+  uint8_t backlog;
+  uint8_t accepts_pending;
+#endif /* TCP_LISTEN_BACKLOG */
+};
+
+#if 0 //LWIP_EVENT_API
+
+enum lwip_event {
+  LWIP_EVENT_ACCEPT,
+  LWIP_EVENT_SENT,
+  LWIP_EVENT_RECV,
+  LWIP_EVENT_CONNECTED,
+  LWIP_EVENT_POLL,
+  LWIP_EVENT_ERR
+};
+
+error_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb,
+         enum lwip_event,
+         struct pbuf *p,
+         uint16_t size,
+         error_t err);
+
+#endif /* LWIP_EVENT_API */
+
+/* Application program's interface: */
+struct tcp_pcb * tcp_new (void);
+error_t tcp_bind(struct tcp_pcb *pcb, const struct in_addr *ipaddr, uint16_t port);
+void             tcp_abort (struct tcp_pcb *pcb);
+
+void             tcp_arg     (struct tcp_pcb *pcb, void *arg);
+void             tcp_accept  (struct tcp_pcb *pcb, tcp_accept_fn accept);
+void             tcp_recv    (struct tcp_pcb *pcb, tcp_recv_fn recv);
+void             tcp_sent    (struct tcp_pcb *pcb, tcp_sent_fn sent);
+void             tcp_poll    (struct tcp_pcb *pcb, tcp_poll_fn poll, uint8_t interval);
+void             tcp_err     (struct tcp_pcb *pcb, tcp_err_fn err);
+
+#define          tcp_mss(pcb)             (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12)  : (pcb)->mss)
+#define          tcp_sndbuf(pcb)          ((pcb)->snd_buf)
+#define          tcp_sndqueuelen(pcb)     ((pcb)->snd_queuelen)
+#define          tcp_nagle_disable(pcb)   ((pcb)->flags |= TF_NODELAY)
+#define          tcp_nagle_enable(pcb)    ((pcb)->flags &= ~TF_NODELAY)
+#define          tcp_nagle_disabled(pcb)  (((pcb)->flags & TF_NODELAY) != 0)
+
+#if TCP_LISTEN_BACKLOG
+#define          tcp_accepted(pcb) do { \
+  LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \
+  (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0)
+#else  /* TCP_LISTEN_BACKLOG */
+#define          tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \
+                                               pcb->state == LISTEN)
+#endif /* TCP_LISTEN_BACKLOG */
+
+void             tcp_recved  (struct tcp_pcb *pcb, uint16_t len);
+error_t            tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+                              uint16_t port, tcp_connected_fn connected);
+
+struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, uint8_t backlog);
+#define          tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
+
+error_t            tcp_close   (struct tcp_pcb *pcb);
+error_t            tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx);
+
+/* Flags for "apiflags" parameter in tcp_write */
+#define TCP_WRITE_FLAG_COPY 0x01
+#define TCP_WRITE_FLAG_MORE 0x02
+
+error_t            tcp_write   (struct tcp_pcb *pcb, const void *dataptr, uint16_t len,
+                              uint8_t apiflags);
+
+void             tcp_setprio (struct tcp_pcb *pcb, uint8_t prio);
+
+#define TCP_PRIO_MIN    1
+#define TCP_PRIO_NORMAL 64
+#define TCP_PRIO_MAX    127
+
+error_t            tcp_output  (struct tcp_pcb *pcb);
+
+
+const char* tcp_debug_state_str(enum tcp_state s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/kern/include/net/tcp_impl.h b/kern/include/net/tcp_impl.h
new file mode 100644 (file)
index 0000000..7758447
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_TCP_IMPL_H__
+#define __LWIP_TCP_IMPL_H__
+#include "error.h"
+#include "net.h"
+#include "socket.h"
+#include "net/pbuf.h"
+#include "net/ip.h"
+#include "bits/netinet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// akaros specific option
+
+#define LWIP_EVENT_API 0
+
+struct tcp_hdr;
+struct tcp_pcb* tcp_alloc(uint8_t prio);
+/* Functions for interfacing with TCP: */
+
+/* Lower layer interface to TCP: */
+#define tcp_init() /* Compatibility define, no init needed. */
+void             tcp_tmr     (void);  /* Must be called every
+                                         TCP_TMR_INTERVAL
+                                         ms. (Typically 250 ms). */
+/* It is also possible to call these two functions at the right
+   intervals (instead of calling tcp_tmr()). */
+void             tcp_slowtmr (void);
+void             tcp_fasttmr (void);
+
+
+/* Only used by IP to pass a TCP segment to TCP: */
+// Change: assumed on network interface
+void             tcp_input   (struct pbuf *p);
+/* Used within the TCP code only: */
+void             tcp_abandon (struct tcp_pcb *pcb, int reset);
+error_t            tcp_send_empty_ack(struct tcp_pcb *pcb);
+void             tcp_rexmit  (struct tcp_pcb *pcb);
+void             tcp_rexmit_rto  (struct tcp_pcb *pcb);
+void             tcp_rexmit_fast (struct tcp_pcb *pcb);
+uint32_t            tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
+
+/**
+ * This is the Nagle algorithm: try to combine user data to send as few TCP
+ * segments as possible. Only send if
+ * - no previously transmitted data on the connection remains unacknowledged or
+ * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or
+ * - the only unsent segment is at least pcb->mss bytes long (or there is more
+ *   than one unsent segment - with lwIP, this can happen although unsent->len < mss)
+ * - or if we are in fast-retransmit (TF_INFR)
+ */
+#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \
+                            ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \
+                            (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \
+                              ((tpcb)->unsent->len >= (tpcb)->mss))) \
+                            ) ? 1 : 0)
+#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ESUCCESS)
+
+
+#define TCP_SEQ_LT(a,b)     ((int32_t)((a)-(b)) < 0)
+#define TCP_SEQ_LEQ(a,b)    ((int32_t)((a)-(b)) <= 0)
+#define TCP_SEQ_GT(a,b)     ((int32_t)((a)-(b)) > 0)
+#define TCP_SEQ_GEQ(a,b)    ((int32_t)((a)-(b)) >= 0)
+#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c))
+#define TCP_FIN 0x01U
+#define TCP_SYN 0x02U
+#define TCP_RST 0x04U
+#define TCP_PSH 0x08U
+#define TCP_ACK 0x10U
+#define TCP_URG 0x20U
+#define TCP_ECE 0x40U
+#define TCP_CWR 0x80U
+
+#define TCP_FLAGS 0x3fU
+
+/* Length of the TCP header, excluding options. */
+#define TCP_HLEN 20
+
+#ifndef TCP_TMR_INTERVAL
+#define TCP_TMR_INTERVAL       250  /* The TCP timer interval in milliseconds. */
+#endif /* TCP_TMR_INTERVAL */
+
+#ifndef TCP_FAST_INTERVAL
+#define TCP_FAST_INTERVAL      TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */
+#endif /* TCP_FAST_INTERVAL */
+
+#ifndef TCP_SLOW_INTERVAL
+#define TCP_SLOW_INTERVAL      (2*TCP_TMR_INTERVAL)  /* the coarse grained timeout in milliseconds */
+#endif /* TCP_SLOW_INTERVAL */
+
+#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */
+#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */
+
+#define TCP_OOSEQ_TIMEOUT        6U /* x RTO */
+
+#ifndef TCP_MSL
+#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */
+#endif
+
+/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */
+#ifndef  TCP_KEEPIDLE_DEFAULT
+#define  TCP_KEEPIDLE_DEFAULT     7200000UL /* Default KEEPALIVE timer in milliseconds */
+#endif
+
+#ifndef  TCP_KEEPINTVL_DEFAULT
+#define  TCP_KEEPINTVL_DEFAULT    75000UL   /* Default Time between KEEPALIVE probes in milliseconds */
+#endif
+
+#ifndef  TCP_KEEPCNT_DEFAULT
+#define  TCP_KEEPCNT_DEFAULT      9U        /* Default Counter for KEEPALIVE probes */
+#endif
+
+#define  TCP_MAXIDLE              TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT  /* Maximum KEEPALIVE probe time */
+
+#define TCPH_OFFSET(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 8)
+#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12)
+#define TCPH_FLAGS(phdr)  (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS)
+
+#define TCPH_OFFSET_SET(phdr, offset) (phdr)->_hdrlen_rsvd_flags = htons(((offset) << 8) | TCPH_FLAGS(phdr))
+#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr))
+#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((uint16_t)(~(uint16_t)(TCP_FLAGS)))) | htons(flags))
+#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags))
+
+#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags))
+#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) )
+
+#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0))
+
+/** Flags used on input processing, not on pcb->flags
+*/
+#define TF_RESET     (uint8_t)0x08U   /* Connection was reset. */
+#define TF_CLOSED    (uint8_t)0x10U   /* Connection was sucessfully closed. */
+#define TF_GOT_FIN   (uint8_t)0x20U   /* Connection was closed by the remote end. */
+
+#if LWIP_EVENT_API
+
+#define TCP_EVENT_ACCEPT(pcb,err,ret)    ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_ACCEPT, NULL, 0, err)
+#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                   LWIP_EVENT_SENT, NULL, space, ESUCCESS)
+#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_RECV, (p), 0, (err))
+#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_RECV, NULL, 0, ESUCCESS)
+#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_CONNECTED, NULL, 0, (err))
+#define TCP_EVENT_POLL(pcb,ret)       ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_POLL, NULL, 0, ESUCCESS)
+#define TCP_EVENT_ERR(errf,arg,err)  lwip_tcp_event((arg), NULL, \
+                LWIP_EVENT_ERR, NULL, 0, (err))
+
+#else /* LWIP_EVENT_API */
+
+#define TCP_EVENT_ACCEPT(pcb,err,ret)                          \
+  do {                                                         \
+    if((pcb)->accept != NULL)                                  \
+      (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err));  \
+    else (ret) = EFAIL;                                      \
+  } while (0)
+
+#define TCP_EVENT_SENT(pcb,space,ret)                          \
+  do {                                                         \
+    if((pcb)->sent != NULL)                                    \
+      (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space));  \
+    else (ret) = ESUCCESS;                                       \
+  } while (0)
+
+#define TCP_EVENT_RECV(pcb,p,err,ret)                          \
+  do {                                                         \
+    if((pcb)->recv != NULL) {                                  \
+      (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\
+    } else {                                                   \
+      (ret) = tcp_recv_null(NULL, (pcb), (p), (err));          \
+    }                                                          \
+  } while (0)
+
+#define TCP_EVENT_CLOSED(pcb,ret)                                \
+  do {                                                           \
+    if(((pcb)->recv != NULL)) {                                  \
+      (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ESUCCESS);\
+    } else {                                                     \
+      (ret) = ESUCCESS;                                            \
+    }                                                            \
+  } while (0)
+
+#define TCP_EVENT_CONNECTED(pcb,err,ret)                         \
+  do {                                                           \
+    if((pcb)->connected != NULL)                                 \
+      (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \
+    else (ret) = ESUCCESS;                                         \
+  } while (0)
+
+#define TCP_EVENT_POLL(pcb,ret)                                \
+  do {                                                         \
+    if((pcb)->poll != NULL)                                    \
+      (ret) = (pcb)->poll((pcb)->callback_arg,(pcb));          \
+    else (ret) = ESUCCESS;                                       \
+  } while (0)
+
+#define TCP_EVENT_ERR(errf,arg,err)                            \
+  do {                                                         \
+    if((errf) != NULL)                                         \
+      (errf)((arg),(err));                                     \
+  } while (0)
+
+#endif /* LWIP_EVENT_API */
+
+/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */
+#if TCP_OVERSIZE && defined(LWIP_DEBUG)
+#define TCP_OVERSIZE_DBGCHECK 1
+#else
+#define TCP_OVERSIZE_DBGCHECK 0
+#endif
+
+/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */
+#define TCP_CHECKSUM_ON_COPY  (CHECKSUM_GEN_TCP)
+
+/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */
+struct tcp_seg {
+  struct tcp_seg *next;    /* used when putting segements on a queue */
+  struct pbuf *p;          /* buffer containing data + TCP header */
+  void *dataptr;           /* pointer to the TCP data in the pbuf */
+  uint16_t len;               /* the TCP length of this segment */
+#if TCP_OVERSIZE_DBGCHECK
+  uint16_t oversize_left;     /* Extra bytes available at the end of the last
+                              pbuf in unsent (used for asserting vs.
+                              tcp_pcb.unsent_oversized only) */
+#endif /* TCP_OVERSIZE_DBGCHECK */ 
+#if TCP_CHECKSUM_ON_COPY
+  uint16_t chksum;
+  uint8_t  chksum_swapped;
+#endif /* TCP_CHECKSUM_ON_COPY */
+  uint8_t  flags;
+#define TF_SEG_OPTS_MSS         (uint8_t)0x01U /* Include MSS option. */
+#define TF_SEG_OPTS_TS          (uint8_t)0x02U /* Include timestamp option. */
+#define TF_SEG_DATA_CHECKSUMMED (uint8_t)0x04U /* ALL data (not the header) is
+                                               checksummed into 'chksum' */
+  struct tcp_hdr *tcphdr;  /* the TCP header */
+};
+
+#define LWIP_TCP_OPT_LENGTH(flags)              \
+  (flags & TF_SEG_OPTS_MSS ? 4  : 0) +          \
+  (flags & TF_SEG_OPTS_TS  ? 12 : 0)
+
+/** This returns a TCP header option for MSS in an uint32_t */
+#define TCP_BUILD_MSS_OPTION(x) (x) = PP_HTONL(((uint32_t)2 << 24) |          \
+                                               ((uint32_t)4 << 16) |          \
+                                               (((uint32_t)TCP_MSS / 256) << 8) | \
+                                               (TCP_MSS & 255))
+
+/* Global variables: */
+extern struct tcp_pcb *tcp_input_pcb;
+extern uint32_t tcp_ticks;
+
+/* The TCP PCB lists. */
+union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */
+  struct tcp_pcb_listen *listen_pcbs; 
+  struct tcp_pcb *pcbs;
+};
+extern struct tcp_pcb *tcp_bound_pcbs;
+extern union tcp_listen_pcbs_t tcp_listen_pcbs;
+extern struct tcp_pcb *tcp_active_pcbs;  /* List of all TCP PCBs that are in a
+              state in which they accept or send
+              data. */
+extern struct tcp_pcb *tcp_tw_pcbs;      /* List of all TCP PCBs in TIME-WAIT. */
+
+extern struct tcp_pcb *tcp_tmp_pcb;      /* Only used for temporary storage. */
+
+/* Axioms about the above lists:   
+   1) Every TCP PCB that is not CLOSED is in one of the lists.
+   2) A PCB is only in one of the lists.
+   3) All PCBs in the tcp_listen_pcbs list is in LISTEN state.
+   4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state.
+*/
+/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB
+   with a PCB list or removes a PCB from a list, respectively. */
+#ifndef TCP_DEBUG_PCB_LISTS
+#define TCP_DEBUG_PCB_LISTS 0
+#endif
+#if TCP_DEBUG_PCB_LISTS
+#define TCP_REG(pcbs, npcb) do {\
+                            LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \
+                            for(tcp_tmp_pcb = *(pcbs); \
+          tcp_tmp_pcb != NULL; \
+        tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+                                LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \
+                            } \
+                            LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \
+                            (npcb)->next = *(pcbs); \
+                            LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \
+                            *(pcbs) = (npcb); \
+                            LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+              tcp_timer_needed(); \
+                            } while(0)
+#define TCP_RMV(pcbs, npcb) do { \
+                            LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \
+                            LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \
+                            if(*(pcbs) == (npcb)) { \
+                               *(pcbs) = (*pcbs)->next; \
+                            } else for(tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+                               if(tcp_tmp_pcb->next == (npcb)) { \
+                                  tcp_tmp_pcb->next = (npcb)->next; \
+                                  break; \
+                               } \
+                            } \
+                            (npcb)->next = NULL; \
+                            LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+                            LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \
+                            } while(0)
+
+#else /* LWIP_DEBUG */
+
+#define TCP_REG(pcbs, npcb)                        \
+  do {                                             \
+    (npcb)->next = *pcbs;                          \
+    *(pcbs) = (npcb);                              \
+    tcp_timer_needed();                            \
+  } while (0)
+
+#define TCP_RMV(pcbs, npcb)                        \
+  do {                                             \
+    if(*(pcbs) == (npcb)) {                        \
+      (*(pcbs)) = (*pcbs)->next;                   \
+    }                                              \
+    else {                                         \
+      for(tcp_tmp_pcb = *pcbs;                     \
+          tcp_tmp_pcb != NULL;                     \
+          tcp_tmp_pcb = tcp_tmp_pcb->next) {       \
+        if(tcp_tmp_pcb->next == (npcb)) {          \
+          tcp_tmp_pcb->next = (npcb)->next;        \
+          break;                                   \
+        }                                          \
+      }                                            \
+    }                                              \
+    (npcb)->next = NULL;                           \
+  } while(0)
+
+#endif /* LWIP_DEBUG */
+
+
+/* Internal functions: */
+struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb);
+void tcp_pcb_purge(struct tcp_pcb *pcb);
+void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb);
+
+void tcp_segs_free(struct tcp_seg *seg);
+void tcp_seg_free(struct tcp_seg *seg);
+struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg);
+
+#define tcp_ack(pcb)                               \
+  do {                                             \
+    if((pcb)->flags & TF_ACK_DELAY) {              \
+      (pcb)->flags &= ~TF_ACK_DELAY;               \
+      (pcb)->flags |= TF_ACK_NOW;                  \
+    }                                              \
+    else {                                         \
+      (pcb)->flags |= TF_ACK_DELAY;                \
+    }                                              \
+  } while (0)
+
+#define tcp_ack_now(pcb)                           \
+  do {                                             \
+    (pcb)->flags |= TF_ACK_NOW;                    \
+  } while (0)
+
+error_t tcp_send_fin(struct tcp_pcb *pcb);
+error_t tcp_enqueue_flags(struct tcp_pcb *pcb, uint8_t flags);
+
+void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg);
+
+void tcp_rst(uint32_t seqno, uint32_t ackno,
+       ip_addr_t *local_ip, ip_addr_t *remote_ip,
+       uint16_t local_port, uint16_t remote_port);
+
+uint32_t tcp_next_iss(void);
+
+void tcp_keepalive(struct tcp_pcb *pcb);
+void tcp_zero_window_probe(struct tcp_pcb *pcb);
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+uint16_t tcp_eff_send_mss(uint16_t sendmss, ip_addr_t *addr);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+#if LWIP_CALLBACK_API
+error_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, error_t err);
+#endif /* LWIP_CALLBACK_API */
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+void tcp_debug_print(struct tcp_hdr *tcphdr);
+void tcp_debug_print_flags(uint8_t flags);
+void tcp_debug_print_state(enum tcp_state s);
+void tcp_debug_print_pcbs(void);
+s16_t tcp_pcbs_sane(void);
+#else
+#  define tcp_debug_print(tcphdr)
+#  define tcp_debug_print_flags(flags)
+#  define tcp_debug_print_state(s)
+#  define tcp_debug_print_pcbs()
+#  define tcp_pcbs_sane() 1
+#endif /* TCP_DEBUG */
+
+/** External function (implemented in timers.c), called when TCP detects
+ * that a timer is needed (i.e. active- or time-wait-pcb found). */
+void tcp_timer_needed(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_TCP_H__ */
diff --git a/kern/include/net/timers.h b/kern/include/net/timers.h
new file mode 100644 (file)
index 0000000..c7aaa71
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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
+ * Modified to fit Akaros by David Zhu<yuzhu@cs.berkeley.edu>
+ *
+ */
+#ifndef __NET_TIMERS_H__
+#define __NET_TIMERS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LWIP_DEBUG_TIMERNAMES
+#define LWIP_DEBUG_TIMERNAMES 1
+#else
+#define LWIP_DEBUG_TIMERNAMES 0
+#endif
+
+#include <alarm.h>
+
+/** Function prototype for a timeout callback function. Register such a function
+ * using sys_timeout().
+ *
+ * @param arg Additional argument to pass to the function - set up by sys_timeout()
+ */
+typedef void (* sys_timeout_handler)(struct alarm_waiter *arg);
+struct timer_req {
+       sys_timeout_handler func;
+       void *arg;
+};
+#if 0
+struct sys_timeo {
+  struct sys_timeo *next;
+  uint32_t time;
+  sys_timeout_handler h;
+  void *arg;
+#if LWIP_DEBUG_TIMERNAMES
+  const char* handler_name;
+#endif /* LWIP_DEBUG_TIMERNAMES */
+};
+#endif
+void sys_timeouts_init(void);
+
+struct alarm_waiter* sys_timeout(uint32_t msecs, sys_timeout_handler handler, struct alarm_waiter* waiter);
+
+void sys_untimeout(sys_timeout_handler handler, void *arg);
+void sys_check_timeouts(void);
+void sys_restart_timeouts(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NET_TIMERS_H__  */
diff --git a/kern/include/net/udp.h b/kern/include/net/udp.h
new file mode 100644 (file)
index 0000000..6e9f274
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef ROS_KERN_UDP_H
+#define ROS_KERN_UDP_H
+#include <net/pbuf.h>
+#include <net/ip.h>
+#include <net.h>
+#include <bits/netinet.h>
+#include <socket.h>
+
+#define UDP_HLEN 8
+#define UDP_TTL 255
+
+
+struct udp_pcb {
+               IP_PCB;
+    /** ports are in host byte order */
+    uint16_t local_port, remote_port;
+    uint8_t flags;
+               uint8_t pad2;
+    /* Protocol specific PCB members */
+    struct udp_pcb *next;
+               struct socket *pcbsock;
+};
+
+extern struct udp_pcb *udp_pcbs;
+#define GLOBAL_IP 0x0A000001 // 10.0.0.1
+struct udp_pcb * udp_new(void);
+int udp_send(struct udp_pcb *pcb, struct pbuf *p);
+int udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+                    struct in_addr *dst_ip, uint16_t dst_port);
+int udp_bind(struct udp_pcb *pcb, const struct in_addr *ip, uint16_t port);
+int udp_input(struct pbuf *p);
+
+#define UDP_FLAGS_NOCHKSUM       0x01U
+#define UDP_FLAGS_UDPLITE        0x02U
+#define UDP_FLAGS_CONNECTED      0x04U
+#define UDP_FLAGS_MULTICAST_LOOP 0x08U
+
+#endif
index f1b0777..782dc79 100644 (file)
 #define SYS_change_to_m                                29
 #define SYS_poke_ksched                                30
 
+/* Socket Syscalls */
+#define SYS_socket                                     40
+#define SYS_sendto                                     41
+#define SYS_recvfrom                           42
+#define SYS_select          43
+#define SYS_connect                              44
+#define SYS_send                                               45
+#define SYS_recv                                               46
+#define SYS_bind                                               47
+#define SYS_accept                                     48
+#define SYS_listen                               49
+
 /* Platform specific syscalls */
 #define SYS_serial_read                                75
 #define SYS_serial_write                       76
index 6670875..c1c6add 100644 (file)
@@ -47,6 +47,7 @@ typedef size_t uintreg_t;
 })
 #endif
 
+#define ROS_MEM_ALIGN 4
 // Rounding operations (efficient when n is a power of 2)
 // Round down to the nearest multiple of n
 #define ROUNDDOWN(a, n)                                                \
@@ -61,6 +62,8 @@ typedef size_t uintreg_t;
        (typeof(a)) (ROUNDDOWN((uintptr_t) (a) + __n - 1, __n));        \
 })
 
+#define MEM_ALIGN_SIZE(size) ROUNDUP(size, ROS_MEM_ALIGN)
+
 // Round down to the nearest multiple of n
 #define PTRROUNDDOWN(a, n)                                             \
 ({                                                             \
index 9cd2c5b..cfacf10 100644 (file)
@@ -33,7 +33,6 @@ struct syscall {
 
 /* Temp hack, til the rest of glibc/userspace uses sys/syscall.h */
 #include <sys/syscall.h>
-
 #endif /* ifndef ROS_KERNEL */
 
 #endif /* ROS_INC_SYSCALL_H */
diff --git a/kern/include/socket.h b/kern/include/socket.h
new file mode 100644 (file)
index 0000000..89dacb7
--- /dev/null
@@ -0,0 +1,183 @@
+#ifndef ROS_SOCKET_H
+#define ROS_SOCKET_H
+
+#include <ros/common.h>
+#include <sys/queue.h>
+#include <atomic.h>
+#include <net/pbuf.h>
+#include <kthread.h>
+#include <net/ip.h>
+#include <vfs.h>
+// Just a couple of AF types that we might support
+#define AF_UNSPEC      0
+#define AF_UNIX                1       /* Unix domain sockets          */
+#define AF_LOCAL       1       /* POSIX name for AF_UNIX       */
+#define AF_INET                2       /* Internet IP Protocol         */
+
+#define PF_UNSPEC      AF_UNSPEC
+#define PF_UNIX                AF_UNIX
+#define PF_LOCAL       AF_LOCAL
+#define PF_INET                AF_INET
+
+#define        SS_NOFDREF              0x0001  /* no file table ref any more */
+#define        SS_ISCONNECTED          0x0002  /* socket connected to a peer */
+#define        SS_ISCONNECTING         0x0004  /* in process of connecting to peer */
+#define        SS_ISDISCONNECTING      0x0008  /* in process of disconnecting */
+#define        SS_NBIO                 0x0100  /* non-blocking ops */
+#define        SS_ASYNC                0x0200  /* async i/o notify */
+#define        SS_ISCONFIRMING         0x0400  /* deciding to accept connection req */
+#define        SS_ISDISCONNECTED       0x2000  /* socket disconnected from peer */
+
+/* Define an range for automatic port assignment */
+#define SOCKET_PORT_START 4096
+#define SOCKET_PORT_END  0x7fff
+
+struct socket;
+struct proc;
+STAILQ_HEAD(socket_tailq, socket);
+
+// These are probably defined elsewhere too..
+#ifndef socklen_t
+typedef int socklen_t;
+typedef int sa_family_t;
+#endif
+#define inet_addr_to_ipaddr_p(target_ipaddr_p, source_inaddr)   ((target_ipaddr_p) = (ip_addr_t*)&((source_inaddr)->s_addr))
+enum sock_type {
+    SOCK_STREAM = 1,
+    SOCK_DGRAM  = 2,
+    SOCK_RAW    = 3,
+    SOCK_RDM    = 4,
+    SOCK_SEQPACKET  = 5,
+    SOCK_DCCP   = 6,
+    SOCK_PACKET = 10,
+};
+
+struct socket{
+  //int so_count;       /* (b) reference count */
+  short   so_type;        /* (a) generic type, see socket.h */
+       short   so_family;
+       int     so_protocol;
+  short   so_options;     /* from socket call, see socket.h */
+  //short   so_linger;      /* time to linger while closing */
+  short   so_state;       /* (b) internal state flags SS_* */
+       //int so_qstate;      /* (e) internal state flags SQ_* */
+       void    *so_pcb;        /* protocol control block */
+       struct pbuf_head recv_buff;
+       struct pbuf_head send_buff;
+       struct semaphore sem;
+       struct semaphore accept_sem;
+       spinlock_t waiter_lock;
+       struct semaphore_list waiters;   /* semaphone to for a process to sleep on */
+       struct socket_tailq acceptq;
+       STAILQ_ENTRY(socket) next;
+       //struct  vnet *so_vnet;      /* network stack instance */
+       //struct  protosw *so_proto;  /* (a) protocol handle */
+};
+
+
+/* members are in network byte order */
+struct sockaddr_in {
+    // uint8_t sin_len; -- bsd only field
+    uint8_t sin_family;
+    uint16_t sin_port;
+    struct in_addr sin_addr;
+    char sin_zero[8];
+};
+
+
+struct sockaddr {
+       unsigned char   sa_len;         /* bsd only total length */
+       sa_family_t     sa_family;      /* address family */
+       char            sa_data[14];    /* actually longer; address value */
+};
+
+/*
+ * Message header for recvmsg and sendmsg calls.
+ * Used value-result for recvmsg, value only for sendmsg.
+ */ 
+struct msghdr {
+    void        *msg_name;      /* optional address */
+    socklen_t    msg_namelen;       /* size of address */
+    struct iovec    *msg_iov;       /* scatter/gather array */
+    int      msg_iovlen;        /* # elements in msg_iov */
+    void        *msg_control;       /* ancillary data, see below */
+    socklen_t    msg_controllen;    /* ancillary data buffer len */
+    int      msg_flags;     /* flags on received message */
+};
+
+
+/* Socket-level options for `getsockopt' and `setsockopt'.  */
+enum
+  {
+    SO_DEBUG = 0x0001,         /* Record debugging information.  */
+#define SO_DEBUG SO_DEBUG
+    SO_ACCEPTCONN = 0x0002,    /* Accept connections on socket.  */
+#define SO_ACCEPTCONN SO_ACCEPTCONN
+    SO_REUSEADDR = 0x0004,     /* Allow reuse of local addresses.  */
+#define SO_REUSEADDR SO_REUSEADDR
+    SO_KEEPALIVE = 0x0008,     /* Keep connections alive and send
+                                  SIGPIPE when they die.  */
+#define SO_KEEPALIVE SO_KEEPALIVE
+    SO_DONTROUTE = 0x0010,     /* Don't do local routing.  */
+#define SO_DONTROUTE SO_DONTROUTE
+    SO_BROADCAST = 0x0020,     /* Allow transmission of
+                                  broadcast messages.  */
+#define SO_BROADCAST SO_BROADCAST
+    SO_USELOOPBACK = 0x0040,   /* Use the software loopback to avoid
+                                  hardware use when possible.  */
+#define SO_USELOOPBACK SO_USELOOPBACK
+    SO_LINGER = 0x0080,                /* Block on close of a reliable
+                                  socket to transmit pending data.  */
+#define SO_LINGER SO_LINGER
+    SO_OOBINLINE = 0x0100,     /* Receive out-of-band data in-band.  */
+#define SO_OOBINLINE SO_OOBINLINE
+    SO_REUSEPORT = 0x0200,     /* Allow local address and port reuse.  */
+#define SO_REUSEPORT SO_REUSEPORT
+    SO_SNDBUF = 0x1001,                /* Send buffer size.  */
+#define SO_SNDBUF SO_SNDBUF
+    SO_RCVBUF = 0x1002,                /* Receive buffer.  */
+#define SO_RCVBUF SO_RCVBUF
+    SO_SNDLOWAT = 0x1003,      /* Send low-water mark.  */
+#define SO_SNDLOWAT SO_SNDLOWAT
+    SO_RCVLOWAT = 0x1004,      /* Receive low-water mark.  */
+#define SO_RCVLOWAT SO_RCVLOWAT
+    SO_SNDTIMEO = 0x1005,      /* Send timeout.  */
+#define SO_SNDTIMEO SO_SNDTIMEO
+    SO_RCVTIMEO = 0x1006,      /* Receive timeout.  */
+#define SO_RCVTIMEO SO_RCVTIMEO
+    SO_ERROR = 0x1007,         /* Get and clear error status.  */
+#define SO_ERROR SO_ERROR
+    SO_STYLE = 0x1008,         /* Get socket connection style.  */
+#define SO_STYLE SO_STYLE
+    SO_TYPE = SO_STYLE         /* Compatible name for SO_STYLE.  */
+#define SO_TYPE SO_TYPE
+  };
+#define SO_INHERITED   (SO_REUSEADDR|SO_KEEPALIVE|SO_LINGER)
+
+extern struct kmem_cache *sock_kcache;
+extern struct kmem_cache *mbuf_kcache;
+extern struct kmem_cache *udp_pcb_kcache;
+extern struct kmem_cache *tcp_pcb_kcache;
+extern struct kmem_cache *tcp_pcb_listen_kcache;
+extern struct kmem_cache *tcp_segment_kcache;
+
+
+void socket_init();
+intreg_t send_iov(struct socket* sock, struct iovec* iov, int flags);
+int send_datagram(struct socket* sock, struct iovec* iov, int flags);
+
+intreg_t sys_socket(struct proc *p, int socket_family, int socket_type, int protocol);
+intreg_t sys_sendto(struct proc *p, int socket, const void *buffer, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
+intreg_t sys_recvfrom(struct proc *p, int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len);
+intreg_t sys_select(struct proc *p, int nfds, fd_set *readfds, fd_set *writefds,
+                               fd_set *exceptfds, struct timeval *timeout);
+intreg_t sys_connect(struct proc *p, int sockfd, const struct sockaddr *addr, socklen_t addrlen);
+intreg_t sys_send(struct proc *p, int sockfd, const void *buf, size_t len, int flags);
+intreg_t sys_recv(struct proc *p, int sockfd, void *buf, size_t len, int flags);
+intreg_t sys_bind(struct proc* p, int sockfd, const struct sockaddr *addr, socklen_t addrlen);
+intreg_t sys_accept(struct proc *p, int sockfd, struct sockaddr *addr, socklen_t *addrlen);
+intreg_t sys_listen(struct proc *p, int sockfd, int backlog);
+
+
+#endif /* ROS_SOCKET_H */
+
diff --git a/kern/include/sys/uio.h b/kern/include/sys/uio.h
new file mode 100644 (file)
index 0000000..0981f89
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _ROS_SYS_UIO_H
+#define _ROS_SYS_UIO_H
+
+#include <sys/types.h>
+
+typedef long off_t;
+
+enum uio_rw {
+       UIO_READ,
+       UIO_WRITE
+};
+
+/* Segment flag values. */
+enum uio_seg {
+       UIO_USERSPACE,          /* from user data space */
+       UIO_SYSSPACE,           /* from system space */
+       UIO_NOCOPY              /* don't copy, already in object */
+};
+
+// Straight out of bsd definition
+struct iovec {
+    void    *iov_base;  /* Base address. */
+    size_t   iov_len;   /* Length. */
+};
+
+struct uio {
+       struct  iovec *uio_iov;         /* scatter/gather list */
+       int     uio_iovcnt;             /* length of scatter/gather list */
+       off_t   uio_offset;             /* offset in target object */
+       ssize_t uio_resid;              /* remaining bytes to process */
+       enum    uio_seg uio_segflg;     /* address space */
+       enum    uio_rw uio_rw;          /* operation */
+       struct  proc *uio_proc;         /* owner */
+};
+
+
+
+#endif
index c3292ae..28bdec6 100644 (file)
@@ -17,6 +17,11 @@ struct itimerspec {
   struct timespec  it_value;     /* Timer expiration */
 };
 
+struct timeval {
+       time_t tv_sec;  /* seconds */
+       time_t tv_usec; /* microseconds */
+};
+
 void train_timing();
 void udelay(uint64_t usec);
 uint64_t tsc2sec(uint64_t tsc_time);
index 219ddbd..4d2f809 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <ros/common.h>
 #include <sys/queue.h>
+#include <sys/uio.h>
 #include <bitmask.h>
 #include <kref.h>
 #include <time.h>
@@ -31,7 +32,6 @@ struct vm_region;
 typedef int dev_t;
 typedef int kdev_t;
 typedef int ino_t;
-typedef long off_t; // out there in other .h's, but not in the kernel yet
 struct io_writeback    {int x;};
 struct event_poll {int x;};
 struct poll_table_struct {int x;};
@@ -50,11 +50,6 @@ struct file_operations;
 struct fs_type;
 struct vfsmount;
 
-struct iovec {
-    void *iov_base;
-    size_t iov_len;
-};
-
 /* List def's we need */
 TAILQ_HEAD(sb_tailq, super_block);
 TAILQ_HEAD(dentry_tailq, dentry);
@@ -282,7 +277,7 @@ struct file {
        int                                                     f_error;
        struct event_poll_tailq         f_ep_links;
        spinlock_t                                      f_ep_lock;
-       void                                            *f_fs_info;             /* tty driver hook */
+       void                                            *f_privdata;    /* tty/socket driver hook */
        struct page_map                         *f_mapping;             /* page cache mapping */
 
        /* Ghetto appserver support */
@@ -348,13 +343,22 @@ struct vfsmount {
  * could just use the fd_array to check for openness instead of the bitmask,
  * but eventually we might want to use the bitmasks for other things (like
  * which files are close_on_exec. */
-struct fd_set {
+
+typedef struct fd_set {
     uint8_t fds_bits[BYTES_FOR_BITMASK(NR_FILE_DESC_MAX)];
-};
+} fd_set;
+
+
 struct small_fd_set {
     uint8_t fds_bits[BYTES_FOR_BITMASK(NR_FILE_DESC_DEFAULT)];
 };
 
+/* Helper macros to manage fd_sets */
+#define FD_SET(n, p)  ((p)->fds_bits[(n)/8] |=  (1 << ((n) & 7)))
+#define FD_CLR(n, p)  ((p)->fds_bits[(n)/8] &= ~(1 << ((n) & 7)))
+#define FD_ISSET(n,p) ((p)->fds_bits[(n)/8] &   (1 << ((n) & 7)))
+#define FD_ZERO(p)    memset((void*)(p),0,sizeof(*(p)))
+
 /* Describes an open file.  We need this, since the FD flags are supposed to be
  * per file descriptor, not per file (like the file status flags). */
 struct file_desc {
@@ -374,7 +378,7 @@ struct files_struct {
        struct file_desc                        fd_array[NR_OPEN_FILES_DEFAULT];
 };
 
-/* Process specific filesysten info */
+/* Process specific filesystem info */
 struct fs_struct {
        spinlock_t                                      lock;
        int                                                     umask;
@@ -447,6 +451,7 @@ ssize_t generic_file_write(struct file *file, const char *buf, size_t count,
                            off64_t *offset);
 ssize_t generic_dir_read(struct file *file, char *u_buf, size_t count,
                          off64_t *offset);
+struct file *alloc_file(void);
 struct file *do_file_open(char *path, int flags, int mode);
 int do_symlink(char *path, const char *symname, int mode);
 int do_link(char *old_path, char *new_path);
index 76d9b1e..1adf3f5 100644 (file)
@@ -60,6 +60,14 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/ucq.c \
                  $(KERN_SRC_DIR)/console.c \
                  $(KERN_SRC_DIR)/trap.c \
+                 $(KERN_SRC_DIR)/socket.c \
+                 $(KERN_SRC_DIR)/net/udp.c \
+                 $(KERN_SRC_DIR)/net/tcp.c \
+                 $(KERN_SRC_DIR)/net/ip.c \
+                 $(KERN_SRC_DIR)/net/pbuf.c \
+                 $(KERN_SRC_DIR)/net/tcp_out.c \
+                 $(KERN_SRC_DIR)/net/tcp_in.c \
+                 $(KERN_SRC_DIR)/net/timers.c \
                  $(KERN_SRC_DIR)/arsc.c
 
 # Only build files if they exist.
index bc7bb87..f5d20db 100644 (file)
@@ -71,8 +71,9 @@ static void eth_audio_sendpacket(void *buf)
         * Eth-audio device shouldn't care (and Linux seems to be okay with packets
         * that have no checksum (but not a wrong checksum)).  Technically, this
         * hurts our performance a bit (and some NICs can offload this). */
-       eth_udp_out.udp_hdr.checksum = htons(udp_checksum(&eth_udp_out.ip_hdr,
-                                                         &eth_udp_out.udp_hdr));
+       eth_udp_out.udp_hdr.checksum = 0;
+       eth_udp_out.udp_hdr.checksum = udp_checksum(&eth_udp_out.ip_hdr,
+                                                         &eth_udp_out.udp_hdr);
        /* Send it out */
        retval = send_frame((const char*)&eth_udp_out, ETH_AUDIO_FRAME_SZ);
        assert(retval >= 0);
@@ -100,7 +101,8 @@ static void eth_audio_prep_response(struct ethaud_udp_packet *incoming,
        outgoing->ip_hdr.src_addr = htonl(ntohl(incoming->ip_hdr.src_addr) + 1);
        outgoing->ip_hdr.dst_addr = incoming->ip_hdr.src_addr;
        /* Since the IP header is set already, we can compute the checksum. */
-       outgoing->ip_hdr.checksum = htons(ip_checksum(&outgoing->ip_hdr));
+       outgoing->ip_hdr.checksum = 0;
+       outgoing->ip_hdr.checksum = ip_checksum(&outgoing->ip_hdr);
        outgoing->udp_hdr.src_port = htons(ETH_AUDIO_SRC_PORT);
        outgoing->udp_hdr.dst_port = htons(ETH_AUDIO_DST_PORT);
        outgoing->udp_hdr.length = htons(ETH_AUDIO_PAYLOAD_SZ + UDP_HDR_SZ);
index dd1a027..2f323f5 100644 (file)
@@ -43,6 +43,7 @@
 #include <ext2fs.h>
 #include <kthread.h>
 #include <net.h>
+#include <socket.h>
 #include <eth_audio.h>
 #include <console.h>
 
@@ -100,6 +101,7 @@ void kernel_init(multiboot_info_t *mboot_info)
        arch_init();
        block_init();
        enable_irq();
+       socket_init();
 #ifdef __CONFIG_EXT2FS__
        mount_fs(&ext2_fs_type, "/dev/ramdisk", "/mnt", 0);
 #endif /* __CONFIG_EXT2FS__ */
index cd8d98b..bdba916 100644 (file)
 
 #include <net.h>
 #include <stdio.h>
+#include <arch/types.h>
 
-uint16_t __ip_checksum(void *buf, unsigned int len, uint32_t sum)
+#ifndef FOLD_U32T
+#define FOLD_U32T(u)          (((u) >> 16) + ((u) & 0x0000ffffUL))
+#endif
+
+/* New version of ip_checksum, notice this version does not change it into 
+ * network order. It is useful to keep it in host order for further processing
+ */ 
+uint16_t __ip_checksum(void *dataptr, unsigned int len, uint32_t sum)
 {
-       /* Knock out 2 bytes at a time */
-       while (len > 1) {
-               /* Careful of endianness.  The packet is in network ordering */
-               sum += ntohs(*((uint16_t*)buf));
-               buf += sizeof(uint16_t);
-               /* In case we get close to overflowing while summing. */
-               if (sum & 0x80000000)
-                       sum = (sum & 0xFFFF) + (sum >> 16);
-               len -= 2;
-       }
-       /* Handle the last byte, if any */
-       if (len)
-               sum += *(uint8_t*)buf;
-       /* Add the top 16 bytes to the lower ones, til it is done */
-       while (sum >> 16)
-               sum = (sum & 0xFFFF) + (sum >> 16);
-       return ~sum;
+  uint8_t *pb = (uint8_t *)dataptr;
+  uint16_t *ps, t = 0;
+  int odd = ((uintptr_t)pb & 1);
+
+  /* Get aligned to uint16_t */
+       // this means pb started on some weird address..
+       // but in our world this should never happen.. since the payload should always be aligned..
+       // right..
+
+  if (odd && len > 0) {
+               // change the second half of t to what pb is
+               // and advance pb
+    ((uint8_t *)&t)[1] = *pb++;
+    len--;
+  }
+
+  /* Add the bulk of the data */
+  ps = (uint16_t *)(void *)pb;
+  while (len > 1) {
+    sum += *ps++;
+    len -= 2;
+  }
+
+  /* Consume left-over byte, if any */
+  if (len > 0) {
+    ((uint8_t *)&t)[0] = *(uint8_t *)ps;
+  }
+
+  /* Add end bytes */
+  sum += t;
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is propably faster than if statements... */
+  sum = FOLD_U32T(sum);
+  sum = FOLD_U32T(sum);
+
+  /* Swap if alignment was odd */
+  if (odd) {
+    sum = byte_swap16(sum);
+  }
+
+  return (uint16_t)sum;
 }
 
 /* Computes the checksum for the IP header.  We could write it in, but for now
@@ -42,28 +75,67 @@ uint16_t __ip_checksum(void *buf, unsigned int len, uint32_t sum)
 uint16_t ip_checksum(struct ip_hdr *ip_hdr)
 {
        unsigned int ip_hdr_len = ip_hdr->hdr_len * sizeof(uint32_t);
-       ip_hdr->checksum = 0;
-       return __ip_checksum(ip_hdr, ip_hdr_len, 0);
+       return ~__ip_checksum(ip_hdr, ip_hdr_len, 0);
 }
 
 /* Computes the checksum for the UDP header.  We could write it in, but for now
  * we'll return the checksum (in host-ordering) and have the caller store the
  * value.  Note that the UDP header needs info from the IP header (strictly
- * speaking, just the src and destination IPs).  */
+ * speaking, just the src and destination IPs).  
+ * LEGACY: only used for packet that are not in pbuf formats*/
 uint16_t udp_checksum(struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr)
 {
        /* Add up the info for the UDP pseudo-header */
        uint32_t udp_pseudosum = 0;
        uint16_t udp_len = ntohs(udp_hdr->length);
-       udp_hdr->checksum = 0;
-       udp_pseudosum += ntohs(ip_hdr->src_addr & 0xffff);
-       udp_pseudosum += ntohs(ip_hdr->src_addr >> 16);
-       udp_pseudosum += ntohs(ip_hdr->dst_addr & 0xffff);
-       udp_pseudosum += ntohs(ip_hdr->dst_addr >> 16);
-       udp_pseudosum += ip_hdr->protocol;
-       udp_pseudosum += udp_len;
-       return __ip_checksum(udp_hdr, udp_len, udp_pseudosum);
+       udp_pseudosum += (ip_hdr->src_addr & 0xffff);
+       udp_pseudosum += (ip_hdr->src_addr >> 16);
+       udp_pseudosum += (ip_hdr->dst_addr & 0xffff);
+       udp_pseudosum += (ip_hdr->dst_addr >> 16);
+       udp_pseudosum += (ip_hdr->protocol);
+       udp_pseudosum += udp_hdr->length;
+       return ~(__ip_checksum(udp_hdr, udp_len, udp_pseudosum));
+}
+
+/* ip addresses need to be network order, protolen and proto are HO */
+uint16_t inet_chksum_pseudo(struct pbuf *p, uint32_t src, uint32_t dest, uint8_t proto, uint16_t proto_len) {
+  uint32_t acc;
+  uint32_t addr;
+  struct pbuf *q;
+  uint8_t swapped;
+
+  acc = 0;
+  swapped = 0;
+  /* iterate through all pbuf in chain */
+  for(q = p; q != NULL; q = STAILQ_NEXT(q, next)) {
+    acc += __ip_checksum(q->payload, q->len, 0);
+    acc = FOLD_U32T(acc);
+    if (q->len % 2 != 0) {
+      swapped = 1 - swapped;
+      acc = byte_swap16(acc);
+    }
+  }
+
+  if (swapped) {
+    acc = byte_swap16(acc);
+  }
+
+  addr = (src);
+  acc += (addr & 0xffffUL);
+  acc += ((addr >> 16) & 0xffffUL);
+  addr = (dest);
+  acc += (addr & 0xffffUL);
+  acc += ((addr >> 16) & 0xffffUL);
+  acc += (uint32_t)htons((uint16_t)proto);
+  acc += (uint32_t)htons(proto_len);
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is propably faster than if statements... */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+  return (uint16_t)~(acc & 0xffffUL);
 }
+
 /* Print out a network packet in the same format as tcpdump, making it easier 
  * to compare */
 void dumppacket(unsigned char *buff, size_t len)
diff --git a/kern/src/net/ip.c b/kern/src/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/src/net/pbuf.c b/kern/src/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/src/net/tcp.c b/kern/src/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/src/net/tcp_in.c b/kern/src/net/tcp_in.c
new file mode 100644 (file)
index 0000000..ce08f3b
--- /dev/null
@@ -0,0 +1,1587 @@
+/**
+ * @file
+ * Transmission Control Protocol, incoming traffic
+ *
+ * The input processing functions of the TCP layer.
+ *
+ * These functions are generally called in the order (ip_input() ->)
+ * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
+ * 
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "net/tcp.h"
+#include "net/tcp_impl.h"
+#include "bits/netinet.h"
+#include "net/pbuf.h"
+#include "debug.h"
+#include "error.h"
+#include <string.h>
+
+/* These variables are global to all functions involved in the input
+   processing of TCP segments. They are set by the tcp_input()
+   function. */
+// static struct tcp_hdr *tcphdr;
+// static struct tcp_seg inseg;
+
+//static uint32_t seqno, ackno;
+//static uint8_t flags;
+//static uint16_t tcplen;
+
+static uint8_t recv_flags;
+static struct pbuf *recv_data;
+
+struct tcp_pcb *tcp_input_pcb;
+
+/* Forward declarations. */
+static error_t tcp_process(struct tcp_pcb *pcb, struct tcp_seg *insegp, struct ip_hdr *iphdr, uint16_t tcplen);
+static void tcp_receive(struct tcp_pcb *pcb, struct tcp_seg *insegp, uint16_t tcplen);
+static void tcp_parseopt(struct tcp_pcb *pcb, struct tcp_hdr *tcphdr);
+
+static error_t tcp_listen_input(struct tcp_pcb_listen *pcb, struct tcp_seg *inseg, struct ip_hdr *iphdr, uint16_t tcplen);
+static error_t tcp_timewait_input(struct tcp_pcb *pcb, struct tcp_seg *inseg, struct ip_hdr *iphdr, uint16_t tcplen);
+
+/**
+ * The initial input processing of TCP. It verifies the TCP header, demultiplexes
+ * the segment between the PCBs and passes it on to tcp_process(), which implements
+ * the TCP finite state machine. This function is called by the IP layer (in
+ * ip_input()).
+ *
+ * @param p received TCP segment to process (p->payload pointing to the IP header)
+ * @param inp network interface on which this segment was received
+ */
+void
+tcp_input(struct pbuf *p)
+{
+  struct tcp_pcb *pcb, *prev;
+  struct tcp_pcb_listen *lpcb;
+#if SO_REUSE
+  struct tcp_pcb *lpcb_prev = NULL;
+  struct tcp_pcb_listen *lpcb_any = NULL;
+#endif /* SO_REUSE */
+  uint8_t hdrlen;
+  error_t err;
+
+  // TCP_STATS_INC(tcp.recv);
+  // snmp_inc_tcpinsegs();
+
+       struct tcp_seg inseg;
+  struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+  struct tcp_hdr *tcphdr = (struct tcp_hdr *)((uint8_t *)p->payload + (iphdr->hdr_len) * 4);
+       ip_addr_t current_iphdr_src = {iphdr->src_addr};
+       ip_addr_t current_iphdr_dest = {iphdr->dst_addr};
+
+#if TCP_INPUT_DEBUG
+  tcp_debug_print(tcphdr);
+#endif
+
+  /* remove header from payload */
+  if (pbuf_header(p, -((int16_t)((iphdr->hdr_len) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) {
+    /* drop short packets */
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
+    //TCP_STATS_INC(tcp.lenerr);
+    //TCP_STATS_INC(tcp.drop);
+    //snmp_inc_tcpinerrs();
+    pbuf_free(p);
+    return;
+  }
+
+  /* Don't even process incoming broadcasts/multicasts. 
+  if (ip_addr_isbroadcast(&current_iphdr_dest, inp) ||
+      ip_addr_ismulticast(&current_iphdr_dest)) {
+    TCP_STATS_INC(tcp.proterr);
+    TCP_STATS_INC(tcp.drop);
+    snmp_inc_tcpinerrs();
+    pbuf_free(p);
+    return;
+  } */
+
+#if CHECKSUM_CHECK_TCP
+  /* Verify TCP checksum. */
+  if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+      IP_PROTO_TCP, p->tot_len) != 0) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
+        inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+      IP_PROTO_TCP, p->tot_len)));
+#if TCP_DEBUG
+    tcp_debug_print(tcphdr);
+#endif /* TCP_DEBUG */
+    TCP_STATS_INC(tcp.chkerr);
+    TCP_STATS_INC(tcp.drop);
+    // snmp_inc_tcpinerrs();
+    pbuf_free(p);
+    return;
+  }
+#endif
+
+  /* Move the payload pointer in the pbuf so that it points to the
+     TCP data instead of the TCP header. */
+  hdrlen = TCPH_HDRLEN(tcphdr);
+  if(pbuf_header(p, -(hdrlen * 4))){
+    /* drop short packets */
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
+    //TCP_STATS_INC(tcp.lenerr);
+    //TCP_STATS_INC(tcp.drop);
+    //snmp_inc_tcpinerrs();
+    pbuf_free(p);
+    return;
+  }
+
+  /* Convert fields in TCP header to host byte order. */
+  tcphdr->src = ntohs(tcphdr->src);
+  tcphdr->dest = ntohs(tcphdr->dest);
+  uint32_t seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
+  uint32_t ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
+  tcphdr->wnd = ntohs(tcphdr->wnd);
+
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+       // if the packet is a FIN or a SYN, add 1?
+  uint16_t tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);
+
+  /* Demultiplex an incoming segment. First, we check if it is destined
+     for an active connection. */
+  prev = NULL;
+
+  // XXX: find_pcb? 
+  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
+    LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+    LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
+    if (pcb->remote_port == tcphdr->src &&
+       pcb->local_port == tcphdr->dest &&
+       ip_addr_cmp(&(pcb->remote_ip), &current_iphdr_src) &&
+       ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest)) {
+
+      /* Move this PCB to the front of the list so that subsequent
+         lookups will be faster (we exploit locality in TCP segment
+         arrivals). */
+      LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
+      if (prev != NULL) {
+        prev->next = pcb->next;
+        pcb->next = tcp_active_pcbs;
+        tcp_active_pcbs = pcb;
+      }
+      LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
+      break;
+    }
+    prev = pcb;
+  }
+
+  if (pcb == NULL) {
+    /* If it did not go to an active connection, we check the connections
+       in the TIME-WAIT state. */
+    for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+      LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+      if (pcb->remote_port == tcphdr->src &&
+         pcb->local_port == tcphdr->dest &&
+         ip_addr_cmp(&(pcb->remote_ip), &current_iphdr_src) &&
+         ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest)) {
+        /* We don't really care enough to move this PCB to the front
+           of the list since we are not very likely to receive that
+           many segments for connections in TIME-WAIT. */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
+        tcp_timewait_input(pcb, &inseg, iphdr, tcplen);
+        pbuf_free(p);
+        return;
+      }
+    }
+
+    /* Finally, if we still did not get a match, we check all PCBs that
+       are LISTENing for incoming connections. */
+    prev = NULL;
+    for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+      if (lpcb->local_port == tcphdr->dest) {
+#if SO_REUSE
+        if (ip_addr_cmp(&(lpcb->local_ip), &current_iphdr_dest)) {
+          /* found an exact match */
+          break;
+        } else if(ip_addr_isany(&(lpcb->local_ip))) {
+          /* found an ANY-match */
+          lpcb_any = lpcb;
+          lpcb_prev = prev;
+        }
+#else /* SO_REUSE */
+        if (ip_addr_cmp(&(lpcb->local_ip), &current_iphdr_dest) ||
+            ip_addr_isany(&(lpcb->local_ip))) {
+          /* found a match */
+          break;
+        }
+#endif /* SO_REUSE */
+      }
+      prev = (struct tcp_pcb *)lpcb;
+    }
+#if SO_REUSE
+    /* first try specific local IP */
+    if (lpcb == NULL) {
+      /* only pass to ANY if no specific local IP has been found */
+      lpcb = lpcb_any;
+      prev = lpcb_prev;
+    }
+#endif /* SO_REUSE */
+    if (lpcb != NULL) {
+      /* Move this PCB to the front of the list so that subsequent
+         lookups will be faster (we exploit locality in TCP segment
+         arrivals). */
+      if (prev != NULL) {
+        ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
+              /* our successor is the remainder of the listening list */
+        lpcb->next = tcp_listen_pcbs.listen_pcbs;
+              /* put this listening pcb at the head of the listening list */
+        tcp_listen_pcbs.listen_pcbs = lpcb;
+      }
+    
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
+      tcp_listen_input(lpcb, &inseg, iphdr, tcplen);
+      pbuf_free(p);
+      return;
+    }
+  }
+
+#if TCP_INPUT_DEBUG
+  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
+  tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
+#endif /* TCP_INPUT_DEBUG */
+
+
+  if (pcb != NULL) {
+    /* The incoming segment belongs to a connection. */
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+    tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+
+    /* Set up a tcp_seg structure. */
+    inseg.next = NULL;
+    inseg.len = p->tot_len;
+    inseg.dataptr = p->payload;
+    inseg.p = p;
+    inseg.tcphdr = tcphdr;
+
+    recv_data = NULL;
+    recv_flags = 0;
+
+    /* If there is data which was previously "refused" by upper layer */
+    if (pcb->refused_data != NULL) {
+      /* Notify again application with data previously received. */
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
+      TCP_EVENT_RECV(pcb, pcb->refused_data, ESUCCESS, err);
+                       // XXX: err is a global, need better indication of whether the socket receive was successful
+      if (err == ESUCCESS) {
+        pcb->refused_data = NULL;
+      } else if ((err == -ECONNABORTED) || (tcplen > 0)) {
+        /* if err == -ECONNABORTED, 'pcb' is already deallocated */
+        /* Drop incoming packets because pcb is "full" (only if the incoming
+           segment contains data). */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
+        //TCP_STATS_INC(tcp.drop);
+        //snmp_inc_tcpinerrs();
+        pbuf_free(p);
+        return;
+      }
+    }
+    tcp_input_pcb = pcb;
+    err = tcp_process(pcb, &inseg, iphdr, tcplen);
+    /* A return value of -ECONNABORTED means that tcp_abort() was called
+       and that the pcb has been freed. If so, we don't do anything. */
+    if (err != -ECONNABORTED) {
+      if (recv_flags & TF_RESET) {
+        /* TF_RESET means that the connection was reset by the other
+           end. We then call the error callback to inform the
+           application that the connection is dead before we
+           deallocate the PCB. */
+        // XXX: worry about connection reset later
+                               //TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
+        tcp_pcb_remove(&tcp_active_pcbs, pcb);
+                               kmem_cache_free(tcp_pcb_kcache, pcb);
+      } else if (recv_flags & TF_CLOSED) {
+        /* The connection has been closed and we will deallocate the
+           PCB. */
+        tcp_pcb_remove(&tcp_active_pcbs, pcb);
+                               kmem_cache_free(tcp_pcb_kcache, pcb);
+      } else {
+        err = ESUCCESS;
+        /* If the application has registered a "sent" function to be
+           called when new send buffer space is available, we call it
+           now. */
+        if (pcb->acked > 0) {
+          TCP_EVENT_SENT(pcb, pcb->acked, err);
+          if (err == -ECONNABORTED) {
+            goto aborted;
+          }
+        }
+
+        if (recv_data != NULL) {
+          LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
+          if (pcb->flags & TF_RXCLOSED) {
+            /* received data although already closed -> abort (send RST) to
+               notify the remote host that not all data has been processed */
+            pbuf_free(recv_data);
+            tcp_abort(pcb);
+            goto aborted;
+          }
+          if (flags & TCP_PSH) {
+            recv_data->flags |= PBUF_FLAG_PUSH;
+          }
+
+          /* Notify application that data has been received. */
+          TCP_EVENT_RECV(pcb, recv_data, ESUCCESS, err);
+          if (err == -ECONNABORTED) {
+            goto aborted;
+          }
+
+          /* If the upper layer can't receive this data, store it */
+          if (err != ESUCCESS) {
+            pcb->refused_data = recv_data;
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
+          }
+        }
+
+        /* If a FIN segment was received, we call the callback
+           function with a NULL buffer to indicate EOF. */
+        if (recv_flags & TF_GOT_FIN) {
+          /* correct rcv_wnd as the application won't call tcp_recved()
+             for the FIN's seqno */
+          if (pcb->rcv_wnd != TCP_WND) {
+            pcb->rcv_wnd++;
+          }
+          TCP_EVENT_CLOSED(pcb, err);
+          if (err == -ECONNABORTED) {
+            goto aborted;
+          }
+        }
+
+        tcp_input_pcb = NULL;
+        /* Try to send something out. */
+        tcp_output(pcb);
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+        tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+      }
+    }
+    /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
+       Below this line, 'pcb' may not be dereferenced! */
+aborted:
+    tcp_input_pcb = NULL;
+    recv_data = NULL;
+
+    /* give up our reference to inseg.p */
+    if (inseg.p != NULL)
+    {
+      pbuf_free(inseg.p);
+      inseg.p = NULL;
+    }
+  } else {
+
+    /* If no matching PCB was found, send a TCP RST (reset) to the
+       sender. */
+    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
+    if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
+      //TCP_STATS_INC(tcp.proterr);
+      //TCP_STATS_INC(tcp.drop);
+      tcp_rst(ackno, seqno + tcplen,
+        &current_iphdr_dest, &current_iphdr_src,
+        tcphdr->dest, tcphdr->src);
+    }
+    pbuf_free(p);
+  }
+
+  LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a listening
+ * connection (from tcp_input()).
+ *
+ * @param pcb the tcp_pcb_listen for which a segment arrived
+ * @return ESUCCESS if the segment was processed
+ *         another error_t on error
+ *
+ * @note the return value is not (yet?) used in tcp_input()
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static error_t
+tcp_listen_input(struct tcp_pcb_listen *pcb, struct tcp_seg *insegp, struct ip_hdr *iphdr, uint16_t tcplen)
+{
+  struct tcp_pcb *npcb;
+       struct tcp_hdr *tcphdr = insegp->tcphdr;
+
+  error_t rc;
+
+       ip_addr_t current_iphdr_src = {iphdr->src_addr};
+       ip_addr_t current_iphdr_dest = {iphdr->dst_addr};
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+  uint32_t ackno = tcphdr->ackno;
+  uint32_t seqno = tcphdr->seqno;
+  /* In the LISTEN state, we check for incoming SYN segments,
+     creates a new PCB, and responds with a SYN|ACK. */
+  if (flags & TCP_ACK) {
+    /* For incoming segments with the ACK flag set, respond with a
+       RST. */
+    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
+    tcp_rst(ackno + 1, seqno + tcplen,
+      &current_iphdr_dest, &current_iphdr_src,
+      tcphdr->dest, tcphdr->src);
+  } else if (flags & TCP_SYN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
+#if TCP_LISTEN_BACKLOG
+    if (pcb->accepts_pending >= pcb->backlog) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
+      return -ECONNABORTED;
+    }
+#endif /* TCP_LISTEN_BACKLOG */
+    npcb = tcp_alloc(pcb->prio);
+    /* If a new PCB could not be created (probably due to lack of memory),
+       we don't do anything, but rely on the sender will retransmit the
+       SYN at a time when we have more memory available. */
+    if (npcb == NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
+      //TCP_STATS_INC(tcp.memerr);
+      return -ENOMEM;
+    }
+#if TCP_LISTEN_BACKLOG
+    pcb->accepts_pending++;
+#endif /* TCP_LISTEN_BACKLOG */
+    /* Set up the new PCB. */
+    ip_addr_copy(npcb->local_ip, current_iphdr_dest);
+    npcb->local_port = pcb->local_port;
+    ip_addr_copy(npcb->remote_ip, current_iphdr_src);
+    npcb->remote_port = tcphdr->src;
+    npcb->state = SYN_RCVD;
+    npcb->rcv_nxt = seqno + 1;
+    npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+    npcb->snd_wnd = tcphdr->wnd;
+    npcb->ssthresh = npcb->snd_wnd;
+    npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+    npcb->callback_arg = pcb->callback_arg;
+#if LWIP_CALLBACK_API
+    npcb->accept = pcb->accept;
+#endif /* LWIP_CALLBACK_API */
+    /* inherit socket options */
+    npcb->so_options = pcb->so_options & SO_INHERITED;
+    /* Register the new PCB so that we can begin receiving segments
+       for it. */
+    TCP_REG(&tcp_active_pcbs, npcb);
+
+    /* Parse any options in the SYN. */
+    tcp_parseopt(npcb, tcphdr);
+#if TCP_CALCULATE_EFF_SEND_MSS
+    npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+    // snmp_inc_tcppassiveopens();
+
+    /* Send a SYN|ACK together with the MSS option. */
+    rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
+    if (rc != ESUCCESS) {
+      tcp_abandon(npcb, 0);
+      return rc;
+    }
+    return tcp_output(npcb);
+  }
+  return ESUCCESS;
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a connection in
+ * TIME_WAIT.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static error_t tcp_timewait_input(struct tcp_pcb *pcb, struct tcp_seg *insegp, struct ip_hdr *iphdr, uint16_t tcplen)
+{
+  /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
+  /* RFC 793 3.9 Event Processing - Segment Arrives:
+   * - first check sequence number - we skip that one in TIME_WAIT (always
+   *   acceptable since we only send ACKs)
+   * - second check the RST bit (... return) */
+       struct tcp_hdr *tcphdr = insegp->tcphdr;
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+       ip_addr_t current_iphdr_src = {iphdr->src_addr};
+       ip_addr_t current_iphdr_dest = {iphdr->dst_addr};
+  uint32_t ackno = tcphdr->ackno;
+  uint32_t seqno = tcphdr->seqno;
+  if (flags & TCP_RST)  {
+    return ESUCCESS;
+  }
+  /* - fourth, check the SYN bit, */
+  if (flags & TCP_SYN) {
+    /* If an incoming segment is not acceptable, an acknowledgment
+       should be sent in reply */
+    if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) {
+      /* If the SYN is in the window it is an error, send a reset */
+      tcp_rst(ackno, seqno + tcplen, &current_iphdr_dest, &current_iphdr_src,
+        tcphdr->dest, tcphdr->src);
+      return ESUCCESS;
+    }
+  } else if (flags & TCP_FIN) {
+    /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
+         Restart the 2 MSL time-wait timeout.*/
+    pcb->tmr = tcp_ticks;
+  }
+
+  if ((tcplen > 0))  {
+    /* Acknowledge data, FIN or out-of-window SYN */
+    pcb->flags |= TF_ACK_NOW;
+    return tcp_output(pcb);
+  }
+  return ESUCCESS;
+}
+
+/**
+ * Implements the TCP state machine. Called by tcp_input. In some
+ * states tcp_receive() is called to receive data. The tcp_seg
+ * argument will be freed by the caller (tcp_input()) unless the
+ * recv_data pointer in the pcb is set.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static error_t
+tcp_process(struct tcp_pcb *pcb, struct tcp_seg *insegp, struct ip_hdr *iphdr, uint16_t tcplen)
+{
+       struct tcp_hdr *tcphdr = insegp->tcphdr;
+  struct tcp_seg *rseg;
+  uint8_t acceptable = 0;
+  error_t err;
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+  uint32_t ackno = tcphdr->ackno;
+  uint32_t seqno = tcphdr->seqno;
+
+       ip_addr_t current_iphdr_src = {iphdr->src_addr};
+       ip_addr_t current_iphdr_dest = {iphdr->dst_addr};
+  err = ESUCCESS;
+
+  /* Process incoming RST segments. */
+  if (flags & TCP_RST) {
+    /* First, determine if the reset is acceptable. */
+    if (pcb->state == SYN_SENT) {
+      if (ackno == pcb->snd_nxt) {
+        acceptable = 1;
+      }
+    } else {
+      if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, 
+                          pcb->rcv_nxt+pcb->rcv_wnd)) {
+        acceptable = 1;
+      }
+    }
+
+    if (acceptable) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
+      LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
+      recv_flags |= TF_RESET;
+      pcb->flags &= ~TF_ACK_DELAY;
+      return -ENETRESET;
+    } else {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+       seqno, pcb->rcv_nxt));
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+       seqno, pcb->rcv_nxt));
+      return ESUCCESS;
+    }
+  }
+
+  if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { 
+    /* Cope with new connection attempt after remote end crashed */
+    tcp_ack_now(pcb);
+    return ESUCCESS;
+  }
+  
+  if ((pcb->flags & TF_RXCLOSED) == 0) {
+    /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
+    pcb->tmr = tcp_ticks;
+  }
+  pcb->keep_cnt_sent = 0;
+
+  tcp_parseopt(pcb, tcphdr);
+
+  /* Do different things depending on the TCP state. */
+  switch (pcb->state) {
+  case SYN_SENT:
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
+     pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
+    /* received SYN ACK with expected sequence number? */
+    if ((flags & TCP_ACK) && (flags & TCP_SYN)
+        && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
+      pcb->snd_buf++;
+      pcb->rcv_nxt = seqno + 1;
+      pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+      pcb->lastack = ackno;
+      pcb->snd_wnd = tcphdr->wnd;
+      pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
+      pcb->state = ESTABLISHED;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+      pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+      /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
+       * but for the default value of pcb->mss) */
+      pcb->ssthresh = pcb->mss * 10;
+
+      pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+      LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
+      --pcb->snd_queuelen;
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (uint16_t)pcb->snd_queuelen));
+      rseg = pcb->unacked;
+      pcb->unacked = rseg->next;
+
+      /* If there's nothing left to acknowledge, stop the retransmit
+         timer, otherwise reset it to start again */
+      if(pcb->unacked == NULL)
+        pcb->rtime = -1;
+      else {
+        pcb->rtime = 0;
+        pcb->nrtx = 0;
+      }
+
+      tcp_seg_free(rseg);
+
+      /* Call the user specified function to call when sucessfully
+       * connected. */
+      TCP_EVENT_CONNECTED(pcb, ESUCCESS, err);
+      if (err == -ECONNABORTED) {
+        return -ECONNABORTED;
+      }
+      tcp_ack_now(pcb);
+    }
+    /* received ACK? possibly a half-open connection */
+    else if (flags & TCP_ACK) {
+      /* send a RST to bring the other side in a non-synchronized state. */
+      tcp_rst(ackno, seqno + tcplen, &current_iphdr_dest, &current_iphdr_src,
+        tcphdr->dest, tcphdr->src);
+    }
+    break;
+  case SYN_RCVD:
+    if (flags & TCP_ACK) {
+      /* expected ACK number? */
+      if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
+        uint16_t old_cwnd;
+        pcb->state = ESTABLISHED;
+        LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+#if LWIP_CALLBACK_API
+        LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
+#endif
+        /* Call the accept function. */
+        TCP_EVENT_ACCEPT(pcb, ESUCCESS, err);
+        if (err != ESUCCESS) {
+          /* If the accept function returns with an error, we abort
+           * the connection. */
+          /* Already aborted? */
+          if (err != -ECONNABORTED) {
+            tcp_abort(pcb);
+          }
+          return -ECONNABORTED;
+        }
+        old_cwnd = pcb->cwnd;
+        /* If there was any data contained within this ACK,
+         * we'd better pass it on to the application as well. */
+        tcp_receive(pcb, insegp, tcplen);
+
+        /* Prevent ACK for SYN to generate a sent event */
+        if (pcb->acked != 0) {
+          pcb->acked--;
+        }
+
+        pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+
+        if (recv_flags & TF_GOT_FIN) {
+          tcp_ack_now(pcb);
+          pcb->state = CLOSE_WAIT;
+        }
+      } else {
+        /* incorrect ACK number, send RST */
+       tcp_rst(ackno, seqno + tcplen, &current_iphdr_dest, &current_iphdr_src,
+               tcphdr->dest, tcphdr->src);
+      }
+    } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
+      /* Looks like another copy of the SYN - retransmit our SYN-ACK */
+      tcp_rexmit(pcb);
+    }
+    break;
+  case CLOSE_WAIT:
+    /* FALLTHROUGH */
+  case ESTABLISHED:
+               tcp_receive(pcb, insegp, tcplen);
+    if (recv_flags & TF_GOT_FIN) { /* passive close */
+      tcp_ack_now(pcb);
+      pcb->state = CLOSE_WAIT;
+    }
+    break;
+  case FIN_WAIT_1:
+    tcp_receive(pcb, insegp, tcplen);
+    if (recv_flags & TF_GOT_FIN) {
+      if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
+        LWIP_DEBUGF(TCP_DEBUG,
+          ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+        tcp_ack_now(pcb);
+        tcp_pcb_purge(pcb);
+        TCP_RMV(&tcp_active_pcbs, pcb);
+        pcb->state = TIME_WAIT;
+        TCP_REG(&tcp_tw_pcbs, pcb);
+      } else {
+        tcp_ack_now(pcb);
+        pcb->state = CLOSING;
+      }
+    } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
+      pcb->state = FIN_WAIT_2;
+    }
+    break;
+  case FIN_WAIT_2:
+    tcp_receive(pcb, insegp, tcplen);
+    if (recv_flags & TF_GOT_FIN) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      tcp_ack_now(pcb);
+      tcp_pcb_purge(pcb);
+      TCP_RMV(&tcp_active_pcbs, pcb);
+      pcb->state = TIME_WAIT;
+      TCP_REG(&tcp_tw_pcbs, pcb);
+    }
+    break;
+  case CLOSING:
+    tcp_receive(pcb, insegp, tcplen);
+    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      tcp_pcb_purge(pcb);
+      TCP_RMV(&tcp_active_pcbs, pcb);
+      pcb->state = TIME_WAIT;
+      TCP_REG(&tcp_tw_pcbs, pcb);
+    }
+    break;
+  case LAST_ACK:
+    tcp_receive(pcb, insegp, tcplen);
+    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
+      recv_flags |= TF_CLOSED;
+    }
+    break;
+  default:
+    break;
+  }
+  return ESUCCESS;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Insert segment into the list (segments covered with new one will be deleted)
+ *
+ * Called from tcp_receive()
+ */
+static void
+tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
+{
+  struct tcp_seg *old_seg;
+
+  if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+    /* received segment overlaps all following segments */
+    tcp_segs_free(next);
+    next = NULL;
+  }
+  else {
+    /* delete some following segments
+       oos queue may have segments with FIN flag */
+    while (next &&
+           TCP_SEQ_GEQ((seqno + cseg->len),
+                      (next->tcphdr->seqno + next->len))) {
+      /* cseg with FIN already processed */
+      if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+        TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
+      }
+      old_seg = next;
+      next = next->next;
+      tcp_seg_free(old_seg);
+    }
+    if (next &&
+        TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
+      /* We need to trim the incoming segment. */
+      cseg->len = (uint16_t)(next->tcphdr->seqno - seqno);
+      pbuf_realloc(cseg->p, cseg->len);
+    }
+  }
+  cseg->next = next;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+/**
+ * Called by tcp_process. Checks if the given segment is an ACK for outstanding
+ * data, and if so frees the memory of the buffered data. Next, is places the
+ * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
+ * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
+ * i it has been removed from the buffer.
+ *
+ * If the incoming segment constitutes an ACK for a segment that was used for RTT
+ * estimation, the RTT is estimated here as well.
+ *
+ * Called from tcp_process().
+ */
+static void
+tcp_receive(struct tcp_pcb *pcb, struct tcp_seg *insegp, uint16_t tcplen) {
+  struct tcp_seg *next;
+#if TCP_QUEUE_OOSEQ
+  struct tcp_seg *prev, *cseg;
+#endif /* TCP_QUEUE_OOSEQ */
+       struct tcp_hdr *tcphdr = insegp->tcphdr;
+  struct pbuf *p;
+  int32_t off;
+  int16_t m;
+  uint32_t right_wnd_edge;
+  uint16_t new_tot_len;
+  uint8_t flags = TCPH_FLAGS(tcphdr);
+  uint32_t ackno = tcphdr->ackno;
+  uint32_t seqno = tcphdr->seqno;
+  int found_dupack = 0;
+
+  if (flags & TCP_ACK) {
+    right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
+
+    /* Update window. */
+    if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
+       (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
+       (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
+      pcb->snd_wnd = tcphdr->wnd;
+      pcb->snd_wl1 = seqno;
+      pcb->snd_wl2 = ackno;
+      if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) {
+          pcb->persist_backoff = 0;
+      }
+      LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd));
+#if TCP_WND_DEBUG
+    } else {
+      if (pcb->snd_wnd != tcphdr->wnd) {
+        LWIP_DEBUGF(TCP_WND_DEBUG, 
+                    ("tcp_receive: no window update lastack %"U32_F" ackno %"
+                     U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
+                     pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
+      }
+#endif /* TCP_WND_DEBUG */
+    }
+
+    /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
+     * duplicate ack if:
+     * 1) It doesn't ACK new data 
+     * 2) length of received packet is zero (i.e. no payload) 
+     * 3) the advertised window hasn't changed 
+     * 4) There is outstanding unacknowledged data (retransmission timer running)
+     * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
+     * 
+     * If it passes all five, should process as a dupack: 
+     * a) dupacks < 3: do nothing 
+     * b) dupacks == 3: fast retransmit 
+     * c) dupacks > 3: increase cwnd 
+     * 
+     * If it only passes 1-3, should reset dupack counter (and add to
+     * stats, which we don't do in lwIP)
+     *
+     * If it only passes 1, should reset dupack counter
+     *
+     */
+
+    /* Clause 1 */
+    if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
+      pcb->acked = 0;
+      /* Clause 2 */
+      if (tcplen == 0) {
+        /* Clause 3 */
+        if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
+          /* Clause 4 */
+          if (pcb->rtime >= 0) {
+            /* Clause 5 */
+            if (pcb->lastack == ackno) {
+              found_dupack = 1;
+              if (pcb->dupacks + 1 > pcb->dupacks)
+                ++pcb->dupacks;
+              if (pcb->dupacks > 3) {
+                /* Inflate the congestion window, but not if it means that
+                   the value overflows. */
+                if ((uint16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+                  pcb->cwnd += pcb->mss;
+                }
+              } else if (pcb->dupacks == 3) {
+                /* Do fast retransmit */
+                tcp_rexmit_fast(pcb);
+              }
+            }
+          }
+        }
+      }
+      /* If Clause (1) or more is true, but not a duplicate ack, reset
+       * count of consecutive duplicate acks */
+      if (!found_dupack) {
+        pcb->dupacks = 0;
+      }
+    } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){
+      /* We come here when the ACK acknowledges new data. */
+
+      /* Reset the "IN Fast Retransmit" flag, since we are no longer
+         in fast retransmit. Also reset the congestion window to the
+         slow start threshold. */
+      if (pcb->flags & TF_INFR) {
+        pcb->flags &= ~TF_INFR;
+        pcb->cwnd = pcb->ssthresh;
+      }
+
+      /* Reset the number of retransmissions. */
+      pcb->nrtx = 0;
+
+      /* Reset the retransmission time-out. */
+      pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+      /* Update the send buffer space. Diff between the two can never exceed 64K? */
+      pcb->acked = (uint16_t)(ackno - pcb->lastack);
+
+      pcb->snd_buf += pcb->acked;
+
+      /* Reset the fast retransmit variables. */
+      pcb->dupacks = 0;
+      pcb->lastack = ackno;
+
+      /* Update the congestion control variables (cwnd and
+         ssthresh). */
+      if (pcb->state >= ESTABLISHED) {
+        if (pcb->cwnd < pcb->ssthresh) {
+          if ((uint16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+            pcb->cwnd += pcb->mss;
+          }
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));
+        } else {
+          uint16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
+          if (new_cwnd > pcb->cwnd) {
+            pcb->cwnd = new_cwnd;
+          }
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd));
+        }
+      }
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
+                                    ackno,
+                                    pcb->unacked != NULL?
+                                    ntohl(pcb->unacked->tcphdr->seqno): 0,
+                                    pcb->unacked != NULL?
+                                    ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
+
+      /* Remove segment from the unacknowledged list if the incoming
+         ACK acknowlegdes them. */
+      while (pcb->unacked != NULL &&
+             TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
+                         TCP_TCPLEN(pcb->unacked), ackno)) {
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
+                                      ntohl(pcb->unacked->tcphdr->seqno),
+                                      ntohl(pcb->unacked->tcphdr->seqno) +
+                                      TCP_TCPLEN(pcb->unacked)));
+
+        next = pcb->unacked;
+        pcb->unacked = pcb->unacked->next;
+
+        LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (uint16_t)pcb->snd_queuelen));
+        LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+        /* Prevent ACK for FIN to generate a sent event */
+        if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+          pcb->acked--;
+        }
+
+        pcb->snd_queuelen -= pbuf_clen(next->p);
+        tcp_seg_free(next);
+
+        LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (uint16_t)pcb->snd_queuelen));
+        if (pcb->snd_queuelen != 0) {
+          LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
+                      pcb->unsent != NULL);
+        }
+      }
+
+      /* If there's nothing left to acknowledge, stop the retransmit
+         timer, otherwise reset it to start again */
+      if(pcb->unacked == NULL)
+        pcb->rtime = -1;
+      else
+        pcb->rtime = 0;
+
+      pcb->polltmr = 0;
+    } else {
+      /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
+      pcb->acked = 0;
+    }
+
+    /* We go through the ->unsent list to see if any of the segments
+       on the list are acknowledged by the ACK. This may seem
+       strange since an "unsent" segment shouldn't be acked. The
+       rationale is that lwIP puts all outstanding segments on the
+       ->unsent list after a retransmission, so these segments may
+       in fact have been sent once. */
+    while (pcb->unsent != NULL &&
+           TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + 
+                           TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
+                                    ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) +
+                                    TCP_TCPLEN(pcb->unsent)));
+
+      next = pcb->unsent;
+      pcb->unsent = pcb->unsent->next;
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (uint16_t)pcb->snd_queuelen));
+      LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+      /* Prevent ACK for FIN to generate a sent event */
+      if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+        pcb->acked--;
+      }
+      pcb->snd_queuelen -= pbuf_clen(next->p);
+      tcp_seg_free(next);
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (uint16_t)pcb->snd_queuelen));
+      if (pcb->snd_queuelen != 0) {
+        LWIP_ASSERT("tcp_receive: valid queue length",
+          pcb->unacked != NULL || pcb->unsent != NULL);
+      }
+    }
+    /* End of ACK for new data processing. */
+
+    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
+                                pcb->rttest, pcb->rtseq, ackno));
+
+    /* RTT estimation calculations. This is done by checking if the
+       incoming segment acknowledges the segment we use to take a
+       round-trip time measurement. */
+    if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
+      /* diff between this shouldn't exceed 32K since this are tcp timer ticks
+         and a round-trip shouldn't be that long... */
+      m = (int16_t)(tcp_ticks - pcb->rttest);
+
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
+                                  m, m * TCP_SLOW_INTERVAL));
+
+      /* This is taken directly from VJs original code in his paper */
+      m = m - (pcb->sa >> 3);
+      pcb->sa += m;
+      if (m < 0) {
+        m = -m;
+      }
+      m = m - (pcb->sv >> 2);
+      pcb->sv += m;
+      pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
+                                  pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
+
+      pcb->rttest = 0;
+    }
+  }
+
+  /* If the incoming segment contains data, we must process it
+     further. */
+  if (tcplen > 0) {
+    /* This code basically does three things:
+
+    +) If the incoming segment contains data that is the next
+    in-sequence data, this data is passed to the application. This
+    might involve trimming the first edge of the data. The rcv_nxt
+    variable and the advertised window are adjusted.
+
+    +) If the incoming segment has data that is above the next
+    sequence number expected (->rcv_nxt), the segment is placed on
+    the ->ooseq queue. This is done by finding the appropriate
+    place in the ->ooseq queue (which is ordered by sequence
+    number) and trim the segment in both ends if needed. An
+    immediate ACK is sent to indicate that we received an
+    out-of-sequence segment.
+
+    +) Finally, we check if the first segment on the ->ooseq queue
+    now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
+    rcv_nxt > ooseq->seqno, we must trim the first edge of the
+    segment on ->ooseq before we adjust rcv_nxt. The data in the
+    segments that are now on sequence are chained onto the
+    incoming segment so that we only need to call the application
+    once.
+    */
+
+    /* First, we check if we must trim the first edge. We have to do
+       this if the sequence number of the incoming segment is less
+       than rcv_nxt, and the sequence number plus the length of the
+       segment is larger than rcv_nxt. */
+    /*    if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
+          if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
+    if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){
+      /* Trimming the first edge is done by pushing the payload
+         pointer in the pbuf downwards. This is somewhat tricky since
+         we do not want to discard the full contents of the pbuf up to
+         the new starting point of the data since we have to keep the
+         TCP header which is present in the first pbuf in the chain.
+
+         What is done is really quite a nasty hack: the first pbuf in
+         the pbuf chain is pointed to by inseg.p. Since we need to be
+         able to deallocate the whole pbuf, we cannot change this
+         inseg.p pointer to point to any of the later pbufs in the
+         chain. Instead, we point the ->payload pointer in the first
+         pbuf to data in one of the later pbufs. We also set the
+         inseg.data pointer to point to the right place. This way, the
+         ->p pointer will still point to the first pbuf, but the
+         ->p->payload pointer will point to data in another pbuf.
+
+         After we are done with adjusting the pbuf pointers we must
+         adjust the ->data pointer in the seg and the segment
+         length.*/
+
+      off = pcb->rcv_nxt - seqno;
+      p = insegp->p;
+      LWIP_ASSERT("inseg.p != NULL", insegp->p);
+      LWIP_ASSERT("insane offset!", (off < 0x7fff));
+      if (insegp->p->len < off) {
+        LWIP_ASSERT("pbuf too short!", (((int32_t)insegp->p->tot_len) >= off));
+        new_tot_len = (uint16_t)(insegp->p->tot_len - off);
+        while (p->len < off) {
+          off -= p->len;
+          /* KJM following line changed (with addition of new_tot_len var)
+             to fix bug #9076
+             inseg.p->tot_len -= p->len; */
+          p->tot_len = new_tot_len;
+          p->len = 0;
+          p = STAILQ_NEXT(p, next);
+        }
+        if(pbuf_header(p, (int16_t)-off)) {
+          /* Do we need to cope with this failing?  Assert for now */
+          LWIP_ASSERT("pbuf_header failed", 0);
+        }
+      } else {
+        if(pbuf_header(insegp->p, (int16_t)-off)) {
+          /* Do we need to cope with this failing?  Assert for now */
+          LWIP_ASSERT("pbuf_header failed", 0);
+        }
+      }
+      /* KJM following line changed to use p->payload rather than inseg->p->payload
+         to fix bug #9076 */
+      insegp->dataptr = p->payload;
+      insegp->len -= (uint16_t)(pcb->rcv_nxt - seqno);
+      insegp->tcphdr->seqno = seqno = pcb->rcv_nxt;
+    }
+    else {
+      if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
+        /* the whole segment is < rcv_nxt */
+        /* must be a duplicate of a packet that has already been correctly handled */
+
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
+        tcp_ack_now(pcb);
+      }
+    }
+
+    /* The sequence number must be within the window (above rcv_nxt
+       and below rcv_nxt + rcv_wnd) in order to be further
+       processed. */
+    if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, 
+                        pcb->rcv_nxt + pcb->rcv_wnd - 1)){
+      if (pcb->rcv_nxt == seqno) {
+        /* The incoming segment is the next in sequence. We check if
+           we have to trim the end of the segment and update rcv_nxt
+           and pass the data to the application. */
+        tcplen = TCP_TCPLEN(insegp);
+
+        if (tcplen > pcb->rcv_wnd) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, 
+                      ("tcp_receive: other end overran receive window"
+                       "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+                       seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+          if (TCPH_FLAGS(insegp->tcphdr) & TCP_FIN) {
+            /* Must remove the FIN from the header as we're trimming 
+             * that byte of sequence-space from the packet */
+            TCPH_FLAGS_SET(insegp->tcphdr, TCPH_FLAGS(insegp->tcphdr) &~ TCP_FIN);
+          }
+          /* Adjust length of segment to fit in the window. */
+          insegp->len = pcb->rcv_wnd;
+          if (TCPH_FLAGS(insegp->tcphdr) & TCP_SYN) {
+            insegp->len -= 1;
+          }
+          pbuf_realloc(insegp->p, insegp->len);
+          tcplen = TCP_TCPLEN(insegp);
+          LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+                      (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+        }
+#if TCP_QUEUE_OOSEQ
+        /* Received in-sequence data, adjust ooseq data if:
+           - FIN has been received or
+           - inseq overlaps with ooseq */
+        if (pcb->ooseq != NULL) {
+          if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, 
+                        ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
+            /* Received in-order FIN means anything that was received
+             * out of order must now have been received in-order, so
+             * bin the ooseq queue */
+            while (pcb->ooseq != NULL) {
+              struct tcp_seg *old_ooseq = pcb->ooseq;
+              pcb->ooseq = pcb->ooseq->next;
+              tcp_seg_free(old_ooseq);
+            }
+          }
+          else {
+            next = pcb->ooseq;
+            /* Remove all segments on ooseq that are covered by inseg already.
+             * FIN is copied from ooseq to inseg if present. */
+            while (next &&
+                   TCP_SEQ_GEQ(seqno + tcplen,
+                               next->tcphdr->seqno + next->len)) {
+              /* inseg cannot have FIN here (already processed above) */
+              if (TCPH_FLAGS(next->tcphdr) & TCP_FIN &&
+                  (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
+                TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
+                tcplen = TCP_TCPLEN(&inseg);
+              }
+              prev = next;
+              next = next->next;
+              tcp_seg_free(prev);
+            }
+            /* Now trim right side of inseg if it overlaps with the first
+             * segment on ooseq */
+            if (next &&
+                TCP_SEQ_GT(seqno + tcplen,
+                           next->tcphdr->seqno)) {
+              /* inseg cannot have FIN here (already processed above) */
+              inseg.len = (uint16_t)(next->tcphdr->seqno - seqno);
+              if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+                inseg.len -= 1;
+              }
+              pbuf_realloc(inseg.p, inseg.len);
+              tcplen = TCP_TCPLEN(&inseg);
+              LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
+                          (seqno + tcplen) == next->tcphdr->seqno);
+            }
+            pcb->ooseq = next;
+          }
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+        pcb->rcv_nxt = seqno + tcplen;
+
+        /* Update the receiver's (our) window. */
+        LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
+        pcb->rcv_wnd -= tcplen;
+
+        tcp_update_rcv_ann_wnd(pcb);
+
+        /* If there is data in the segment, we make preparations to
+           pass this up to the application. The ->recv_data variable
+           is used for holding the pbuf that goes to the
+           application. The code for reassembling out-of-sequence data
+           chains its data on this pbuf as well.
+
+           If the segment was a FIN, we set the TF_GOT_FIN flag that will
+           be used to indicate to the application that the remote side has
+           closed its end of the connection. */
+        if (insegp->p->tot_len > 0) {
+          recv_data = insegp->p;
+          /* Since this pbuf now is the responsibility of the
+             application, we delete our reference to it so that we won't
+             (mistakingly) deallocate it. */
+          insegp->p = NULL;
+        }
+        if (TCPH_FLAGS(insegp->tcphdr) & TCP_FIN) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
+          recv_flags |= TF_GOT_FIN;
+        }
+
+#if TCP_QUEUE_OOSEQ
+        /* We now check if we have segments on the ->ooseq queue that
+           are now in sequence. */
+        while (pcb->ooseq != NULL &&
+               pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
+
+          cseg = pcb->ooseq;
+          seqno = pcb->ooseq->tcphdr->seqno;
+
+          pcb->rcv_nxt += TCP_TCPLEN(cseg);
+          LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
+                      pcb->rcv_wnd >= TCP_TCPLEN(cseg));
+          pcb->rcv_wnd -= TCP_TCPLEN(cseg);
+
+          tcp_update_rcv_ann_wnd(pcb);
+
+          if (cseg->p->tot_len > 0) {
+            /* Chain this pbuf onto the pbuf that we will pass to
+               the application. */
+            if (recv_data) {
+              pbuf_cat(recv_data, cseg->p);
+            } else {
+              recv_data = cseg->p;
+            }
+            cseg->p = NULL;
+          }
+          if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
+            recv_flags |= TF_GOT_FIN;
+            if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
+              pcb->state = CLOSE_WAIT;
+            } 
+          }
+
+          pcb->ooseq = cseg->next;
+          tcp_seg_free(cseg);
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+        /* Acknowledge the segment(s). */
+        tcp_ack(pcb);
+
+      } else {
+        /* We get here if the incoming segment is out-of-sequence. */
+        tcp_send_empty_ack(pcb);
+#if TCP_QUEUE_OOSEQ
+        /* We queue the segment on the ->ooseq queue. */
+        if (pcb->ooseq == NULL) {
+          pcb->ooseq = tcp_seg_copy(&inseg);
+        } else {
+          /* If the queue is not empty, we walk through the queue and
+             try to find a place where the sequence number of the
+             incoming segment is between the sequence numbers of the
+             previous and the next segment on the ->ooseq queue. That is
+             the place where we put the incoming segment. If needed, we
+             trim the second edges of the previous and the incoming
+             segment so that it will fit into the sequence.
+
+             If the incoming segment has the same sequence number as a
+             segment on the ->ooseq queue, we discard the segment that
+             contains less data. */
+
+          prev = NULL;
+          for(next = pcb->ooseq; next != NULL; next = next->next) {
+            if (seqno == next->tcphdr->seqno) {
+              /* The sequence number of the incoming segment is the
+                 same as the sequence number of the segment on
+                 ->ooseq. We check the lengths to see which one to
+                 discard. */
+              if (inseg.len > next->len) {
+                /* The incoming segment is larger than the old
+                   segment. We replace some segments with the new
+                   one. */
+                cseg = tcp_seg_copy(&inseg);
+                if (cseg != NULL) {
+                  if (prev != NULL) {
+                    prev->next = cseg;
+                  } else {
+                    pcb->ooseq = cseg;
+                  }
+                  tcp_oos_insert_segment(cseg, next);
+                }
+                break;
+              } else {
+                /* Either the lenghts are the same or the incoming
+                   segment was smaller than the old one; in either
+                   case, we ditch the incoming segment. */
+                break;
+              }
+            } else {
+              if (prev == NULL) {
+                if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
+                  /* The sequence number of the incoming segment is lower
+                     than the sequence number of the first segment on the
+                     queue. We put the incoming segment first on the
+                     queue. */
+                  cseg = tcp_seg_copy(&inseg);
+                  if (cseg != NULL) {
+                    pcb->ooseq = cseg;
+                    tcp_oos_insert_segment(cseg, next);
+                  }
+                  break;
+                }
+              } else {
+                /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
+                  TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
+                if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
+                  /* The sequence number of the incoming segment is in
+                     between the sequence numbers of the previous and
+                     the next segment on ->ooseq. We trim trim the previous
+                     segment, delete next segments that included in received segment
+                     and trim received, if needed. */
+                  cseg = tcp_seg_copy(&inseg);
+                  if (cseg != NULL) {
+                    if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
+                      /* We need to trim the prev segment. */
+                      prev->len = (uint16_t)(seqno - prev->tcphdr->seqno);
+                      pbuf_realloc(prev->p, prev->len);
+                    }
+                    prev->next = cseg;
+                    tcp_oos_insert_segment(cseg, next);
+                  }
+                  break;
+                }
+              }
+              /* If the "next" segment is the last segment on the
+                 ooseq queue, we add the incoming segment to the end
+                 of the list. */
+              if (next->next == NULL &&
+                  TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
+                if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+                  /* segment "next" already contains all data */
+                  break;
+                }
+                next->next = tcp_seg_copy(&inseg);
+                if (next->next != NULL) {
+                  if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
+                    /* We need to trim the last segment. */
+                    next->len = (uint16_t)(seqno - next->tcphdr->seqno);
+                    pbuf_realloc(next->p, next->len);
+                  }
+                  /* check if the remote side overruns our receive window */
+                  if ((uint32_t)tcplen + seqno > pcb->rcv_nxt + (uint32_t)pcb->rcv_wnd) {
+                    LWIP_DEBUGF(TCP_INPUT_DEBUG, 
+                                ("tcp_receive: other end overran receive window"
+                                 "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+                                 seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+                    if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
+                      /* Must remove the FIN from the header as we're trimming 
+                       * that byte of sequence-space from the packet */
+                      TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN);
+                    }
+                    /* Adjust length of segment to fit in the window. */
+                    next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno;
+                    pbuf_realloc(next->next->p, next->next->len);
+                    tcplen = TCP_TCPLEN(next->next);
+                    LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+                                (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+                  }
+                }
+                break;
+              }
+            }
+            prev = next;
+          }
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+      }
+    } else {
+      /* The incoming segment is not withing the window. */
+      tcp_send_empty_ack(pcb);
+    }
+  } else {
+    /* Segments with length 0 is taken care of here. Segments that
+       fall out of the window are ACKed. */
+    /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) ||
+      TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
+    if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
+      tcp_ack_now(pcb);
+    }
+  }
+}
+
+/**
+ * Parses the options contained in the incoming segment. 
+ *
+ * Called from tcp_listen_input() and tcp_process().
+ * Currently, only the MSS option is supported!
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ */
+static void
+tcp_parseopt(struct tcp_pcb *pcb, struct tcp_hdr *tcphdr)
+{
+  uint16_t c, max_c;
+  uint16_t mss;
+  uint8_t *opts, opt;
+#if LWIP_TCP_TIMESTAMPS
+  uint32_t tsval;
+#endif
+
+  opts = (uint8_t *)tcphdr + TCP_HLEN;
+
+  /* Parse the TCP MSS option, if present. */
+  if(TCPH_HDRLEN(tcphdr) > 0x5) {
+    max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
+    for (c = 0; c < max_c; ) {
+      opt = opts[c];
+      switch (opt) {
+      case 0x00:
+        /* End of options. */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
+        return;
+      case 0x01:
+        /* NOP option. */
+        ++c;
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
+        break;
+      case 0x02:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
+        if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
+          /* Bad length */
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          return;
+        }
+        /* An MSS option with the right option length. */
+        mss = (opts[c + 2] << 8) | opts[c + 3];
+        /* Limit the mss to the configured TCP_MSS and prevent division by zero */
+        pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
+        /* Advance to next option */
+        c += 0x04;
+        break;
+#if LWIP_TCP_TIMESTAMPS
+      case 0x08:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
+        if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
+          /* Bad length */
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          return;
+        }
+        /* TCP timestamp option with valid length */
+        tsval = (opts[c+2]) | (opts[c+3] << 8) | 
+          (opts[c+4] << 16) | (opts[c+5] << 24);
+        if (flags & TCP_SYN) {
+          pcb->ts_recent = ntohl(tsval);
+          pcb->flags |= TF_TIMESTAMP;
+        } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
+          pcb->ts_recent = ntohl(tsval);
+        }
+        /* Advance to next option */
+        c += 0x0A;
+        break;
+#endif
+      default:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
+        if (opts[c + 1] == 0) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          /* If the length field is zero, the options are malformed
+             and we don't process them further. */
+          return;
+        }
+        /* All other options have a length field, so that we easily
+           can skip past them. */
+        c += opts[c + 1];
+      }
+    }
+  }
+}
+
diff --git a/kern/src/net/tcp_out.c b/kern/src/net/tcp_out.c
new file mode 100644 (file)
index 0000000..87afdb7
--- /dev/null
@@ -0,0 +1,1463 @@
+/**
+ * @file
+ * Transmission Control Protocol, outgoing traffic
+ *
+ * The output functions of TCP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Adapted by David Zhu for Akaros <yuzhu@cs.berkeley.edu>
+ *
+ */
+#include "net/tcp.h"
+#include "net/tcp_impl.h"
+#include "bits/netinet.h"
+#include "net/pbuf.h"
+#include "debug.h"
+#include "error.h"
+#include <string.h>
+
+/* Define some copy-macros for checksum-on-copy so that the code looks
+   nicer by preventing too many ifdef's. */
+#if TCP_CHECKSUM_ON_COPY
+#define TCP_DATA_COPY(dst, src, len, seg) do { \
+  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
+                     len, &seg->chksum, &seg->chksum_swapped); \
+  seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped)  \
+  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
+#else /* TCP_CHECKSUM_ON_COPY*/
+#define TCP_DATA_COPY(dst, src, len, seg)                     memcpy(dst, src, len)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) memcpy(dst, src, len)
+#endif /* TCP_CHECKSUM_ON_COPY*/
+
+/** Define this to 1 for an extra check that the output checksum is valid
+ * (usefule when the checksum is generated by the application, not the stack) */
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK   0
+#endif
+
+/* Forward declarations.*/
+static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
+
+/** Allocate a pbuf and create a tcphdr at p->payload, used for output
+ * functions other than the default tcp_output -> tcp_output_segment
+ * (e.g. tcp_send_empty_ack, etc.)
+ *
+ * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
+ * @param optlen length of header-options
+ * @param datalen length of tcp data to reserve in pbuf
+ * @param seqno_be seqno in network byte order (big-endian)
+ * @return pbuf with p->payload being the tcp_hdr
+ */
+static struct pbuf *
+tcp_output_alloc_header(struct tcp_pcb *pcb, uint16_t optlen, uint16_t datalen,
+                      uint32_t seqno_be /* already in network byte order */)
+{
+  struct tcp_hdr *tcphdr;
+  struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
+  if (p != NULL) {
+    LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+                 (p->len >= TCP_HLEN + optlen));
+    tcphdr = (struct tcp_hdr *)p->payload;
+    tcphdr->src = htons(pcb->local_port);
+    tcphdr->dest = htons(pcb->remote_port);
+    tcphdr->seqno = seqno_be;
+    tcphdr->ackno = htonl(pcb->rcv_nxt);
+    TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
+    tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+    tcphdr->chksum = 0;
+    tcphdr->urgp = 0;
+
+    /* If we're sending a packet, update the announced right window edge */
+    pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+  }
+  return p;
+}
+
+/**
+ * Called by tcp_close() to send a segment including FIN flag but not data.
+ *
+ * @param pcb the tcp_pcb over which to send a segment
+ * @return ESUCCESS if sent, another error_t otherwise
+ */
+error_t
+tcp_send_fin(struct tcp_pcb *pcb)
+{
+  /* first, try to add the fin to the last unsent segment */
+  if (pcb->unsent != NULL) {
+    struct tcp_seg *last_unsent;
+    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+         last_unsent = last_unsent->next);
+
+    if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
+      /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
+      TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
+      return ESUCCESS;
+    }
+  }
+  /* no data, no length, flags, copy=1, no optdata */
+  return tcp_enqueue_flags(pcb, TCP_FIN);
+}
+
+/**
+ * Create a TCP segment with prefilled header.
+ *
+ * Called by tcp_write and tcp_enqueue_flags.
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param p pbuf that is used to hold the TCP header.
+ * @param flags TCP flags for header.
+ * @param seqno TCP sequence number of this packet
+ * @param optflags options to include in TCP header
+ * @return a new tcp_seg pointing to p, or NULL.
+ * The TCP header is filled in except ackno and wnd.
+ * p is freed on failure.
+ */
+static struct tcp_seg *
+tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, uint8_t flags, uint32_t seqno, uint8_t optflags)
+{
+  struct tcp_seg *seg;
+  uint8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+  if ((seg = (struct tcp_seg *)kmem_cache_alloc(tcp_segment_kcache, 0)) == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));
+    pbuf_free(p);
+    return NULL;
+  }
+  seg->flags = optflags;
+  seg->next = NULL;
+  seg->p = p;
+  seg->dataptr = p->payload;
+  seg->len = p->tot_len - optlen;
+#if TCP_OVERSIZE_DBGCHECK
+  seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+  seg->chksum = 0;
+  seg->chksum_swapped = 0;
+  /* check optflags */
+  LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
+              (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  /* build TCP header */
+  if (pbuf_header(p, TCP_HLEN)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
+    //TCP_STATS_INC(tcp.err);
+    tcp_seg_free(seg);
+    return NULL;
+  }
+  seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
+  seg->tcphdr->src = htons(pcb->local_port);
+  seg->tcphdr->dest = htons(pcb->remote_port);
+  seg->tcphdr->seqno = htonl(seqno);
+  /* ackno is set in tcp_output */
+  TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);
+  /* wnd and chksum are set in tcp_output */
+  seg->tcphdr->urgp = 0;
+  return seg;
+} 
+
+/**
+ * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
+ *
+ * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
+ * there may be extra bytes available at the end.
+ *
+ * @param layer flag to define header size.
+ * @param length size of the pbuf's payload.
+ * @param max_length maximum usable size of payload+oversize.
+ * @param oversize pointer to a uint16_t that will receive the number of usable tail bytes.
+ * @param pcb The TCP connection that willo enqueue the pbuf.
+ * @param apiflags API flags given to tcp_write.
+ * @param first_seg true when this pbuf will be used in the first enqueued segment.
+ * @param 
+ */
+#if TCP_OVERSIZE
+static struct pbuf *
+tcp_pbuf_prealloc(pbuf_layer layer, uint16_t length, uint16_t max_length,
+                  uint16_t *oversize, struct tcp_pcb *pcb, uint8_t apiflags,
+                  uint8_t first_seg)
+{
+  struct pbuf *p;
+  uint16_t alloc = length;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  LWIP_UNUSED_ARG(max_length);
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(apiflags);
+  LWIP_UNUSED_ARG(first_seg);
+  /* always create MSS-sized pbufs */
+  alloc = TCP_MSS;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+  if (length < max_length) {
+    /* Should we allocate an oversized pbuf, or just the minimum
+     * length required? If tcp_write is going to be called again
+     * before this segment is transmitted, we want the oversized
+     * buffer. If the segment will be transmitted immediately, we can
+     * save memory by allocating only length. We use a simple
+     * heuristic based on the following information:
+     *
+     * Did the user set TCP_WRITE_FLAG_MORE?
+     *
+     * Will the Nagle algorithm defer transmission of this segment?
+     */
+    if ((apiflags & TCP_WRITE_FLAG_MORE) ||
+        (!(pcb->flags & TF_NODELAY) &&
+         (!first_seg ||
+          pcb->unsent != NULL ||
+          pcb->unacked != NULL))) {
+      alloc = MIN(max_length, MEM_ALIGN_SIZE(length + TCP_OVERSIZE));
+    }
+  }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+  p = pbuf_alloc(layer, alloc, PBUF_RAM);
+  if (p == NULL) {
+    return NULL;
+  }
+  LWIP_ASSERT("need unchained pbuf", (STAILQ_NEXT(p, next) == NULL));
+  *oversize = p->len - length;
+  /* trim p->len to the currently used size */
+  p->len = p->tot_len = length;
+  return p;
+}
+#else /* TCP_OVERSIZE */
+#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
+#endif /* TCP_OVERSIZE */
+
+#if TCP_CHECKSUM_ON_COPY
+/** Add a checksum of newly added data to the segment */
+static void
+tcp_seg_add_chksum(uint16_t chksum, uint16_t len, uint16_t *seg_chksum,
+                   uint8_t *seg_chksum_swapped)
+{
+  uint32_t helper;
+  /* add chksum to old chksum and fold to uint16_t */
+  helper = chksum + *seg_chksum;
+  chksum = FOLD_U32T(helper);
+  if ((len & 1) != 0) {
+    *seg_chksum_swapped = 1 - *seg_chksum_swapped;
+    chksum = SWAP_BYTES_IN_WORD(chksum);
+  }
+  *seg_chksum = chksum;
+}
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
+ *
+ * @param pcb the tcp pcb to check for
+ * @param len length of data to send (checked agains snd_buf)
+ * @return ESUCCESS if tcp_write is allowed to proceed, another error_t otherwise
+ */
+static error_t
+tcp_write_checks(struct tcp_pcb *pcb, uint16_t len)
+{
+  /* connection is in invalid state for data transmission? */
+  if ((pcb->state != ESTABLISHED) &&
+      (pcb->state != CLOSE_WAIT) &&
+      (pcb->state != SYN_SENT) &&
+      (pcb->state != SYN_RCVD)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
+    return -ENOTCONN;
+  } else if (len == 0) {
+    return ESUCCESS;
+  }
+
+  /* fail on too much data */
+  if (len > pcb->snd_buf) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",
+      len, pcb->snd_buf));
+    pcb->flags |= TF_NAGLEMEMERR;
+    return -ENOMEM;
+  }
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (uint16_t)pcb->snd_queuelen));
+
+  /* If total number of pbufs on the unsent/unacked queues exceeds the
+   * configured maximum, return an error */
+  /* check for configured max queuelen and possible overflow */
+  if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
+      pcb->snd_queuelen, TCP_SND_QUEUELEN));
+    //TCP_STATS_INC(tcp.memerr);
+    pcb->flags |= TF_NAGLEMEMERR;
+    return -ENOMEM;
+  }
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
+      pcb->unacked != NULL || pcb->unsent != NULL);
+  } else {
+    LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
+      pcb->unacked == NULL && pcb->unsent == NULL);
+  }
+  return ESUCCESS;
+}
+
+/**
+ * Write data for sending (but does not send it immediately).
+ *
+ * It waits in the expectation of more data being sent soon (as
+ * it can send them more efficiently by combining them together).
+ * To prompt the system to send data now, call tcp_output() after
+ * calling tcp_write().
+ *
+ * @param pcb Protocol control block for the TCP connection to enqueue data for.
+ * @param arg Pointer to the data to be enqueued for sending.
+ * @param len Data length in bytes
+ * @param apiflags combination of following flags :
+ * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
+ * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
+ * @return ESUCCESS if enqueued, another error_t on error
+ */
+error_t
+tcp_write(struct tcp_pcb *pcb, const void *arg, uint16_t len, uint8_t apiflags)
+{
+  struct pbuf *concat_p = NULL;
+  struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
+  uint16_t pos = 0; /* position in 'arg' data */
+  uint16_t queuelen;
+  uint8_t optlen = 0;
+  uint8_t optflags = 0;
+#if TCP_OVERSIZE
+  uint16_t oversize = 0;
+  uint16_t oversize_used = 0;
+#endif /* TCP_OVERSIZE */
+#if TCP_CHECKSUM_ON_COPY
+  uint16_t concat_chksum = 0;
+  uint8_t concat_chksum_swapped = 0;
+  uint16_t concat_chksummed = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+  error_t err;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  /* Always copy to try to create single pbufs for TX */
+  apiflags |= TCP_WRITE_FLAG_COPY;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
+    (void *)pcb, arg, len, (uint16_t)apiflags));
+  LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", 
+             arg != NULL, return -EFAIL;);
+
+  err = tcp_write_checks(pcb, len);
+  if (err != ESUCCESS) {
+    return err;
+  }
+  queuelen = pcb->snd_queuelen;
+
+#if LWIP_TCP_TIMESTAMPS
+  if ((pcb->flags & TF_TIMESTAMP)) {
+    optflags = TF_SEG_OPTS_TS;
+    optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+  }
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+
+  /*
+   * TCP segmentation is done in three phases with increasing complexity:
+   *
+   * 1. Copy data directly into an oversized pbuf.
+   * 2. Chain a new pbuf to the end of pcb->unsent.
+   * 3. Create new segments.
+   *
+   * We may run out of memory at any point. In that case we must
+   * return -ENOMEM and not change anything in pcb. Therefore, all
+   * changes are recorded in local variables and committed at the end
+   * of the function. Some pcb fields are maintained in local copies:
+   *
+   * queuelen = pcb->snd_queuelen
+   * oversize = pcb->unsent_oversize
+   *
+   * These variables are set consistently by the phases:
+   *
+   * seg points to the last segment tampered with.
+   *
+   * pos records progress as data is segmented.
+   */
+
+  /* Find the tail of the unsent queue. */
+  if (pcb->unsent != NULL) {
+    uint16_t space;
+    uint16_t unsent_optlen;
+
+    /* @todo: this could be sped up by keeping last_unsent in the pcb */
+    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+         last_unsent = last_unsent->next);
+
+    /* Usable space at the end of the last unsent segment */
+    unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
+    space = pcb->mss - (last_unsent->len + unsent_optlen);
+
+    /*
+     * Phase 1: Copy data directly into an oversized pbuf.
+     *
+     * The number of bytes copied is recorded in the oversize_used
+     * variable. The actual copying is done at the bottom of the
+     * function.
+     */
+#if TCP_OVERSIZE
+#if TCP_OVERSIZE_DBGCHECK
+    /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */
+    LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
+                pcb->unsent_oversize == last_unsent->oversize_left);
+#endif /* TCP_OVERSIZE_DBGCHECK */
+    oversize = pcb->unsent_oversize;
+    if (oversize > 0) {
+      LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);
+      seg = last_unsent;
+      oversize_used = oversize < len ? oversize : len;
+      pos += oversize_used;
+      oversize -= oversize_used;
+      space -= oversize_used;
+    }
+    /* now we are either finished or oversize is zero */
+    LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len));
+#endif /* TCP_OVERSIZE */
+
+    /*
+     * Phase 2: Chain a new pbuf to the end of pcb->unsent.
+     *
+     * We don't extend segments containing SYN/FIN flags or options
+     * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
+     * the end.
+     */
+    if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
+      uint16_t seglen = space < len - pos ? space : len - pos;
+      seg = last_unsent;
+
+      /* Create a pbuf with a copy or reference to seglen bytes. We
+       * can use PBUF_RAW here since the data appears in the middle of
+       * a segment. A header will never be prepended. */
+      if (apiflags & TCP_WRITE_FLAG_COPY) {
+        /* Data is copied */
+        if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
+          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+                      ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
+                       seglen));
+          goto memerr;
+        }
+#if TCP_OVERSIZE_DBGCHECK
+        last_unsent->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+        TCP_DATA_COPY2(concat_p->payload, (uint8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
+#if TCP_CHECKSUM_ON_COPY
+        concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+      } else {
+        /* Data is not copied */
+        if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+                      ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+          goto memerr;
+        }
+#if TCP_CHECKSUM_ON_COPY
+        /* calculate the checksum of nocopy-data */
+        tcp_seg_add_chksum(~inet_chksum((uint8_t*)arg + pos, seglen), seglen,
+          &concat_chksum, &concat_chksum_swapped);
+        concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+        /* reference the non-volatile payload data */
+        concat_p->payload = (uint8_t*)arg + pos;
+      }
+
+      pos += seglen;
+      queuelen += pbuf_clen(concat_p);
+    }
+  } else {
+#if TCP_OVERSIZE
+    LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
+                pcb->unsent_oversize == 0);
+#endif /* TCP_OVERSIZE */
+  }
+
+  /*
+   * Phase 3: Create new segments.
+   *
+   * The new segments are chained together in the local 'queue'
+   * variable, ready to be appended to pcb->unsent.
+   */
+  while (pos < len) {
+    struct pbuf *p;
+    uint16_t left = len - pos;
+    uint16_t max_len = pcb->mss - optlen;
+    uint16_t seglen = left > max_len ? max_len : left;
+#if TCP_CHECKSUM_ON_COPY
+    uint16_t chksum = 0;
+    uint8_t chksum_swapped = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+    if (apiflags & TCP_WRITE_FLAG_COPY) {
+      /* If copy is set, memory should be allocated and data copied
+       * into pbuf */
+      if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+        goto memerr;
+      }
+      LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
+                  (p->len >= seglen));
+      TCP_DATA_COPY2((char *)p->payload + optlen, (uint8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
+    } else {
+      /* Copy is not set: First allocate a pbuf for holding the data.
+       * Since the referenced data is available at least until it is
+       * sent out on the link (as it has to be ACKed by the remote
+       * party) we can safely use PBUF_ROM instead of PBUF_REF here.
+       */
+      struct pbuf *p2;
+#if TCP_OVERSIZE
+      LWIP_ASSERT("oversize == 0", oversize == 0);
+#endif /* TCP_OVERSIZE */
+      if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+        goto memerr;
+      }
+#if TCP_CHECKSUM_ON_COPY
+      /* calculate the checksum of nocopy-data */
+      chksum = ~inet_chksum((uint8_t*)arg + pos, seglen);
+#endif /* TCP_CHECKSUM_ON_COPY */
+      /* reference the non-volatile payload data */
+      p2->payload = (uint8_t*)arg + pos;
+
+      /* Second, allocate a pbuf for the headers. */
+      if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+        /* If allocation fails, we have to deallocate the data pbuf as
+         * well. */
+        pbuf_free(p2);
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n"));
+        goto memerr;
+      }
+      /* Concatenate the headers and data pbufs together. */
+      pbuf_cat(p/*header*/, p2/*data*/);
+    }
+
+    queuelen += pbuf_clen(p);
+
+    /* Now that there are more segments queued, we check again if the
+     * length of the queue exceeds the configured maximum or
+     * overflows. */
+    if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
+      pbuf_free(p);
+      goto memerr;
+    }
+
+    if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
+      goto memerr;
+    }
+#if TCP_OVERSIZE_DBGCHECK
+    seg->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+    seg->chksum = chksum;
+    seg->chksum_swapped = chksum_swapped;
+    seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+    /* Fix dataptr for the nocopy case */
+    if ((apiflags & TCP_WRITE_FLAG_COPY) == 0) {
+      seg->dataptr = (uint8_t*)arg + pos;
+    }
+
+    /* first segment of to-be-queued data? */
+    if (queue == NULL) {
+      queue = seg;
+    } else {
+      /* Attach the segment to the end of the queued segments */
+      LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
+      prev_seg->next = seg;
+    }
+    /* remember last segment of to-be-queued data for next iteration */
+    prev_seg = seg;
+
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
+      ntohl(seg->tcphdr->seqno),
+      ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
+
+    pos += seglen;
+  }
+
+  /*
+   * All three segmentation phases were successful. We can commit the
+   * transaction.
+   */
+
+  /*
+   * Phase 1: If data has been added to the preallocated tail of
+   * last_unsent, we update the length fields of the pbuf chain.
+   */
+#if TCP_OVERSIZE
+  if (oversize_used > 0) {
+    struct pbuf *p;
+    /* Bump tot_len of whole chain, len of tail */
+    for (p = last_unsent->p; p; p = STAILQ_NEXT(p, next)) {
+      p->tot_len += oversize_used;
+      if (STAILQ_NEXT(p, next) == NULL) {
+        TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
+        p->len += oversize_used;
+      }
+    }
+    last_unsent->len += oversize_used;
+#if TCP_OVERSIZE_DBGCHECK
+    last_unsent->oversize_left -= oversize_used;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+  }
+  pcb->unsent_oversize = oversize;
+#endif /* TCP_OVERSIZE */
+
+  /*
+   * Phase 2: concat_p can be concatenated onto last_unsent->p
+   */
+  if (concat_p != NULL) {
+    LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
+      (last_unsent != NULL));
+    pbuf_cat(last_unsent->p, concat_p);
+    last_unsent->len += concat_p->tot_len;
+#if TCP_CHECKSUM_ON_COPY
+    if (concat_chksummed) {
+      tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+        &last_unsent->chksum_swapped);
+      last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+    }
+#endif /* TCP_CHECKSUM_ON_COPY */
+  }
+
+  /*
+   * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
+   * is harmless
+   */
+  if (last_unsent == NULL) {
+    pcb->unsent = queue;
+  } else {
+    last_unsent->next = queue;
+  }
+
+  /*
+   * Finally update the pcb state.
+   */
+  pcb->snd_lbb += len;
+  pcb->snd_buf -= len;
+  pcb->snd_queuelen = queuelen;
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
+    pcb->snd_queuelen));
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: valid queue length",
+                pcb->unacked != NULL || pcb->unsent != NULL);
+  }
+
+  /* Set the PSH flag in the last segment that we enqueued. */
+  if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
+    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
+  }
+
+  return ESUCCESS;
+memerr:
+  pcb->flags |= TF_NAGLEMEMERR;
+  // TCP_STATS_INC(tcp.memerr);
+
+  if (concat_p != NULL) {
+    pbuf_free(concat_p);
+  }
+  if (queue != NULL) {
+    tcp_segs_free(queue);
+  }
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
+      pcb->unsent != NULL);
+  }
+  LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
+  return -ENOMEM;
+}
+
+/**
+ * Enqueue TCP options for transmission.
+ *
+ * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param flags TCP header flags to set in the outgoing segment.
+ * @param optdata pointer to TCP options, or NULL.
+ * @param optlen length of TCP options in bytes.
+ */
+error_t
+tcp_enqueue_flags(struct tcp_pcb *pcb, uint8_t flags)
+{
+  struct pbuf *p;
+  struct tcp_seg *seg;
+  uint8_t optflags = 0;
+  uint8_t optlen = 0;
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (uint16_t)pcb->snd_queuelen));
+
+  LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
+              (flags & (TCP_SYN | TCP_FIN)) != 0);
+
+  /* check for configured max queuelen and possible overflow */
+  if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
+                                       pcb->snd_queuelen, TCP_SND_QUEUELEN));
+    // TCP_STATS_INC(tcp.memerr);
+    pcb->flags |= TF_NAGLEMEMERR;
+    return -ENOMEM;
+  }
+
+  if (flags & TCP_SYN) {
+    optflags = TF_SEG_OPTS_MSS;
+  }
+#if LWIP_TCP_TIMESTAMPS
+  if ((pcb->flags & TF_TIMESTAMP)) {
+    optflags |= TF_SEG_OPTS_TS;
+  }
+#endif /* LWIP_TCP_TIMESTAMPS */
+  optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+  /* tcp_enqueue_flags is always called with either SYN or FIN in flags.
+   * We need one available snd_buf byte to do that.
+   * This means we can't send FIN while snd_buf==0. A better fix would be to
+   * not include SYN and FIN sequence numbers in the snd_buf count. */
+  if (pcb->snd_buf == 0) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n"));
+    // TCP_STATS_INC(tcp.memerr);
+    return -ENOMEM;
+  }
+
+  /* Allocate pbuf with room for TCP header + options */
+  if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+    pcb->flags |= TF_NAGLEMEMERR;
+    // TCP_STATS_INC(tcp.memerr);
+    return -ENOMEM;
+  }
+  LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
+              (p->len >= optlen));
+
+  /* Allocate memory for tcp_seg, and fill in fields. */
+  if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
+    pcb->flags |= TF_NAGLEMEMERR;
+    //TCP_STATS_INC(tcp.memerr);
+    return -ENOMEM;
+  }
+  LWIP_ASSERT("seg->tcphdr not aligned", ((uint32_t)seg->tcphdr % 4) == 0);
+  LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
+              ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
+               ntohl(seg->tcphdr->seqno),
+               ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
+               (uint16_t)flags));
+
+  /* Now append seg to pcb->unsent queue */
+  if (pcb->unsent == NULL) {
+    pcb->unsent = seg;
+  } else {
+    struct tcp_seg *useg;
+    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
+    useg->next = seg;
+  }
+#if TCP_OVERSIZE
+  /* The new unsent tail has no space */
+  pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+
+  /* SYN and FIN bump the sequence number */
+  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
+    pcb->snd_lbb++;
+    /* optlen does not influence snd_buf */
+    pcb->snd_buf--;
+  }
+  if (flags & TCP_FIN) {
+    pcb->flags |= TF_FIN;
+  }
+
+  /* update number of segments on the queues */
+  pcb->snd_queuelen += pbuf_clen(seg->p);
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
+      pcb->unacked != NULL || pcb->unsent != NULL);
+  }
+
+  return ESUCCESS;
+}
+
+#if LWIP_TCP_TIMESTAMPS
+/* Build a timestamp option (12 bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the timestamp option
+ */
+static void
+tcp_build_timestamp_option(struct tcp_pcb *pcb, uint32_t *opts)
+{
+  /* Pad with two NOP options to make everything nicely aligned */
+  opts[0] = PP_HTONL(0x0101080A);
+  opts[1] = htonl(sys_now());
+  opts[2] = htonl(pcb->ts_recent);
+}
+#endif
+
+/** Send an ACK without data.
+ *
+ * @param pcb Protocol control block for the TCP connection to send the ACK
+ */
+error_t
+tcp_send_empty_ack(struct tcp_pcb *pcb)
+{
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+  uint8_t optlen = 0;
+
+#if LWIP_TCP_TIMESTAMPS
+  if (pcb->flags & TF_TIMESTAMP) {
+    optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+  }
+#endif
+
+  p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
+  if (p == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+    return -ENOMEM;
+  }
+  tcphdr = (struct tcp_hdr *)p->payload;
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, 
+              ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
+  /* remove ACK flags from the PCB, as we send an empty ACK now */
+  pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+
+  /* NB. MSS option is only sent on SYNs, so ignore it here */
+#if LWIP_TCP_TIMESTAMPS
+  pcb->ts_lastacksent = pcb->rcv_nxt;
+
+  if (pcb->flags & TF_TIMESTAMP) {
+    tcp_build_timestamp_option(pcb, (uint32_t *)(tcphdr + 1));
+  }
+#endif 
+
+#if CHECKSUM_GEN_TCP
+  tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
+        IP_PROTO_TCP, p->tot_len);
+#endif
+#if LWIP_NETIF_HWADDRHINT
+   ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+      IPPROTO_TCP, &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+   ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+      IPPROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+  pbuf_free(p);
+
+  return ESUCCESS;
+}
+
+/**
+ * Find out what we can send and send it
+ *
+ * @param pcb Protocol control block for the TCP connection to send data
+ * @return ESUCCESS if data has been sent or nothing to send
+ *         another error_t on error
+ */
+error_t
+tcp_output(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg, *useg;
+  uint32_t wnd, snd_nxt;
+#if TCP_CWND_DEBUG
+  s16_t i = 0;
+#endif /* TCP_CWND_DEBUG */
+
+  /* First, check if we are invoked by the TCP input processing
+     code. If so, we do not output anything. Instead, we rely on the
+     input processing code to call us when input processing is done
+     with. */
+  if (tcp_input_pcb == pcb) {
+    return ESUCCESS;
+  }
+
+  wnd = MIN(pcb->snd_wnd, pcb->cwnd);
+
+  seg = pcb->unsent;
+
+  /* If the TF_ACK_NOW flag is set and no data will be sent (either
+   * because the ->unsent queue is empty or because the window does
+   * not allow it), construct an empty ACK segment and send it.
+   *
+   * If data is to be sent, we will just piggyback the ACK (see below).
+   */
+  if (pcb->flags & TF_ACK_NOW &&
+     (seg == NULL ||
+      ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
+     return tcp_send_empty_ack(pcb);
+  }
+
+  /* useg should point to last segment on unacked queue */
+  useg = pcb->unacked;
+  if (useg != NULL) {
+    for (; useg->next != NULL; useg = useg->next);
+  }
+
+#if TCP_OUTPUT_DEBUG
+  if (seg == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
+                                   (void*)pcb->unsent));
+  }
+#endif /* TCP_OUTPUT_DEBUG */
+#if TCP_CWND_DEBUG
+  if (seg == NULL) {
+    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
+                                 ", cwnd %"U16_F", wnd %"U32_F
+                                 ", seg == NULL, ack %"U32_F"\n",
+                                 pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
+  } else {
+    LWIP_DEBUGF(TCP_CWND_DEBUG, 
+                ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F
+                 ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
+                 pcb->snd_wnd, pcb->cwnd, wnd,
+                 ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+                 ntohl(seg->tcphdr->seqno), pcb->lastack));
+  }
+#endif /* TCP_CWND_DEBUG */
+  /* data available and window allows it to be sent? */
+  while (seg != NULL &&
+         ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
+    LWIP_ASSERT("RST not expected here!", 
+                (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
+    /* Stop sending if the nagle algorithm would prevent it
+     * Don't stop:
+     * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
+     * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
+     *   either seg->next != NULL or pcb->unacked == NULL;
+     *   RST is no sent using tcp_write/tcp_output.
+     */
+    if((tcp_do_output_nagle(pcb) == 0) &&
+      ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){
+      break;
+    }
+#if TCP_CWND_DEBUG
+    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
+                            pcb->snd_wnd, pcb->cwnd, wnd,
+                            ntohl(seg->tcphdr->seqno) + seg->len -
+                            pcb->lastack,
+                            ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+    ++i;
+#endif /* TCP_CWND_DEBUG */
+
+    pcb->unsent = seg->next;
+
+    if (pcb->state != SYN_SENT) {
+      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
+      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+    }
+
+    tcp_output_segment(seg, pcb);
+    snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+    if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+      pcb->snd_nxt = snd_nxt;
+    }
+    /* put segment on unacknowledged list if length > 0 */
+    if (TCP_TCPLEN(seg) > 0) {
+      seg->next = NULL;
+      /* unacked list is empty? */
+      if (pcb->unacked == NULL) {
+        pcb->unacked = seg;
+        useg = seg;
+      /* unacked list is not empty? */
+      } else {
+        /* In the case of fast retransmit, the packet should not go to the tail
+         * of the unacked queue, but rather somewhere before it. We need to check for
+         * this case. -STJ Jul 27, 2004 */
+        if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg-&g