NetServices Stack source

Dependents:   HelloWorld ServoInterfaceBoardExample1 4180_Lab4

Revision:
5:dd63a1e02b1b
Parent:
0:632c9925f013
--- a/lwip/core/tcp.c	Fri Jul 09 14:46:47 2010 +0000
+++ b/lwip/core/tcp.c	Tue Jul 27 15:59:42 2010 +0000
@@ -55,7 +55,7 @@
 
 #include <string.h>
 
-const char *tcp_state_str[] = {
+const char * const tcp_state_str[] = {
   "CLOSED",      
   "LISTEN",      
   "SYN_SENT",    
@@ -88,6 +88,12 @@
 /** 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};
+
 /** Only used for temporary storage. */
 struct tcp_pcb *tcp_tmp_pcb;
 
@@ -134,11 +140,26 @@
   err_t err;
 
   if (rst_on_unacked_data && (pcb->state != LISTEN)) {
-    if ((pcb->refused_data != NULL) ||  (pcb->rcv_wnd != TCP_WND)) {
+    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 ERR_OK;
     }
   }
 
@@ -158,7 +179,7 @@
     break;
   case LISTEN:
     err = ERR_OK;
-    tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb);
+    tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
     memp_free(MEMP_TCP_PCB_LISTEN, pcb);
     pcb = NULL;
     break;
@@ -302,7 +323,9 @@
 #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. */
@@ -310,7 +333,6 @@
     tcp_pcb_remove(&tcp_tw_pcbs, pcb);
     memp_free(MEMP_TCP_PCB, pcb);
   } else {
-    /* @todo: pcb->state, LISTEN not allowed */
     seqno = pcb->snd_nxt;
     ackno = pcb->rcv_nxt;
     ip_addr_copy(local_ip, pcb->local_ip);
@@ -374,55 +396,46 @@
 err_t
 tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_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 ERR_ISCONN);
 
+#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 SO_REUSE
+  if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+    max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
+  }
+#endif /* SO_REUSE */
+#endif /* SO_REUSE */
+
   if (port == 0) {
     port = tcp_new_port();
   }
-  /* Check if the address already is in use. */
-  /* Check the listen pcbs. */
-  for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs;
-      cpcb != NULL; cpcb = cpcb->next) {
-    if (cpcb->local_port == port) {
-      if (ip_addr_isany(&(cpcb->local_ip)) ||
-          ip_addr_isany(ipaddr) ||
-          ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
-        return ERR_USE;
-      }
-    }
-  }
-  /* Check the connected pcbs. */
-  for(cpcb = tcp_active_pcbs;
-      cpcb != NULL; cpcb = cpcb->next) {
-    if (cpcb->local_port == port) {
-      if (ip_addr_isany(&(cpcb->local_ip)) ||
-          ip_addr_isany(ipaddr) ||
-          ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
-        return ERR_USE;
-      }
-    }
-  }
-  /* Check the bound, not yet connected pcbs. */
-  for(cpcb = tcp_bound_pcbs; cpcb != NULL; cpcb = cpcb->next) {
-    if (cpcb->local_port == port) {
-      if (ip_addr_isany(&(cpcb->local_ip)) ||
-          ip_addr_isany(ipaddr) ||
-          ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
-        return ERR_USE;
-      }
-    }
-  }
-  /* Unless the REUSEADDR flag is set,
-   * we have to check the pcbs in TIME-WAIT state, also: */
-  if ((pcb->so_options & SOF_REUSEADDR) == 0) {
-    for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) {
+
+  /* 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 (ip_addr_isany(&(cpcb->local_ip)) ||
-            ip_addr_isany(ipaddr) ||
-            ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
-          return ERR_USE;
+#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 & SOF_REUSEADDR) == 0) ||
+          ((cpcb->so_options & SOF_REUSEADDR) == 0))
+#endif /* SO_REUSE */
+        {
+          if (ip_addr_isany(&(cpcb->local_ip)) ||
+              ip_addr_isany(ipaddr) ||
+              ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
+            return ERR_USE;
+          }
         }
       }
     }
@@ -477,6 +490,21 @@
   if (pcb->state == LISTEN) {
     return pcb;
   }
+#if SO_REUSE
+  if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+    /* Since SOF_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 = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
   if (lpcb == NULL) {
     return NULL;
@@ -484,6 +512,7 @@
   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 |= SOF_ACCEPTCONN;
   lpcb->ttl = pcb->ttl;
@@ -498,7 +527,7 @@
   lpcb->accepts_pending = 0;
   lpcb->backlog = (backlog ? backlog : 1);
 #endif /* TCP_LISTEN_BACKLOG */
-  TCP_REG(&tcp_listen_pcbs.listen_pcbs, lpcb);
+  TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
   return (struct tcp_pcb *)lpcb;
 }
 
@@ -576,6 +605,7 @@
 static u16_t
 tcp_new_port(void)
 {
+  int i;
   struct tcp_pcb *pcb;
 #ifndef TCP_LOCAL_PORT_RANGE_START
 #define TCP_LOCAL_PORT_RANGE_START 4096
@@ -587,20 +617,12 @@
   if (++port > TCP_LOCAL_PORT_RANGE_END) {
     port = TCP_LOCAL_PORT_RANGE_START;
   }
-  
-  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
-    if (pcb->local_port == port) {
-      goto again;
-    }
-  }
-  for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
-    if (pcb->local_port == port) {
-      goto again;
-    }
-  }
-  for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
-    if (pcb->local_port == port) {
-      goto again;
+  /* Check all PCB lists. */
+  for (i = 1; 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;
@@ -634,9 +656,43 @@
     return ERR_VAL;
   }
   pcb->remote_port = port;
+
+  /* check if we have a route to the remote host */
+  if (ip_addr_isany(&(pcb->local_ip))) {
+    /* no local IP address set, yet. */
+    struct netif *netif = ip_route(&(pcb->remote_ip));
+    if (netif == NULL) {
+      /* Don't even try to send a SYN packet if we have no route
+         since that will fail. */
+      return ERR_RTE;
+    }
+    /* Use the netif's IP address as local address. */
+    ip_addr_copy(pcb->local_ip, netif->ip_addr);
+  }
+
   if (pcb->local_port == 0) {
     pcb->local_port = tcp_new_port();
   }
+#if SO_REUSE
+  if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+    /* Since SOF_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 PCBs, check bound-, active- and TIME-WAIT PCBs. */
+    for (i = 1; 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;
@@ -656,8 +712,12 @@
   pcb->ssthresh = pcb->mss * 10;
 #if LWIP_CALLBACK_API
   pcb->connected = connected;
+#else /* LWIP_CALLBACK_API */  
+  LWIP_UNUSED_ARG(connected);
 #endif /* LWIP_CALLBACK_API */
 
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect: tcp_ticks=%"U32_F" iss=%"U32_F"\n", tcp_ticks, iss));
+
   /* Send a SYN together with the MSS option. */
   ret = tcp_enqueue_flags(pcb, TCP_SYN);
   if (ret == ERR_OK) {
@@ -687,10 +747,13 @@
   u8_t pcb_remove;      /* flag if a PCB should be removed */
   u8_t pcb_reset;       /* flag if a RST should be sent when removing */
   err_t err;
+  
+  tcp_debug_print_pcbs(); //DG
 
   err = ERR_OK;
 
   ++tcp_ticks;
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: tcp_ticks=%"U32_F"\n", tcp_ticks));
 
   /* Steps through all of the active PCBs. */
   prev = NULL;
@@ -998,8 +1061,8 @@
 {
   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
@@ -1020,7 +1083,7 @@
   pbuf_ref(cseg->p);
   return cseg;
 }
-#endif
+#endif /* TCP_QUEUE_OOSEQ */
 
 #if LWIP_CALLBACK_API
 /**
@@ -1072,7 +1135,7 @@
     LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
            (void *)inactive, inactivity));
     tcp_abort(inactive);
-  }      
+  }
 }
 
 /**
@@ -1098,7 +1161,7 @@
     LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
            (void *)inactive, inactivity));
     tcp_abort(inactive);
-  }      
+  }
 }
 
 /**
@@ -1159,6 +1222,8 @@
     pcb->lastack = iss;
     pcb->snd_lbb = iss;   
     pcb->tmr = tcp_ticks;
+    
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: tcp_ticks=%"U32_F" iss=%"U32_F"\n", tcp_ticks, iss));
 
     pcb->polltmr = 0;
 
@@ -1278,6 +1343,8 @@
 {
 #if LWIP_CALLBACK_API
   pcb->poll = poll;
+#else /* LWIP_CALLBACK_API */  
+  LWIP_UNUSED_ARG(poll);
 #endif /* LWIP_CALLBACK_API */  
   pcb->pollinterval = interval;
 }
@@ -1329,7 +1396,7 @@
     if (pcb->unacked != NULL) {
       LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
     }
-#if TCP_QUEUE_OOSEQ /* LW */
+#if TCP_QUEUE_OOSEQ
     if (pcb->ooseq != NULL) {
       LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
     }