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

17 files changed:
1  2 
Makeconfig
kern/arch/i686/nic_common.c
kern/arch/i686/x86.h
kern/include/alarm.h
kern/include/kthread.h
kern/include/ros/bits/syscall.h
kern/include/ros/common.h
kern/include/time.h
kern/include/vfs.h
kern/src/Makefrag
kern/src/eth_audio.c
kern/src/init.c
kern/src/net/tcp.c
kern/src/net/udp.c
kern/src/socket.c
kern/src/syscall.c
kern/src/vfs.c

diff --cc Makeconfig
@@@ -34,7 -33,7 +34,8 @@@ CONFIG_PCI_VERBOSE:=               -D__
  CONFIG_ETH_AUDIO:=                 -D__CONFIG_ETH_AUDIO__
  CONFIG_KB_CORE0_ONLY:=             -D__CONFIG_KB_CORE0_ONLY__
  CONFIG_KTHREAD_POISON:=            -D__CONFIG_KTHREAD_POISON__
 -CONFIG_SOCKET:=                    -D__CONFIG_SOCKET__                                                                                
 +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
Simple merge
Simple merge
@@@ -62,6 -64,8 +62,8 @@@ struct alarm_waiter 
  };
  TAILQ_HEAD(awaiters_tailq, alarm_waiter);             /* ideally not a LL */
  
 -typedef void (* alarm_handler)(struct alarm_waiter *waiter);
++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. */
@@@ -35,41 -40,69 +39,48 @@@ struct semaphore 
        struct kthread_tailq            waiters;
        int                                             nr_signals;
        spinlock_t                                      lock;
 +      bool                                            irq_okay;
  };
  
 -/* This doesn't have to be inline, but it doesn't matter for now */
 -static inline void init_sem(struct semaphore *sem, int signals)
 -{
 -      TAILQ_INIT(&sem->waiters);
 -      sem->nr_signals = signals;
 -      spinlock_init(&sem->lock);
 -}
 -
 -/* Down and up for the semaphore are a little more low-level than usual, since
 - * they are meant to be called by functions that manage the sleeping of a
 - * kthread.  For instance, __down_sem() always returns right away.  For now,
 - * these are just examples, since the actual usage will probably need lower
 - * access. */
 -
 -/* Down : decrement, if it was 0 or less, we need to sleep.  Returns false if
 - * the kthread did not need to sleep (the signal was already there). */
 -static inline bool __down_sem(struct semaphore *sem, struct kthread *kthread)
 -{
 -      bool retval = FALSE;
 -      spin_lock(&sem->lock);
 -      if (sem->nr_signals-- <= 0 && kthread != NULL) {
 -              /* Need to sleep */
 -              retval = TRUE;
 -              TAILQ_INSERT_TAIL(&sem->waiters, kthread, link);
 -      }
 -      spin_unlock(&sem->lock);
 -      return retval;
 -}
 -
 -/* Ups the semaphore.  If it was < 0, we need to wake up someone, which is the
 - * return value.  If you think there should be at most one, set exactly_one. */
 -static inline struct kthread *__up_sem(struct semaphore *sem, bool exactly_one)
 -{
 -      struct kthread *kthread = 0;
 -      spin_lock(&sem->lock);
 -      if (sem->nr_signals++ < 0) {
 -              /* could do something with 'priority' here */
 -              kthread = TAILQ_FIRST(&sem->waiters);
 -              if (kthread == NULL) warn ("kthread is null\n");
 -              TAILQ_REMOVE(&sem->waiters, kthread, link);
 -              if (exactly_one)
 -                      assert(TAILQ_EMPTY(&sem->waiters));
 -      } else {
 -              assert(TAILQ_EMPTY(&sem->waiters));
 -      }
 -      spin_unlock(&sem->lock);
 -      return kthread;
 -}
 +struct cond_var {
 +      struct semaphore                        sem;
 +      spinlock_t                                      lock;
 +      unsigned long                           nr_waiters;
 +      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 sleep_on(struct semaphore *sem);
  void restart_kthread(struct kthread *kthread);
  void kthread_runnable(struct kthread *kthread);
 -/* Kmsg handler to launch/run a kthread.  This must be a routine message, since
 - * it does not return. */
 -void __launch_kthread(struct trapframe *tf, uint32_t srcid, long a0, long a1,
 -                        long a2);
 +void kthread_yield(void);
 +
 +void sem_init(struct semaphore *sem, int signals);
 +void sem_init_irqsave(struct semaphore *sem, int signals);
 +void sem_down(struct semaphore *sem);
 +bool sem_up(struct semaphore *sem);
 +void sem_down_irqsave(struct semaphore *sem, int8_t *irq_state);
 +bool sem_up_irqsave(struct semaphore *sem, int8_t *irq_state);
 +
 +void cv_init(struct cond_var *cv);
 +void cv_init_irqsave(struct cond_var *cv);
 +void cv_lock(struct cond_var *cv);
 +void cv_unlock(struct cond_var *cv);
 +void cv_lock_irqsave(struct cond_var *cv, int8_t *irq_state);
 +void cv_unlock_irqsave(struct cond_var *cv, int8_t *irq_state);
 +void cv_wait_and_unlock(struct cond_var *cv); /* does not mess with irqs */
 +void cv_wait(struct cond_var *cv);
 +void __cv_signal(struct cond_var *cv);
 +void __cv_broadcast(struct cond_var *cv);
 +void cv_signal(struct cond_var *cv);
 +void cv_broadcast(struct cond_var *cv);
 +void cv_signal_irqsave(struct cond_var *cv, int8_t *irq_state);
 +void cv_broadcast_irqsave(struct cond_var *cv, int8_t *irq_state);
  
  #endif /* ROS_KERN_KTHREAD_H */
Simple merge
Simple merge
@@@ -17,7 -17,11 +17,12 @@@ struct itimerspec 
    struct timespec  it_value;     /* Timer expiration */
  };
  
 -      time_t tv_sec;          /* seconds */
+ 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);
  uint64_t tsc2msec(uint64_t tsc_time);
@@@ -442,11 -445,12 +446,12 @@@ struct inode *icache_remove(struct supe
  
  /* File-ish functions */
  ssize_t generic_file_read(struct file *file, char *buf, size_t count,
 -                          off_t *offset);
 +                          off64_t *offset);
  ssize_t generic_file_write(struct file *file, const char *buf, size_t count,
 -                           off_t *offset);
 +                           off64_t *offset);
  ssize_t generic_dir_read(struct file *file, char *u_buf, size_t count,
 -                         off_t *offset);
 -struct file* alloc_file();
 +                         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);
@@@ -59,8 -59,16 +59,16 @@@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) 
                   $(KERN_SRC_DIR)/kdebug.c \
                   $(KERN_SRC_DIR)/ucq.c \
                   $(KERN_SRC_DIR)/console.c \
 -                 $(KERN_SRC_DIR)/arsc.c \
 +                 $(KERN_SRC_DIR)/trap.c \
+                  $(KERN_SRC_DIR)/socket.c \
 -                 $(KERN_SRC_DIR)/net/timers.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.
  KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
@@@ -100,7 -101,8 +101,8 @@@ static void eth_audio_prep_response(str
        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->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);
diff --cc kern/src/init.c
Simple merge
index 0000000,6035661..bc3ce94
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1628 +1,1616 @@@
 -static void wrap_restart_kthread(struct trapframe *tf, uint32_t srcid,
 -                                      long a0, long a1, long a2){
 -      restart_kthread((struct kthread*) a0);
 -}
 -
+ /**
+  * @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);
+ }
 -              kthread = __up_sem(&(sock->sem), FALSE);
 -              if (kthread) {
 -                       send_kernel_message(core_id(), (amr_t)wrap_restart_kthread, (long)kthread, 0, 0,
 -                                                                                                KMSG_ROUTINE);
 -              } else {
+ /**
+  * 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..
 -                LIST_FOREACH_SAFE(sentry, &(sock->waiters), link, sentry_tmp){
 -                              //should only wake up one waiter
 -                              kthread = __up_sem(&sentry->sem, true);
 -                              if (kthread){
 -                              send_kernel_message(core_id(), (amr_t)wrap_restart_kthread, (long)kthread, 0, 0,
 -                                                                                                KMSG_ROUTINE);
 -                              }
++              if (!sem_up_irqsave(&sock->sem, &irq_state)) {
+                       // wake up all waiters
+                       struct semaphore_entry *sentry, *sentry_tmp;
+                       spin_lock(&sock->waiter_lock);
 -                              /* do not need to free since all the sentry are stack-based vars */
++                      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 */
index 0000000,1b9e535..6f1e7ad
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,324 +1,314 @@@
 -static void wrap_restart_kthread(struct trapframe *tf, uint32_t srcid,
 -                                      long a0, long a1, long a2){
 -      restart_kthread((struct kthread*) a0);
 -}
 -
+ /**
+  * Contains shamelessly stolen code from BSD & lwip, both have
+  * BSD-style licenses
+  *
+  */
+ #include <ros/common.h>
+ #include <string.h>
+ #include <kmalloc.h>
+ #include <socket.h>
+ #include <net.h>
+ #include <sys/queue.h>
+ #include <atomic.h>
+ #include <bits/netinet.h>
+ #include <net/ip.h>
+ #include <net/udp.h>
+ #include <slab.h>
+ #include <socket.h>
+ #include <debug.h>
+ struct udp_pcb *udp_pcbs;
+ uint16_t udp_port_num = SOCKET_PORT_START;
+ struct udp_pcb* udp_new(void){
+       struct udp_pcb *pcb = kmem_cache_alloc(udp_pcb_kcache, 0);
+     // if pcb is only tracking ttl, then no need!
+       if (pcb!= NULL){
+               pcb->ttl = UDP_TTL;
+       memset(pcb, 0, sizeof(struct udp_pcb));
+       }
+       return pcb;
+ }
+ int udp_send(struct udp_pcb *pcb, struct pbuf *p)
+ {
+   /* send to the packet using remote ip and port stored in the pcb */
+   // rip and rport should be in socket not pcb?
+   return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
+ }
+ typedef unsigned char u16;
+ typedef unsigned long u32;
+ u16 udp_sum_calc(u16 len_udp, u16 src_addr[],u16 dest_addr[],  int padding, u16 buff[])
+ {
+ u16 prot_udp=17;
+ u16 padd=0;
+ u16 word16;
+ u32 sum;
+ int i;
+       
+       // Find out if the length of data is even or odd number. If odd,
+       // add a padding byte = 0 at the end of packet
+       if ((padding&1)==1){
+               padd=1;
+               buff[len_udp]=0;
+       }
+       
+       //initialize sum to zero
+       sum=0;
+       
+       // make 16 bit words out of every two adjacent 8 bit words and 
+       // calculate the sum of all 16 vit words
+       for (i=0;i<len_udp+padd;i=i+2){
+               word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF);
+               sum = sum + (unsigned long)word16;
+       }       
+       // add the UDP pseudo header which contains the IP source and destinationn addresses
+       for (i=0;i<4;i=i+2){
+               word16 =((src_addr[i]<<8)&0xFF00)+(src_addr[i+1]&0xFF);
+               sum=sum+word16; 
+       }
+       for (i=0;i<4;i=i+2){
+               word16 =((dest_addr[i]<<8)&0xFF00)+(dest_addr[i+1]&0xFF);
+               sum=sum+word16;         
+       }
+       // the protocol number and the length of the UDP packet
+       sum = sum + prot_udp + len_udp;
+       // keep only the last 16 bits of the 32 bit calculated sum and add the carries
+       while (sum>>16)
+               sum = (sum & 0xFFFF)+(sum >> 16);
+               
+       // Take the one's complement of sum
+       sum = ~sum;
+ return ((u16) sum);
+ }
+ int udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+                     struct in_addr *dst_ip, uint16_t dst_port){
+     // we now have one netif to send to, otherwise we need to route
+     // ip_route();
+     struct udp_hdr *udphdr;
+     struct pbuf *q;
+               printd("udp_sendto ip %x, port %d\n", dst_ip->s_addr, dst_port); 
+     // broadcast?
+     if (pcb->local_port == 0) {
+                               /* if the PCB not bound to a port, bind it and give local ip */
+         if (udp_bind(pcb, &pcb->local_ip, pcb->local_port)!=0)
+                                       warn("udp binding failed \n");
+     }
+     if (pbuf_header(p, UDP_HLEN)){ // we could probably save this check for block.
+         // CHECK: should allocate enough for the other headers too
+         q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
+         if (q == NULL)
+            panic("out of memory");
+         // if the original packet is not empty
+         if (p->tot_len !=0) {
+             pbuf_chain(q,p);
+             // check if it is chained properly ..
+         }
+     } else {
+                               /* Successfully padded the header*/
+                               q = p;
+     }
+     udphdr = (struct udp_hdr *) q->payload;
+               printd("src port %d, dst port %d \n, length %d ", pcb->local_port, ntohs(dst_port), q->tot_len);
+     udphdr->src_port = htons(pcb->local_port);
+     udphdr->dst_port = (dst_port);
+     udphdr->length = htons(q->tot_len); 
+               udphdr->checksum = 0; // just to be sure.
+               // printd("checksum inet_chksum %x \n", udphdr->checksum);
+               printd("params src addr %x, dst addr %x, length %x \n", LOCAL_IP_ADDR.s_addr, (dst_ip->s_addr), 
+                                         q->tot_len);
+     udphdr->checksum = inet_chksum_pseudo(q, htonl(LOCAL_IP_ADDR.s_addr), dst_ip->s_addr,
+                                                                                        IPPROTO_UDP, q->tot_len);
+               printd ("method ours %x\n", udphdr->checksum);
+               // 0x0000; //either use brho's checksum or use cards' capabilities
+               ip_output(q, &LOCAL_IP_ADDR, dst_ip, pcb->ttl, pcb->tos, IPPROTO_UDP);
+               // ip_output(q, &global_ip, dst_ip, IPPROTO_UDP);
+     return 0;
+ }
+ /* TODO: use the real queues we have implemented... */
+ int udp_bind(struct udp_pcb *pcb, const struct in_addr *ip, uint16_t port){ 
+     int rebind = pcb->local_port;
+     struct udp_pcb *ipcb;
+               assert(pcb);
+               /* trying to assign port */
+     if (port != 0)
+         pcb->local_port = port;
+     /* no lock needed since we are just traversing/reading */
+     /* Check for double bind and rebind of the same pcb */
+     for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+         /* is this UDP PCB already on active list? */
+         if (pcb == ipcb) {
+             rebind = 1; //already on the list
+         } else if (ipcb->local_port == port){
+             warn("someone else is using the port %d\n" , port); 
+             return -1;
+         }
+     }
+     /* accept data for all interfaces */
+     if (ip == NULL || (ip->s_addr == INADDR_ANY.s_addr))
+               /* true right now */
+         pcb->local_ip = INADDR_ANY;
+     /* assign a port */
+     if (port == 0) {
+         port = SOCKET_PORT_START; 
+         ipcb = udp_pcbs;
+         while ((ipcb != NULL) && (port != SOCKET_PORT_END)) {
+             if (ipcb->local_port == port) {
+                 /* port is already used by another udp_pcb */
+                 port++;
+                 /* restart scanning all udp pcbs */
+                 ipcb = udp_pcbs;
+             } else {
+                 /* go on with next udp pcb */
+                 ipcb = ipcb->next;
+             }
+         }
+         if (ipcb != NULL){
+             warn("No more udp ports available!");
+         }
+     }
+     if (rebind == 0) {
+         /* place the PCB on the active list if not already there */
+                               pcb->next = udp_pcbs;
+                               udp_pcbs = pcb;
+     }
+               printk("local port bound to 0x%x \n", port);
+     pcb->local_port = port;
+     return 0;
+ }
+ /* port are in host order, ips are in network order */
+ /* Think: a pcb is here, if someone is waiting for a connection or the udp conn
+  * has been established */
+ static struct udp_pcb* find_pcb(struct udp_pcb* list, uint16_t src_port, uint16_t dst_port,
+                                                               uint16_t srcip, uint16_t dstip) {
+       struct udp_pcb* uncon_pcb = NULL;
+       struct udp_pcb* pcb = NULL;
+       uint8_t local_match = 0;
+       for (pcb = list; pcb != NULL; pcb = pcb->next) {
+               local_match = 0;
+               if ((pcb->local_port == dst_port) 
+                       && (pcb->local_ip.s_addr == dstip 
+                       || ip_addr_isany(&pcb->local_ip))){
+                               local_match = 1;
+         if ((uncon_pcb == NULL) && 
+             ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
+           /* the first unconnected matching PCB */
+           uncon_pcb = pcb;
+         }
+               }
+               if (local_match && (pcb->remote_port == src_port) &&
+                               (ip_addr_isany(&pcb->remote_ip) ||
+                                pcb->remote_ip.s_addr == srcip))
+                       /* perfect match */
+                       return pcb;
+       }
+       return uncon_pcb;
+ }
+ #if 0 // not working yet
+ // need to have pbuf queue support
+ int udp_attach(struct pbuf *p, struct sock *socket) {
+       // pretend the attaching of packet is succesful
+       /*
+       recv_q->last->next = p;
+       recv_q->last=p->last
+       */ 
+ }
+ #endif 
+ /** Process an incoming UDP datagram. 
+  * Given an incoming UDP datagram, this function finds the right PCB
+  * which links to the right socket buffer, and attaches the datagram
+  * to the right socket. 
+  * If no appropriate PCB is found, the pbuf is freed.
+  */ 
+ /** TODO: think about combining udp_input and ip_input together */
+ // TODO: figure out if we even need a PCB? or just socket buff. 
+ // TODO: test out looking up pcbs.. since matching function may fail
 -              kthread = __up_sem(&(sock->sem), FALSE);
 -              if (kthread) {
 -                       send_kernel_message(core_id(), (amr_t)wrap_restart_kthread, (long)kthread, 0, 0,
 -                                                                                                KMSG_ROUTINE);
 -              } else {
+ int udp_input(struct pbuf *p){
+       struct udp_hdr *udphdr;
+       struct udp_pcb *pcb, uncon_pcb;
+       struct ip_hdr *iphdr;
+       uint16_t src, dst;
+       bool local_match = 0;
+       iphdr = (struct ip_hdr *)p->payload;
+       /* Move the header to where the udp header is */
+       if (pbuf_header(p, -((int16_t)((iphdr->hdr_len) * 4)))) {
+               warn("udp_input: Did not find a matching PCB for a udp packet\n");
+               pbuf_free(p);
+               return -1;
+       }
+       printk("start of udp %p\n", p->payload);
+       udphdr = (struct udp_hdr *)p->payload;
+       /* convert the src port and dst port to host order */
+       src = ntohs(udphdr->src_port);
+       dst = ntohs(udphdr->dst_port);
+       pcb = find_pcb(udp_pcbs, src, dst, iphdr->src_addr, iphdr->dst_addr);
+       /* TODO: Possibly adjust the pcb to the head of the queue? */
+       /* TODO: Linux uses a set of hashtables to lookup PCBs 
+        * Look at __udp4_lib_lookup function in Linux kernel 2.6.21.1
+        */
+       /* Anything that is not directed at this pcb should have been dropped */
+       if (pcb == NULL){
+               warn("udp_input: Did not find a matching PCB for a udp packet\n");
+               pbuf_free(p);
+               return -1;
+       }
+       /* checksum check */
+   if (udphdr->checksum != 0) {
+     if (inet_chksum_pseudo(p, (iphdr->src_addr), (iphdr->dst_addr), 
+                                IPPROTO_UDP, p->tot_len) != 0){
+                       warn("udp_input: UPD datagram discarded due to failed chksum!");
+                       pbuf_free(p);
+                       return -1;
+     }
+       }
+   /* ignore SO_REUSE */
+       if (pcb != NULL && pcb->pcbsock != NULL){
+               /* For each in the pbuf chain, disconnect from the chain and add it to the
+                * recv_buff of the correct socket 
+                */ 
+               struct socket *sock = pcb->pcbsock;
+               attach_pbuf(p, &sock->recv_buff);
+               struct kthread *kthread;
++              int8_t irq_state = 0;
+               /* First notify any blocking recv calls,
+                * then notify anyone who might be waiting in a select
+                */ 
+               // multiple people might be waiting on the socket here..
 -                LIST_FOREACH_SAFE(sentry, &(sock->waiters), link, sentry_tmp){
++              /* TODO: consider a helper with this and tcp.c */
++              if (!sem_up_irqsave(&sock->sem, &irq_state)) {
+                       // wake up all waiters
+                       struct semaphore_entry *sentry, *sentry_tmp;
+                       spin_lock(&sock->waiter_lock);
 -                              kthread = __up_sem(&sentry->sem, true);
 -                              if (kthread){
 -                              send_kernel_message(core_id(), (amr_t)wrap_restart_kthread, (long)kthread, 0, 0,
 -                                                                                                KMSG_ROUTINE);
 -                              }
++                      LIST_FOREACH_SAFE(sentry, &sock->waiters, link, sentry_tmp) {
+                               //should only wake up one waiter
 -                              /* do not need to free since all the sentry are stack-based vars */
++                              sem_up_irqsave(&sentry->sem, &irq_state);
+                               LIST_REMOVE(sentry, link);
++                              /* do not need to free since all the sentry are stack-based vars
++                               * */
+                       }
+                       spin_unlock(&sock->waiter_lock);
+               }
+               // the attaching of pbuf should have increfed pbuf ref, so free is simply a decref
+               pbuf_free(p);
+       }
+       return 0;
+ }
index 0000000,1972e40..b9f4084
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,505 +1,502 @@@
 -#include <mbuf.h>
+ /*
+  * Copyright (c) 2011 The Regents of the University of California
+  * David Zhu <yuzhu@cs.berkeley.edu>
+  * See LICENSE for details.
+  * 
+  * Socket layer on top of TCP abstraction. Similar to the BSD implementation.
+  *
+  */
+ #include <ros/common.h>
+ #include <socket.h>
+ #include <vfs.h>
+ #include <time.h>
+ #include <kref.h>
+ #include <syscall.h>
+ #include <sys/uio.h>
 -      init_sem(&newsock->sem, 0);
 -      init_sem(&newsock->accept_sem, 0);
+ #include <ros/errno.h>
+ #include <net.h>
+ #include <net/udp.h>
+ #include <net/tcp.h>
+ #include <net/pbuf.h>
+ #include <net/tcp_impl.h>
+ #include <umem.h>
+ #include <kthread.h>
+ #include <bitmask.h>
+ #include <debug.h>
+ /*
+  *TODO: Figure out which socket.h is used where
+  *There are several socket.h in kern, and a couple more in glibc. Perhaps the glibc ones
+  *should grab from here..
+  */
+ struct kmem_cache *sock_kcache;
+ struct kmem_cache *mbuf_kcache;
+ struct kmem_cache *udp_pcb_kcache;
+ struct kmem_cache *tcp_pcb_kcache;
+ struct kmem_cache *tcp_pcb_listen_kcache;
+ struct kmem_cache *tcp_segment_kcache;
+ // file ops needed to support read/write on socket fd
+ static struct file_operations socket_op = {
+       0,
+       0,//soo_read,
+       0,//soo_write,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,//soo_poll,
+       0,
+       0,
+       0, // sendpage might apply here
+       0,
+ };
+ static struct socket* getsocket(struct proc *p, int fd){
+       /* look up fd -> file */
+       struct file *so_file = get_file_from_fd(&(p->open_files), fd);
+       /* get socket and verify its type */
+       if (so_file == NULL){
+               printd("getsocket() fd -> null file: fd %d\n", fd);
+               return NULL;
+       }
+       if (so_file->f_op != &socket_op) {
+               set_errno(ENOTSOCK);
+               printd("fd %d maps to non-socket file\n");
+               return NULL;
+       } else
+               return (struct socket*) so_file->f_privdata;
+ }
+ struct socket* alloc_sock(int socket_family, int socket_type, int protocol){
+       struct socket *newsock = kmem_cache_alloc(sock_kcache, 0);
+       assert(newsock);
+       newsock->so_family = socket_family;
+       newsock->so_type = socket_type;
+       newsock->so_protocol = protocol;
+       newsock->so_state = SS_ISDISCONNECTED;
+       STAILQ_INIT(&(newsock->acceptq));
+       pbuf_head_init(&newsock->recv_buff);
+       pbuf_head_init(&newsock->send_buff);
 -                      sleep_on(&sock->accept_sem);
++      sem_init_irqsave(&newsock->sem, 0);
++      sem_init_irqsave(&newsock->accept_sem, 0);
+       spinlock_init(&newsock->waiter_lock);
+       LIST_INIT(&newsock->waiters);
+       return newsock;
+ }
+ // TODO: refactor vfs so we can allocate fd and do the basic initialization
+ struct file *alloc_socket_file(struct socket* sock) {
+       struct file *file = alloc_file();
+       if (file == NULL) return 0;
+       // Linux fakes a dentry and an inode for socks, see socket.c : sock_alloc_file
+       file->f_dentry = NULL; // This might break things?
+       file->f_vfsmnt = 0;
+       file->f_flags = 0;
+       file->f_mode = S_IRUSR | S_IWUSR; // both read and write for socket files
+       file->f_pos = 0;
+       file->f_uid = 0;
+       file->f_gid = 0;
+       file->f_error = 0;
+       file->f_op = &socket_op;
+       file->f_privdata = sock;
+       file->f_mapping = 0;
+       return file;
+ }
+ void socket_init(){
+       
+       /* allocate buf for socket */
+       sock_kcache = kmem_cache_create("socket", sizeof(struct socket),
+                                                                       __alignof__(struct socket), 0, 0, 0);
+       udp_pcb_kcache = kmem_cache_create("udppcb", sizeof(struct udp_pcb),
+                                                                       __alignof__(struct udp_pcb), 0, 0, 0);
+       tcp_pcb_kcache = kmem_cache_create("tcppcb", sizeof(struct tcp_pcb),
+                                                                       __alignof__(struct tcp_pcb), 0, 0, 0);
+       tcp_pcb_listen_kcache = kmem_cache_create("tcppcblisten", sizeof(struct tcp_pcb_listen),
+                                                                       __alignof__(struct tcp_pcb_listen), 0, 0, 0);
+       tcp_segment_kcache = kmem_cache_create("tcpsegment", sizeof(struct tcp_seg),
+                                                                       __alignof__(struct tcp_seg), 0, 0, 0);
+       pbuf_init();
+ }
+ intreg_t sys_accept(struct proc *p, int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
+       printk ("sysaccept called\n");
+       struct socket* sock = getsocket(p, sockfd);
+       struct sockaddr_in *in_addr = (struct sockaddr_in *)addr;
+       uint16_t r_port;
+       struct socket *accepted = NULL;
++      int8_t irq_state = 0;
+       if (sock == NULL) {
+               set_errno(EBADF);
+               return -1;      
+       }
+       if (sock->so_type == SOCK_DGRAM){
+               return -1; // indicates false for connect
+       } else if (sock->so_type == SOCK_STREAM) {
++              /* XXX these do the same thing, what is it you actually wanted to do?
++               * (Originally the first was sleep_on, and the second __down_sem */
+               if (STAILQ_EMPTY(&(sock->acceptq))) {
+                       // block on the acceptq
 -                      __down_sem(&sock->accept_sem, NULL);
++                      sem_down_irqsave(&sock->accept_sem, &irq_state);
+               } else {
 -static void wrap_restart_kthread(struct trapframe *tf, uint32_t srcid,
 -                                      long a0, long a1, long a2){
 -      restart_kthread((struct kthread*) a0);
 -}
 -
++                      sem_down_irqsave(&sock->accept_sem, &irq_state);
+               }
+               spin_lock_irqsave(&sock->waiter_lock);
+               accepted = STAILQ_FIRST(&(sock->acceptq));
+               STAILQ_REMOVE_HEAD((&(sock->acceptq)), next);
+               spin_unlock_irqsave(&sock->waiter_lock);
+               if (accepted == NULL) return -1;
+               struct file *file = alloc_socket_file(accepted);
+               if (file == NULL) return -1;
+               int fd = insert_file(&p->open_files, file, 0);
+               if (fd < 0) {
+                       warn("File insertion for socket open failed");
+                       return -1;
+               }
+               kref_put(&file->f_kref);
+       }
+       return -1;
+ }
 -      struct kthread *kthread = __up_sem(&(sock->accept_sem), FALSE);
 -      if (kthread) {
 -              send_kernel_message(core_id(), (amr_t)wrap_restart_kthread, (long)kthread, 0, 0,
 -                                                                                                KMSG_ROUTINE);
 -      } 
+ static error_t accept_callback(void *arg, struct tcp_pcb *newpcb, error_t err) {
+       struct socket *sockold = (struct socket *) arg;
+       struct socket *sock = alloc_sock(sockold->so_family, sockold->so_type, sockold->so_protocol);
++      int8_t irq_state = 0;
+       
+       sock->so_pcb = newpcb;
+       newpcb->pcbsock = sock;
+       spin_lock_irqsave(&sockold->waiter_lock);
+       STAILQ_INSERT_TAIL(&sockold->acceptq, sock, next);
+       // wake up any kthread who is potentially waiting
+       spin_unlock_irqsave(&sockold->waiter_lock);
 -intreg_t sys_send(struct proc *p, int sockfd, const void *buf, size_t len, int flags) {
++      sem_up_irqsave(&sock->accept_sem, &irq_state);
+       return 0;
+ }
+ intreg_t sys_listen(struct proc *p, int sockfd, int backlog) {
+       struct socket* sock = getsocket(p, sockfd);
+       if (sock == NULL) {
+               set_errno(EBADF);
+               return -1;      
+       }
+       if (sock->so_type == SOCK_DGRAM){
+               return -1; // indicates false for connect
+       } else if (sock->so_type == SOCK_STREAM) {
+               // check if the socket is in WAIT state
+               struct tcp_pcb *tpcb = (struct tcp_pcb*)sock->so_pcb;
+               struct tcp_pcb* lpcb = tcp_listen_with_backlog(tpcb, backlog);
+               if (lpcb == NULL) {
+                       return -1;
+               }
+               sock->so_pcb = lpcb;
+               // register callback for new connection
+               tcp_arg(lpcb, sock);                                                  
+               tcp_accept(lpcb, accept_callback); 
+               return 0;
+               // XXX: add backlog later
+       }
+       return -1;
+ }
+ intreg_t sys_connect(struct proc *p, int sock_fd, const struct sockaddr* addr, int addrlen) {
+       printk("sys_connect called \n");
+       struct socket* sock = getsocket(p, sock_fd);
+       struct sockaddr_in *in_addr = (struct sockaddr_in *)addr;
+       uint16_t r_port;
+       if (sock == NULL) {
+               set_errno(EBADF);
+               return -1;      
+       }
+       if (sock->so_type == SOCK_DGRAM){
+               return -1; // indicates false for connect
+       } else if (sock->so_type == SOCK_STREAM) {
+               error_t err = tcp_connect((struct tcp_pcb*)sock->so_pcb, & (in_addr->sin_addr), in_addr->sin_port, NULL);
+               return err;
+       }
+       return -1;
+ }
 -      struct socket* sock = getsocket(p_proc, fd);
 -      const struct sockaddr_in *in_addr = (const struct sockaddr_in *)addr;
++intreg_t sys_send(struct proc *p, int sockfd, const void *buf, size_t len,
++                  int flags) {
+       printk("sys_send called \n");
 -                      sleep_on(&sock->sem);
++      struct socket* sock = getsocket(p, sockfd);
++      const struct sockaddr_in *in_addr = (const struct sockaddr_in *)buf;
+       uint16_t r_port;
+       if (sock == NULL) {
+               set_errno(EBADF);
+               return -1;      
+       }
+       return len;
+ }
+ intreg_t sys_recv(struct proc *p, int sockfd, void *buf, size_t len, int flags) {
+       printk("sys_recv called \n");
+       // return actual length filled
+       return len;
+ }
+ intreg_t sys_bind(struct proc* p_proc, int fd, const struct sockaddr *addr, socklen_t addrlen) {
+       struct socket* sock = getsocket(p_proc, fd);
+       const struct sockaddr_in *in_addr = (const struct sockaddr_in *)addr;
+       uint16_t r_port;
+       if (sock == NULL) {
+               set_errno(EBADF);
+               return -1;      
+       }
+       if (sock->so_type == SOCK_DGRAM){
+               return udp_bind((struct udp_pcb*)sock->so_pcb, & (in_addr->sin_addr), in_addr->sin_port);
+       } else if (sock->so_type == SOCK_STREAM) {
+               return tcp_bind((struct tcp_pcb*)sock->so_pcb, & (in_addr->sin_addr), in_addr->sin_port);
+       } else {
+               printk("SOCK type not supported in bind operation \n");
+               return -1;
+       }
+       return 0;
+ }
+  
+ intreg_t sys_socket(struct proc *p, int socket_family, int socket_type, int protocol){
+       //check validity of params
+       if (socket_family != AF_INET && socket_type != SOCK_DGRAM)
+               return 0;
+       struct socket *sock = alloc_sock(socket_family, socket_type, protocol);
+       if (socket_type == SOCK_DGRAM){
+               /* udp socket */
+               sock->so_pcb = udp_new();
+               /* back link */
+               ((struct udp_pcb*) (sock->so_pcb))->pcbsock = sock;
+       } else if (socket_type == SOCK_STREAM) {
+               /* tcp socket */
+               sock->so_pcb = tcp_new();
+               ((struct tcp_pcb*) (sock->so_pcb))->pcbsock = sock;
+       }
+       struct file *file = alloc_socket_file(sock);
+       
+       if (file == NULL) return -1;
+       int fd = insert_file(&p->open_files, file, 0);
+       if (fd < 0) {
+               warn("File insertion for socket open failed");
+               return -1;
+       }
+       kref_put(&file->f_kref);
+       printk("Socket open, res = %d\n", fd);
+       return fd;
+ }
+ intreg_t send_iov(struct socket* sock, struct iovec* iov, int flags){
+       // COPY_COUNT: for each iov, copy into mbuf, and send
+       // should not copy here, copy in the protocol..
+       // should be esomething like this sock->so_proto->pr_send(sock, iov, flags);
+       // make it datagram specific for now...
+       send_datagram(sock, iov, flags);
+       // finally time to check for validity of UA, in the protocol send
+       return 0;       
+ }
+ /*TODO: iov support currently broken */
+ int send_datagram(struct socket* sock, struct iovec* iov, int flags){
+       // is this a connection oriented protocol? 
+       struct pbuf *prev = NULL;
+       struct pbuf *curr = NULL;
+       if (sock->so_type == SOCK_STREAM){
+               set_errno(ENOTCONN);
+               return -1;
+       }
+       
+       // possible sock locks needed
+       if ((sock->so_state & SS_ISCONNECTED) == 0){
+               set_errno(EINVAL);
+               return -1;
+       }
+     // pbuf_ref needs to map in the user ref
+       for (int i = 0; i< sizeof(iov) / sizeof (struct iovec); i++){
+               prev = curr;
+               curr = pbuf_alloc(PBUF_TRANSPORT, iov[i].iov_len, PBUF_REF);
+               if (prev!=NULL) pbuf_chain(prev, curr);
+       }
+       // struct pbuf* pb = pbuf_alloc(PBUF_TRANSPORT, PBUF_REF);
+       udp_send(sock->so_pcb, prev);
+       return 0;
+       
+ }
+ /* sys_sendto can send SOCK_DGRAM and eventually SOCK_STREAM 
+  * SOCK_DGRAM uses PBUF_REF since UDP does not need to wait for ack
+  * SOCK_STREAM uses PBUF_
+  *
+  */
+ intreg_t sys_sendto(struct proc *p_proc, int fd, const void *buffer, size_t length, 
+                       int flags, const struct sockaddr *dest_addr, socklen_t dest_len){
+       // look up the socket
+       struct socket* sock = getsocket(p_proc, fd);
+       int error;
+       struct sockaddr_in *in_addr;
+       uint16_t r_port;
+       if (sock == NULL) {
+               set_errno(EBADF);
+               return -1;      
+       }
+       if (sock->so_type == SOCK_DGRAM){
+               in_addr = (struct sockaddr_in *)dest_addr;
+               struct pbuf* buf = pbuf_alloc(PBUF_TRANSPORT, length, PBUF_REF);
+               if (buf != NULL)
+                       buf->payload = (void*)buffer;
+               else 
+                       warn("pbuf alloc failed \n");
+               // potentially unsafe cast to udp_pcb 
+               return udp_sendto((struct udp_pcb*) sock->so_pcb, buf, &in_addr->sin_addr, in_addr->sin_port);
+       }
+       return -1;
+   //TODO: support for sendmsg and iovectors? Let's get the basics working first!
+       #if 0 
+       // use iovector to handle sendmsg calls too, and potentially scatter-gather
+       struct msghdr msg;
+       struct iovec iov;
+       struct uio auio;
+       
+       // checking for permission only when you are sending it
+       // potential bug TOCTOU, especially with async calls
+               
+     msg.msg_name = dest_addr;
+     msg.msg_namelen = dest_len;
+     msg.msg_iov = &iov;
+     msg.msg_iovlen = 1;
+     msg.msg_control = 0;
+     
+       iov.iov_base = buffer;
+     iov.iov_len = length;
+       
+       // this is why we need another function to populate auio
+       auio.uio_iov = iov;
+       auio.uio_iovcnt = 1;
+       auio.uio_offset = 0;
+       auio.uio_resid = 0;
+       auio.uio_rw = UIO_WRITE;
+       auio.uio_proc = p;
+       // consider changing to send_uaio, since we care about progress.
+     error = send_iov(soc, iov, flags);
+       #endif
+ }
+ /* UDP and TCP has different waiting semantics
+  * UDP requires any packet to be available. 
+  * TCP requires accumulation of certain size? 
+  */
+ 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){
+       struct socket* sock = getsocket(p, socket);     
+       int copied = 0;
+       int returnval = 0;
++      int8_t irq_state = 0;
+       if (sock == NULL) {
+               set_errno(EBADF);
+               return -1;
+       }
+       if (sock->so_type == SOCK_DGRAM){
+               struct pbuf_head *ph = &(sock->recv_buff);
+               struct pbuf* buf = NULL;
+               buf = detach_pbuf(ph);
+               if (!buf){
+                       // about to sleep
 -                      __down_sem(&sock->sem, NULL);
++                      sem_down_irqsave(&sock->sem, &irq_state);
+                       buf = detach_pbuf(ph);
+                       // Someone woke me up, there should be data..
+                       assert(buf);
+               } else {
 -      init_sem(&(read_sem.sem), 0);
++                      sem_down_irqsave(&sock->sem, &irq_state);
+               }
+                       copied = buf->len - sizeof(struct udp_hdr);
+                       if (copied > length)
+                               copied = length;
+                       pbuf_header(buf, -UDP_HDR_SZ);
+                       // copy it to user space
+                       returnval = memcpy_to_user_errno(p, buffer, buf->payload, copied);
+               }
+       if (returnval < 0) 
+               return -1;
+       else
+               return copied;
+ }
+ static int selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
+              fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out){
+       return 0;
+ }
+ /* TODO: Start respecting the time out value */ 
+ /* TODO: start respecting writefds and exceptfds */
+ intreg_t sys_select(struct proc *p, int nfds, fd_set *readfds, fd_set *writefds,
+                               fd_set *exceptfds, struct timeval *timeout){
+       /* Create a semaphore */
+       struct semaphore_entry read_sem; 
++      int8_t irq_state = 0;
 -  sleep_on(&(read_sem.sem));
++      sem_init_irqsave(&(read_sem.sem), 0);
+       /* insert into the sem list of a fd / socket */
+       int low_fd = 0;
+       for (int i = low_fd; i< nfds; i++) {
+               if(FD_ISSET(i, readfds)){
+                 struct socket* sock = getsocket(p, i);
+                       /* if the fd is not open or if the file descriptor is not a socket 
+                        * go to the next in the fd set 
+                        */
+                       if (sock == NULL) continue;
+                       /* for each file that is open, insert this semaphore to be woken up when there
+                       * is data available to be read
+                       */
+                       spin_lock(&sock->waiter_lock);
+                       LIST_INSERT_HEAD(&sock->waiters, &read_sem, link);
+                       spin_unlock(&sock->waiter_lock);
+               }
+       }
+       /* At this point wait on the semaphore */
++      sem_down_irqsave(&read_sem.sem, &irq_state);
+       /* someone woke me up, so walk through the list of descriptors and find one that is ready */
+       /* remove itself from all the lists that it is waiting on */
+       for (int i = low_fd; i<nfds; i++) {
+               if (FD_ISSET(i, readfds)){
+                       struct socket* sock = getsocket(p,i);
+                       if (sock == NULL) continue;
+                       spin_lock(&sock->waiter_lock);
+                       LIST_REMOVE(&read_sem, link);
+                       spin_unlock(&sock->waiter_lock);
+               }
+       }
+       fd_set readout, writeout, exceptout;
+       FD_ZERO(&readout);
+       FD_ZERO(&writeout);
+       FD_ZERO(&exceptout);
+       for (int i = low_fd; i< nfds; i ++){
+               if (readfds && FD_ISSET(i, readfds)){
+                 struct socket* sock = getsocket(p, i);
+                       if ((sock->recv_buff).qlen > 0){
+                               FD_SET(i, &readout);
+                       }
+                       /* if the socket is ready, then we can return it */
+               }
+       }
+       if (readfds)
+               memcpy(readfds, &readout, sizeof(*readfds));
+       if (writefds)
+               memcpy(writefds, &writeout, sizeof(*writefds));
+       if (exceptfds)
+               memcpy(readfds, &readout, sizeof(*readfds));
+       /* Sleep on that semaphore */
+       /* Somehow get these file descriptors to wake me up when there is new data */
+       return 0;
+ }
@@@ -33,7 -33,7 +33,8 @@@
  #include <smp.h>
  #include <arsc_server.h>
  #include <event.h>
 +#include <termios.h>
+ #include <socket.h>
  
  
  #ifdef __CONFIG_NETWORKING__
diff --cc kern/src/vfs.c
@@@ -1638,16 -1631,23 +1638,24 @@@ out_path_only
        return retval;
  }
  
- /* Opens and returns the file specified by dentry */
- struct file *dentry_open(struct dentry *dentry, int flags)
 -struct file *alloc_file()
++struct file *alloc_file(void)
  {
-       struct inode *inode;
-       int desired_mode;
        struct file *file = kmem_cache_alloc(file_kcache, 0);
        if (!file) {
                set_errno(ENOMEM);
                return 0;
        }
+       /* one for the ref passed out*/
+       kref_init(&file->f_kref, file_release, 1);
+       return file;
+ }
+ /* Opens and returns the file specified by dentry */
+ struct file *dentry_open(struct dentry *dentry, int flags)
+ {
+       struct inode *inode;
++      struct file *file;
+       int desired_mode;
        inode = dentry->d_inode;
        /* Do the mode first, since we can still error out.  f_mode stores how the
         * OS file is open, which can be more restrictive than the i_mode */
        }
        if (check_perms(inode, desired_mode))
                goto error_access;
 -      // Check permission before we allocate file struct      
 -      struct file *file = alloc_file();
 -      if (!file) return 0;
 -
++      file = alloc_file();
++      if (!file)
++              return 0;
        file->f_mode = desired_mode;
-       /* one for the ref passed out, and *none* for the sb TAILQ */
-       kref_init(&file->f_kref, file_release, 1);
        /* Add to the list of all files of this SB */
        TAILQ_INSERT_TAIL(&inode->i_sb->s_files, file, f_list);
        kref_get(&dentry->d_kref, 1);