Procházet zdrojové kódy

Update lwip to 1710fc1a89578dfaaff684a1aafbc4d16e346f79

Ambroz Bizjak před 8 roky
rodič
revize
79efa642ea

+ 1 - 1
lwip/lwip-base-version

@@ -1 +1 @@
-931b5e643c25820a99bb8df94ab37db6b58c446b
+1710fc1a89578dfaaff684a1aafbc4d16e346f79

+ 393 - 57
lwip/src/api/sockets.c

@@ -262,7 +262,7 @@ static void lwip_socket_drop_registered_mld6_memberships(int s);
 /** The global array of available sockets */
 /** The global array of available sockets */
 static struct lwip_sock sockets[NUM_SOCKETS];
 static struct lwip_sock sockets[NUM_SOCKETS];
 
 
-#if LWIP_SOCKET_SELECT
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
 #if LWIP_TCPIP_CORE_LOCKING
 #if LWIP_TCPIP_CORE_LOCKING
 /* protect the select_cb_list using core lock */
 /* protect the select_cb_list using core lock */
 #define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)
 #define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)
@@ -279,7 +279,7 @@ static volatile int select_cb_ctr;
 #endif /* LWIP_TCPIP_CORE_LOCKING */
 #endif /* LWIP_TCPIP_CORE_LOCKING */
 /** The global list of tasks waiting for select */
 /** The global list of tasks waiting for select */
 static struct lwip_select_cb *select_cb_list;
 static struct lwip_select_cb *select_cb_list;
-#endif /* LWIP_SOCKET_SELECT */
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
 
 
 #define sock_set_errno(sk, e) do { \
 #define sock_set_errno(sk, e) do { \
   const int sockerr = (e); \
   const int sockerr = (e); \
@@ -287,10 +287,10 @@ static struct lwip_select_cb *select_cb_list;
 } while (0)
 } while (0)
 
 
 /* Forward declaration of some functions */
 /* Forward declaration of some functions */
-#if LWIP_SOCKET_SELECT
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
 static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
 static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
 #define DEFAULT_SOCKET_EVENTCB event_callback
 #define DEFAULT_SOCKET_EVENTCB event_callback
-static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent);
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent, struct lwip_sock *sock);
 #else
 #else
 #define DEFAULT_SOCKET_EVENTCB NULL
 #define DEFAULT_SOCKET_EVENTCB NULL
 #endif
 #endif
@@ -1692,6 +1692,57 @@ lwip_writev(int s, const struct iovec *iov, int iovcnt)
   return lwip_sendmsg(s, &msg, 0);
   return lwip_sendmsg(s, &msg, 0);
 }
 }
 
 
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+/* Add select_cb to select_cb_list. */
+static void
+lwip_link_select_cb(struct lwip_select_cb *select_cb)
+{
+  LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
+
+  /* Protect the select_cb_list */
+  LWIP_SOCKET_SELECT_PROTECT(lev);
+
+  /* Put this select_cb on top of list */
+  select_cb->next = select_cb_list;
+  if (select_cb_list != NULL) {
+    select_cb_list->prev = select_cb;
+  }
+  select_cb_list = select_cb;
+#if !LWIP_TCPIP_CORE_LOCKING
+  /* Increasing this counter tells select_check_waiters that the list has changed. */
+  select_cb_ctr++;
+#endif
+
+  /* Now we can safely unprotect */
+  LWIP_SOCKET_SELECT_UNPROTECT(lev);
+}
+
+/* Remove select_cb from select_cb_list. */
+static void
+lwip_unlink_select_cb(struct lwip_select_cb *select_cb)
+{
+  LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
+
+  /* Take us off the list */
+  LWIP_SOCKET_SELECT_PROTECT(lev);
+  if (select_cb->next != NULL) {
+    select_cb->next->prev = select_cb->prev;
+  }
+  if (select_cb_list == select_cb) {
+    LWIP_ASSERT("select_cb->prev == NULL", select_cb->prev == NULL);
+    select_cb_list = select_cb->next;
+  } else {
+    LWIP_ASSERT("select_cb->prev != NULL", select_cb->prev != NULL);
+    select_cb->prev->next = select_cb->next;
+  }
+#if !LWIP_TCPIP_CORE_LOCKING
+  /* Increasing this counter tells select_check_waiters that the list has changed. */
+  select_cb_ctr++;
+#endif
+  LWIP_SOCKET_SELECT_UNPROTECT(lev);
+}
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+
 #if LWIP_SOCKET_SELECT
 #if LWIP_SOCKET_SELECT
 /**
 /**
  * Go through the readset and writeset lists and see which socket of the sockets
  * Go through the readset and writeset lists and see which socket of the sockets
@@ -1853,7 +1904,6 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
   fd_set used_sockets;
   fd_set used_sockets;
 #endif
 #endif
   SYS_ARCH_DECL_PROTECT(lev);
   SYS_ARCH_DECL_PROTECT(lev);
-  LWIP_SOCKET_SELECT_DECL_PROTECT(lev2);
 
 
   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
                               maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
                               maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
@@ -1892,14 +1942,12 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
          to use local variables (unless we're running in MPU compatible
          to use local variables (unless we're running in MPU compatible
          mode). */
          mode). */
       API_SELECT_CB_VAR_DECLARE(select_cb);
       API_SELECT_CB_VAR_DECLARE(select_cb);
-      API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(ENOMEM); return -1);
+      API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(ENOMEM); lwip_select_dec_sockets_used(maxfdp1, &used_sockets); return -1);
+      memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
 
 
-      API_SELECT_CB_VAR_REF(select_cb).next = NULL;
-      API_SELECT_CB_VAR_REF(select_cb).prev = NULL;
       API_SELECT_CB_VAR_REF(select_cb).readset = readset;
       API_SELECT_CB_VAR_REF(select_cb).readset = readset;
       API_SELECT_CB_VAR_REF(select_cb).writeset = writeset;
       API_SELECT_CB_VAR_REF(select_cb).writeset = writeset;
       API_SELECT_CB_VAR_REF(select_cb).exceptset = exceptset;
       API_SELECT_CB_VAR_REF(select_cb).exceptset = exceptset;
-      API_SELECT_CB_VAR_REF(select_cb).sem_signalled = 0;
 #if LWIP_NETCONN_SEM_PER_THREAD
 #if LWIP_NETCONN_SEM_PER_THREAD
       API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
       API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
 #else /* LWIP_NETCONN_SEM_PER_THREAD */
 #else /* LWIP_NETCONN_SEM_PER_THREAD */
@@ -1912,22 +1960,7 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
       }
       }
 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
 
 
-      /* Protect the select_cb_list */
-      LWIP_SOCKET_SELECT_PROTECT(lev2);
-
-      /* Put this select_cb on top of list */
-      API_SELECT_CB_VAR_REF(select_cb).next = select_cb_list;
-      if (select_cb_list != NULL) {
-        select_cb_list->prev = &API_SELECT_CB_VAR_REF(select_cb);
-      }
-      select_cb_list = &API_SELECT_CB_VAR_REF(select_cb);
-#if !LWIP_TCPIP_CORE_LOCKING
-      /* Increasing this counter tells select_check_waiters that the list has changed. */
-      select_cb_ctr++;
-#endif
-
-      /* Now we can safely unprotect */
-      LWIP_SOCKET_SELECT_UNPROTECT(lev2);
+      lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
 
 
       /* Increase select_waiting for each socket we are interested in */
       /* Increase select_waiting for each socket we are interested in */
       maxfdp2 = maxfdp1;
       maxfdp2 = maxfdp1;
@@ -2012,23 +2045,8 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
           SYS_ARCH_UNPROTECT(lev);
           SYS_ARCH_UNPROTECT(lev);
         }
         }
       }
       }
-      /* Take us off the list */
-      LWIP_SOCKET_SELECT_PROTECT(lev2);
-      if (API_SELECT_CB_VAR_REF(select_cb).next != NULL) {
-        API_SELECT_CB_VAR_REF(select_cb).next->prev = API_SELECT_CB_VAR_REF(select_cb).prev;
-      }
-      if (select_cb_list == &API_SELECT_CB_VAR_REF(select_cb)) {
-        LWIP_ASSERT("select_cb.prev == NULL", API_SELECT_CB_VAR_REF(select_cb).prev == NULL);
-        select_cb_list = API_SELECT_CB_VAR_REF(select_cb).next;
-      } else {
-        LWIP_ASSERT("select_cb.prev != NULL", API_SELECT_CB_VAR_REF(select_cb).prev != NULL);
-        API_SELECT_CB_VAR_REF(select_cb).prev->next = API_SELECT_CB_VAR_REF(select_cb).next;
-      }
-#if !LWIP_TCPIP_CORE_LOCKING
-      /* Increasing this counter tells select_check_waiters that the list has changed. */
-      select_cb_ctr++;
-#endif
-      LWIP_SOCKET_SELECT_UNPROTECT(lev2);
+
+      lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
 
 
 #if LWIP_NETCONN_SEM_PER_THREAD
 #if LWIP_NETCONN_SEM_PER_THREAD
       if (API_SELECT_CB_VAR_REF(select_cb).sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
       if (API_SELECT_CB_VAR_REF(select_cb).sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
@@ -2072,7 +2090,309 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
   }
   }
   return nready;
   return nready;
 }
 }
+#endif /* LWIP_SOCKET_SELECT */
+
+#if LWIP_SOCKET_POLL
+/** Options for the lwip_pollscan function. */
+enum lwip_pollscan_opts
+{
+  /** Clear revents in each struct pollfd. */
+  LWIP_POLLSCAN_CLEAR = 1,
+
+  /** Increment select_waiting in each struct lwip_sock. */
+  LWIP_POLLSCAN_INC_WAIT = 2,
+
+  /** Decrement select_waiting in each struct lwip_sock. */
+  LWIP_POLLSCAN_DEC_WAIT = 4
+};
 
 
+/**
+ * Update revents in each struct pollfd.
+ * Optionally update select_waiting in struct lwip_sock.
+ *
+ * @param fds          array of structures to update
+ * @param nfds         number of structures in fds
+ * @param opts         what to update and how
+ * @return number of structures that have revents != 0
+ */
+static int
+lwip_pollscan(struct pollfd *fds, nfds_t nfds, enum lwip_pollscan_opts opts)
+{
+  int nready = 0;
+  nfds_t fdi;
+  struct lwip_sock *sock;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  /* Go through each struct pollfd in the array. */
+  for (fdi = 0; fdi < nfds; fdi++) {
+    if ((opts & LWIP_POLLSCAN_CLEAR) != 0) {
+      fds[fdi].revents = 0;
+    }
+
+    /* Negative fd means the caller wants us to ignore this struct.
+       POLLNVAL means we already detected that the fd is invalid;
+       if another thread has since opened a new socket with that fd,
+       we must not use that socket. */
+    if (fds[fdi].fd >= 0 && (fds[fdi].revents & POLLNVAL) == 0) {
+      /* First get the socket's status (protected)... */
+      SYS_ARCH_PROTECT(lev);
+      sock = tryget_socket_unconn(fds[fdi].fd);
+      if (sock != NULL) {
+        void* lastdata = sock->lastdata.pbuf;
+        s16_t rcvevent = sock->rcvevent;
+        u16_t sendevent = sock->sendevent;
+        u16_t errevent = sock->errevent;
+
+        if ((opts & LWIP_POLLSCAN_INC_WAIT) != 0) {
+          sock->select_waiting++;
+          if (sock->select_waiting == 0) {
+            /* overflow - too many threads waiting */
+            sock->select_waiting--;
+            done_socket(sock);
+            nready = -1;
+            SYS_ARCH_UNPROTECT(lev);
+            break;
+          }
+          done_socket(sock);
+        } else if ((opts & LWIP_POLLSCAN_DEC_WAIT) != 0) {
+          /* for now, handle select_waiting==0... */
+          LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+          if (sock->select_waiting > 0) {
+            sock->select_waiting--;
+          }
+          done_socket(sock);
+        }
+
+        SYS_ARCH_UNPROTECT(lev);
+
+        /* ... then examine it: */
+        /* See if netconn of this socket is ready for read */
+        if ((fds[fdi].events & POLLIN) != 0 && ((lastdata != NULL) || (rcvevent > 0))) {
+          fds[fdi].revents |= POLLIN;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for reading\n", fds[fdi].fd));
+        }
+        /* See if netconn of this socket is ready for write */
+        if ((fds[fdi].events & POLLOUT) != 0 && (sendevent != 0)) {
+          fds[fdi].revents |= POLLOUT;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for writing\n", fds[fdi].fd));
+        }
+        /* See if netconn of this socket had an error */
+        if (errevent != 0) {
+          /* POLLERR is output only. */
+          fds[fdi].revents |= POLLERR;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for exception\n", fds[fdi].fd));
+        }
+      } else {
+        /* Not a valid socket */
+        SYS_ARCH_UNPROTECT(lev);
+        /* POLLNVAL is output only. */
+        fds[fdi].revents |= POLLNVAL;
+        return -1;
+      }
+    }
+
+    /* Will return the number of structures that have events,
+       not the number of events. */
+    if (fds[fdi].revents != 0) {
+      nready++;
+    }
+  }
+
+  LWIP_ASSERT("nready >= 0", nready >= 0);
+  return nready;
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+/* Mark all sockets as used.
+ *
+ * All sockets are marked (and later unmarked), whether they are open or not.
+ * This is OK as lwip_pollscan aborts select when non-open sockets are found.
+ */
+static void
+lwip_poll_inc_sockets_used(struct pollfd *fds, nfds_t nfds)
+{
+  nfds_t fdi;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  if(fds) {
+    /* Go through each struct pollfd in the array. */
+    for (fdi = 0; fdi < nfds; fdi++) {
+      SYS_ARCH_PROTECT(lev);
+      /* Increase the reference counter */
+      tryget_socket_unconn(fds[fdi].fd);
+      SYS_ARCH_UNPROTECT(lev);
+    }
+  }
+}
+
+/* Let go all sockets that were marked as used when starting poll */
+static void
+lwip_poll_dec_sockets_used(struct pollfd *fds, nfds_t nfds)
+{
+  nfds_t fdi;
+  struct lwip_sock *sock;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  if(fds) {
+    /* Go through each struct pollfd in the array. */
+    for (fdi = 0; fdi < nfds; fdi++) {
+      sock = tryget_socket_unconn_nouse(fds[fdi].fd);
+      LWIP_ASSERT("socket gone at the end of select", sock != NULL);
+      if (sock != NULL) {
+        done_socket(sock);
+      }
+    }
+  }
+}
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define lwip_poll_inc_sockets_used(fds, nfds)
+#define lwip_poll_dec_sockets_used(fds, nfds)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+int
+lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+  u32_t waitres = 0;
+  int nready;
+  u32_t msectimeout;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  int waited = 0;
+#endif
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll(%p, %d, %d)\n",
+                  (void*)fds, (int)nfds, timeout));
+
+  lwip_poll_inc_sockets_used(fds, nfds);
+
+  /* Go through each struct pollfd to count number of structures
+     which currently match */
+  nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_CLEAR);
+
+  if (nready < 0) {
+    lwip_poll_dec_sockets_used(fds, nfds);
+    return -1;
+  }
+
+  /* If we don't have any current events, then suspend if we are supposed to */
+  if (!nready) {
+    API_SELECT_CB_VAR_DECLARE(select_cb);
+
+    if (timeout == 0) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: no timeout, returning 0\n"));
+      goto return_success;
+    }
+    API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(EAGAIN); lwip_poll_dec_sockets_used(fds, nfds); return -1);
+    memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
+
+    /* None ready: add our semaphore to list:
+       We don't actually need any dynamic memory. Our entry on the
+       list is only valid while we are in this function, so it's ok
+       to use local variables. */
+
+    API_SELECT_CB_VAR_REF(select_cb).poll_fds = fds;
+    API_SELECT_CB_VAR_REF(select_cb).poll_nfds = nfds;
+#if LWIP_NETCONN_SEM_PER_THREAD
+    API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+    if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
+      /* failed to create semaphore */
+      set_errno(EAGAIN);
+      lwip_poll_dec_sockets_used(fds, nfds);
+      API_SELECT_CB_VAR_FREE(select_cb);
+      return -1;
+    }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+    lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+    /* Increase select_waiting for each socket we are interested in.
+       Also, check for events again: there could have been events between
+       the last scan (without us on the list) and putting us on the list! */
+    nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_INC_WAIT);
+
+    if (!nready) {
+      /* Still none ready, just wait to be woken */
+      if (timeout < 0) {
+        /* Wait forever */
+        msectimeout = 0;
+      } else {
+        /* timeout == 0 would have been handled earlier. */
+        LWIP_ASSERT("timeout > 0", timeout > 0);
+        msectimeout = timeout;
+      }
+      waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+      waited = 1;
+#endif
+    }
+
+    /* Decrease select_waiting for each socket we are interested in,
+       and check which events occurred while we waited. */
+    nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_DEC_WAIT);
+
+    lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+    if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+      /* don't leave the thread-local semaphore signalled */
+      sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
+    }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+    sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+    API_SELECT_CB_VAR_FREE(select_cb);
+
+    if (nready < 0) {
+      /* This happens when a socket got closed while waiting */
+      lwip_poll_dec_sockets_used(fds, nfds);
+      return -1;
+    }
+
+    if (waitres == SYS_ARCH_TIMEOUT) {
+      /* Timeout */
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: timeout expired\n"));
+      goto return_success;
+    }
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: nready=%d\n", nready));
+return_success:
+  lwip_poll_dec_sockets_used(fds, nfds);
+  set_errno(0);
+  return nready;
+}
+
+/**
+ * Check whether event_callback should wake up a thread waiting in
+ * lwip_poll.
+ */
+static int
+lwip_poll_should_wake(const struct lwip_select_cb *scb, int fd, struct lwip_sock *sock)
+{
+  nfds_t fdi;
+  for (fdi = 0; fdi < scb->poll_nfds; fdi++) {
+    const struct pollfd *pollfd = &scb->poll_fds[fdi];
+    if (pollfd->fd == fd) {
+      /* Do not update pollfd->revents right here;
+         that would be a data race because lwip_pollscan
+         accesses revents without protecting. */
+      if (sock->rcvevent > 0 && (pollfd->events & POLLIN) != 0) {
+        return 1;
+      }
+      if (sock->sendevent != 0 && (pollfd->events & POLLOUT) != 0) {
+        return 1;
+      }
+      if (sock->errevent != 0) {
+        /* POLLERR is output only. */
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+#endif /* LWIP_SOCKET_POLL */
+
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
 /**
 /**
  * Callback registered in the netconn layer for each socket-netconn.
  * Callback registered in the netconn layer for each socket-netconn.
  * Processes recvevent (data available) and wakes up tasks waiting for select.
  * Processes recvevent (data available) and wakes up tasks waiting for select.
@@ -2162,7 +2482,7 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
     has_errevent = sock->errevent != 0;
     has_errevent = sock->errevent != 0;
     SYS_ARCH_UNPROTECT(lev);
     SYS_ARCH_UNPROTECT(lev);
     /* Check any select calls waiting on this socket */
     /* Check any select calls waiting on this socket */
-    select_check_waiters(s, has_recvevent, has_sendevent, has_errevent);
+    select_check_waiters(s, has_recvevent, has_sendevent, has_errevent, sock);
   } else {
   } else {
     SYS_ARCH_UNPROTECT(lev);
     SYS_ARCH_UNPROTECT(lev);
   }
   }
@@ -2182,7 +2502,7 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
  * select_cb_list during our UNPROTECT/PROTECT. We use a generational counter to
  * select_cb_list during our UNPROTECT/PROTECT. We use a generational counter to
  * detect this change and restart the list walk. The list is expected to be small
  * detect this change and restart the list walk. The list is expected to be small
  */
  */
-static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent)
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent, struct lwip_sock *sock)
 {
 {
   struct lwip_select_cb *scb;
   struct lwip_select_cb *scb;
 #if !LWIP_TCPIP_CORE_LOCKING
 #if !LWIP_TCPIP_CORE_LOCKING
@@ -2200,22 +2520,38 @@ again:
     if (scb->sem_signalled == 0) {
     if (scb->sem_signalled == 0) {
       /* semaphore not signalled yet */
       /* semaphore not signalled yet */
       int do_signal = 0;
       int do_signal = 0;
-      /* Test this select call for our socket */
-      if (has_recvevent) {
-        if (scb->readset && FD_ISSET(s, scb->readset)) {
-          do_signal = 1;
-        }
+#if LWIP_SOCKET_POLL
+      if (scb->poll_fds != NULL) {
+        LWIP_UNUSED_ARG(has_recvevent);
+        LWIP_UNUSED_ARG(has_sendevent);
+        LWIP_UNUSED_ARG(has_errevent);
+        do_signal = lwip_poll_should_wake(scb, s, sock);
       }
       }
-      if (has_sendevent) {
-        if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
-          do_signal = 1;
+#endif /* LWIP_SOCKET_POLL */
+#if LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL
+      else
+#endif /* LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL */
+#if LWIP_SOCKET_SELECT
+      {
+        LWIP_UNUSED_ARG(sock);
+        /* Test this select call for our socket */
+        if (has_recvevent) {
+          if (scb->readset && FD_ISSET(s, scb->readset)) {
+            do_signal = 1;
+          }
         }
         }
-      }
-      if (has_errevent) {
-        if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
-          do_signal = 1;
+        if (has_sendevent) {
+          if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+            do_signal = 1;
+          }
+        }
+        if (has_errevent) {
+          if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+            do_signal = 1;
+          }
         }
         }
       }
       }
+#endif /* LWIP_SOCKET_SELECT */
       if (do_signal) {
       if (do_signal) {
         scb->sem_signalled = 1;
         scb->sem_signalled = 1;
         /* For !LWIP_TCPIP_CORE_LOCKING, we don't call SYS_ARCH_UNPROTECT() before signaling
         /* For !LWIP_TCPIP_CORE_LOCKING, we don't call SYS_ARCH_UNPROTECT() before signaling
@@ -2241,7 +2577,7 @@ again:
   SYS_ARCH_UNPROTECT(lev);
   SYS_ARCH_UNPROTECT(lev);
 #endif
 #endif
 }
 }
-#endif /* LWIP_SOCKET_SELECT */
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
 
 
 /**
 /**
  * Close one end of a full-duplex connection.
  * Close one end of a full-duplex connection.

+ 3 - 3
lwip/src/apps/httpd/makefsdata/makefsdata.c

@@ -421,7 +421,7 @@ int process_sub(FILE *data_file, FILE *struct_file)
         ret = tinydir_readfile_n(&dir, &file, i);
         ret = tinydir_readfile_n(&dir, &file, i);
 
 
         if (ret == 0) {
         if (ret == 0) {
-#if (defined _MSC_VER || defined __MINGW32__)
+#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
           size_t   i;
           size_t   i;
           char currName[256];
           char currName[256];
           wcstombs_s(&i, currName, sizeof(currName), file.name, sizeof(currName));
           wcstombs_s(&i, currName, sizeof(currName), file.name, sizeof(currName));
@@ -461,12 +461,12 @@ int process_sub(FILE *data_file, FILE *struct_file)
 
 
         if (ret == 0) {
         if (ret == 0) {
           if (!file.is_dir) {
           if (!file.is_dir) {
-#if (defined _MSC_VER || defined __MINGW32__)
+#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
             size_t   i;
             size_t   i;
             char curName[256];
             char curName[256];
             wcstombs_s(&i, curName, sizeof(curName), file.name, sizeof(curName));
             wcstombs_s(&i, curName, sizeof(curName), file.name, sizeof(curName));
 #else
 #else
-            const char *currName = file.name;
+            const char *curName = file.name;
 #endif
 #endif
 
 
             if (strcmp(curName, "fsdata.tmp") == 0) {
             if (strcmp(curName, "fsdata.tmp") == 0) {

+ 7 - 3
lwip/src/apps/sntp/sntp.c

@@ -71,9 +71,11 @@
 #define SNTP_SUPPORT_MULTIPLE_SERVERS 0
 #define SNTP_SUPPORT_MULTIPLE_SERVERS 0
 #endif /* NTP_MAX_SERVERS > 1 */
 #endif /* NTP_MAX_SERVERS > 1 */
 
 
-#if (SNTP_UPDATE_DELAY < 15000) && !defined(SNTP_SUPPRESS_DELAY_CHECK)
+#ifndef SNTP_SUPPRESS_DELAY_CHECK
+#if SNTP_UPDATE_DELAY < 15000
 #error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
 #error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
 #endif
 #endif
+#endif
 
 
 /* the various debug levels for this file */
 /* the various debug levels for this file */
 #define SNTP_DEBUG_TRACE        (SNTP_DEBUG | LWIP_DBG_TRACE)
 #define SNTP_DEBUG_TRACE        (SNTP_DEBUG | LWIP_DBG_TRACE)
@@ -499,15 +501,17 @@ sntp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
 
 
     /* Set up timeout for next request (only if poll response was received)*/
     /* Set up timeout for next request (only if poll response was received)*/
     if (sntp_opmode == SNTP_OPMODE_POLL) {
     if (sntp_opmode == SNTP_OPMODE_POLL) {
+      u32_t sntp_update_delay;
       sys_untimeout(sntp_try_next_server, NULL);
       sys_untimeout(sntp_try_next_server, NULL);
       sys_untimeout(sntp_request, NULL);
       sys_untimeout(sntp_request, NULL);
 
 
       /* Correct response, reset retry timeout */
       /* Correct response, reset retry timeout */
       SNTP_RESET_RETRY_TIMEOUT();
       SNTP_RESET_RETRY_TIMEOUT();
 
 
-      sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL);
+      sntp_update_delay = (u32_t)SNTP_UPDATE_DELAY;
+      sys_timeout(sntp_update_delay, sntp_request, NULL);
       LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
       LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
-                                     (u32_t)SNTP_UPDATE_DELAY));
+                                     sntp_update_delay));
     }
     }
   } else if (err == SNTP_ERR_KOD) {
   } else if (err == SNTP_ERR_KOD) {
     /* KOD errors are only processed in case of an explicit poll response */
     /* KOD errors are only processed in case of an explicit poll response */

+ 27 - 1
lwip/src/core/altcp_tcp.c

@@ -152,6 +152,17 @@ altcp_tcp_err(void *arg, err_t err)
 }
 }
 
 
 /* setup functions */
 /* setup functions */
+
+static void
+altcp_tcp_remove_callbacks(struct tcp_pcb *tpcb)
+{
+  tcp_arg(tpcb, NULL);
+  tcp_recv(tpcb, NULL);
+  tcp_sent(tpcb, NULL);
+  tcp_err(tpcb, NULL);
+  tcp_poll(tpcb, NULL, tpcb->pollinterval);
+}
+
 static void
 static void
 altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
 altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
 {
 {
@@ -288,7 +299,22 @@ altcp_tcp_close(struct altcp_pcb *conn)
   }
   }
   ALTCP_TCP_ASSERT_CONN(conn);
   ALTCP_TCP_ASSERT_CONN(conn);
   pcb = (struct tcp_pcb *)conn->state;
   pcb = (struct tcp_pcb *)conn->state;
-  return tcp_close(pcb);
+  if (pcb) {
+    err_t err;
+    tcp_poll_fn oldpoll = pcb->poll;
+    altcp_tcp_remove_callbacks(pcb);
+    err = tcp_close(pcb);
+    if (err != ERR_OK) {
+      /* not closed, set up all callbacks again */
+      altcp_tcp_setup_callbacks(conn, pcb);
+      /* poll callback is not included in the above */
+      tcp_poll(pcb, oldpoll, pcb->pollinterval);
+      return err;
+    }
+    conn->state = NULL; /* unsafe to reference pcb after tcp_close(). */
+  }
+  altcp_free(conn);
+  return ERR_OK;
 }
 }
 
 
 static err_t
 static err_t

+ 1 - 1
lwip/src/core/ipv4/icmp.c

@@ -57,7 +57,7 @@
 
 
 /** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
 /** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
  * used to modify and send a response packet (and to 1 if this is not the case,
  * used to modify and send a response packet (and to 1 if this is not the case,
- * e.g. when link header is stripped of when receiving) */
+ * e.g. when link header is stripped off when receiving) */
 #ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
 #ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
 #define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
 #define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
 #endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
 #endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */

+ 5 - 3
lwip/src/core/ipv6/icmp6.c

@@ -282,7 +282,8 @@ icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
  * Send an icmpv6 'parameter problem' packet.
  * Send an icmpv6 'parameter problem' packet.
  *
  *
  * This function must be used only in direct response to a packet that is being
  * This function must be used only in direct response to a packet that is being
- * received right now. Otherwise, address zones would be lost.
+ * received right now. Otherwise, address zones would be lost and the calculated
+ * offset would be wrong (calculated against ip6_current_header()).
  *
  *
  * @param p the input packet for which the 'param problem' should be sent,
  * @param p the input packet for which the 'param problem' should be sent,
  *          p->payload pointing to the IP header
  *          p->payload pointing to the IP header
@@ -290,9 +291,10 @@ icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
  * @param pointer the pointer to the byte where the parameter is found
  * @param pointer the pointer to the byte where the parameter is found
  */
  */
 void
 void
-icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
+icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, const void *pointer)
 {
 {
-  icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
+  u32_t pointer_u32 = (u32_t)((const u8_t *)pointer - (const u8_t *)ip6_current_header());
+  icmp6_send_response(p, c, pointer_u32, ICMP6_TYPE_PP);
 }
 }
 
 
 /**
 /**

+ 245 - 37
lwip/src/core/ipv6/ip6.c

@@ -508,7 +508,7 @@ ip6_input(struct pbuf *p, struct netif *inp)
 {
 {
   struct ip6_hdr *ip6hdr;
   struct ip6_hdr *ip6hdr;
   struct netif *netif;
   struct netif *netif;
-  u8_t nexth;
+  const u8_t *nexth;
   u16_t hlen, hlen_tot; /* the current header length */
   u16_t hlen, hlen_tot; /* the current header length */
 #if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/
 #if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/
   @todo
   @todo
@@ -695,7 +695,7 @@ netif_found:
   ip_data.current_netif = netif;
   ip_data.current_netif = netif;
 
 
   /* Save next header type. */
   /* Save next header type. */
-  nexth = IP6H_NEXTH(ip6hdr);
+  nexth = &IP6H_NEXTH(ip6hdr);
 
 
   /* Init header length. */
   /* Init header length. */
   hlen = hlen_tot = IP6_HLEN;
   hlen = hlen_tot = IP6_HLEN;
@@ -704,13 +704,25 @@ netif_found:
   pbuf_remove_header(p, IP6_HLEN);
   pbuf_remove_header(p, IP6_HLEN);
 
 
   /* Process known option extension headers, if present. */
   /* Process known option extension headers, if present. */
-  while (nexth != IP6_NEXTH_NONE)
+  while (*nexth != IP6_NEXTH_NONE)
   {
   {
-    switch (nexth) {
+    switch (*nexth) {
     case IP6_NEXTH_HOPBYHOP:
     case IP6_NEXTH_HOPBYHOP:
+    {
+      s32_t opt_offset;
+      struct ip6_hbh_hdr *hbh_hdr;
+      struct ip6_opt_hdr *opt_hdr;
       LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n"));
       LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n"));
+
       /* Get and check the header length, while staying in packet bounds. */
       /* Get and check the header length, while staying in packet bounds. */
-      hlen = (u16_t)(8 * (1 + *((u8_t *)p->payload + 1)));
+      hbh_hdr = (struct ip6_hbh_hdr *)p->payload;
+
+      /* Get next header type. */
+      nexth = &IP6_HBH_NEXTH(hbh_hdr);
+
+      /* Get the header length. */
+      hlen = (u16_t)(8 * (1 + hbh_hdr->_hlen));
+
       if ((p->len < 8) || (hlen > p->len)) {
       if ((p->len < 8) || (hlen > p->len)) {
         LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
         LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
           ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
           ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
@@ -724,17 +736,82 @@ netif_found:
 
 
       hlen_tot = (u16_t)(hlen_tot + hlen);
       hlen_tot = (u16_t)(hlen_tot + hlen);
 
 
-      /* Get next header type. */
-      nexth = *((u8_t *)p->payload);
+      /* The extended option header starts right after Hop-by-Hop header. */
+      opt_offset = IP6_HBH_HLEN;
+      while (opt_offset < hlen)
+      {
+        s32_t opt_dlen = 0;
 
 
-      /* Skip over this header. */
+        opt_hdr = (struct ip6_opt_hdr *)((u8_t *)hbh_hdr + opt_offset);
+
+        switch (IP6_OPT_TYPE(opt_hdr)) {
+        /* @todo: process IPV6 Hop-by-Hop option data */
+        case IP6_PAD1_OPTION:
+          /* PAD1 option doesn't have length and value field */
+          opt_dlen = -1;
+          break;
+        case IP6_PADN_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_ROUTER_ALERT_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_JUMBO_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        default:
+          /* Check 2 MSB of Hop-by-Hop header type. */
+          switch (IP6_OPT_TYPE_ACTION(opt_hdr)) {
+          case 1:
+            /* Discard the packet. */
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid Hop-by-Hop option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          case 2:
+            /* Send ICMP Parameter Problem */
+            icmp6_param_problem(p, ICMP6_PP_OPTION, opt_hdr);
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid Hop-by-Hop option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          case 3:
+            /* Send ICMP Parameter Problem if destination address is not a multicast address */
+            if (!ip6_addr_ismulticast(ip6_current_dest_addr())) {
+              icmp6_param_problem(p, ICMP6_PP_OPTION, opt_hdr);
+            }
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid Hop-by-Hop option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          default:
+            /* Skip over this option. */
+            opt_dlen = IP6_OPT_DLEN(opt_hdr);
+            break;
+          }
+          break;
+        }
+
+        /* Adjust the offset to move to the next extended option header */
+        opt_offset = opt_offset + IP6_OPT_HLEN + opt_dlen;
+      }
       pbuf_remove_header(p, hlen);
       pbuf_remove_header(p, hlen);
       break;
       break;
+    }
     case IP6_NEXTH_DESTOPTS:
     case IP6_NEXTH_DESTOPTS:
+    {
+      s32_t opt_offset;
+      struct ip6_dest_hdr *dest_hdr;
+      struct ip6_opt_hdr *opt_hdr;
       LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
       LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
 
 
-      /* Get and check the header length, while staying in packet bounds. */
-      hlen = (u16_t)(8 * (1 + *((u8_t *)p->payload + 1)));
+      dest_hdr = (struct ip6_dest_hdr *)p->payload;
+
+      /* Get next header type. */
+      nexth = &IP6_DEST_NEXTH(dest_hdr);
+
+      /* Get the header length. */
+      hlen = 8 * (1 + dest_hdr->_hlen);
       if ((p->len < 8) || (hlen > p->len)) {
       if ((p->len < 8) || (hlen > p->len)) {
         LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
         LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
           ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
           ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
@@ -748,17 +825,87 @@ netif_found:
 
 
       hlen_tot = (u16_t)(hlen_tot + hlen);
       hlen_tot = (u16_t)(hlen_tot + hlen);
 
 
-      /* Get next header type. */
-      nexth = *((u8_t *)p->payload);
+      /* The extended option header starts right after Destination header. */
+      opt_offset = IP6_DEST_HLEN;
+      while (opt_offset < hlen)
+      {
+        s32_t opt_dlen = 0;
+
+        opt_hdr = (struct ip6_opt_hdr *)((u8_t *)dest_hdr + opt_offset);
+
+        switch (IP6_OPT_TYPE(opt_hdr))
+        {
+        /* @todo: process IPV6 Destination option data */
+        case IP6_PAD1_OPTION:
+          /* PAD1 option deosn't have length and value field */
+          opt_dlen = -1;
+          break;
+        case IP6_PADN_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_ROUTER_ALERT_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_JUMBO_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_HOME_ADDRESS_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        default:
+          /* Check 2 MSB of Destination header type. */
+          switch (IP6_OPT_TYPE_ACTION(opt_hdr))
+          {
+          case 1:
+            /* Discard the packet. */
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid destination option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          case 2:
+            /* Send ICMP Parameter Problem */
+            icmp6_param_problem(p, ICMP6_PP_OPTION, opt_hdr);
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid destination option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          case 3:
+            /* Send ICMP Parameter Problem if destination address is not a multicast address */
+            if (!ip6_addr_ismulticast(ip6_current_dest_addr())) {
+              icmp6_param_problem(p, ICMP6_PP_OPTION, opt_hdr);
+            }
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid destination option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          default:
+            /* Skip over this option. */
+            opt_dlen = IP6_OPT_DLEN(opt_hdr);
+            break;
+          }
+          break;
+        }
+
+        /* Adjust the offset to move to the next extended option header */
+        opt_offset = opt_offset + IP6_OPT_HLEN + opt_dlen;
+      }
 
 
-      /* Skip over this header. */
       pbuf_remove_header(p, hlen);
       pbuf_remove_header(p, hlen);
       break;
       break;
+    }
     case IP6_NEXTH_ROUTING:
     case IP6_NEXTH_ROUTING:
+    {
+      struct ip6_rout_hdr *rout_hdr;
       LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
       LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
 
 
-      /* Get and check the header length, while staying in packet bounds. */
-      hlen = (u16_t)(8 * (1 + *((u8_t *)p->payload + 1)));
+      rout_hdr = (struct ip6_rout_hdr *)p->payload;
+
+      /* Get next header type. */
+      nexth = &IP6_ROUT_NEXTH(rout_hdr);
+
+      /* Get the header length. */
+      hlen = 8 * (1 + rout_hdr->_hlen);
+
       if ((p->len < 8) || (hlen > p->len)) {
       if ((p->len < 8) || (hlen > p->len)) {
         LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
         LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
           ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
           ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
@@ -770,20 +917,51 @@ netif_found:
         goto ip6_input_cleanup;
         goto ip6_input_cleanup;
       }
       }
 
 
-      /* Get next header type. */
-      nexth = *((u8_t *)p->payload);
-
       /* Skip over this header. */
       /* Skip over this header. */
       hlen_tot = (u16_t)(hlen_tot + hlen);
       hlen_tot = (u16_t)(hlen_tot + hlen);
 
 
+      /* if segment left value is 0 in routing header, ignore the option */
+      if (IP6_ROUT_SEG_LEFT(rout_hdr)) {
+        /* The length field of routing option header must be even */
+        if (rout_hdr->_hlen & 0x1) {
+          /* Discard and send parameter field error */
+          icmp6_param_problem(p, ICMP6_PP_FIELD, &rout_hdr->_hlen);
+          LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid routing type dropped\n"));
+          pbuf_free(p);
+          IP6_STATS_INC(ip6.drop);
+          goto ip6_input_cleanup;
+        }
+
+        switch (IP6_ROUT_TYPE(rout_hdr))
+        {
+        /* TODO: process routing by the type */
+        case IP6_ROUT_TYPE2:
+          break;
+        case IP6_ROUT_RPL:
+          break;
+        default:
+          /* Discard unrecognized routing type and send parameter field error */
+          icmp6_param_problem(p, ICMP6_PP_FIELD, &IP6_ROUT_TYPE(rout_hdr));
+          LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid routing type dropped\n"));
+          pbuf_free(p);
+          IP6_STATS_INC(ip6.drop);
+          goto ip6_input_cleanup;
+        }
+      }
+
       pbuf_remove_header(p, hlen);
       pbuf_remove_header(p, hlen);
       break;
       break;
-
+    }
     case IP6_NEXTH_FRAGMENT:
     case IP6_NEXTH_FRAGMENT:
     {
     {
       struct ip6_frag_hdr *frag_hdr;
       struct ip6_frag_hdr *frag_hdr;
       LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n"));
       LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n"));
 
 
+      frag_hdr = (struct ip6_frag_hdr *)p->payload;
+
+      /* Get next header type. */
+      nexth = &IP6_FRAG_NEXTH(frag_hdr);
+
       /* Fragment Header length. */
       /* Fragment Header length. */
       hlen = 8;
       hlen = 8;
 
 
@@ -801,10 +979,15 @@ netif_found:
 
 
       hlen_tot = (u16_t)(hlen_tot + hlen);
       hlen_tot = (u16_t)(hlen_tot + hlen);
 
 
-      frag_hdr = (struct ip6_frag_hdr *)p->payload;
-
-      /* Get next header type. */
-      nexth = frag_hdr->_nexth;
+      /* check payload length is multiple of 8 octets when mbit is set */
+      if (IP6_FRAG_MBIT(frag_hdr) && (IP6H_PLEN(ip6hdr) & 0x7)) {
+        /* ipv6 payload length is not multiple of 8 octets */
+        icmp6_param_problem(p, ICMP6_PP_FIELD, &ip6hdr->_plen);
+        LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid payload length dropped\n"));
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+      }
 
 
       /* Offset == 0 and more_fragments == 0? */
       /* Offset == 0 and more_fragments == 0? */
       if ((frag_hdr->_fragment_offset &
       if ((frag_hdr->_fragment_offset &
@@ -813,7 +996,6 @@ netif_found:
         pbuf_remove_header(p, hlen);
         pbuf_remove_header(p, hlen);
       } else {
       } else {
 #if LWIP_IPV6_REASS
 #if LWIP_IPV6_REASS
-
         /* reassemble the packet */
         /* reassemble the packet */
         ip_data.current_ip_header_tot_len = hlen_tot;
         ip_data.current_ip_header_tot_len = hlen_tot;
         p = ip6_reass(p);
         p = ip6_reass(p);
@@ -825,7 +1007,7 @@ netif_found:
         /* Returned p point to IPv6 header.
         /* Returned p point to IPv6 header.
          * Update all our variables and pointers and continue. */
          * Update all our variables and pointers and continue. */
         ip6hdr = (struct ip6_hdr *)p->payload;
         ip6hdr = (struct ip6_hdr *)p->payload;
-        nexth = IP6H_NEXTH(ip6hdr);
+        nexth = &IP6H_NEXTH(ip6hdr);
         hlen = hlen_tot = IP6_HLEN;
         hlen = hlen_tot = IP6_HLEN;
         pbuf_remove_header(p, IP6_HLEN);
         pbuf_remove_header(p, IP6_HLEN);
 
 
@@ -843,9 +1025,18 @@ netif_found:
     default:
     default:
       goto options_done;
       goto options_done;
     }
     }
+
+    if (*nexth == IP6_NEXTH_HOPBYHOP) {
+      /* Hop-by-Hop header comes only as a first option */
+      icmp6_param_problem(p, ICMP6_PP_HEADER, nexth);
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header dropped (only valid as a first option)\n"));
+      pbuf_free(p);
+      IP6_STATS_INC(ip6.drop);
+      goto ip6_input_cleanup;
+    }
   }
   }
-options_done:
 
 
+options_done:
   if (hlen_tot >= 0x8000) {
   if (hlen_tot >= 0x8000) {
     /* s16_t overflow */
     /* s16_t overflow */
     LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: header length overflow: %"U16_F"\n", hlen_tot));
     LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: header length overflow: %"U16_F"\n", hlen_tot));
@@ -873,7 +1064,7 @@ options_done:
 #else /* LWIP_RAW */
 #else /* LWIP_RAW */
   {
   {
 #endif /* LWIP_RAW */
 #endif /* LWIP_RAW */
-    switch (nexth) {
+    switch (*nexth) {
     case IP6_NEXTH_NONE:
     case IP6_NEXTH_NONE:
       pbuf_free(p);
       pbuf_free(p);
       break;
       break;
@@ -902,7 +1093,7 @@ options_done:
       /* send ICMP parameter problem unless it was a multicast or ICMPv6 */
       /* send ICMP parameter problem unless it was a multicast or ICMPv6 */
       if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
       if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
           (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) {
           (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) {
-        icmp6_param_problem(p, ICMP6_PP_HEADER, (u32_t)(hlen_tot - hlen));
+        icmp6_param_problem(p, ICMP6_PP_HEADER, nexth);
       }
       }
 #endif /* LWIP_ICMP */
 #endif /* LWIP_ICMP */
       LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", (u16_t)IP6H_NEXTH(ip6hdr)));
       LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", (u16_t)IP6H_NEXTH(ip6hdr)));
@@ -1207,25 +1398,42 @@ ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
 err_t
 err_t
 ip6_options_add_hbh_ra(struct pbuf *p, u8_t nexth, u8_t value)
 ip6_options_add_hbh_ra(struct pbuf *p, u8_t nexth, u8_t value)
 {
 {
+  u8_t *opt_data;
+  u32_t offset = 0;
   struct ip6_hbh_hdr *hbh_hdr;
   struct ip6_hbh_hdr *hbh_hdr;
+  struct ip6_opt_hdr *opt_hdr;
 
 
+  /* fixed 4 bytes for router alert option and 2 bytes padding */
+  const u8_t hlen = (sizeof(struct ip6_opt_hdr) * 2) + IP6_ROUTER_ALERT_DLEN;
   /* Move pointer to make room for hop-by-hop options header. */
   /* Move pointer to make room for hop-by-hop options header. */
-  if (pbuf_add_header(p, sizeof(struct ip6_hbh_hdr))) {
+  if (pbuf_add_header(p, sizeof(struct ip6_hbh_hdr) + hlen)) {
     LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n"));
     LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n"));
     IP6_STATS_INC(ip6.err);
     IP6_STATS_INC(ip6.err);
     return ERR_BUF;
     return ERR_BUF;
   }
   }
 
 
+  /* Set fields of Hop-by-Hop header */
   hbh_hdr = (struct ip6_hbh_hdr *)p->payload;
   hbh_hdr = (struct ip6_hbh_hdr *)p->payload;
-
-  /* Set fields. */
-  hbh_hdr->_nexth = nexth;
+  IP6_HBH_NEXTH(hbh_hdr) = nexth;
   hbh_hdr->_hlen = 0;
   hbh_hdr->_hlen = 0;
-  hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION;
-  hbh_hdr->_ra_opt_dlen = 2;
-  hbh_hdr->_ra_opt_data = value;
-  hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION;
-  hbh_hdr->_padn_opt_dlen = 0;
+  offset = IP6_HBH_HLEN;
+
+  /* Set router alert options to Hop-by-Hop extended option header */
+  opt_hdr = (struct ip6_opt_hdr *)((u8_t *)hbh_hdr + offset);
+  IP6_OPT_TYPE(opt_hdr) = IP6_ROUTER_ALERT_OPTION;
+  IP6_OPT_DLEN(opt_hdr) = IP6_ROUTER_ALERT_DLEN;
+  offset += IP6_OPT_HLEN;
+
+  /* Set router alert option data */
+  opt_data = (u8_t *)hbh_hdr + offset;
+  opt_data[0] = value;
+  opt_data[1] = 0;
+  offset += IP6_OPT_DLEN(opt_hdr);
+
+  /* add 2 bytes padding to make 8 bytes Hop-by-Hop header length */
+  opt_hdr = (struct ip6_opt_hdr *)((u8_t *)hbh_hdr + offset);
+  IP6_OPT_TYPE(opt_hdr) = IP6_PADN_OPTION;
+  IP6_OPT_DLEN(opt_hdr) = 0;
 
 
   return ERR_OK;
   return ERR_OK;
 }
 }

+ 2 - 2
lwip/src/core/ipv6/mld6.c

@@ -554,14 +554,14 @@ mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
   const ip6_addr_t *src_addr;
   const ip6_addr_t *src_addr;
 
 
   /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
   /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
-  p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM);
+  p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + MLD6_HBH_HLEN, PBUF_RAM);
   if (p == NULL) {
   if (p == NULL) {
     MLD6_STATS_INC(mld6.memerr);
     MLD6_STATS_INC(mld6.memerr);
     return;
     return;
   }
   }
 
 
   /* Move to make room for Hop-by-hop options header. */
   /* Move to make room for Hop-by-hop options header. */
-  if (pbuf_remove_header(p, IP6_HBH_HLEN)) {
+  if (pbuf_remove_header(p, MLD6_HBH_HLEN)) {
     pbuf_free(p);
     pbuf_free(p);
     MLD6_STATS_INC(mld6.lenerr);
     MLD6_STATS_INC(mld6.lenerr);
     return;
     return;

+ 56 - 10
lwip/src/core/mem.c

@@ -66,6 +66,11 @@
 #include <stdlib.h> /* for malloc()/free() */
 #include <stdlib.h> /* for malloc()/free() */
 #endif
 #endif
 
 
+/* This is overridable for tests only... */
+#ifndef LWIP_MEM_ILLEGAL_FREE
+#define LWIP_MEM_ILLEGAL_FREE(msg)         LWIP_ASSERT(msg, 0)
+#endif
+
 #define MEM_STATS_INC_LOCKED(x)         SYS_ARCH_LOCKED(MEM_STATS_INC(x))
 #define MEM_STATS_INC_LOCKED(x)         SYS_ARCH_LOCKED(MEM_STATS_INC(x))
 #define MEM_STATS_INC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_INC_USED(x, y))
 #define MEM_STATS_INC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_INC_USED(x, y))
 #define MEM_STATS_DEC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_DEC_USED(x, y))
 #define MEM_STATS_DEC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_DEC_USED(x, y))
@@ -413,6 +418,25 @@ mem_init(void)
   }
   }
 }
 }
 
 
+/* Check if a struct mem is correctly linked.
+ * If not, double-free is a possible reason.
+ */
+static int
+mem_link_valid(struct mem *mem)
+{
+  struct mem *nmem, *pmem;
+  mem_size_t rmem_idx;
+  rmem_idx = (mem_size_t)((u8_t *)mem - ram);
+  nmem = (struct mem *)(void *)&ram[mem->next];
+  pmem = (struct mem *)(void *)&ram[mem->prev];
+  if ((mem->next > MEM_SIZE_ALIGNED) || (mem->prev > MEM_SIZE_ALIGNED) ||
+      ((mem->prev != rmem_idx) && (pmem->next != rmem_idx)) ||
+      ((nmem != ram_end) && (nmem->prev != rmem_idx))) {
+    return 0;
+  }
+  return 1;
+}
+
 /**
 /**
  * Put a struct mem back on the heap
  * Put a struct mem back on the heap
  *
  *
@@ -429,12 +453,20 @@ mem_free(void *rmem)
     LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
     LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
     return;
     return;
   }
   }
-  LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT - 1)) == 0);
+  if ((((mem_ptr_t)rmem) & (MEM_ALIGNMENT - 1)) != 0) {
+    LWIP_MEM_ILLEGAL_FREE("mem_free: sanity check alignment");
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: sanity check alignment\n"));
+    /* protect mem stats from concurrent access */
+    MEM_STATS_INC_LOCKED(illegal);
+    return;
+  }
 
 
-  LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
-              (u8_t *)rmem < (u8_t *)ram_end);
+  /* Get the corresponding struct mem: */
+  /* cast through void* to get rid of alignment warnings */
+  mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
 
 
-  if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+  if ((u8_t *)mem < ram || (u8_t *)rmem + MIN_SIZE_ALIGNED > (u8_t *)ram_end) {
+    LWIP_MEM_ILLEGAL_FREE("mem_free: illegal memory");
     LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
     LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
     /* protect mem stats from concurrent access */
     /* protect mem stats from concurrent access */
     MEM_STATS_INC_LOCKED(illegal);
     MEM_STATS_INC_LOCKED(illegal);
@@ -442,12 +474,26 @@ mem_free(void *rmem)
   }
   }
   /* protect the heap from concurrent access */
   /* protect the heap from concurrent access */
   LWIP_MEM_FREE_PROTECT();
   LWIP_MEM_FREE_PROTECT();
-  /* Get the corresponding struct mem ... */
-  /* cast through void* to get rid of alignment warnings */
-  mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
-  /* ... which has to be in a used state ... */
-  LWIP_ASSERT("mem_free: mem->used", mem->used);
-  /* ... and is now unused. */
+  /* mem has to be in a used state */
+  if (!mem->used) {
+    LWIP_MEM_ILLEGAL_FREE("mem_free: illegal memory: double free");
+    LWIP_MEM_FREE_UNPROTECT();
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory: double free?\n"));
+    /* protect mem stats from concurrent access */
+    MEM_STATS_INC_LOCKED(illegal);
+    return;
+  }
+
+  if (!mem_link_valid(mem)) {
+    LWIP_MEM_ILLEGAL_FREE("mem_free: illegal memory: non-linked: double free");
+    LWIP_MEM_FREE_UNPROTECT();
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory: non-linked: double free?\n"));
+    /* protect mem stats from concurrent access */
+    MEM_STATS_INC_LOCKED(illegal);
+    return;
+  }
+
+  /* mem is now unused. */
   mem->used = 0;
   mem->used = 0;
 
 
   if (mem < lfree) {
   if (mem < lfree) {

+ 57 - 48
lwip/src/core/tcp_in.c

@@ -96,9 +96,9 @@ static int tcp_input_delayed_close(struct tcp_pcb *pcb);
 #if LWIP_TCP_SACK_OUT
 #if LWIP_TCP_SACK_OUT
 static void tcp_add_sack(struct tcp_pcb *pcb, u32_t left, u32_t right);
 static void tcp_add_sack(struct tcp_pcb *pcb, u32_t left, u32_t right);
 static void tcp_remove_sacks_lt(struct tcp_pcb *pcb, u32_t seq);
 static void tcp_remove_sacks_lt(struct tcp_pcb *pcb, u32_t seq);
-#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+#if defined(TCP_OOSEQ_BYTES_LIMIT) || defined(TCP_OOSEQ_PBUFS_LIMIT)
 static void tcp_remove_sacks_gt(struct tcp_pcb *pcb, u32_t seq);
 static void tcp_remove_sacks_gt(struct tcp_pcb *pcb, u32_t seq);
-#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+#endif /* TCP_OOSEQ_BYTES_LIMIT || TCP_OOSEQ_PBUFS_LIMIT */
 #endif /* LWIP_TCP_SACK_OUT */
 #endif /* LWIP_TCP_SACK_OUT */
 
 
 /**
 /**
@@ -1112,19 +1112,9 @@ tcp_free_acked_segments(struct tcp_pcb *pcb, struct tcp_seg *seg_list, const cha
 static void
 static void
 tcp_receive(struct tcp_pcb *pcb)
 tcp_receive(struct tcp_pcb *pcb)
 {
 {
-#if TCP_QUEUE_OOSEQ || TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
-  struct tcp_seg *next;
-#endif
-#if TCP_QUEUE_OOSEQ
-  struct tcp_seg *prev, *cseg;
-#endif /* TCP_QUEUE_OOSEQ */
   s16_t m;
   s16_t m;
   u32_t right_wnd_edge;
   u32_t right_wnd_edge;
   int found_dupack = 0;
   int found_dupack = 0;
-#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
-  u32_t ooseq_blen;
-  u16_t ooseq_qlen;
-#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
 
 
   LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED);
   LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED);
 
 
@@ -1481,21 +1471,22 @@ tcp_receive(struct tcp_pcb *pcb)
               tcp_seg_free(old_ooseq);
               tcp_seg_free(old_ooseq);
             }
             }
           } else {
           } else {
-            next = pcb->ooseq;
+            struct tcp_seg *next = pcb->ooseq;
             /* Remove all segments on ooseq that are covered by inseg already.
             /* Remove all segments on ooseq that are covered by inseg already.
              * FIN is copied from ooseq to inseg if present. */
              * FIN is copied from ooseq to inseg if present. */
             while (next &&
             while (next &&
                    TCP_SEQ_GEQ(seqno + tcplen,
                    TCP_SEQ_GEQ(seqno + tcplen,
                                next->tcphdr->seqno + next->len)) {
                                next->tcphdr->seqno + next->len)) {
+              struct tcp_seg *tmp;
               /* inseg cannot have FIN here (already processed above) */
               /* inseg cannot have FIN here (already processed above) */
               if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 &&
               if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 &&
                   (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
                   (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
                 TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
                 TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
                 tcplen = TCP_TCPLEN(&inseg);
                 tcplen = TCP_TCPLEN(&inseg);
               }
               }
-              prev = next;
+              tmp = next;
               next = next->next;
               next = next->next;
-              tcp_seg_free(prev);
+              tcp_seg_free(tmp);
             }
             }
             /* Now trim right side of inseg if it overlaps with the first
             /* Now trim right side of inseg if it overlaps with the first
              * segment on ooseq */
              * segment on ooseq */
@@ -1552,7 +1543,7 @@ tcp_receive(struct tcp_pcb *pcb)
         while (pcb->ooseq != NULL &&
         while (pcb->ooseq != NULL &&
                pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
                pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
 
 
-          cseg = pcb->ooseq;
+          struct tcp_seg *cseg = pcb->ooseq;
           seqno = pcb->ooseq->tcphdr->seqno;
           seqno = pcb->ooseq->tcphdr->seqno;
 
 
           pcb->rcv_nxt += TCP_TCPLEN(cseg);
           pcb->rcv_nxt += TCP_TCPLEN(cseg);
@@ -1653,7 +1644,7 @@ tcp_receive(struct tcp_pcb *pcb)
              It may start before the newly received segment (possibly adjusted below). */
              It may start before the newly received segment (possibly adjusted below). */
           u32_t sackbeg = TCP_SEQ_LT(seqno, pcb->ooseq->tcphdr->seqno) ? seqno : pcb->ooseq->tcphdr->seqno;
           u32_t sackbeg = TCP_SEQ_LT(seqno, pcb->ooseq->tcphdr->seqno) ? seqno : pcb->ooseq->tcphdr->seqno;
 #endif /* LWIP_TCP_SACK_OUT */
 #endif /* LWIP_TCP_SACK_OUT */
-          prev = NULL;
+          struct tcp_seg *next, *prev = NULL;
           for (next = pcb->ooseq; next != NULL; next = next->next) {
           for (next = pcb->ooseq; next != NULL; next = next->next) {
             if (seqno == next->tcphdr->seqno) {
             if (seqno == next->tcphdr->seqno) {
               /* The sequence number of the incoming segment is the
               /* The sequence number of the incoming segment is the
@@ -1664,7 +1655,7 @@ tcp_receive(struct tcp_pcb *pcb)
                 /* The incoming segment is larger than the old
                 /* The incoming segment is larger than the old
                    segment. We replace some segments with the new
                    segment. We replace some segments with the new
                    one. */
                    one. */
-                cseg = tcp_seg_copy(&inseg);
+                struct tcp_seg *cseg = tcp_seg_copy(&inseg);
                 if (cseg != NULL) {
                 if (cseg != NULL) {
                   if (prev != NULL) {
                   if (prev != NULL) {
                     prev->next = cseg;
                     prev->next = cseg;
@@ -1687,7 +1678,7 @@ tcp_receive(struct tcp_pcb *pcb)
                      than the sequence number of the first segment on the
                      than the sequence number of the first segment on the
                      queue. We put the incoming segment first on the
                      queue. We put the incoming segment first on the
                      queue. */
                      queue. */
-                  cseg = tcp_seg_copy(&inseg);
+                  struct tcp_seg *cseg = tcp_seg_copy(&inseg);
                   if (cseg != NULL) {
                   if (cseg != NULL) {
                     pcb->ooseq = cseg;
                     pcb->ooseq = cseg;
                     tcp_oos_insert_segment(cseg, next);
                     tcp_oos_insert_segment(cseg, next);
@@ -1703,7 +1694,7 @@ tcp_receive(struct tcp_pcb *pcb)
                      the next segment on ->ooseq. We trim trim the previous
                      the next segment on ->ooseq. We trim trim the previous
                      segment, delete next segments that included in received segment
                      segment, delete next segments that included in received segment
                      and trim received, if needed. */
                      and trim received, if needed. */
-                  cseg = tcp_seg_copy(&inseg);
+                  struct tcp_seg *cseg = tcp_seg_copy(&inseg);
                   if (cseg != NULL) {
                   if (cseg != NULL) {
                     if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
                     if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
                       /* We need to trim the prev segment. */
                       /* We need to trim the prev segment. */
@@ -1796,37 +1787,55 @@ tcp_receive(struct tcp_pcb *pcb)
           }
           }
 #endif /* LWIP_TCP_SACK_OUT */
 #endif /* LWIP_TCP_SACK_OUT */
         }
         }
-#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
-        /* Check that the data on ooseq doesn't exceed one of the limits
-           and throw away everything above that limit. */
-        ooseq_blen = 0;
-        ooseq_qlen = 0;
-        prev = NULL;
-        for (next = pcb->ooseq; next != NULL; prev = next, next = next->next) {
-          struct pbuf *p = next->p;
-          ooseq_blen += p->tot_len;
-          ooseq_qlen += pbuf_clen(p);
-          if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) ||
-              (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) {
-#if LWIP_TCP_SACK_OUT
-            if (pcb->flags & TF_SACK) {
-              /* Let's remove all SACKs from next's seqno up. */
-              tcp_remove_sacks_gt(pcb, next->tcphdr->seqno);
+#if defined(TCP_OOSEQ_BYTES_LIMIT) || defined(TCP_OOSEQ_PBUFS_LIMIT)
+        {
+          /* Check that the data on ooseq doesn't exceed one of the limits
+             and throw away everything above that limit. */
+#ifdef TCP_OOSEQ_BYTES_LIMIT
+          const u32_t ooseq_max_blen = TCP_OOSEQ_BYTES_LIMIT(pcb);
+          u32_t ooseq_blen = 0;
+#endif
+#ifdef TCP_OOSEQ_PBUFS_LIMIT
+          const u16_t ooseq_max_qlen = TCP_OOSEQ_PBUFS_LIMIT(pcb);
+          u16_t ooseq_qlen = 0;
+#endif
+          struct tcp_seg *next, *prev = NULL;
+          for (next = pcb->ooseq; next != NULL; prev = next, next = next->next) {
+            struct pbuf *p = next->p;
+            int stop_here = 0;
+#ifdef TCP_OOSEQ_BYTES_LIMIT
+            ooseq_blen += p->tot_len;
+            if (ooseq_blen > ooseq_max_blen) {
+              stop_here = 1;
+            }
+#endif
+#ifdef TCP_OOSEQ_PBUFS_LIMIT
+            ooseq_qlen += pbuf_clen(p);
+            if (ooseq_qlen > ooseq_max_qlen) {
+              stop_here = 1;
             }
             }
+#endif
+            if (stop_here) {
+#if LWIP_TCP_SACK_OUT
+              if (pcb->flags & TF_SACK) {
+                /* Let's remove all SACKs from next's seqno up. */
+                tcp_remove_sacks_gt(pcb, next->tcphdr->seqno);
+              }
 #endif /* LWIP_TCP_SACK_OUT */
 #endif /* LWIP_TCP_SACK_OUT */
-            /* too much ooseq data, dump this and everything after it */
-            tcp_segs_free(next);
-            if (prev == NULL) {
-              /* first ooseq segment is too much, dump the whole queue */
-              pcb->ooseq = NULL;
-            } else {
-              /* just dump 'next' and everything after it */
-              prev->next = NULL;
+              /* too much ooseq data, dump this and everything after it */
+              tcp_segs_free(next);
+              if (prev == NULL) {
+                /* first ooseq segment is too much, dump the whole queue */
+                pcb->ooseq = NULL;
+              } else {
+                /* just dump 'next' and everything after it */
+                prev->next = NULL;
+              }
+              break;
             }
             }
-            break;
           }
           }
         }
         }
-#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+#endif /* TCP_OOSEQ_BYTES_LIMIT || TCP_OOSEQ_PBUFS_LIMIT */
 #endif /* TCP_QUEUE_OOSEQ */
 #endif /* TCP_QUEUE_OOSEQ */
 
 
         /* We send the ACK packet after we've (potentially) dealt with SACKs,
         /* We send the ACK packet after we've (potentially) dealt with SACKs,
@@ -2092,7 +2101,7 @@ tcp_remove_sacks_lt(struct tcp_pcb *pcb, u32_t seq)
   }
   }
 }
 }
 
 
-#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+#if defined(TCP_OOSEQ_BYTES_LIMIT) || defined(TCP_OOSEQ_PBUFS_LIMIT)
 /**
 /**
  * Called to remove a range of SACKs.
  * Called to remove a range of SACKs.
  *
  *
@@ -2131,7 +2140,7 @@ tcp_remove_sacks_gt(struct tcp_pcb *pcb, u32_t seq)
     pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
     pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
   }
   }
 }
 }
-#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+#endif /* TCP_OOSEQ_BYTES_LIMIT || TCP_OOSEQ_PBUFS_LIMIT */
 
 
 #endif /* LWIP_TCP_SACK_OUT */
 #endif /* LWIP_TCP_SACK_OUT */
 
 

+ 3 - 3
lwip/src/core/tcp_out.c

@@ -1221,15 +1221,15 @@ tcp_output(struct tcp_pcb *pcb)
       TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
       TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
     }
     }
 
 
-#if TCP_OVERSIZE_DBGCHECK
-    seg->oversize_left = 0;
-#endif /* TCP_OVERSIZE_DBGCHECK */
     err = tcp_output_segment(seg, pcb, netif);
     err = tcp_output_segment(seg, pcb, netif);
     if (err != ERR_OK) {
     if (err != ERR_OK) {
       /* segment could not be sent, for whatever reason */
       /* segment could not be sent, for whatever reason */
       tcp_set_flags(pcb, TF_NAGLEMEMERR);
       tcp_set_flags(pcb, TF_NAGLEMEMERR);
       return err;
       return err;
     }
     }
+#if TCP_OVERSIZE_DBGCHECK
+    seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
     pcb->unsent = seg->next;
     pcb->unsent = seg->next;
     if (pcb->state != SYN_SENT) {
     if (pcb->state != SYN_SENT) {
       tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
       tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);

+ 1 - 1
lwip/src/include/lwip/apps/mqtt_opts.h

@@ -39,7 +39,7 @@
 
 
 #include "lwip/opt.h"
 #include "lwip/opt.h"
 
 
-#ifdef	__cplusplus
+#ifdef __cplusplus
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 

+ 1 - 1
lwip/src/include/lwip/icmp6.h

@@ -59,7 +59,7 @@ void icmp6_packet_too_big(struct pbuf *p, u32_t mtu);
 void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c);
 void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c);
 void icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
 void icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
     const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
     const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
-void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer);
+void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, const void *pointer);
 
 
 #endif /* LWIP_ICMP6 && LWIP_IPV6 */
 #endif /* LWIP_ICMP6 && LWIP_IPV6 */
 
 

+ 42 - 4
lwip/src/include/lwip/opt.h

@@ -1300,21 +1300,51 @@
 #endif
 #endif
 
 
 /**
 /**
- * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb.
- * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1.
+ * TCP_OOSEQ_MAX_BYTES: The default maximum number of bytes queued on ooseq per
+ * pcb if TCP_OOSEQ_BYTES_LIMIT is not defined. Default is 0 (no limit).
+ * Only valid for TCP_QUEUE_OOSEQ==1.
  */
  */
 #if !defined TCP_OOSEQ_MAX_BYTES || defined __DOXYGEN__
 #if !defined TCP_OOSEQ_MAX_BYTES || defined __DOXYGEN__
 #define TCP_OOSEQ_MAX_BYTES             0
 #define TCP_OOSEQ_MAX_BYTES             0
 #endif
 #endif
 
 
 /**
 /**
- * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb.
- * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1.
+ * TCP_OOSEQ_BYTES_LIMIT(pcb): Return the maximum number of bytes to be queued
+ * on ooseq per pcb, given the pcb. Only valid for TCP_QUEUE_OOSEQ==1 &&
+ * TCP_OOSEQ_MAX_BYTES==1.
+ * Use this to override TCP_OOSEQ_MAX_BYTES to a dynamic value per pcb.
+ */
+#if !defined TCP_OOSEQ_BYTES_LIMIT
+#if TCP_OOSEQ_MAX_BYTES
+#define TCP_OOSEQ_BYTES_LIMIT(pcb) TCP_OOSEQ_MAX_BYTES
+#elif defined __DOXYGEN__
+#define TCP_OOSEQ_BYTES_LIMIT(pcb)
+#endif
+#endif
+
+/**
+ * TCP_OOSEQ_MAX_PBUFS: The default maximum number of pbufs queued on ooseq per
+ * pcb if TCP_OOSEQ_BYTES_LIMIT is not defined. Default is 0 (no limit).
+ * Only valid for TCP_QUEUE_OOSEQ==1.
  */
  */
 #if !defined TCP_OOSEQ_MAX_PBUFS || defined __DOXYGEN__
 #if !defined TCP_OOSEQ_MAX_PBUFS || defined __DOXYGEN__
 #define TCP_OOSEQ_MAX_PBUFS             0
 #define TCP_OOSEQ_MAX_PBUFS             0
 #endif
 #endif
 
 
+/**
+ * TCP_OOSEQ_PBUFS_LIMIT(pcb): Return the maximum number of pbufs to be queued
+ * on ooseq per pcb, given the pcb.  Only valid for TCP_QUEUE_OOSEQ==1 &&
+ * TCP_OOSEQ_MAX_PBUFS==1.
+ * Use this to override TCP_OOSEQ_MAX_PBUFS to a dynamic value per pcb.
+ */
+#if !defined TCP_OOSEQ_PBUFS_LIMIT
+#if TCP_OOSEQ_MAX_PBUFS
+#define TCP_OOSEQ_PBUFS_LIMIT(pcb) TCP_OOSEQ_MAX_PBUFS
+#elif defined __DOXYGEN__
+#define TCP_OOSEQ_PBUFS_LIMIT(pcb)
+#endif
+#endif
+
 /**
 /**
  * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb.
  * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb.
  */
  */
@@ -1986,6 +2016,14 @@
 #if !defined LWIP_SOCKET_SELECT || defined __DOXYGEN__
 #if !defined LWIP_SOCKET_SELECT || defined __DOXYGEN__
 #define LWIP_SOCKET_SELECT              1
 #define LWIP_SOCKET_SELECT              1
 #endif
 #endif
+
+/**
+ * LWIP_SOCKET_POLL==1 (default): enable poll() for sockets (including
+ * struct pollfd, nfds_t, and constants)
+ */
+#if !defined LWIP_SOCKET_POLL || defined __DOXYGEN__
+#define LWIP_SOCKET_POLL                1
+#endif
 /**
 /**
  * @}
  * @}
  */
  */

+ 2 - 2
lwip/src/include/lwip/priv/memp_std.h

@@ -78,9 +78,9 @@ LWIP_MEMPOOL(DNS_API_MSG,    MEMP_NUM_DNS_API_MSG,     sizeof(struct dns_api_msg
 #if LWIP_SOCKET && !LWIP_TCPIP_CORE_LOCKING
 #if LWIP_SOCKET && !LWIP_TCPIP_CORE_LOCKING
 LWIP_MEMPOOL(SOCKET_SETGETSOCKOPT_DATA, MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA, sizeof(struct lwip_setgetsockopt_data), "SOCKET_SETGETSOCKOPT_DATA")
 LWIP_MEMPOOL(SOCKET_SETGETSOCKOPT_DATA, MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA, sizeof(struct lwip_setgetsockopt_data), "SOCKET_SETGETSOCKOPT_DATA")
 #endif
 #endif
-#if LWIP_SOCKET && LWIP_SOCKET_SELECT
+#if LWIP_SOCKET && (LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL)
 LWIP_MEMPOOL(SELECT_CB,      MEMP_NUM_SELECT_CB,       sizeof(struct lwip_select_cb), "SELECT_CB")
 LWIP_MEMPOOL(SELECT_CB,      MEMP_NUM_SELECT_CB,       sizeof(struct lwip_select_cb), "SELECT_CB")
-#endif /* LWIP_SOCKET && LWIP_SOCKET_SELECT */
+#endif /* LWIP_SOCKET && (LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL) */
 #if LWIP_NETIF_API
 #if LWIP_NETIF_API
 LWIP_MEMPOOL(NETIFAPI_MSG,   MEMP_NUM_NETIFAPI_MSG,    sizeof(struct netifapi_msg),   "NETIFAPI_MSG")
 LWIP_MEMPOOL(NETIFAPI_MSG,   MEMP_NUM_NETIFAPI_MSG,    sizeof(struct netifapi_msg),   "NETIFAPI_MSG")
 #endif
 #endif

+ 13 - 2
lwip/src/include/lwip/priv/sockets_priv.h

@@ -69,7 +69,7 @@ struct lwip_sock {
   struct netconn *conn;
   struct netconn *conn;
   /** data that was left from the previous read */
   /** data that was left from the previous read */
   union lwip_sock_lastdata lastdata;
   union lwip_sock_lastdata lastdata;
-#if LWIP_SOCKET_SELECT
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
   /** number of times data was received, set by event_callback(),
   /** number of times data was received, set by event_callback(),
       tested by the receive and select functions */
       tested by the receive and select functions */
   s16_t rcvevent;
   s16_t rcvevent;
@@ -80,7 +80,7 @@ struct lwip_sock {
   u16_t errevent;
   u16_t errevent;
   /** counter of how many threads are waiting for this socket using select */
   /** counter of how many threads are waiting for this socket using select */
   SELWAIT_T select_waiting;
   SELWAIT_T select_waiting;
-#endif /* LWIP_SOCKET_SELECT */
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
 #if LWIP_NETCONN_FULLDUPLEX
 #if LWIP_NETCONN_FULLDUPLEX
   /* counter of how many threads are using a struct lwip_sock (not the 'int') */
   /* counter of how many threads are using a struct lwip_sock (not the 'int') */
   u8_t fd_used;
   u8_t fd_used;
@@ -133,6 +133,8 @@ struct lwip_setgetsockopt_data {
 
 
 struct lwip_sock* lwip_socket_dbg_get_socket(int fd);
 struct lwip_sock* lwip_socket_dbg_get_socket(int fd);
 
 
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+
 #if LWIP_NETCONN_SEM_PER_THREAD
 #if LWIP_NETCONN_SEM_PER_THREAD
 #define SELECT_SEM_T        sys_sem_t*
 #define SELECT_SEM_T        sys_sem_t*
 #define SELECT_SEM_PTR(sem) (sem)
 #define SELECT_SEM_PTR(sem) (sem)
@@ -147,17 +149,26 @@ struct lwip_select_cb {
   struct lwip_select_cb *next;
   struct lwip_select_cb *next;
   /** Pointer to the previous waiting task */
   /** Pointer to the previous waiting task */
   struct lwip_select_cb *prev;
   struct lwip_select_cb *prev;
+#if LWIP_SOCKET_SELECT
   /** readset passed to select */
   /** readset passed to select */
   fd_set *readset;
   fd_set *readset;
   /** writeset passed to select */
   /** writeset passed to select */
   fd_set *writeset;
   fd_set *writeset;
   /** unimplemented: exceptset passed to select */
   /** unimplemented: exceptset passed to select */
   fd_set *exceptset;
   fd_set *exceptset;
+#endif /* LWIP_SOCKET_SELECT */
+#if LWIP_SOCKET_POLL
+  /** fds passed to poll; NULL if select */
+  struct pollfd *poll_fds;
+  /** nfds passed to poll; 0 if select */
+  nfds_t poll_nfds;
+#endif /* LWIP_SOCKET_POLL */
   /** don't signal the same semaphore twice: set to 1 when signalled */
   /** don't signal the same semaphore twice: set to 1 when signalled */
   int sem_signalled;
   int sem_signalled;
   /** semaphore to wake up a task waiting for select */
   /** semaphore to wake up a task waiting for select */
   SELECT_SEM_T sem;
   SELECT_SEM_T sem;
 };
 };
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
 
 
 #endif /* LWIP_SOCKET */
 #endif /* LWIP_SOCKET */
 
 

+ 94 - 30
lwip/src/include/lwip/prot/ip6.h

@@ -94,13 +94,50 @@ PACK_STRUCT_END
 #ifdef PACK_STRUCT_USE_INCLUDES
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/epstruct.h"
 #  include "arch/epstruct.h"
 #endif
 #endif
+#define IP6H_V(hdr)  ((lwip_ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f)
+#define IP6H_TC(hdr) ((lwip_ntohl((hdr)->_v_tc_fl) >> 20) & 0xff)
+#define IP6H_FL(hdr) (lwip_ntohl((hdr)->_v_tc_fl) & 0x000fffff)
+#define IP6H_PLEN(hdr) (lwip_ntohs((hdr)->_plen))
+#define IP6H_NEXTH(hdr) ((hdr)->_nexth)
+#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6)
+#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim)
+#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (lwip_htonl((((u32_t)(v)) << 28) | (((u32_t)(tc)) << 20) | (fl)))
+#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = lwip_htons(plen)
+#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth)
+#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl)
+
+/* ipv6 extended options header */
+#define IP6_PAD1_OPTION             0
+#define IP6_PADN_OPTION             1
+#define IP6_ROUTER_ALERT_OPTION     5
+#define IP6_JUMBO_OPTION            194
+#define IP6_HOME_ADDRESS_OPTION     201
+#define IP6_ROUTER_ALERT_DLEN       2
+#define IP6_ROUTER_ALERT_VALUE_MLD  0
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_opt_hdr {
+  /* router alert option type */
+  PACK_STRUCT_FLD_8(u8_t _opt_type);
+  /* router alert option data len */
+  PACK_STRUCT_FLD_8(u8_t _opt_dlen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define IP6_OPT_HLEN 2
+#define IP6_OPT_TYPE_ACTION(hdr) ((((hdr)->_opt_type) >> 6) & 0x3)
+#define IP6_OPT_TYPE_CHANGE(hdr) ((((hdr)->_opt_type) >> 5) & 0x1)
+#define IP6_OPT_TYPE(hdr) ((hdr)->_opt_type)
+#define IP6_OPT_DLEN(hdr) ((hdr)->_opt_dlen)
+
+/* Hop-by-Hop header. */
+#define IP6_HBH_HLEN    2
 
 
-/* Hop-by-hop router alert option. */
-#define IP6_HBH_HLEN    8
-#define IP6_PAD1_OPTION         0
-#define IP6_PADN_ALERT_OPTION   1
-#define IP6_ROUTER_ALERT_OPTION 5
-#define IP6_ROUTER_ALERT_VALUE_MLD 0
 #ifdef PACK_STRUCT_USE_INCLUDES
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/bpstruct.h"
 #  include "arch/bpstruct.h"
 #endif
 #endif
@@ -108,28 +145,65 @@ PACK_STRUCT_BEGIN
 struct ip6_hbh_hdr {
 struct ip6_hbh_hdr {
   /* next header */
   /* next header */
   PACK_STRUCT_FLD_8(u8_t _nexth);
   PACK_STRUCT_FLD_8(u8_t _nexth);
-  /* header length */
+  /* header length in 8-octet units */
   PACK_STRUCT_FLD_8(u8_t _hlen);
   PACK_STRUCT_FLD_8(u8_t _hlen);
-  /* router alert option type */
-  PACK_STRUCT_FLD_8(u8_t _ra_opt_type);
-  /* router alert option data len */
-  PACK_STRUCT_FLD_8(u8_t _ra_opt_dlen);
-  /* router alert option data */
-  PACK_STRUCT_FIELD(u16_t _ra_opt_data);
-  /* PadN option type */
-  PACK_STRUCT_FLD_8(u8_t _padn_opt_type);
-  /* PadN option data len */
-  PACK_STRUCT_FLD_8(u8_t _padn_opt_dlen);
 } PACK_STRUCT_STRUCT;
 } PACK_STRUCT_STRUCT;
 PACK_STRUCT_END
 PACK_STRUCT_END
 #ifdef PACK_STRUCT_USE_INCLUDES
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/epstruct.h"
 #  include "arch/epstruct.h"
 #endif
 #endif
+#define IP6_HBH_NEXTH(hdr) ((hdr)->_nexth)
+
+/* Destination header. */
+#define IP6_DEST_HLEN   2
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_dest_hdr {
+  /* next header */
+  PACK_STRUCT_FLD_8(u8_t _nexth);
+  /* header length in 8-octet units */
+  PACK_STRUCT_FLD_8(u8_t _hlen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define IP6_DEST_NEXTH(hdr) ((hdr)->_nexth)
+
+/* Routing header */
+#define IP6_ROUT_TYPE2  2
+#define IP6_ROUT_RPL    3
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_rout_hdr {
+  /* next header */
+  PACK_STRUCT_FLD_8(u8_t _nexth);
+  /* reserved */
+  PACK_STRUCT_FLD_8(u8_t _hlen);
+  /* fragment offset */
+  PACK_STRUCT_FIELD(u8_t _routing_type);
+  /* fragmented packet identification */
+  PACK_STRUCT_FIELD(u8_t _segments_left);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define IP6_ROUT_NEXTH(hdr) ((hdr)->_nexth)
+#define IP6_ROUT_TYPE(hdr) ((hdr)->_routing_type)
+#define IP6_ROUT_SEG_LEFT(hdr) ((hdr)->_segments_left)
 
 
 /* Fragment header. */
 /* Fragment header. */
 #define IP6_FRAG_HLEN    8
 #define IP6_FRAG_HLEN    8
 #define IP6_FRAG_OFFSET_MASK    0xfff8
 #define IP6_FRAG_OFFSET_MASK    0xfff8
 #define IP6_FRAG_MORE_FLAG      0x0001
 #define IP6_FRAG_MORE_FLAG      0x0001
+
 #ifdef PACK_STRUCT_USE_INCLUDES
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/bpstruct.h"
 #  include "arch/bpstruct.h"
 #endif
 #endif
@@ -148,19 +222,9 @@ PACK_STRUCT_END
 #ifdef PACK_STRUCT_USE_INCLUDES
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/epstruct.h"
 #  include "arch/epstruct.h"
 #endif
 #endif
-
-#define IP6H_V(hdr)  ((lwip_ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f)
-#define IP6H_TC(hdr) ((lwip_ntohl((hdr)->_v_tc_fl) >> 20) & 0xff)
-#define IP6H_FL(hdr) (lwip_ntohl((hdr)->_v_tc_fl) & 0x000fffff)
-#define IP6H_PLEN(hdr) (lwip_ntohs((hdr)->_plen))
-#define IP6H_NEXTH(hdr) ((hdr)->_nexth)
-#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6)
-#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim)
-
-#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (lwip_htonl((((u32_t)(v)) << 28) | (((u32_t)(tc)) << 20) | (fl)))
-#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = lwip_htons(plen)
-#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth)
-#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl)
+#define IP6_FRAG_NEXTH(hdr) ((hdr)->_nexth)
+#define IP6_FRAG_MBIT(hdr) (lwip_ntohs((hdr)->_fragment_offset) & 0x1)
+#define IP6_FRAG_ID(hdr) (lwip_ntohl((hdr)->_identification))
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 1 - 0
lwip/src/include/lwip/prot/mld6.h

@@ -44,6 +44,7 @@
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
+#define MLD6_HBH_HLEN 8
 /** Multicast listener report/query/done message header. */
 /** Multicast listener report/query/done message header. */
 #ifdef PACK_STRUCT_USE_INCLUDES
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/bpstruct.h"
 #  include "arch/bpstruct.h"

+ 34 - 1
lwip/src/include/lwip/sockets.h

@@ -486,6 +486,23 @@ typedef struct fd_set
 #error "external FD_SETSIZE too small for number of sockets"
 #error "external FD_SETSIZE too small for number of sockets"
 #endif /* FD_SET */
 #endif /* FD_SET */
 
 
+/* poll-related defines and types */
+/* @todo: find a better way to guard the definition of these defines and types if already defined */
+#if !defined(POLLIN) && !defined(POLLOUT)
+#define POLLIN   1
+#define POLLOUT  2
+#define POLLERR  4
+#define POLLNVAL 8
+/* No support for POLLPRI, POLLHUP, POLLMSG, POLLRDBAND, POLLWRBAND. */
+typedef int nfds_t;
+struct pollfd
+{
+  int fd;
+  short events;
+  short revents;
+};
+#endif
+
 /** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided
 /** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided
  * by your system, set this to 0 and include <sys/time.h> in cc.h */
  * by your system, set this to 0 and include <sys/time.h> in cc.h */
 #ifndef LWIP_TIMEVAL_PRIVATE
 #ifndef LWIP_TIMEVAL_PRIVATE
@@ -522,8 +539,13 @@ void lwip_socket_thread_cleanup(void); /* LWIP_NETCONN_SEM_PER_THREAD==1: destro
 #define lwip_sendmsg      sendmsg
 #define lwip_sendmsg      sendmsg
 #define lwip_sendto       sendto
 #define lwip_sendto       sendto
 #define lwip_socket       socket
 #define lwip_socket       socket
+#if LWIP_SOCKET_SELECT
 #define lwip_select       select
 #define lwip_select       select
-#define lwip_ioctlsocket  ioctl
+#endif
+#if LWIP_SOCKET_POLL
+#define lwip_poll         poll
+#endif
+#define lwip_ioctl        ioctlsocket
 #define lwip_inet_ntop    inet_ntop
 #define lwip_inet_ntop    inet_ntop
 #define lwip_inet_pton    inet_pton
 #define lwip_inet_pton    inet_pton
 
 
@@ -536,7 +558,9 @@ void lwip_socket_thread_cleanup(void); /* LWIP_NETCONN_SEM_PER_THREAD==1: destro
 #define lwip_close        close
 #define lwip_close        close
 #define closesocket(s)    close(s)
 #define closesocket(s)    close(s)
 int fcntl(int s, int cmd, ...);
 int fcntl(int s, int cmd, ...);
+#undef lwip_ioctl
 #define lwip_ioctl        ioctl
 #define lwip_ioctl        ioctl
+#define ioctlsocket       ioctl
 #endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
 #endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
 #endif /* LWIP_COMPAT_SOCKETS == 2 */
 #endif /* LWIP_COMPAT_SOCKETS == 2 */
 
 
@@ -567,6 +591,9 @@ ssize_t lwip_writev(int s, const struct iovec *iov, int iovcnt);
 int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
 int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
                 struct timeval *timeout);
                 struct timeval *timeout);
 #endif
 #endif
+#if LWIP_SOCKET_POLL
+int lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout);
+#endif
 int lwip_ioctl(int s, long cmd, void *argp);
 int lwip_ioctl(int s, long cmd, void *argp);
 int lwip_fcntl(int s, int cmd, int val);
 int lwip_fcntl(int s, int cmd, int val);
 const char *lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size);
 const char *lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size);
@@ -608,8 +635,14 @@ int lwip_inet_pton(int af, const char *src, void *dst);
 #define sendto(s,dataptr,size,flags,to,tolen)     lwip_sendto(s,dataptr,size,flags,to,tolen)
 #define sendto(s,dataptr,size,flags,to,tolen)     lwip_sendto(s,dataptr,size,flags,to,tolen)
 /** @ingroup socket */
 /** @ingroup socket */
 #define socket(domain,type,protocol)              lwip_socket(domain,type,protocol)
 #define socket(domain,type,protocol)              lwip_socket(domain,type,protocol)
+#if LWIP_SOCKET_SELECT
 /** @ingroup socket */
 /** @ingroup socket */
 #define select(maxfdp1,readset,writeset,exceptset,timeout)     lwip_select(maxfdp1,readset,writeset,exceptset,timeout)
 #define select(maxfdp1,readset,writeset,exceptset,timeout)     lwip_select(maxfdp1,readset,writeset,exceptset,timeout)
+#endif
+#if LWIP_SOCKET_POLL
+/** @ingroup socket */
+#define poll(fds,nfds,timeout)                    lwip_poll(fds,nfds,timeout)
+#endif
 /** @ingroup socket */
 /** @ingroup socket */
 #define ioctlsocket(s,cmd,argp)                   lwip_ioctl(s,cmd,argp)
 #define ioctlsocket(s,cmd,argp)                   lwip_ioctl(s,cmd,argp)
 /** @ingroup socket */
 /** @ingroup socket */

+ 102 - 1
lwip/test/unit/core/test_mem.c

@@ -111,13 +111,114 @@ START_TEST(test_mem_random)
 }
 }
 END_TEST
 END_TEST
 
 
+START_TEST(test_mem_invalid_free)
+{
+  u8_t *ptr, *ptr_low, *ptr_high;
+  LWIP_UNUSED_ARG(_i);
+
+  fail_unless(lwip_stats.mem.used == 0);
+  fail_unless(lwip_stats.mem.illegal == 0);
+
+  ptr = (u8_t *)mem_malloc(1);
+  fail_unless(ptr != NULL);
+  fail_unless(lwip_stats.mem.used != 0);
+
+  ptr_low = ptr - 0x10;
+  mem_free(ptr_low);
+  fail_unless(lwip_stats.mem.illegal == 1);
+  lwip_stats.mem.illegal = 0;
+
+  ptr_high = ptr + (MEM_SIZE * 2);
+  mem_free(ptr_high);
+  fail_unless(lwip_stats.mem.illegal == 1);
+  lwip_stats.mem.illegal = 0;
+
+  mem_free(ptr);
+  fail_unless(lwip_stats.mem.illegal == 0);
+  fail_unless(lwip_stats.mem.used == 0);
+}
+END_TEST
+
+START_TEST(test_mem_double_free)
+{
+  u8_t *ptr1b, *ptr1, *ptr2, *ptr3;
+  LWIP_UNUSED_ARG(_i);
+
+  fail_unless(lwip_stats.mem.used == 0);
+  fail_unless(lwip_stats.mem.illegal == 0);
+
+  ptr1 = (u8_t *)mem_malloc(1);
+  fail_unless(ptr1 != NULL);
+  fail_unless(lwip_stats.mem.used != 0);
+
+  ptr2 = (u8_t *)mem_malloc(1);
+  fail_unless(ptr2 != NULL);
+  fail_unless(lwip_stats.mem.used != 0);
+
+  ptr3 = (u8_t *)mem_malloc(1);
+  fail_unless(ptr3 != NULL);
+  fail_unless(lwip_stats.mem.used != 0);
+
+  /* free the middle mem */
+  mem_free(ptr2);
+  fail_unless(lwip_stats.mem.illegal == 0);
+
+  /* double-free of middle mem: should fail */
+  mem_free(ptr2);
+  fail_unless(lwip_stats.mem.illegal == 1);
+  lwip_stats.mem.illegal = 0;
+
+  /* free upper memory and try again */
+  mem_free(ptr3);
+  fail_unless(lwip_stats.mem.illegal == 0);
+
+  mem_free(ptr2);
+  fail_unless(lwip_stats.mem.illegal == 1);
+  lwip_stats.mem.illegal = 0;
+
+  /* free lower memory and try again */
+  mem_free(ptr1);
+  fail_unless(lwip_stats.mem.illegal == 0);
+  fail_unless(lwip_stats.mem.used == 0);
+
+  mem_free(ptr2);
+  fail_unless(lwip_stats.mem.illegal == 1);
+  fail_unless(lwip_stats.mem.used == 0);
+  lwip_stats.mem.illegal = 0;
+
+  /* reallocate lowest memory, now overlapping already freed ptr2 */
+#ifndef MIN_SIZE
+#define MIN_SIZE 12
+#endif
+  ptr1b = (u8_t *)mem_malloc(MIN_SIZE * 2);
+  fail_unless(ptr1b != NULL);
+  fail_unless(lwip_stats.mem.used != 0);
+
+  mem_free(ptr2);
+  fail_unless(lwip_stats.mem.illegal == 1);
+  lwip_stats.mem.illegal = 0;
+
+  memset(ptr1b, 1, MIN_SIZE * 2);
+
+  mem_free(ptr2);
+  fail_unless(lwip_stats.mem.illegal == 1);
+  lwip_stats.mem.illegal = 0;
+
+  mem_free(ptr1b);
+  fail_unless(lwip_stats.mem.illegal == 0);
+  fail_unless(lwip_stats.mem.used == 0);
+}
+END_TEST
+
 /** Create the suite including all tests for this module */
 /** Create the suite including all tests for this module */
 Suite *
 Suite *
 mem_suite(void)
 mem_suite(void)
 {
 {
   testfunc tests[] = {
   testfunc tests[] = {
     TESTFUNC(test_mem_one),
     TESTFUNC(test_mem_one),
-    TESTFUNC(test_mem_random)
+    TESTFUNC(test_mem_random),
+    TESTFUNC(test_mem_invalid_free),
+    TESTFUNC(test_mem_double_free)
   };
   };
   return create_suite("MEM", tests, sizeof(tests)/sizeof(testfunc), mem_setup, mem_teardown);
   return create_suite("MEM", tests, sizeof(tests)/sizeof(testfunc), mem_setup, mem_teardown);
 }
 }

+ 3 - 0
lwip/test/unit/lwipopts.h

@@ -70,4 +70,7 @@
 /* MIB2 stats are required to check IPv4 reassembly results */
 /* MIB2 stats are required to check IPv4 reassembly results */
 #define MIB2_STATS                      1
 #define MIB2_STATS                      1
 
 
+/* Check lwip_stats.mem.illegal instead of asserting */
+#define LWIP_MEM_ILLEGAL_FREE(msg)      /* to nothing */
+
 #endif /* LWIP_HDR_LWIPOPTS_H */
 #endif /* LWIP_HDR_LWIPOPTS_H */