Преглед изворни кода

Update lwip to 1710fc1a89578dfaaff684a1aafbc4d16e346f79

Ambroz Bizjak пре 8 година
родитељ
комит
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 */
 static struct lwip_sock sockets[NUM_SOCKETS];
 
-#if LWIP_SOCKET_SELECT
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
 #if LWIP_TCPIP_CORE_LOCKING
 /* protect the select_cb_list using core lock */
 #define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)
@@ -279,7 +279,7 @@ static volatile int select_cb_ctr;
 #endif /* LWIP_TCPIP_CORE_LOCKING */
 /** The global list of tasks waiting for select */
 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 { \
   const int sockerr = (e); \
@@ -287,10 +287,10 @@ static struct lwip_select_cb *select_cb_list;
 } while (0)
 
 /* 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);
 #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
 #define DEFAULT_SOCKET_EVENTCB NULL
 #endif
@@ -1692,6 +1692,57 @@ lwip_writev(int s, const struct iovec *iov, int iovcnt)
   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
 /**
  * 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;
 #endif
   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",
                               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
          mode). */
       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).writeset = writeset;
       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
       API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
 #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 */
 
-      /* 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 */
       maxfdp2 = maxfdp1;
@@ -2012,23 +2045,8 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
           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 (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;
 }
+#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.
  * 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;
     SYS_ARCH_UNPROTECT(lev);
     /* 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 {
     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
  * 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;
 #if !LWIP_TCPIP_CORE_LOCKING
@@ -2200,22 +2520,38 @@ again:
     if (scb->sem_signalled == 0) {
       /* semaphore not signalled yet */
       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) {
         scb->sem_signalled = 1;
         /* For !LWIP_TCPIP_CORE_LOCKING, we don't call SYS_ARCH_UNPROTECT() before signaling
@@ -2241,7 +2577,7 @@ again:
   SYS_ARCH_UNPROTECT(lev);
 #endif
 }
-#endif /* LWIP_SOCKET_SELECT */
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
 
 /**
  * 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);
 
         if (ret == 0) {
-#if (defined _MSC_VER || defined __MINGW32__)
+#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
           size_t   i;
           char currName[256];
           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 (!file.is_dir) {
-#if (defined _MSC_VER || defined __MINGW32__)
+#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
             size_t   i;
             char curName[256];
             wcstombs_s(&i, curName, sizeof(curName), file.name, sizeof(curName));
 #else
-            const char *currName = file.name;
+            const char *curName = file.name;
 #endif
 
             if (strcmp(curName, "fsdata.tmp") == 0) {

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

@@ -71,9 +71,11 @@
 #define SNTP_SUPPORT_MULTIPLE_SERVERS 0
 #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)!"
 #endif
+#endif
 
 /* the various debug levels for this file */
 #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)*/
     if (sntp_opmode == SNTP_OPMODE_POLL) {
+      u32_t sntp_update_delay;
       sys_untimeout(sntp_try_next_server, NULL);
       sys_untimeout(sntp_request, NULL);
 
       /* Correct response, 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",
-                                     (u32_t)SNTP_UPDATE_DELAY));
+                                     sntp_update_delay));
     }
   } else if (err == SNTP_ERR_KOD) {
     /* 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 */
+
+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
 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);
   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

+ 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
  * 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
 #define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
 #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.
  *
  * 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,
  *          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
  */
 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 netif *netif;
-  u8_t nexth;
+  const u8_t *nexth;
   u16_t hlen, hlen_tot; /* the current header length */
 #if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/
   @todo
@@ -695,7 +695,7 @@ netif_found:
   ip_data.current_netif = netif;
 
   /* Save next header type. */
-  nexth = IP6H_NEXTH(ip6hdr);
+  nexth = &IP6H_NEXTH(ip6hdr);
 
   /* Init header length. */
   hlen = hlen_tot = IP6_HLEN;
@@ -704,13 +704,25 @@ netif_found:
   pbuf_remove_header(p, IP6_HLEN);
 
   /* 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:
+    {
+      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"));
+
       /* 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)) {
         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",
@@ -724,17 +736,82 @@ netif_found:
 
       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);
       break;
+    }
     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"));
 
-      /* 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)) {
         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",
@@ -748,17 +825,87 @@ netif_found:
 
       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);
       break;
+    }
     case IP6_NEXTH_ROUTING:
+    {
+      struct ip6_rout_hdr *rout_hdr;
       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)) {
         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",
@@ -770,20 +917,51 @@ netif_found:
         goto ip6_input_cleanup;
       }
 
-      /* Get next header type. */
-      nexth = *((u8_t *)p->payload);
-
       /* Skip over this header. */
       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);
       break;
-
+    }
     case IP6_NEXTH_FRAGMENT:
     {
       struct ip6_frag_hdr *frag_hdr;
       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. */
       hlen = 8;
 
@@ -801,10 +979,15 @@ netif_found:
 
       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? */
       if ((frag_hdr->_fragment_offset &
@@ -813,7 +996,6 @@ netif_found:
         pbuf_remove_header(p, hlen);
       } else {
 #if LWIP_IPV6_REASS
-
         /* reassemble the packet */
         ip_data.current_ip_header_tot_len = hlen_tot;
         p = ip6_reass(p);
@@ -825,7 +1007,7 @@ netif_found:
         /* Returned p point to IPv6 header.
          * Update all our variables and pointers and continue. */
         ip6hdr = (struct ip6_hdr *)p->payload;
-        nexth = IP6H_NEXTH(ip6hdr);
+        nexth = &IP6H_NEXTH(ip6hdr);
         hlen = hlen_tot = IP6_HLEN;
         pbuf_remove_header(p, IP6_HLEN);
 
@@ -843,9 +1025,18 @@ netif_found:
     default:
       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) {
     /* s16_t overflow */
     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 */
   {
 #endif /* LWIP_RAW */
-    switch (nexth) {
+    switch (*nexth) {
     case IP6_NEXTH_NONE:
       pbuf_free(p);
       break;
@@ -902,7 +1093,7 @@ options_done:
       /* send ICMP parameter problem unless it was a multicast or ICMPv6 */
       if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
           (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 */
       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
 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_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. */
-  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"));
     IP6_STATS_INC(ip6.err);
     return ERR_BUF;
   }
 
+  /* Set fields of Hop-by-Hop header */
   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->_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;
 }

+ 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;
 
   /* 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) {
     MLD6_STATS_INC(mld6.memerr);
     return;
   }
 
   /* 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);
     MLD6_STATS_INC(mld6.lenerr);
     return;

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

@@ -66,6 +66,11 @@
 #include <stdlib.h> /* for malloc()/free() */
 #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_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))
@@ -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
  *
@@ -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"));
     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"));
     /* protect mem stats from concurrent access */
     MEM_STATS_INC_LOCKED(illegal);
@@ -442,12 +474,26 @@ mem_free(void *rmem)
   }
   /* protect the heap from concurrent access */
   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;
 
   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
 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);
-#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);
-#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+#endif /* TCP_OOSEQ_BYTES_LIMIT || TCP_OOSEQ_PBUFS_LIMIT */
 #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
 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;
   u32_t right_wnd_edge;
   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);
 
@@ -1481,21 +1471,22 @@ tcp_receive(struct tcp_pcb *pcb)
               tcp_seg_free(old_ooseq);
             }
           } else {
-            next = pcb->ooseq;
+            struct tcp_seg *next = pcb->ooseq;
             /* Remove all segments on ooseq that are covered by inseg already.
              * FIN is copied from ooseq to inseg if present. */
             while (next &&
                    TCP_SEQ_GEQ(seqno + tcplen,
                                next->tcphdr->seqno + next->len)) {
+              struct tcp_seg *tmp;
               /* inseg cannot have FIN here (already processed above) */
               if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 &&
                   (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
                 TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
                 tcplen = TCP_TCPLEN(&inseg);
               }
-              prev = next;
+              tmp = next;
               next = next->next;
-              tcp_seg_free(prev);
+              tcp_seg_free(tmp);
             }
             /* Now trim right side of inseg if it overlaps with the first
              * segment on ooseq */
@@ -1552,7 +1543,7 @@ tcp_receive(struct tcp_pcb *pcb)
         while (pcb->ooseq != NULL &&
                pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
 
-          cseg = pcb->ooseq;
+          struct tcp_seg *cseg = pcb->ooseq;
           seqno = pcb->ooseq->tcphdr->seqno;
 
           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). */
           u32_t sackbeg = TCP_SEQ_LT(seqno, pcb->ooseq->tcphdr->seqno) ? seqno : pcb->ooseq->tcphdr->seqno;
 #endif /* LWIP_TCP_SACK_OUT */
-          prev = NULL;
+          struct tcp_seg *next, *prev = NULL;
           for (next = pcb->ooseq; next != NULL; next = next->next) {
             if (seqno == next->tcphdr->seqno) {
               /* The sequence number of the incoming segment is the
@@ -1664,7 +1655,7 @@ tcp_receive(struct tcp_pcb *pcb)
                 /* The incoming segment is larger than the old
                    segment. We replace some segments with the new
                    one. */
-                cseg = tcp_seg_copy(&inseg);
+                struct tcp_seg *cseg = tcp_seg_copy(&inseg);
                 if (cseg != NULL) {
                   if (prev != NULL) {
                     prev->next = cseg;
@@ -1687,7 +1678,7 @@ tcp_receive(struct tcp_pcb *pcb)
                      than the sequence number of the first segment on the
                      queue. We put the incoming segment first on the
                      queue. */
-                  cseg = tcp_seg_copy(&inseg);
+                  struct tcp_seg *cseg = tcp_seg_copy(&inseg);
                   if (cseg != NULL) {
                     pcb->ooseq = cseg;
                     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
                      segment, delete next segments that included in received segment
                      and trim received, if needed. */
-                  cseg = tcp_seg_copy(&inseg);
+                  struct tcp_seg *cseg = tcp_seg_copy(&inseg);
                   if (cseg != NULL) {
                     if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
                       /* We need to trim the prev segment. */
@@ -1796,37 +1787,55 @@ tcp_receive(struct tcp_pcb *pcb)
           }
 #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 */
-            /* 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 */
 
         /* 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.
  *
@@ -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;
   }
 }
-#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+#endif /* TCP_OOSEQ_BYTES_LIMIT || TCP_OOSEQ_PBUFS_LIMIT */
 
 #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);
     }
 
-#if TCP_OVERSIZE_DBGCHECK
-    seg->oversize_left = 0;
-#endif /* TCP_OVERSIZE_DBGCHECK */
     err = tcp_output_segment(seg, pcb, netif);
     if (err != ERR_OK) {
       /* segment could not be sent, for whatever reason */
       tcp_set_flags(pcb, TF_NAGLEMEMERR);
       return err;
     }
+#if TCP_OVERSIZE_DBGCHECK
+    seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
     pcb->unsent = seg->next;
     if (pcb->state != SYN_SENT) {
       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"
 
-#ifdef	__cplusplus
+#ifdef __cplusplus
 extern "C" {
 #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_with_addrs(struct pbuf *p, enum icmp6_te_code c,
     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 */
 

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

@@ -1300,21 +1300,51 @@
 #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__
 #define TCP_OOSEQ_MAX_BYTES             0
 #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__
 #define TCP_OOSEQ_MAX_PBUFS             0
 #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.
  */
@@ -1986,6 +2016,14 @@
 #if !defined LWIP_SOCKET_SELECT || defined __DOXYGEN__
 #define LWIP_SOCKET_SELECT              1
 #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
 LWIP_MEMPOOL(SOCKET_SETGETSOCKOPT_DATA, MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA, sizeof(struct lwip_setgetsockopt_data), "SOCKET_SETGETSOCKOPT_DATA")
 #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")
-#endif /* LWIP_SOCKET && LWIP_SOCKET_SELECT */
+#endif /* LWIP_SOCKET && (LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL) */
 #if LWIP_NETIF_API
 LWIP_MEMPOOL(NETIFAPI_MSG,   MEMP_NUM_NETIFAPI_MSG,    sizeof(struct netifapi_msg),   "NETIFAPI_MSG")
 #endif

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

@@ -69,7 +69,7 @@ struct lwip_sock {
   struct netconn *conn;
   /** data that was left from the previous read */
   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(),
       tested by the receive and select functions */
   s16_t rcvevent;
@@ -80,7 +80,7 @@ struct lwip_sock {
   u16_t errevent;
   /** counter of how many threads are waiting for this socket using select */
   SELWAIT_T select_waiting;
-#endif /* LWIP_SOCKET_SELECT */
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
 #if LWIP_NETCONN_FULLDUPLEX
   /* counter of how many threads are using a struct lwip_sock (not the 'int') */
   u8_t fd_used;
@@ -133,6 +133,8 @@ struct lwip_setgetsockopt_data {
 
 struct lwip_sock* lwip_socket_dbg_get_socket(int fd);
 
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+
 #if LWIP_NETCONN_SEM_PER_THREAD
 #define SELECT_SEM_T        sys_sem_t*
 #define SELECT_SEM_PTR(sem) (sem)
@@ -147,17 +149,26 @@ struct lwip_select_cb {
   struct lwip_select_cb *next;
   /** Pointer to the previous waiting task */
   struct lwip_select_cb *prev;
+#if LWIP_SOCKET_SELECT
   /** readset passed to select */
   fd_set *readset;
   /** writeset passed to select */
   fd_set *writeset;
   /** unimplemented: exceptset passed to select */
   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 */
   int sem_signalled;
   /** semaphore to wake up a task waiting for select */
   SELECT_SEM_T sem;
 };
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
 
 #endif /* LWIP_SOCKET */
 

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

@@ -94,13 +94,50 @@ PACK_STRUCT_END
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/epstruct.h"
 #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
 #  include "arch/bpstruct.h"
 #endif
@@ -108,28 +145,65 @@ PACK_STRUCT_BEGIN
 struct ip6_hbh_hdr {
   /* next header */
   PACK_STRUCT_FLD_8(u8_t _nexth);
-  /* header length */
+  /* header length in 8-octet units */
   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_END
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/epstruct.h"
 #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. */
 #define IP6_FRAG_HLEN    8
 #define IP6_FRAG_OFFSET_MASK    0xfff8
 #define IP6_FRAG_MORE_FLAG      0x0001
+
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/bpstruct.h"
 #endif
@@ -148,19 +222,9 @@ PACK_STRUCT_END
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/epstruct.h"
 #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
 }

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

@@ -44,6 +44,7 @@
 extern "C" {
 #endif
 
+#define MLD6_HBH_HLEN 8
 /** Multicast listener report/query/done message header. */
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  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"
 #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
  * by your system, set this to 0 and include <sys/time.h> in cc.h */
 #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_sendto       sendto
 #define lwip_socket       socket
+#if LWIP_SOCKET_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_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 closesocket(s)    close(s)
 int fcntl(int s, int cmd, ...);
+#undef lwip_ioctl
 #define lwip_ioctl        ioctl
+#define ioctlsocket       ioctl
 #endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
 #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,
                 struct timeval *timeout);
 #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_fcntl(int s, int cmd, int val);
 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)
 /** @ingroup socket */
 #define socket(domain,type,protocol)              lwip_socket(domain,type,protocol)
+#if LWIP_SOCKET_SELECT
 /** @ingroup socket */
 #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 */
 #define ioctlsocket(s,cmd,argp)                   lwip_ioctl(s,cmd,argp)
 /** @ingroup socket */

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

@@ -111,13 +111,114 @@ START_TEST(test_mem_random)
 }
 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 */
 Suite *
 mem_suite(void)
 {
   testfunc tests[] = {
     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);
 }

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

@@ -70,4 +70,7 @@
 /* MIB2 stats are required to check IPv4 reassembly results */
 #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 */