Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ambrop72/badvpn.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmbroz Bizjak <ambrop7@gmail.com>2017-09-26 00:27:28 +0300
committerAmbroz Bizjak <ambrop7@gmail.com>2017-09-26 00:27:28 +0300
commit79efa642ea431cbb207b8bff52412b942b6a75e3 (patch)
tree6d8a2ca60760e9440205ea1634dd26f520e97860
parentdce31a8e6a68d518a0fbed6bc644d14f2d548785 (diff)
Update lwip to 1710fc1a89578dfaaff684a1aafbc4d16e346f79
-rw-r--r--lwip/lwip-base-version2
-rw-r--r--lwip/src/api/sockets.c450
-rw-r--r--lwip/src/apps/httpd/makefsdata/makefsdata.c6
-rw-r--r--lwip/src/apps/sntp/sntp.c10
-rw-r--r--lwip/src/core/altcp_tcp.c28
-rw-r--r--lwip/src/core/ipv4/icmp.c2
-rw-r--r--lwip/src/core/ipv6/icmp6.c8
-rw-r--r--lwip/src/core/ipv6/ip6.c282
-rw-r--r--lwip/src/core/ipv6/mld6.c4
-rw-r--r--lwip/src/core/mem.c66
-rw-r--r--lwip/src/core/tcp_in.c105
-rw-r--r--lwip/src/core/tcp_out.c6
-rw-r--r--lwip/src/include/lwip/apps/mqtt_opts.h2
-rw-r--r--lwip/src/include/lwip/icmp6.h2
-rw-r--r--lwip/src/include/lwip/opt.h46
-rw-r--r--lwip/src/include/lwip/priv/memp_std.h4
-rw-r--r--lwip/src/include/lwip/priv/sockets_priv.h15
-rw-r--r--lwip/src/include/lwip/prot/ip6.h124
-rw-r--r--lwip/src/include/lwip/prot/mld6.h1
-rw-r--r--lwip/src/include/lwip/sockets.h35
-rw-r--r--lwip/test/unit/core/test_mem.c103
-rw-r--r--lwip/test/unit/lwipopts.h3
22 files changed, 1093 insertions, 211 deletions
diff --git a/lwip/lwip-base-version b/lwip/lwip-base-version
index ce5f390..dde8268 100644
--- a/lwip/lwip-base-version
+++ b/lwip/lwip-base-version
@@ -1 +1 @@
-931b5e643c25820a99bb8df94ab37db6b58c446b
+1710fc1a89578dfaaff684a1aafbc4d16e346f79
diff --git a/lwip/src/api/sockets.c b/lwip/src/api/sockets.c
index 8cb096d..8b7370d 100644
--- a/lwip/src/api/sockets.c
+++ b/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,8 +2090,310 @@ 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.
diff --git a/lwip/src/apps/httpd/makefsdata/makefsdata.c b/lwip/src/apps/httpd/makefsdata/makefsdata.c
index 1efe89d..9af8bc9 100644
--- a/lwip/src/apps/httpd/makefsdata/makefsdata.c
+++ b/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) {
diff --git a/lwip/src/apps/sntp/sntp.c b/lwip/src/apps/sntp/sntp.c
index 480af60..3ebf3ac 100644
--- a/lwip/src/apps/sntp/sntp.c
+++ b/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 */
diff --git a/lwip/src/core/altcp_tcp.c b/lwip/src/core/altcp_tcp.c
index cbbc422..72be815 100644
--- a/lwip/src/core/altcp_tcp.c
+++ b/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
diff --git a/lwip/src/core/ipv4/icmp.c b/lwip/src/core/ipv4/icmp.c
index 3c9445d..3fe13cc 100644
--- a/lwip/src/core/ipv4/icmp.c
+++ b/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 */
diff --git a/lwip/src/core/ipv6/icmp6.c b/lwip/src/core/ipv6/icmp6.c
index 08db381..62be025 100644
--- a/lwip/src/core/ipv6/icmp6.c
+++ b/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);
}
/**
diff --git a/lwip/src/core/ipv6/ip6.c b/lwip/src/core/ipv6/ip6.c
index 907f0aa..d65de89 100644
--- a/lwip/src/core/ipv6/ip6.c
+++ b/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;
}
diff --git a/lwip/src/core/ipv6/mld6.c b/lwip/src/core/ipv6/mld6.c
index b5a159b..64a204d 100644
--- a/lwip/src/core/ipv6/mld6.c
+++ b/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;
diff --git a/lwip/src/core/mem.c b/lwip/src/core/mem.c
index 8a77a67..689d7f0 100644
--- a/lwip/src/core/mem.c
+++ b/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) {
diff --git a/lwip/src/core/tcp_in.c b/lwip/src/core/tcp_in.c
index 5f502a1..c4ee51d 100644
--- a/lwip/src/core/tcp_in.c
+++ b/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 */
diff --git a/lwip/src/core/tcp_out.c b/lwip/src/core/tcp_out.c
index f72c03a..58fdaac 100644
--- a/lwip/src/core/tcp_out.c
+++ b/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);
diff --git a/lwip/src/include/lwip/apps/mqtt_opts.h b/lwip/src/include/lwip/apps/mqtt_opts.h
index ffefacd..4226d21 100644
--- a/lwip/src/include/lwip/apps/mqtt_opts.h
+++ b/lwip/src/include/lwip/apps/mqtt_opts.h
@@ -39,7 +39,7 @@
#include "lwip/opt.h"
-#ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
#endif
diff --git a/lwip/src/include/lwip/icmp6.h b/lwip/src/include/lwip/icmp6.h
index 374ff44..0ccb789 100644
--- a/lwip/src/include/lwip/icmp6.h
+++ b/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 */
diff --git a/lwip/src/include/lwip/opt.h b/lwip/src/include/lwip/opt.h
index 465a9c7..420985c 100644
--- a/lwip/src/include/lwip/opt.h
+++ b/lwip/src/include/lwip/opt.h
@@ -1300,22 +1300,52 @@
#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.
*/
#if !defined TCP_LISTEN_BACKLOG || defined __DOXYGEN__
@@ -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
/**
* @}
*/
diff --git a/lwip/src/include/lwip/priv/memp_std.h b/lwip/src/include/lwip/priv/memp_std.h
index 4ad0b6d..c694194 100644
--- a/lwip/src/include/lwip/priv/memp_std.h
+++ b/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
diff --git a/lwip/src/include/lwip/priv/sockets_priv.h b/lwip/src/include/lwip/priv/sockets_priv.h
index 9918aa2..d8f9904 100644
--- a/lwip/src/include/lwip/priv/sockets_priv.h
+++ b/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 */
diff --git a/lwip/src/include/lwip/prot/ip6.h b/lwip/src/include/lwip/prot/ip6.h
index 6e1e263..996bcca 100644
--- a/lwip/src/include/lwip/prot/ip6.h
+++ b/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
}
diff --git a/lwip/src/include/lwip/prot/mld6.h b/lwip/src/include/lwip/prot/mld6.h
index be3a006..71f1dcb 100644
--- a/lwip/src/include/lwip/prot/mld6.h
+++ b/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"
diff --git a/lwip/src/include/lwip/sockets.h b/lwip/src/include/lwip/sockets.h
index d51cfb6..07d15ec 100644
--- a/lwip/src/include/lwip/sockets.h
+++ b/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 */
diff --git a/lwip/test/unit/core/test_mem.c b/lwip/test/unit/core/test_mem.c
index c362758..2aeb20c 100644
--- a/lwip/test/unit/core/test_mem.c
+++ b/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);
}
diff --git a/lwip/test/unit/lwipopts.h b/lwip/test/unit/lwipopts.h
index 7b82fd6..953c5d0 100644
--- a/lwip/test/unit/lwipopts.h
+++ b/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 */