Merge commit 'e94c9ffa' from origin/upstream/master

Change-Id: I529c6cb0b6b8b1bcd105d03a92ba2b80e0dce57d
diff --git a/.gitignore b/.gitignore
index 0092ea6..cb0ed53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+*.o
+*.a
 /doc/doxygen/output/html
 /src/apps/snmp/LwipMibCompiler/CCodeGeneration/bin/
 /src/apps/snmp/LwipMibCompiler/CCodeGeneration/obj/
@@ -11,3 +13,6 @@
 /src/apps/snmp/LwipMibCompiler/SharpSnmpLib/obj/
 /src/apps/snmp/LwipMibCompiler/LwipMibCompiler.userprefs
 /src/apps/snmp/LwipMibCompiler/*.suo
+/test/fuzz/output
+/test/fuzz/lwip_fuzz
+/test/fuzz/.depend
diff --git a/CHANGELOG b/CHANGELOG
index 8f030fd..051f7ae 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,74 @@
 (git master)
 
   * [Enter new changes just after this line - do not remove this line]
+ 
+  ++ New features:
+
+  2016-12-31: Simon Goldschmidt
+  * tcp.h/.c: added function tcp_listen_with_backlog_and_err() to get the error
+    reason when listening fails (bug #49861)
+
+  2016-12-14: Jan Breuer:
+  * opt.h, ndc.h/.c: add support for RDNSS option (as per RFC 6106)
+
+  2016-12-14: David van Moolenbroek
+  * opt.h, nd6.c: Added LWIP_HOOK_ND6_GET_GW()
+
+  2016-12-09: Dirk Ziegelmeier
+  * ip6_frag.c: Implemented support for LWIP_NETIF_TX_SINGLE_PBUF
+
+  2016-12-09: Simon Goldschmidt
+  * dns.c: added one-shot multicast DNS queries
+
+  2016-11-24: Ambroz Bizjak, David van Moolenbroek
+  * tcp_out.c: Optimize passing contiguous nocopy buffers to tcp_write (bug #46290)
+
+  2016-11-16: Dirk Ziegelmeier
+  * sockets.c: added support for IPv6 mapped IPv4 addresses
+
+  ++ Bugfixes:
+
+  2016-12-16: Thomas Mueller
+  * api_lib.c: fixed race condition in return value of netconn_gethostbyname()
+    (and thus also lwip_gethostbyname/_r() and lwip_getaddrinfo())
+
+  2016-12-15: David van Moolenbroek
+  * opt.h, tcp: added LWIP_HOOK_TCP_ISN() to implement less predictable initial
+    sequence numbers (see contrib/addons/tcp_isn for an example implementation)
+
+  2016-12-05: Dirk Ziegelmeier
+  * fixed compiling with IPv4 disabled (IPv6 only case)
+  
+  2016-11-28: Simon Goldschmidt
+  * api_lib.c: fixed bug #49725 (send-timeout: netconn_write() can return
+    ERR_OK without all bytes being written)
+
+  2016-11-28: Ambroz Bizjak
+  * tcpi_in.c: fixed bug #49717 (window size in received SYN and SYN-ACK
+    assumed scaled)
+
+  2016-11-25: Simon Goldschmidt
+  * dhcp.c: fixed bug #49676 (Possible endless loop when parsing dhcp options)
+
+  2016-11-23: Dirk Ziegelmeier
+  * udp.c: fixed bug #49662: multicast traffic is now only received on a UDP PCB
+   (and therefore on a UDP socket/netconn) when the PCB is bound to IP_ADDR_ANY
+
+  2016-11-16: Dirk Ziegelmeier
+  * *: Fixed dual-stack behaviour, IPv6 mapped IPv4 support in socket API
+
+  2016-11-14: Joel Cunningham
+  * tcp_out.c: fixed bug #49533 (start persist timer when unsent seg can't fit
+    in window) 
+
+  2016-11-16: Roberto Barbieri Carrera
+  * autoip.c: fixed bug #49610 (sometimes AutoIP fails to reuse the same address)
+
+  2016-11-11: Dirk Ziegelmeier
+  * sockets.c: fixed bug #49578 (dropping multicast membership does not work
+    with LWIP_SOCKET_OFFSET)
+  
+(STABLE-2.0.0)
 
   ++ New features:
 
diff --git a/UPGRADING b/UPGRADING
index c42c67d..d9b9d4a 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -8,7 +8,12 @@
 
   * [Enter new changes just after this line - do not remove this line]
 
-  * TODO
+  ++ Application changes:
+
+  * UDP does NOT receive multicast traffic from ALL netifs on an UDP PCB bound to a specific
+    netif any more. Users need to bind to IP_ADDR_ANY to receive multicast traffic and compare 
+    ip_current_netif() to the desired netif for every packet.
+    See bug #49662 for an explanation.
 
 (2.0.0)
 
diff --git a/doc/doxygen/generate.sh b/doc/doxygen/generate.sh
index 99afb12..89344b0 100755
--- a/doc/doxygen/generate.sh
+++ b/doc/doxygen/generate.sh
@@ -1 +1,3 @@
+#!/bin/sh
+
 doxygen lwip.Doxyfile
diff --git a/doc/doxygen/lwip.Doxyfile b/doc/doxygen/lwip.Doxyfile
index 125d323..177d4c4 100644
--- a/doc/doxygen/lwip.Doxyfile
+++ b/doc/doxygen/lwip.Doxyfile
@@ -32,19 +32,19 @@
 # title of most generated pages and in a few other places.
 # The default value is: My Project.
 
-PROJECT_NAME           = "lwIP 2.0.0"
+PROJECT_NAME           = "lwIP"
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = "lwIP 2.0.0"
+PROJECT_NUMBER         = "2.0.1"
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
 # quick idea about the purpose of the project. Keep the description short.
 
-PROJECT_BRIEF          = Lightweight IP stack
+PROJECT_BRIEF          = "Lightweight IP stack"
 
 # With the PROJECT_LOGO tag one can specify a logo or an icon that is included
 # in the documentation. The maximum height of the logo should not exceed 55
diff --git a/doc/doxygen/main_page.h b/doc/doxygen/main_page.h
index 31cc0df..4f43d60 100644
--- a/doc/doxygen/main_page.h
+++ b/doc/doxygen/main_page.h
@@ -103,7 +103,8 @@
  * *not* *from* *interrupt* *context*. You can allocate a @ref pbuf in interrupt
  * context and put them into a queue which is processed from mainloop.\n
  * Call sys_check_timeouts() periodically in the mainloop.\n
- * Porting: implement all functions in @ref sys_time and @ref sys_prot.\n
+ * Porting: implement all functions in @ref sys_time, @ref sys_prot and 
+ * @ref compiler_abstraction.\n
  * You can only use @ref callbackstyle_api in this mode.\n
  * Sample code:\n
  * @include NO_SYS_SampleCode.c
diff --git a/doc/mqtt_client.txt b/doc/mqtt_client.txt
new file mode 100644
index 0000000..3e67def
--- /dev/null
+++ b/doc/mqtt_client.txt
@@ -0,0 +1,162 @@
+MQTT client for lwIP
+
+Author: Erik Andersson
+
+Details of the MQTT protocol can be found at:
+http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html 
+
+-----------------------------------------------------------------
+1. Initial steps, reserve memory and make connection to server:
+
+1.1: Provide storage
+
+Static allocation:
+  mqtt_client_t static_client;
+  example_do_connect(&static_client);
+
+Dynamic allocation:
+  mqtt_client_t *client = mqtt_client_new();
+  if(client != NULL) {
+    example_do_connect(&client);
+  }
+  
+1.2: Establish Connection with server
+
+void example_do_connect(mqtt_client_t *client)
+{
+  struct mqtt_connect_client_info_t ci;
+  err_t err;
+  
+  /* Setup an empty client info structure */
+  memset(&ci, 0, sizeof(ci));
+  
+  /* Minimal amount of information required is client identifier, so set it here */ 
+  ci.client_id = "lwip_test";
+  
+  /* Initiate client and connect to server, if this fails immediately an error code is returned
+     otherwise mqtt_connection_cb will be called with connection result after attempting 
+     to establish a connection with the server. 
+     For now MQTT version 3.1.1 is always used */
+  
+  err = mqtt_client_connect(client, ip_addr, MQTT_PORT, mqtt_connection_cb, 0, &ci);
+  
+  /* For now just print the result code if something goes wrong
+  if(err != ERR_OK) {
+    printf("mqtt_connect return %d\n", err);
+  }
+}
+
+Connection to server can also be probed by calling mqtt_client_is_connected(client) 
+
+-----------------------------------------------------------------
+2. Implementing the connection status callback
+
+
+static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
+{
+  err_t err;
+  if(status == MQTT_CONNECT_ACCEPTED) {
+    printf("mqtt_connection_cb: Successfully connected\n");
+    
+    /* Setup callback for incoming publish requests */
+    mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg);
+    
+    /* Subscribe to a topic named "subtopic" with QoS level 1, call mqtt_sub_request_cb with result */ 
+    err = mqtt_subscribe(client, "subtopic", 1, mqtt_sub_request_cb, arg);
+
+    if(err != ERR_OK) {
+      printf("mqtt_subscribe return: %d\n", err);
+    }
+  } else {
+    printf("mqtt_connection_cb: Disconnected, reason: %d\n", status);
+    
+    /* Its more nice to be connected, so try to reconnect */
+    example_do_connect(client);
+  }  
+}
+
+static void mqtt_sub_request_cb(void *arg, err_t result)
+{
+  /* Just print the result code here for simplicity, 
+     normal behaviour would be to take some action if subscribe fails like 
+     notifying user, retry subscribe or disconnect from server */
+  printf("Subscribe result: %d\n", result);
+}
+
+-----------------------------------------------------------------
+3. Implementing callbacks for incoming publish and data
+
+/* The idea is to demultiplex topic and create some reference to be used in data callbacks
+   Example here uses a global variable, better would be to use a member in arg
+   If RAM and CPU budget allows it, the easiest implementation might be to just take a copy of
+   the topic string and use it in mqtt_incoming_data_cb
+*/
+static int inpub_id;
+static void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len)
+{
+  printf("Incoming publish at topic %s with total length %u\n", topic, (unsigned int)tot_len);
+
+  /* Decode topic string into a user defined reference */
+  if(strcmp(topic, "print_payload") == 0) {
+    inpub_id = 0;
+  } else if(topic[0] == 'A') {
+    /* All topics starting with 'A' might be handled at the same way */
+    inpub_id = 1;
+  } else {
+    /* For all other topics */
+    inpub_id = 2;
+  }
+}
+
+static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags)
+{
+  printf("Incoming publish payload with length %d, flags %u\n", len, (unsigned int)flags);
+
+  if(flags & MQTT_DATA_FLAG_LAST) {
+    /* Last fragment of payload received (or whole part if payload fits receive buffer
+       See MQTT_VAR_HEADER_BUFFER_LEN)  */
+
+    /* Call function or do action depending on reference, in this case inpub_id */
+    if(inpub_id == 0) {
+      /* Don't trust the publisher, check zero termination */
+      if(data[len-1] == 0) {
+        printf("mqtt_incoming_data_cb: %s\n", (const char *)data);
+      }
+    } else if(inpub_id == 1) {
+      /* Call an 'A' function... */
+    } else {
+      printf("mqtt_incoming_data_cb: Ignoring payload...\n");
+    }
+  } else {
+    /* Handle fragmented payload, store in buffer, write to file or whatever */
+  }
+}
+
+-----------------------------------------------------------------
+4. Using outgoing publish
+
+
+void example_publish(mqtt_client_t *client, void *arg)
+{
+  const char *pub_payload= "PubSubHubLubJub";
+  err_t err;
+  u8_t qos = 2; /* 0 1 or 2, see MQTT specification */
+  u8_t retain = 0; /* No don't retain such crappy payload... */
+  err = mqtt_publish(client, "pub_topic", pub_payload, strlen(pub_payload), qos, retain, mqtt_pub_request_cb, arg);
+  if(err != ERR_OK) {
+    printf("Publish err: %d\n", err);
+  }
+}
+
+/* Called when publish is complete either with sucess or failure */
+static void mqtt_pub_request_cb(void *arg, err_t result)
+{
+  if(result != ERR_OK) {
+    printf("Publish result: %d\n", result);
+  }
+}
+
+-----------------------------------------------------------------
+5. Disconnecting
+
+Simply call mqtt_disconnect(client)
diff --git a/doc/sys_arch.txt b/doc/sys_arch.txt
index 333946d..4dc727b 100644
--- a/doc/sys_arch.txt
+++ b/doc/sys_arch.txt
@@ -29,7 +29,7 @@
 
 Semaphores are represented by the type "sys_sem_t" which is typedef'd
 in the sys_arch.h file. Mailboxes are equivalently represented by the
-type "sys_mbox_t". Mutexes are represented ny the type "sys_mutex_t".
+type "sys_mbox_t". Mutexes are represented by the type "sys_mutex_t".
 lwIP does not place any restrictions on how these types are represented
 internally.
 
diff --git a/src/Filelists.mk b/src/Filelists.mk
index ac79422..7d30bb8 100644
--- a/src/Filelists.mk
+++ b/src/Filelists.mk
@@ -167,6 +167,9 @@
 # TFTPFILES: TFTP server files
 TFTPFILES=$(LWIPDIR)/apps/tftp/tftp_server.c
 
+# MQTTFILES: MQTT client files
+MQTTFILES=$(LWIPDIR)/apps/mqtt/mqtt.c
+
 # LWIPAPPFILES: All LWIP APPs
 LWIPAPPFILES=$(SNMPFILES) \
 	$(HTTPDFILES) \
@@ -174,4 +177,5 @@
 	$(SNTPFILES) \
 	$(MDNSFILES) \
 	$(NETBIOSNSFILES) \
-	$(TFTPFILES)
+	$(TFTPFILES) \
+	$(MQTTFILES)
diff --git a/src/api/api_lib.c b/src/api/api_lib.c
index b4fc403..3c1d6a6 100644
--- a/src/api/api_lib.c
+++ b/src/api/api_lib.c
@@ -254,10 +254,22 @@
   
   LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
 
+#if LWIP_IPV4
   /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
   if (addr == NULL) {
     addr = IP4_ADDR_ANY;
   }
+#endif /* LWIP_IPV4 */
+  
+#if LWIP_IPV4 && LWIP_IPV6
+  /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
+   * and NETCONN_FLAG_IPV6_V6ONLY is 0, use IP_ANY_TYPE to bind
+   */
+  if ((netconn_get_ipv6only(conn) == 0) &&
+     ip_addr_cmp(addr, IP6_ADDR_ANY)) {
+    addr = IP_ANY_TYPE;
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
 
   API_MSG_VAR_ALLOC(msg);
   API_MSG_VAR_REF(msg).conn = conn;
@@ -286,10 +298,12 @@
 
   LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
 
+#if LWIP_IPV4
   /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
   if (addr == NULL) {
     addr = IP4_ADDR_ANY;
   }
+#endif /* LWIP_IPV4 */
 
   API_MSG_VAR_ALLOC(msg);
   API_MSG_VAR_REF(msg).conn = conn;
@@ -376,7 +390,6 @@
 #if LWIP_TCP
   void *accept_ptr;
   struct netconn *newconn;
-  err_t err;
 #if TCP_LISTEN_BACKLOG
   API_MSG_VAR_DECLARE(msg);
 #endif /* TCP_LISTEN_BACKLOG */
@@ -385,11 +398,10 @@
   *new_conn = NULL;
   LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return ERR_ARG;);
 
-  err = conn->last_err;
-  if (ERR_IS_FATAL(err)) {
+  if (ERR_IS_FATAL(conn->last_err)) {
     /* don't recv on fatal errors: this might block the application task
        waiting on acceptmbox forever! */
-    return err;
+    return conn->last_err;
   }
   if (!sys_mbox_valid(&conn->acceptmbox)) {
     return ERR_CLSD;
@@ -465,7 +477,6 @@
 {
   void *buf = NULL;
   u16_t len;
-  err_t err;
 #if LWIP_TCP
   API_MSG_VAR_DECLARE(msg);
 #if LWIP_MPU_COMPATIBLE
@@ -489,13 +500,12 @@
 #endif /* LWIP_TCP */
   LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
 
-  err = conn->last_err;
-  if (ERR_IS_FATAL(err)) {
+  if (ERR_IS_FATAL(conn->last_err)) {
     /* don't recv on fatal errors: this might block the application task
        waiting on recvmbox forever! */
     /* @todo: this does not allow us to fetch data that has been put into recvmbox
        before the fatal error occurred - is that a problem? */
-    return err;
+    return conn->last_err;
   }
 #if LWIP_TCP
 #if (LWIP_UDP || LWIP_RAW)
@@ -562,7 +572,7 @@
 #if (LWIP_UDP || LWIP_RAW)
   {
     LWIP_ASSERT("buf != NULL", buf != NULL);
-    len = netbuf_len((struct netbuf *)buf);
+    len = netbuf_len((struct netbuf*)buf);
   }
 #endif /* (LWIP_UDP || LWIP_RAW) */
 
@@ -697,6 +707,7 @@
   LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
 
   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+
   API_MSG_VAR_ALLOC(msg);
   API_MSG_VAR_REF(msg).conn = conn;
   API_MSG_VAR_REF(msg).msg.b = buf;
@@ -734,6 +745,11 @@
     return ERR_OK;
   }
   dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+#if LWIP_SO_SNDTIMEO
+  if (conn->send_timeout != 0) {
+    dontblock = 1;
+  }
+#endif /* LWIP_SO_SNDTIMEO */
   if (dontblock && !bytes_written) {
     /* This implies netconn_write() cannot be used for non-blocking send, since
        it has no way to return the number of bytes written. */
@@ -761,11 +777,7 @@
      non-blocking version here. */
   err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
   if ((err == ERR_OK) && (bytes_written != NULL)) {
-    if (dontblock
-#if LWIP_SO_SNDTIMEO
-        || (conn->send_timeout != 0)
-#endif /* LWIP_SO_SNDTIMEO */
-       ) {
+    if (dontblock) {
       /* nonblocking write: maybe the data has been sent partly */
       *bytes_written = API_MSG_VAR_REF(msg).msg.w.len;
     } else {
@@ -869,6 +881,7 @@
 
   API_MSG_VAR_ALLOC(msg);
 
+#if LWIP_IPV4
   /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
   if (multiaddr == NULL) {
     multiaddr = IP4_ADDR_ANY;
@@ -876,6 +889,7 @@
   if (netif_addr == NULL) {
     netif_addr = IP4_ADDR_ANY;
   }
+#endif /* LWIP_IPV4 */
 
   API_MSG_VAR_REF(msg).conn = conn;
   API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
@@ -914,6 +928,7 @@
   sys_sem_t sem;
 #endif /* LWIP_MPU_COMPATIBLE */
   err_t err;
+  err_t cberr;
 
   LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
   LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
@@ -946,13 +961,13 @@
   }
 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
 
-  err = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
-  if (err != ERR_OK) {
+  cberr = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
+  if (cberr != ERR_OK) {
 #if !LWIP_NETCONN_SEM_PER_THREAD
     sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
 #endif /* !LWIP_NETCONN_SEM_PER_THREAD */
     API_VAR_FREE(MEMP_DNS_API_MSG, msg);
-    return err;
+    return cberr;
   }
   sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem));
 #if !LWIP_NETCONN_SEM_PER_THREAD
diff --git a/src/api/api_msg.c b/src/api/api_msg.c
index 86546c2..4927ee5 100644
--- a/src/api/api_msg.c
+++ b/src/api/api_msg.c
@@ -43,6 +43,7 @@
 #include "lwip/priv/api_msg.h"
 
 #include "lwip/ip.h"
+#include "lwip/ip_addr.h"
 #include "lwip/udp.h"
 #include "lwip/tcp.h"
 #include "lwip/raw.h"
@@ -544,13 +545,22 @@
 static void
 pcb_new(struct api_msg *msg)
 {
-  LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+  enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
 
+  LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+ 
+#if LWIP_IPV6 && LWIP_IPV4
+  /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
+  if(NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
+    iptype = IPADDR_TYPE_ANY;
+  }
+#endif
+  
   /* Allocate a PCB for this connection */
   switch(NETCONNTYPE_GROUP(msg->conn->type)) {
 #if LWIP_RAW
   case NETCONN_RAW:
-    msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
+    msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
     if (msg->conn->pcb.raw != NULL) {
       raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
     }
@@ -558,7 +568,7 @@
 #endif /* LWIP_RAW */
 #if LWIP_UDP
   case NETCONN_UDP:
-    msg->conn->pcb.udp = udp_new();
+    msg->conn->pcb.udp = udp_new_ip_type(iptype);
     if (msg->conn->pcb.udp != NULL) {
 #if LWIP_UDPLITE
       if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
@@ -574,7 +584,7 @@
 #endif /* LWIP_UDP */
 #if LWIP_TCP
   case NETCONN_TCP:
-    msg->conn->pcb.tcp = tcp_new();
+    msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
     if (msg->conn->pcb.tcp != NULL) {
       setup_tcp(msg->conn);
     }
@@ -588,15 +598,6 @@
   if (msg->conn->pcb.ip == NULL) {
     msg->err = ERR_MEM;
   }
-#if LWIP_IPV4 && LWIP_IPV6
-  else {
-    if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
-      /* Convert IPv4 PCB manually to an IPv6 PCB */
-      IP_SET_TYPE_VAL(msg->conn->pcb.ip->local_ip,  IPADDR_TYPE_V6);
-      IP_SET_TYPE_VAL(msg->conn->pcb.ip->remote_ip, IPADDR_TYPE_V6);
-    }
-  }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
 }
 
 /**
@@ -1113,37 +1114,20 @@
   } else {
     msg->err = ERR_VAL;
     if (msg->conn->pcb.tcp != NULL) {
-      const ip_addr_t *ipaddr = API_EXPR_REF(msg->msg.bc.ipaddr);
-
-#if LWIP_IPV4 && LWIP_IPV6
-      /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
-       * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to bind
-       */
-      if (ip_addr_cmp(ipaddr, IP6_ADDR_ANY) &&
-          (netconn_get_ipv6only(msg->conn) == 0)) {
-        /* change PCB type to IPADDR_TYPE_ANY */
-        IP_SET_TYPE_VAL(msg->conn->pcb.ip->local_ip,  IPADDR_TYPE_ANY);
-        IP_SET_TYPE_VAL(msg->conn->pcb.ip->remote_ip, IPADDR_TYPE_ANY);
-        
-        /* bind to IPADDR_TYPE_ANY */
-        ipaddr = IP_ANY_TYPE;
-      }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-
       switch (NETCONNTYPE_GROUP(msg->conn->type)) {
 #if LWIP_RAW
       case NETCONN_RAW:
-        msg->err = raw_bind(msg->conn->pcb.raw, ipaddr);
+        msg->err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
         break;
 #endif /* LWIP_RAW */
 #if LWIP_UDP
       case NETCONN_UDP:
-        msg->err = udp_bind(msg->conn->pcb.udp, ipaddr, msg->msg.bc.port);
+        msg->err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
         break;
 #endif /* LWIP_UDP */
 #if LWIP_TCP
       case NETCONN_TCP:
-        msg->err = tcp_bind(msg->conn->pcb.tcp, ipaddr, msg->msg.bc.port);
+        msg->err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
         break;
 #endif /* LWIP_TCP */
       default:
@@ -1323,6 +1307,13 @@
             /* connection is not closed, cannot listen */
             msg->err = ERR_VAL;
           } else {
+            err_t err;
+            u8_t backlog;
+#if TCP_LISTEN_BACKLOG
+            backlog = msg->msg.lb.backlog;
+#else  /* TCP_LISTEN_BACKLOG */
+            backlog = TCP_DEFAULT_LISTEN_BACKLOG;
+#endif /* TCP_LISTEN_BACKLOG */
 #if LWIP_IPV4 && LWIP_IPV6
             /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
              * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
@@ -1335,15 +1326,11 @@
             }
 #endif /* LWIP_IPV4 && LWIP_IPV6 */
 
-#if TCP_LISTEN_BACKLOG
-            lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
-#else  /* TCP_LISTEN_BACKLOG */
-            lpcb = tcp_listen(msg->conn->pcb.tcp);
-#endif /* TCP_LISTEN_BACKLOG */
+            lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
 
             if (lpcb == NULL) {
               /* in this case, the old pcb is still allocated */
-              msg->err = ERR_MEM;
+              msg->err = err;
             } else {
               /* delete the recvmbox and allocate the acceptmbox */
               if (sys_mbox_valid(&msg->conn->recvmbox)) {
@@ -1400,7 +1387,7 @@
       switch (NETCONNTYPE_GROUP(msg->conn->type)) {
 #if LWIP_RAW
       case NETCONN_RAW:
-        if (ip_addr_isany(&msg->msg.b->addr)) {
+        if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
           msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
         } else {
           msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
@@ -1588,10 +1575,11 @@
         write_finished = 1;
         conn->current_msg->msg.w.len = 0;
       }
-    } else if ((err == ERR_MEM) && !dontblock) {
-      /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
-         we do NOT return to the application thread, since ERR_MEM is
-         only a temporary error! */
+    } else if (err == ERR_MEM) {
+      /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
+         For blocking sockets, we do NOT return to the application
+         thread, since ERR_MEM is only a temporary error! Non-blocking
+         will remain non-writable until sent_tcp/poll_tcp is called */
 
       /* tcp_write returned ERR_MEM, try tcp_output anyway */
       err_t out_err = tcp_output(conn->pcb.tcp);
@@ -1602,6 +1590,11 @@
         err = out_err;
         write_finished = 1;
         conn->current_msg->msg.w.len = 0;
+      } else if (dontblock) {
+        /* non-blocking write is done on ERR_MEM */
+        err = ERR_WOULDBLOCK;
+        write_finished = 1;
+        conn->current_msg->msg.w.len = 0;
       }
     } else {
       /* On errors != ERR_MEM, we don't try writing any more but return
@@ -1710,6 +1703,7 @@
       ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
         msg->conn->pcb.ip->remote_ip);
     }
+
     msg->err = ERR_OK;
     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
 #if LWIP_RAW
diff --git a/src/api/err.c b/src/api/err.c
index 1593e70..35e9c02 100644
--- a/src/api/err.c
+++ b/src/api/err.c
@@ -64,6 +64,15 @@
   ENOTCONN,      /* ERR_CLSD       -15     Connection closed.       */
   EIO            /* ERR_ARG        -16     Illegal argument.        */
 };
+
+int
+err_to_errno(err_t err)
+{
+  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
+    return EIO;
+  }
+  return err_to_errno_table[-err];
+}
 #endif /* !NO_SYS */
 
 #ifdef LWIP_DEBUG
@@ -104,14 +113,3 @@
 }
 
 #endif /* LWIP_DEBUG */
-
-#if !NO_SYS
-int
-err_to_errno(err_t err)
-{
-  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
-    return EIO;
-  }
-  return err_to_errno_table[-err];
-}
-#endif /* !NO_SYS */
diff --git a/src/api/netdb.c b/src/api/netdb.c
index ef0c79b..ac1d8c7 100644
--- a/src/api/netdb.c
+++ b/src/api/netdb.c
@@ -47,7 +47,6 @@
 #include "lwip/dns.h"
 
 #include <string.h>
-#include <stdlib.h>
 
 /** helper struct for gethostbyname_r to access the char* buffer */
 struct gethostbyname_r_helper {
@@ -382,7 +381,7 @@
 #if LWIP_IPV4
     struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
     /* set up sockaddr */
-    inet_addr_from_ipaddr(&sa4->sin_addr, ip_2_ip4(&addr));
+    inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr));
     sa4->sin_family = AF_INET;
     sa4->sin_len = sizeof(struct sockaddr_in);
     sa4->sin_port = lwip_htons((u16_t)port_nr);
diff --git a/src/api/sockets.c b/src/api/sockets.c
index 960ac7e..24b3b5f 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -82,10 +82,10 @@
       (sin)->sin_len = sizeof(struct sockaddr_in); \
       (sin)->sin_family = AF_INET; \
       (sin)->sin_port = lwip_htons((port)); \
-      inet_addr_from_ipaddr(&(sin)->sin_addr, ipaddr); \
+      inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
       memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
 #define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
-    inet_addr_to_ipaddr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
+    inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
     (port) = lwip_ntohs((sin)->sin_port); }while(0)
 #endif /* LWIP_IPV4 */
 
@@ -482,7 +482,7 @@
 
   if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
-    sock_set_errno(sock, EWOULDBLOCK);
+    set_errno(EWOULDBLOCK);
     return -1;
   }
 
@@ -584,6 +584,14 @@
   ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
   LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
 
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: Unmap IPv6 mapped IPv4 addresses */
+  if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv6mappedipv4(ip_2_ip6(&local_addr))) {
+    unmap_ipv6_mapped_ipv4(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
+    IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
   err = netconn_bind(sock->conn, &local_addr, local_port);
 
   if (err != ERR_OK) {
@@ -668,6 +676,14 @@
     ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
     LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
 
+#if LWIP_IPV4 && LWIP_IPV6
+    /* Dual-stack: Unmap IPv6 mapped IPv4 addresses */
+    if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv6mappedipv4(ip_2_ip6(&remote_addr))) {
+      unmap_ipv6_mapped_ipv4(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
+      IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
+    }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
     err = netconn_connect(sock->conn, &remote_addr, remote_port);
   }
 
@@ -755,7 +771,7 @@
           return off;
         }
         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
-        sock_set_errno(sock, EWOULDBLOCK);
+        set_errno(EWOULDBLOCK);
         return -1;
       }
 
@@ -847,6 +863,15 @@
           port = netbuf_fromport((struct netbuf *)buf);
           fromaddr = netbuf_fromaddr((struct netbuf *)buf);
         }
+
+#if LWIP_IPV4 && LWIP_IPV6
+        /* Dual-stack: Map IPv4 addresses to IPv6 mapped IPv4 */
+        if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) && IP_IS_V4(fromaddr)) {
+          ip4_2_ipv6_mapped_ipv4(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
+          IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
+        }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
         IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
         ip_addr_debug_print(SOCKETS_DEBUG, fromaddr);
         LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
@@ -1066,6 +1091,14 @@
 #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
 
     if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+      /* Dual-stack: Unmap IPv6 mapped IPv4 addresses */
+      if (IP_IS_V6_VAL(chain_buf->addr) && ip6_addr_isipv6mappedipv4(ip_2_ip6(&chain_buf->addr))) {
+        unmap_ipv6_mapped_ipv4(ip_2_ip4(&chain_buf->addr), ip_2_ip6(&chain_buf->addr));
+        IP_SET_TYPE_VAL(chain_buf->addr, IPADDR_TYPE_V4);
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
       /* send the data */
       err = netconn_send(sock->conn, chain_buf);
     }
@@ -1107,12 +1140,6 @@
 #endif /* LWIP_TCP */
   }
 
-  if ((to != NULL) && !SOCK_ADDR_TYPE_MATCH(to, sock)) {
-    /* sockaddr does not match socket type (IPv4/IPv6) */
-    sock_set_errno(sock, err_to_errno(ERR_VAL));
-    return -1;
-  }
-
   /* @todo: split into multiple sendto's? */
   LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
   short_size = (u16_t)size;
@@ -1162,6 +1189,14 @@
   err = netbuf_ref(&buf, data, short_size);
 #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
   if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+    /* Dual-stack: Unmap IPv6 mapped IPv4 addresses */
+    if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv6mappedipv4(ip_2_ip6(&buf.addr))) {
+      unmap_ipv6_mapped_ipv4(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
+      IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
+    }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
     /* send the data */
     err = netconn_send(sock->conn, &buf);
   }
@@ -1179,9 +1214,7 @@
   struct netconn *conn;
   int i;
 
-#if !LWIP_IPV6
   LWIP_UNUSED_ARG(domain); /* @todo: check this */
-#endif /* LWIP_IPV6 */
 
   /* create a netconn */
   switch (type) {
@@ -1244,7 +1277,7 @@
   msg.msg_namelen = 0;
   /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
      Blame the opengroup standard for this inconsistency. */
-  msg.msg_iov = (struct iovec *)(size_t)iov;
+  msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
   msg.msg_iovlen = iovcnt;
   msg.msg_control = NULL;
   msg.msg_controllen = 0;
@@ -1710,12 +1743,21 @@
   }
 
   /* get the IP address and port */
-  /* @todo: this does not work for IPv6, yet */
   err = netconn_getaddr(sock->conn, &naddr, &port, local);
   if (err != ERR_OK) {
     sock_set_errno(sock, err_to_errno(err));
     return -1;
   }
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: Map IPv4 addresses to IPv6 mapped IPv4 */
+  if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
+      IP_IS_V4_VAL(naddr)) {
+    ip4_2_ipv6_mapped_ipv4(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
+    IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
   IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
 
   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
@@ -2001,7 +2043,7 @@
       if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
         return ENOPROTOOPT;
       }
-      inet_addr_from_ipaddr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
+      inet_addr_from_ip4addr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
                   s, *(u32_t *)optval));
       break;
@@ -2029,6 +2071,9 @@
   case IPPROTO_TCP:
     /* Special case: all IPPROTO_TCP option take an int */
     LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
+    if (sock->conn->pcb.tcp->state == LISTEN) {
+      return EINVAL;
+    }
     switch (optname) {
     case TCP_NODELAY:
       *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
@@ -2073,10 +2118,6 @@
     switch (optname) {
     case IPV6_V6ONLY:
       LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
-      /* @todo: this does not work for datagram sockets, yet */
-      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
-        return ENOPROTOOPT;
-      }
       *(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
                   s, *(int *)optval));
@@ -2365,7 +2406,7 @@
       {
         ip4_addr_t if_addr;
         LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
-        inet_addr_to_ipaddr(&if_addr, (const struct in_addr*)optval);
+        inet_addr_to_ip4addr(&if_addr, (const struct in_addr*)optval);
         udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
       }
       break;
@@ -2389,8 +2430,8 @@
         ip4_addr_t if_addr;
         ip4_addr_t multi_addr;
         LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
-        inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
-        inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
+        inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
+        inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
         if (optname == IP_ADD_MEMBERSHIP) {
           if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
             /* cannot track membership (out of memory) */
@@ -2422,6 +2463,9 @@
   case IPPROTO_TCP:
     /* Special case: all IPPROTO_TCP option take an int */
     LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+    if (sock->conn->pcb.tcp->state == LISTEN) {
+      return EINVAL;
+    }
     switch (optname) {
     case TCP_NODELAY:
       if (*(const int*)optval) {
@@ -2469,7 +2513,6 @@
   case IPPROTO_IPV6:
     switch (optname) {
     case IPV6_V6ONLY:
-      /* @todo: this does not work for datagram sockets, yet */
       LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
       if (*(const int*)optval) {
         netconn_set_ipv6only(sock->conn, 1);
diff --git a/src/apps/httpd/httpd.c b/src/apps/httpd/httpd.c
index 103f9ac..bdffee3 100644
--- a/src/apps/httpd/httpd.c
+++ b/src/apps/httpd/httpd.c
@@ -99,7 +99,6 @@
 #include "lwip/tcp.h"
 
 #include <string.h>
-#include <stdlib.h>
 #include <stdio.h>
 
 #if LWIP_TCP
@@ -2563,6 +2562,7 @@
   tcp_setprio(pcb, HTTPD_TCP_PRIO);
   /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
   err = tcp_bind(pcb, IP_ANY_TYPE, HTTPD_SERVER_PORT);
+  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
   LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
   pcb = tcp_listen(pcb);
   LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
diff --git a/src/apps/lwiperf/lwiperf.c b/src/apps/lwiperf/lwiperf.c
index 1996cd1..54bf2bc 100644
--- a/src/apps/lwiperf/lwiperf.c
+++ b/src/apps/lwiperf/lwiperf.c
@@ -294,7 +294,7 @@
     } else {
       /* transmit data */
       /* @todo: every x bytes, transmit the settings again */
-      txptr = (void*)(size_t)&lwiperf_txbuf_const[conn->bytes_transferred % 10];
+      txptr = LWIP_CONST_CAST(void*, &lwiperf_txbuf_const[conn->bytes_transferred % 10]);
       txlen_max = TCP_MSS;
       if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
         txlen_max = TCP_MSS - 24;
@@ -494,10 +494,8 @@
         return ERR_VAL;
       }
     }
-    packet_idx += i;
-#else
-    packet_idx += q->len;
 #endif
+    packet_idx += q->len;
   }
   LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
   conn->bytes_transferred += packet_idx;
@@ -579,7 +577,7 @@
 void*
 lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg)
 {
-  return lwiperf_start_tcp_server(IP4_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
+  return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
     report_fn, report_arg);
 }
 
diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c
index 3c5cf2d..14334fc 100644
--- a/src/apps/mdns/mdns.c
+++ b/src/apps/mdns/mdns.c
@@ -65,7 +65,6 @@
 #include "lwip/prot/dns.h"
 
 #include <string.h>
-#include <stdlib.h>
 
 #if LWIP_MDNS_RESPONDER
 
@@ -82,13 +81,13 @@
 #if LWIP_IPV4
 #include "lwip/igmp.h"
 /* IPv4 multicast group 224.0.0.251 */
-static const ip_addr_t v4group = IPADDR4_INIT(PP_HTONL(0xE00000FBUL));
+static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT;
 #endif
 
 #if LWIP_IPV6
 #include "lwip/mld6.h"
 /* IPv6 multicast group FF02::FB */
-static const ip_addr_t v6group = IPADDR6_INIT(PP_HTONL(0xFF020000UL), PP_HTONL(0x00000000UL), PP_HTONL(0x00000000UL), PP_HTONL(0x000000FBUL));
+static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
 #endif
 
 #define MDNS_PORT 5353
@@ -570,7 +569,7 @@
   LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
   res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd")-1));
   LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
-  res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)(sizeof(dnssd_protos[DNSSD_PROTO_UDP])-1));
+  res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
   LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
   return mdns_add_dotlocal(domain);
 }
@@ -595,7 +594,7 @@
   }
   res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
   LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
-  res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)(sizeof(dnssd_protos[DNSSD_PROTO_UDP])-1));
+  res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
   LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
   return mdns_add_dotlocal(domain);
 }
@@ -1821,6 +1820,7 @@
   mdns_pcb->ttl = MDNS_TTL;
 #endif
   res = udp_bind(mdns_pcb, IP_ANY_TYPE, MDNS_PORT);
+  LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
   LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
   udp_recv(mdns_pcb, mdns_recv, NULL);
 
@@ -2019,7 +2019,7 @@
 err_t
 mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
 {
-  LWIP_ASSERT("mdns_resp_add_service: service != NULL", service);
+  LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service);
 
   /* Use a mdns_domain struct to store txt chunks since it is the same encoding */
   return mdns_domain_add_label(&service->txtdata, txt, txt_len);
diff --git a/src/apps/mqtt/mqtt.c b/src/apps/mqtt/mqtt.c
new file mode 100644
index 0000000..5f4a9ef
--- /dev/null
+++ b/src/apps/mqtt/mqtt.c
@@ -0,0 +1,1335 @@
+/**
+ * @file
+ * MQTT client
+ *
+ * @defgroup mqtt MQTT client
+ * @ingroup apps
+ * @verbinclude mqtt_client.txt
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson <erian747@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack
+ *
+ * Author: Erik Andersson <erian747@gmail.com>
+ *
+ *
+ * @todo:
+ * - Handle large outgoing payloads for PUBLISH messages
+ * - Fix restriction of a single topic in each (UN)SUBSCRIBE message (protocol has support for multiple topics)
+ * - Add support for legacy MQTT protocol version
+ *
+ * Please coordinate changes and requests with Erik Andersson
+ * Erik Andersson <erian747@gmail.com>
+ *
+ */
+#include <string.h>
+#include "lwip/timeouts.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/tcp.h"
+#include "lwip/apps/mqtt.h"
+
+/**
+ * MQTT_DEBUG: Default is off.
+ */
+#if !defined MQTT_DEBUG || defined __DOXYGEN__
+#define MQTT_DEBUG                  LWIP_DBG_OFF
+#endif
+
+#define MQTT_DEBUG_TRACE        (MQTT_DEBUG | LWIP_DBG_TRACE)
+#define MQTT_DEBUG_STATE        (MQTT_DEBUG | LWIP_DBG_STATE)
+#define MQTT_DEBUG_WARN         (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define MQTT_DEBUG_WARN_STATE   (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define MQTT_DEBUG_SERIOUS      (MQTT_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+static void mqtt_cyclic_timer(void *arg);
+
+/**
+ * MQTT client connection states
+ */
+enum {
+  TCP_DISCONNECTED,
+  TCP_CONNECTING,
+  MQTT_CONNECTING,
+  MQTT_CONNECTED
+};
+
+/**
+ * MQTT control message types
+ */
+enum mqtt_message_type {
+  MQTT_MSG_TYPE_CONNECT = 1,
+  MQTT_MSG_TYPE_CONNACK = 2,
+  MQTT_MSG_TYPE_PUBLISH = 3,
+  MQTT_MSG_TYPE_PUBACK = 4,
+  MQTT_MSG_TYPE_PUBREC = 5,
+  MQTT_MSG_TYPE_PUBREL = 6,
+  MQTT_MSG_TYPE_PUBCOMP = 7,
+  MQTT_MSG_TYPE_SUBSCRIBE = 8,
+  MQTT_MSG_TYPE_SUBACK = 9,
+  MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
+  MQTT_MSG_TYPE_UNSUBACK = 11,
+  MQTT_MSG_TYPE_PINGREQ = 12,
+  MQTT_MSG_TYPE_PINGRESP = 13,
+  MQTT_MSG_TYPE_DISCONNECT = 14
+};
+
+/** Helpers to extract control packet type and qos from first byte in fixed header */
+#define MQTT_CTL_PACKET_TYPE(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0xf0) >> 4)
+#define MQTT_CTL_PACKET_QOS(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0x6) >> 1)
+
+/**
+ * MQTT connect flags, only used in CONNECT message
+ */
+enum mqtt_connect_flag {
+  MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
+  MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
+  MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
+  MQTT_CONNECT_FLAG_WILL = 1 << 2,
+  MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
+};
+
+
+#if defined(LWIP_DEBUG)
+static const char * const mqtt_message_type_str[15] =
+{
+  "UNDEFINED",
+  "CONNECT",
+  "CONNACK",
+  "PUBLISH",
+  "PUBACK",
+  "PUBREC",
+  "PUBREL",
+  "PUBCOMP",
+  "SUBSCRIBE",
+  "SUBACK",
+  "UNSUBSCRIBE",
+  "UNSUBACK",
+  "PINGREQ",
+  "PINGRESP",
+  "DISCONNECT"
+};
+
+/**
+ * Message type value to string
+ * @param msg_type see enum mqtt_message_type
+ * 
+ * @return Control message type text string
+ */
+static const char *
+mqtt_msg_type_to_str(u8_t msg_type)
+{
+  if (msg_type >= LWIP_ARRAYSIZE(mqtt_message_type_str)) {
+    msg_type = 0;
+  }
+  return mqtt_message_type_str[msg_type];
+}
+
+#endif
+
+
+/**
+ * Generate MQTT packet identifier
+ * @param client MQTT client
+ * @return New packet identifier, range 1 to 65535
+ */
+static u16_t
+msg_generate_packet_id(mqtt_client_t *client)
+{
+  client->pkt_id_seq++;
+  if (client->pkt_id_seq == 0) {
+    client->pkt_id_seq++;
+  }
+  return client->pkt_id_seq;
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output ring buffer */
+
+
+#define MQTT_RINGBUF_IDX_MASK ((MQTT_OUTPUT_RINGBUF_SIZE) - 1)
+
+/** Add single item to ring buffer */
+#define mqtt_ringbuf_put(rb, item) ((rb)->buf)[(rb)->put++ & MQTT_RINGBUF_IDX_MASK] = (item)
+
+/** Return number of bytes in ring buffer */
+#define mqtt_ringbuf_len(rb) ((u16_t)((rb)->put - (rb)->get))
+
+/** Return number of bytes free in ring buffer */
+#define mqtt_ringbuf_free(rb) (MQTT_OUTPUT_RINGBUF_SIZE - mqtt_ringbuf_len(rb))
+
+/** Return number of bytes possible to read without wrapping around */
+#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - ((rb)->get & MQTT_RINGBUF_IDX_MASK)))
+
+/** Return pointer to ring buffer get position */
+#define mqtt_ringbuf_get_ptr(rb) (&(rb)->buf[(rb)->get & MQTT_RINGBUF_IDX_MASK])
+
+#define mqtt_ringbuf_advance_get_idx(rb, len) ((rb)->get += (len))
+
+
+/**
+ * Try send as many bytes as possible from output ring buffer
+ * @param rb Output ring buffer
+ * @param tpcb TCP connection handle
+ */
+static void
+mqtt_output_send(struct mqtt_ringbuf_t *rb, struct tcp_pcb *tpcb)
+{
+  err_t err;
+  u8_t wrap = 0;
+  u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb);
+  u16_t send_len = tcp_sndbuf(tpcb);
+  LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL);
+
+  if (send_len == 0 || ringbuf_lin_len == 0) {
+    return;
+  }
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n",
+                                send_len, ringbuf_lin_len, ((rb)->get & MQTT_RINGBUF_IDX_MASK), ((rb)->put & MQTT_RINGBUF_IDX_MASK)));
+
+  if (send_len > ringbuf_lin_len) {
+    /* Space in TCP output buffer is larger than available in ring buffer linear portion */
+    send_len = ringbuf_lin_len;
+    /* Wrap around if more data in ring buffer after linear portion */
+    wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len);
+  }
+  err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0));
+  if ((err == ERR_OK) && wrap) {
+    mqtt_ringbuf_advance_get_idx(rb, send_len);
+    /* Use the lesser one of ring buffer linear length and TCP send buffer size */
+    send_len = LWIP_MIN(tcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb));
+    err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY);
+  }
+
+  if (err == ERR_OK) {
+    mqtt_ringbuf_advance_get_idx(rb, send_len);
+    /* Flush */
+    tcp_output(tpcb);
+  } else {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+  }
+}
+
+
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Request queue */
+
+/**
+ * Create request item
+ * @param r_objs Pointer to request objects
+ * @param pkt_id Packet identifier of request
+ * @param cb Packet callback to call when requests lifetime ends
+ * @param arg Parameter following callback
+ * @return Request or NULL if failed to create
+ */
+static struct mqtt_request_t *
+mqtt_create_request(struct mqtt_request_t *r_objs, u16_t pkt_id, mqtt_request_cb_t cb, void *arg)
+{
+  struct mqtt_request_t *r = NULL;
+  u8_t n;
+  LWIP_ASSERT("mqtt_create_request: r_objs != NULL", r_objs != NULL);
+  for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT && r == NULL; n++) {
+    /* Item point to itself if not in use */
+    if (r_objs[n].next == &r_objs[n]) {
+      r = &r_objs[n];
+      r->next = NULL;
+      r->cb = cb;
+      r->arg = arg;
+      r->pkt_id = pkt_id;
+    }
+  }
+  return r;
+}
+
+
+/**
+ * Append request to pending request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param r Request to append
+ */
+static void
+mqtt_append_request(struct mqtt_request_t **tail, struct mqtt_request_t *r)
+{
+  struct mqtt_request_t *head = NULL;
+  s16_t time_before = 0;
+  struct mqtt_request_t *iter = *tail;
+
+  LWIP_ASSERT("mqtt_append_request: tail != NULL", tail != NULL);
+
+  /* Iterate trough queue to find head, and count total timeout time */
+  for (iter = *tail; iter != NULL; iter = iter->next) {
+    time_before += iter->timeout_diff;
+    head = iter;
+  }
+
+  LWIP_ASSERT("mqtt_append_request: time_before <= MQTT_REQ_TIMEOUT", time_before <= MQTT_REQ_TIMEOUT);
+  r->timeout_diff = MQTT_REQ_TIMEOUT - time_before;
+  if (head == NULL) {
+    *tail = r;
+  } else {
+    head->next = r;
+  }
+}
+
+
+/**
+ * Delete request item
+ * @param r Request item to delete
+ */
+static void
+mqtt_delete_request(struct mqtt_request_t *r)
+{
+  if (r != NULL) {
+    r->next = r;
+  }
+}
+
+/**
+ * Remove a request item with a specific packet identifier from request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param pkt_id Packet identifier of request to take
+ * @return Request item if found, NULL if not
+ */
+static struct mqtt_request_t *
+mqtt_take_request(struct mqtt_request_t **tail, u16_t pkt_id)
+{
+  struct mqtt_request_t *iter = NULL, *prev = NULL;
+  LWIP_ASSERT("mqtt_take_request: tail != NULL", tail != NULL);
+  /* Search all request for pkt_id */
+  for (iter = *tail; iter != NULL; iter = iter->next) {
+    if (iter->pkt_id == pkt_id) {
+      break;
+    }
+    prev = iter;
+  }
+
+  /* If request was found */
+  if (iter != NULL) {
+    /* unchain */
+    if (prev == NULL) {
+      *tail= iter->next;
+    } else {
+      prev->next = iter->next;
+    }
+    /* If exists, add remaining timeout time for the request to next */
+    if (iter->next != NULL) {
+      iter->next->timeout_diff += iter->timeout_diff;
+    }
+    iter->next = NULL;
+  }
+  return iter;
+}
+
+/**
+ * Handle requests timeout
+ * @param tail Pointer to request queue tail pointer
+ * @param t Time since last call in seconds
+ */
+static void
+mqtt_request_time_elapsed(struct mqtt_request_t **tail, u8_t t)
+{
+  struct mqtt_request_t *r = *tail;
+  LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL);
+  while (t > 0 && r != NULL) {
+    if (t >= r->timeout_diff) {
+      t -= (u8_t)r->timeout_diff;
+      /* Unchain */
+      *tail = r->next;
+      /* Notify upper layer about timeout */
+      if (r->cb != NULL) {
+        r->cb(r->arg, ERR_TIMEOUT);
+      }
+      mqtt_delete_request(r);
+      /* Tail might be be modified in callback, so re-read it in every iteration */
+      r = *(struct mqtt_request_t * const volatile *)tail;
+    } else {
+      r->timeout_diff -= t;
+      t = 0;
+    }
+  }
+}
+
+/**
+ * Free all request items
+ * @param tail Pointer to request queue tail pointer
+ */
+static void
+mqtt_clear_requests(struct mqtt_request_t **tail)
+{
+  struct mqtt_request_t *iter, *next;
+  LWIP_ASSERT("mqtt_clear_requests: tail != NULL", tail != NULL);
+  for (iter = *tail; iter != NULL; iter = next) {
+    next = iter->next;
+    mqtt_delete_request(iter);
+  }
+  *tail = NULL;
+}
+/**
+ * Initialize all request items
+ * @param r_objs Pointer to request objects
+ */
+static void
+mqtt_init_requests(struct mqtt_request_t *r_objs)
+{
+  u8_t n;
+  LWIP_ASSERT("mqtt_init_requests: r_objs != NULL", r_objs != NULL);
+  for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) {
+    /* Item pointing to itself indicates unused */
+    r_objs[n].next = &r_objs[n];
+  }
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output message build helpers */
+
+
+static void
+mqtt_output_append_u8(struct mqtt_ringbuf_t *rb, u8_t value)
+{
+  mqtt_ringbuf_put(rb, value);
+}
+
+static
+void mqtt_output_append_u16(struct mqtt_ringbuf_t *rb, u16_t value)
+{
+  mqtt_ringbuf_put(rb, value >> 8);
+  mqtt_ringbuf_put(rb, value & 0xff);
+}
+
+static void
+mqtt_output_append_buf(struct mqtt_ringbuf_t *rb, const void *data, u16_t length)
+{
+  u16_t n;
+  for (n = 0; n < length; n++) {
+    mqtt_ringbuf_put(rb, ((const u8_t *)data)[n]);
+  }
+}
+
+static void
+mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t length)
+{
+  u16_t n;
+  mqtt_ringbuf_put(rb, length >> 8);
+  mqtt_ringbuf_put(rb, length & 0xff);
+  for (n = 0; n < length; n++) {
+    mqtt_ringbuf_put(rb, str[n]);
+  }
+}
+
+/**
+ * Append fixed header
+ * @param rb Output ring buffer
+ * @param msg_type see enum mqtt_message_type
+ * @param dup MQTT DUP flag
+ * @param qos MQTT QoS field
+ * @param retain MQTT retain flag
+ * @param r_length Remaining length after fixed header
+ */
+
+static void
+mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t dup,
+                 u8_t qos, u8_t retain, u16_t r_length)
+{
+  /* Start with control byte */
+  mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1)));
+  /* Encode remaining length field */
+  do {
+    mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0));
+    r_length >>= 7;
+  } while (r_length > 0);
+}
+
+
+/**
+ * Check output buffer space
+ * @param rb Output ring buffer
+ * @param r_length Remaining length after fixed header
+ * @return 1 if message will fit, 0 if not enough buffer space
+ */
+static u8_t
+mqtt_output_check_space(struct mqtt_ringbuf_t *rb, u16_t r_length)
+{
+  /* Start with length of type byte + remaining length */
+  u16_t total_len = 1 + r_length;
+
+  LWIP_ASSERT("mqtt_output_check_space: rb != NULL", rb != NULL);
+
+ /* Calculate number of required bytes to contain the remaining bytes field and add to total*/
+  do {
+    total_len++;
+    r_length >>= 7;
+  } while (r_length > 0);
+
+  return (total_len <= mqtt_ringbuf_free(rb));
+}
+
+
+/**
+ * Close connection to server
+ * @param client MQTT client
+ * @param reason Reason for disconnection
+ */
+static void
+mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason)
+{
+  LWIP_ASSERT("mqtt_close: client != NULL", client != NULL);
+
+  /* Bring down TCP connection if not already done */
+  if (client->conn != NULL) {
+    err_t res;
+    tcp_recv(client->conn, NULL);
+    tcp_err(client->conn,  NULL);
+    tcp_sent(client->conn, NULL);
+    res = tcp_close(client->conn);
+    if (res != ERR_OK) {
+      tcp_abort(client->conn);
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_close: Close err=%s\n", lwip_strerr(res)));
+    }
+    client->conn = NULL;
+  }
+
+  /* Remove all pending requests */
+  mqtt_clear_requests(&client->pend_req_queue);
+  /* Stop cyclic timer */
+  sys_untimeout(mqtt_cyclic_timer, client);
+
+  /* Notify upper layer of disconnection if changed state */
+  if (client->conn_state != TCP_DISCONNECTED) {
+
+    client->conn_state = TCP_DISCONNECTED;
+    if (client->connect_cb != NULL) {
+      client->connect_cb(client, client->connect_arg, reason);
+    }
+  }
+}
+
+
+/**
+ * Interval timer, called every MQTT_CYCLIC_TIMER_INTERVAL seconds in MQTT_CONNECTING and MQTT_CONNECTED states
+ * @param arg MQTT client
+ */
+static void
+mqtt_cyclic_timer(void *arg)
+{
+  u8_t restart_timer = 1;
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  LWIP_ASSERT("mqtt_cyclic_timer: client != NULL", client != NULL);
+
+  if (client->conn_state == MQTT_CONNECTING) {
+    client->cyclic_tick++;
+    if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= MQTT_CONNECT_TIMOUT) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: CONNECT attempt to server timed out\n"));
+      /* Disconnect TCP */
+      mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+      restart_timer = 0;
+    }
+  } else if (client->conn_state == MQTT_CONNECTED) {
+    /* Handle timeout for pending requests */
+    mqtt_request_time_elapsed(&client->pend_req_queue, MQTT_CYCLIC_TIMER_INTERVAL);
+
+    /* keep_alive > 0 means keep alive functionality shall be used */
+    if (client->keep_alive > 0) {
+
+      client->server_watchdog++;
+      /* If reception from server has been idle for 1.5*keep_alive time, server is considered unresponsive */
+      if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive + client->keep_alive/2)) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Server incoming keep-alive timeout\n"));
+        mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+        restart_timer = 0;
+      }
+
+      /* If time for a keep alive message to be sent, transmission has been idle for keep_alive time */
+      if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) {
+        LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: Sending keep-alive message to server\n"));
+        if (mqtt_output_check_space(&client->output, 0) != 0) {
+          mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0);
+          client->cyclic_tick = 0;
+        }
+      } else {
+        client->cyclic_tick++;
+      }
+    }
+  } else {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Timer should not be running in state %d\n", client->conn_state));
+    restart_timer = 0;
+  }
+  if (restart_timer) {
+    sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, arg);
+  }
+}
+
+
+/**
+ * Send PUBACK, PUBREC or PUBREL response message
+ * @param client MQTT client
+ * @param msg PUBACK, PUBREC or PUBREL
+ * @param pkt_id Packet identifier
+ * @param qos QoS value
+ * @return ERR_OK if successful, ERR_MEM if out of memory
+ */
+static err_t
+pub_ack_rec_rel_response(mqtt_client_t *client, u8_t msg, u16_t pkt_id, u8_t qos)
+{
+  err_t err = ERR_OK;
+  if (mqtt_output_check_space(&client->output, 2)) {
+    mqtt_output_append_fixed_header(&client->output, msg, 0, qos, 0, 2);
+    mqtt_output_append_u16(&client->output, pkt_id);
+    mqtt_output_send(&client->output, client->conn);
+  } else {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE,("pub_ack_rec_rel_response: OOM creating response: %s with pkt_id: %d\n",
+                                  mqtt_msg_type_to_str(msg), pkt_id));
+    err = ERR_MEM;
+  }
+  return err;
+}
+
+/**
+ * Subscribe response from server
+ * @param r Matching request
+ * @param result Result code from server
+ */
+static void
+mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result)
+{
+  if (r->cb != NULL) {
+    r->cb(r->arg, result < 3 ? ERR_OK : ERR_ABRT);
+  }
+}
+
+
+/**
+ * Complete MQTT message received or buffer full
+ * @param client MQTT client
+ * @param fixed_hdr_idx header index
+ * @param length length received part
+ * @param remaining_length Remaining length of complete message
+ */
+static mqtt_connection_status_t
+mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length)
+{
+  mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED;
+
+  u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_idx;
+
+  /* Control packet type */
+  u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]);
+  u16_t pkt_id = 0;
+
+  if (pkt_type == MQTT_MSG_TYPE_CONNACK) {
+    if (client->conn_state == MQTT_CONNECTING) {
+      /* Get result code from CONNACK */
+      res = (mqtt_connection_status_t)var_hdr_payload[1];
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: Connect response code %d\n", res));
+      if (res == MQTT_CONNECT_ACCEPTED) {
+        /* Reset cyclic_tick when changing to connected state */
+        client->cyclic_tick = 0;
+        client->conn_state = MQTT_CONNECTED;
+        /* Notify upper layer */
+        if (client->connect_cb != 0) {
+          client->connect_cb(client, client->connect_arg, res);
+        }
+      }
+    } else {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Received CONNACK in connected state\n"));
+    }
+  } else if (pkt_type == MQTT_MSG_TYPE_PINGRESP) {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE,( "mqtt_message_received: Received PINGRESP from server\n"));
+
+  } else if (pkt_type == MQTT_MSG_TYPE_PUBLISH) {
+    u16_t payload_offset = 0;
+    u16_t payload_length = length;
+    u8_t qos = MQTT_CTL_PACKET_QOS(client->rx_buffer[0]);
+
+    if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) {
+      /* Should have topic and pkt id*/
+      uint8_t *topic;
+      uint16_t after_topic;
+      u8_t bkp;
+      u16_t topic_len = var_hdr_payload[0];
+      topic_len = (topic_len << 8) + (u16_t)(var_hdr_payload[1]);
+
+      topic = var_hdr_payload + 2;
+      after_topic = 2 + topic_len;
+      /* Check length, add one byte even for QoS 0 so that zero termination will fit */
+      if ((after_topic + qos ? 2 : 1) > length) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Receive buffer can not fit topic + pkt_id\n"));
+        goto out_disconnect;
+      }
+
+      /* id for QoS 1 and 2 */
+      if (qos > 0) {
+        client->inpub_pkt_id = ((u16_t)var_hdr_payload[after_topic] << 8) + (u16_t)var_hdr_payload[after_topic + 1];
+        after_topic += 2;
+      } else {
+        client->inpub_pkt_id = 0;
+      }
+      /* Take backup of byte after topic */
+      bkp = topic[topic_len];
+      /* Zero terminate string */
+      topic[topic_len] = 0;
+      /* Payload data remaining in receive buffer */
+      payload_length = length - after_topic;
+      payload_offset = after_topic;
+
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Received message with QoS %d at topic: %s, payload length %d\n",
+                                    qos, topic, remaining_length + payload_length));
+      if (client->pub_cb != NULL) {
+        client->pub_cb(client->inpub_arg, (const char *)topic, remaining_length + payload_length);
+      }
+      /* Restore byte after topic */
+      topic[topic_len] = bkp;
+    }
+    if (payload_length > 0 || remaining_length == 0) {
+      client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0);
+      /* Reply if QoS > 0 */
+      if (remaining_length == 0 && qos > 0) {
+        /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */
+        u8_t resp_msg = (qos == 1) ? MQTT_MSG_TYPE_PUBACK : MQTT_MSG_TYPE_PUBREC;
+        LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Sending publish response: %s with pkt_id: %d\n",
+                                      mqtt_msg_type_to_str(resp_msg), client->inpub_pkt_id));
+        pub_ack_rec_rel_response(client, resp_msg, client->inpub_pkt_id, 0);
+      }
+    }
+  } else {
+    /* Get packet identifier */
+    pkt_id = (u16_t)var_hdr_payload[0] << 8;
+    pkt_id |= (u16_t)var_hdr_payload[1];
+    if (pkt_id == 0) {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Got message with illegal packet identifier: 0\n"));
+      goto out_disconnect;
+    }
+    if (pkt_type == MQTT_MSG_TYPE_PUBREC) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREC, sending PUBREL with pkt_id: %d\n",pkt_id));
+      pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBREL, pkt_id, 1);
+
+    } else if (pkt_type == MQTT_MSG_TYPE_PUBREL) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREL, sending PUBCOMP response with pkt_id: %d\n",pkt_id));
+      pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBCOMP, pkt_id, 0);
+
+    } else if (pkt_type == MQTT_MSG_TYPE_SUBACK || pkt_type == MQTT_MSG_TYPE_UNSUBACK ||
+              pkt_type == MQTT_MSG_TYPE_PUBCOMP || pkt_type == MQTT_MSG_TYPE_PUBACK) {
+      struct mqtt_request_t *r = mqtt_take_request(&client->pend_req_queue, pkt_id);
+      if (r != NULL) {
+        LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: %s response with id %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+        if (pkt_type == MQTT_MSG_TYPE_SUBACK) {
+          if (length < 3) {
+            LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: To small SUBACK packet\n"));
+            goto out_disconnect;
+          } else {
+            mqtt_incomming_suback(r, var_hdr_payload[2]);
+          }
+        } else if (r->cb != NULL) {
+          r->cb(r->arg, ERR_OK);
+        }
+        mqtt_delete_request(r);
+      } else {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received %s reply, with wrong pkt_id: %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+      }
+    } else {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received unknown message type: %d\n", pkt_type));
+      goto out_disconnect;
+    }
+  }
+  return res;
+out_disconnect:
+  return MQTT_CONNECT_DISCONNECTED;
+}
+
+
+/**
+ * MQTT incoming message parser
+ * @param client MQTT client
+ * @param p PBUF chain of received data
+ * @return Connection status
+ */
+static mqtt_connection_status_t
+mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p)
+{
+  u16_t in_offset = 0;
+  u32_t msg_rem_len = 0;
+  u8_t fixed_hdr_idx = 0;
+  u8_t b = 0;
+
+  while (p->tot_len > in_offset) {
+    if ((fixed_hdr_idx < 2) || ((b & 0x80) != 0)) {
+
+      if (fixed_hdr_idx < client->msg_idx) {
+        b = client->rx_buffer[fixed_hdr_idx];
+      } else {
+        b = pbuf_get_at(p, in_offset++);
+        client->rx_buffer[client->msg_idx++] = b;
+      }
+      fixed_hdr_idx++;
+
+      if (fixed_hdr_idx >= 2) {
+        msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_idx - 2) * 7);
+        if ((b & 0x80) == 0) {
+          LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: Remaining length after fixed header: %d\n", msg_rem_len));
+          if (msg_rem_len == 0) {
+            /* Complete message with no extra headers of payload received */
+            mqtt_message_received(client, fixed_hdr_idx, 0, 0);
+            client->msg_idx = 0;
+            fixed_hdr_idx = 0;
+          } else {
+            /* Bytes remaining in message */
+            msg_rem_len = (msg_rem_len + fixed_hdr_idx) - client->msg_idx;
+          }
+        }
+      }
+    } else {
+      u16_t cpy_len, cpy_start, buffer_space;
+
+      cpy_start = (client->msg_idx - fixed_hdr_idx) % (MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_idx) + fixed_hdr_idx;
+
+      /* Allow to copy the lesser one of available length in input data or bytes remaining in message */
+      cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len);
+
+      /* Limit to available space in buffer */
+      buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - cpy_start;
+      if (cpy_len > buffer_space) {
+        cpy_len = buffer_space;
+      }
+      pbuf_copy_partial(p, client->rx_buffer+cpy_start, cpy_len, in_offset);
+
+      /* Advance get and put indexes  */
+      client->msg_idx += cpy_len;
+      in_offset += cpy_len;
+      msg_rem_len -= cpy_len;
+
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: msg_idx: %d, cpy_len: %d, remaining %d\n", client->msg_idx, cpy_len, msg_rem_len));
+      if (msg_rem_len == 0 || cpy_len == buffer_space) {
+        /* Whole message received or buffer is full */
+        mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_idx, (cpy_start + cpy_len) - fixed_hdr_idx, msg_rem_len);
+        if (res != MQTT_CONNECT_ACCEPTED) {
+          return res;
+        }
+        if (msg_rem_len == 0) {
+          /* Reset parser state */
+          client->msg_idx = 0;
+          /* msg_tot_len = 0; */
+          fixed_hdr_idx = 0;
+        }
+      }
+    }
+  }
+  return MQTT_CONNECT_ACCEPTED;
+}
+
+
+/**
+ * TCP received callback function. @see tcp_recv_fn
+ * @param arg MQTT client
+ * @param p PBUF chain of received data
+ * @param err Passed as return value if not ERR_OK
+ * @return ERR_OK or err passed into callback
+ */
+static err_t
+mqtt_tcp_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL);
+  LWIP_ASSERT("mqtt_tcp_recv_cb: client->conn == pcb", client->conn == pcb);
+
+  if (p == NULL) {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_recv_cb: Recv pbuf=NULL, remote has closed connection\n"));
+    mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+  } else {
+    mqtt_connection_status_t res;
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_recv_cb: Recv err=%d\n", err));
+      pbuf_free(p);
+      return err;
+    }
+
+    /* Tell remote that data has been received */
+    tcp_recved(pcb, p->tot_len);
+    res = mqtt_parse_incoming(client, p);
+    pbuf_free(p);
+
+    if (res != MQTT_CONNECT_ACCEPTED) {
+      mqtt_close(client, res);
+    }
+    /* If keep alive functionality is used */
+    if (client->keep_alive != 0) {
+      /* Reset server alive watchdog */
+      client->server_watchdog = 0;
+    }
+
+  }
+  return ERR_OK;
+}
+
+
+/**
+ * TCP data sent callback function. @see tcp_sent_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @param len Number of bytes sent
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+
+  LWIP_UNUSED_ARG(tpcb);
+  LWIP_UNUSED_ARG(len);
+
+  if (client->conn_state == MQTT_CONNECTED) {
+    struct mqtt_request_t *r;
+
+    /* Reset keep-alive send timer and server watchdog */
+    client->cyclic_tick = 0;
+    client->server_watchdog = 0;
+    /* QoS 0 publish has no response from server, so call its callbacks here */
+    while ((r = mqtt_take_request(&client->pend_req_queue, 0)) != NULL) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback\n"));
+      if (r->cb != NULL) {
+        r->cb(r->arg, ERR_OK);
+      }
+      mqtt_delete_request(r);
+    }
+    /* Try send any remaining buffers from output queue */
+    mqtt_output_send(&client->output, client->conn);
+  }
+  return ERR_OK;
+}
+
+/**
+ * TCP error callback function. @see tcp_err_fn
+ * @param arg MQTT client
+ * @param err Error encountered
+ */
+static void
+mqtt_tcp_err_cb(void *arg, err_t err)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  LWIP_UNUSED_ARG(err); /* only used for debug output */
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_err_cb: TCP error callback: error %d, arg: %p\n", err, arg));
+  LWIP_ASSERT("mqtt_tcp_err_cb: client != NULL", client != NULL);
+  /* Set conn to null before calling close as pcb is already deallocated*/
+  client->conn = 0;
+  mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+}
+
+/**
+ * TCP poll callback function. @see tcp_poll_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @return err ERR_OK
+ */
+static err_t
+mqtt_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  if (client->conn_state == MQTT_CONNECTED) {
+    /* Try send any remaining buffers from output queue */
+    mqtt_output_send(&client->output, tpcb);
+  }
+  return ERR_OK;
+}
+
+/**
+ * TCP connect callback function. @see tcp_connected_fn
+ * @param arg MQTT client
+ * @param err Always ERR_OK, mqtt_tcp_err_cb is called in case of error
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_connect_cb(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+  mqtt_client_t* client = (mqtt_client_t *)arg;
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_connect_cb: TCP connect error %d\n", err));
+    return err;
+  }
+
+  /* Initiate receiver state */
+  client->msg_idx = 0;
+
+  /* Setup TCP callbacks */
+  tcp_recv(tpcb, mqtt_tcp_recv_cb);
+  tcp_sent(tpcb, mqtt_tcp_sent_cb);
+  tcp_poll(tpcb, mqtt_tcp_poll_cb, 2);
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_connect_cb: TCP connection established to server\n"));
+  /* Enter MQTT connect state */
+  client->conn_state = MQTT_CONNECTING;
+
+  /* Start cyclic timer */
+  sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, client);
+  client->cyclic_tick = 0;
+
+  /* Start transmission from output queue, connect message is the first one out*/
+  mqtt_output_send(&client->output, client->conn);
+
+  return ERR_OK;
+}
+
+
+
+/*---------------------------------------------------------------------------------------------------- */
+/* Public API */
+
+
+/**
+ * @ingroup mqtt
+ * MQTT publish function.
+ * @param client MQTT client
+ * @param topic Publish topic string
+ * @param payload Data to publish (NULL is allowed)
+ * @param payload_length: Length of payload (0 is allowed)
+ * @param qos Quality of service, 0 1 or 2
+ * @param retain MQTT retain flag
+ * @param cb Callback to call when publish is complete or has timed out
+ * @param arg User supplied argument to publish callback
+ * @return ERR_OK if successful
+ *         ERR_CONN if client is disconnected
+ *         ERR_MEM if short on memory
+ */
+err_t
+mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+             mqtt_request_cb_t cb, void *arg)
+{
+  struct mqtt_request_t *r;
+  u16_t pkt_id;
+  size_t topic_strlen;
+  size_t total_len;
+  u16_t topic_len;
+  u16_t remaining_length;
+
+  LWIP_ASSERT("mqtt_publish: client != NULL", client);
+  LWIP_ASSERT("mqtt_publish: topic != NULL", topic);
+  LWIP_ERROR("mqtt_publish: TCP disconnected", (client->conn_state != TCP_DISCONNECTED), return ERR_CONN);
+
+  topic_strlen = strlen(topic);
+  LWIP_ERROR("mqtt_publish: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+  topic_len = (u16_t)topic_strlen;
+  total_len = 2 + topic_len + payload_length;
+  LWIP_ERROR("mqtt_publish: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+  remaining_length = (u16_t)total_len;
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_publish: Publish with payload length %d to topic \"%s\"\n", payload_length, topic));
+
+  if (qos > 0) {
+    remaining_length += 2;
+    /* Generate pkt_id id for QoS1 and 2 */
+    pkt_id = msg_generate_packet_id(client);
+  } else {
+    /* Use reserved value pkt_id 0 for QoS 0 in request handle */
+    pkt_id = 0;
+  }
+
+  r = mqtt_create_request(client->req_list, pkt_id, cb, arg);
+  if (r == NULL) {
+    return ERR_MEM;
+  }
+
+  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+    mqtt_delete_request(r);
+    return ERR_MEM;
+  }
+  /* Append fixed header */
+  mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain, remaining_length);
+
+  /* Append Topic */
+  mqtt_output_append_string(&client->output, topic, topic_len);
+
+  /* Append packet if for QoS 1 and 2*/
+  if (qos > 0) {
+    mqtt_output_append_u16(&client->output, pkt_id);
+  }
+
+  /* Append optional publish payload */
+  if ((payload != NULL) && (payload_length > 0)) {
+    mqtt_output_append_buf(&client->output, payload, payload_length);
+  }
+
+  mqtt_append_request(&client->pend_req_queue, r);
+  mqtt_output_send(&client->output, client->conn);
+  return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * MQTT subscribe/unsubscribe function.
+ * @param client MQTT client
+ * @param topic topic to subscribe to
+ * @param qos Quality of service, 0 1 or 2 (only used for subscribe)
+ * @param cb Callback to call when subscribe/unsubscribe reponse is received
+ * @param arg User supplied argument to publish callback
+ * @param sub 1 for subscribe, 0 for unsubscribe
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub)
+{
+  size_t topic_strlen;
+  size_t total_len;
+  u16_t topic_len;
+  u16_t remaining_length;
+  u16_t pkt_id;
+  struct mqtt_request_t *r;
+
+  LWIP_ASSERT("mqtt_sub_unsub: client != NULL", client);
+  LWIP_ASSERT("mqtt_sub_unsub: topic != NULL", topic);
+
+  topic_strlen = strlen(topic);
+  LWIP_ERROR("mqtt_sub_unsub: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+  topic_len = (u16_t)topic_strlen;
+  /* Topic string, pkt_id, qos for subscribe */
+  total_len =  topic_len + 2 + 2 + (sub != 0);
+  LWIP_ERROR("mqtt_sub_unsub: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+  remaining_length = (u16_t)total_len;
+
+  LWIP_ASSERT("mqtt_sub_unsub: qos < 3", qos < 3);
+  if (client->conn_state == TCP_DISCONNECTED) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_sub_unsub: Can not (un)subscribe in disconnected state\n"));
+    return ERR_CONN;
+  }
+
+  pkt_id = msg_generate_packet_id(client);
+  r = mqtt_create_request(client->req_list, pkt_id, cb, arg);
+  if (r == NULL) {
+    return ERR_MEM;
+  }
+
+  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+    mqtt_delete_request(r);
+    return ERR_MEM;
+  }
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_sub_unsub: Client (un)subscribe to topic \"%s\", id: %d\n", topic, pkt_id));
+
+  mqtt_output_append_fixed_header(&client->output, sub ? MQTT_MSG_TYPE_SUBSCRIBE : MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0, remaining_length);
+  /* Packet id */
+  mqtt_output_append_u16(&client->output, pkt_id);
+  /* Topic */
+  mqtt_output_append_string(&client->output, topic, topic_len);
+  /* QoS */
+  if (sub != 0) {
+    mqtt_output_append_u8(&client->output, LWIP_MIN(qos, 2));
+  }
+
+  mqtt_append_request(&client->pend_req_queue, r);
+  mqtt_output_send(&client->output, client->conn);
+  return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Set callback to handle incoming publish requests from server
+ * @param client MQTT client
+ * @param pub_cb Callback invoked when publish starts, contain topic and total length of payload
+ * @param data_cb Callback for each fragment of payload that arrives
+ * @param arg User supplied argument to both callbacks
+ */
+void
+mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb,
+                             mqtt_incoming_data_cb_t data_cb, void *arg)
+{
+  LWIP_ASSERT("mqtt_set_inpub_callback: client != NULL", client != NULL);
+  client->data_cb = data_cb;
+  client->pub_cb = pub_cb;
+  client->inpub_arg = arg;
+}
+
+/**
+ * @ingroup mqtt
+ * Create a new MQTT client instance
+ * @return Pointer to instance on success, NULL otherwise
+ */
+mqtt_client_t *
+mqtt_client_new(void)
+{
+  mqtt_client_t *client = (mqtt_client_t *)mem_malloc(sizeof(mqtt_client_t));
+  if (client != NULL) {
+    memset(client, 0, sizeof(mqtt_client_t));
+  }
+  return client;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Connect to MQTT server
+ * @param client MQTT client
+ * @param ip_addr Server IP
+ * @param port Server port
+ * @param cb Connection state change callback
+ * @param arg User supplied argument to connection callback
+ * @param client_info Client identification and connection options
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+                    const struct mqtt_connect_client_info_t *client_info)
+{
+  err_t err;
+  size_t len;
+  u16_t client_id_length;
+  /* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */
+  u16_t remaining_length = 2 + 4 + 1 + 1 + 2;
+  u8_t flags = 0, will_topic_len = 0, will_msg_len = 0;
+
+  LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL);
+  LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL);
+  LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL);
+  LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL);
+
+  if (client->conn_state != TCP_DISCONNECTED) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Already connected\n"));
+    return ERR_ISCONN;
+  }
+
+  /* Wipe clean */
+  memset(client, 0, sizeof(mqtt_client_t));
+  client->connect_arg = arg;
+  client->connect_cb = cb;
+  client->keep_alive = client_info->keep_alive;
+  mqtt_init_requests(client->req_list);
+
+  /* Build connect message */
+  if (client_info->will_topic != NULL && client_info->will_msg != NULL) {
+    flags |= MQTT_CONNECT_FLAG_WILL;
+    flags |= (client_info->will_qos & 3) << 3;
+    if (client_info->will_retain) {
+      flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
+    }
+    len = strlen(client_info->will_topic);
+    LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL);
+    will_topic_len = (u8_t)len;
+    len = strlen(client_info->will_msg);
+    LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL);
+    will_msg_len = (u8_t)len;
+    len = remaining_length + 2 + will_topic_len + 2 + will_msg_len;
+    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+    remaining_length = (u16_t)len;
+  }
+
+  /* Don't complicate things, always connect using clean session */
+  flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
+
+  len = strlen(client_info->client_id);
+  LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL);
+  client_id_length = (u16_t)len;
+  len = remaining_length + 2 + client_id_length;
+  LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+  remaining_length = (u16_t)len;
+
+  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+    return ERR_MEM;
+  }
+
+  client->conn = tcp_new();
+  if (client->conn == NULL) {
+    return ERR_MEM;
+  }
+
+  /* Set arg pointer for callbacks */
+  tcp_arg(client->conn, client);
+  /* Any local address, pick random local port number */
+  err = tcp_bind(client->conn, IP_ADDR_ANY, 0);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Error binding to local ip/port, %d\n", err));
+    goto tcp_fail;
+  }
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port));
+
+  /* Connect to server */
+  err = tcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err));
+    goto tcp_fail;
+  }
+  /* Set error callback */
+  tcp_err(client->conn, mqtt_tcp_err_cb);
+  client->conn_state = TCP_CONNECTING;
+
+  /* Append fixed header */
+  mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length);
+  /* Append Protocol string */
+  mqtt_output_append_string(&client->output, "MQTT", 4);
+  /* Append Protocol level */
+  mqtt_output_append_u8(&client->output, 4);
+  /* Append connect flags */
+  mqtt_output_append_u8(&client->output, flags);
+  /* Append keep-alive */
+  mqtt_output_append_u16(&client->output, client_info->keep_alive);
+  /* Append client id */
+  mqtt_output_append_string(&client->output, client_info->client_id, client_id_length);
+  /* Append will message if used */
+  if (will_topic_len > 0) {
+    mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len);
+    mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len);
+  }
+  return ERR_OK;
+
+tcp_fail:
+  tcp_abort(client->conn);
+  client->conn = NULL;
+  return err;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Disconnect from MQTT server
+ * @param client MQTT client
+ */
+void
+mqtt_disconnect(mqtt_client_t *client)
+{
+  LWIP_ASSERT("mqtt_disconnect: client != NULL", client);
+  /* If connection in not already closed */
+  if (client->conn_state != TCP_DISCONNECTED) {
+    /* Set conn_state before calling mqtt_close to prevent callback from being called */
+    client->conn_state = TCP_DISCONNECTED;
+    mqtt_close(client, (mqtt_connection_status_t)0);
+  }
+}
+
+/**
+ * @ingroup mqtt
+ * Check connection with server
+ * @param client MQTT client
+ * @return 1 if connected to server, 0 otherwise
+ */
+u8_t
+mqtt_client_is_connected(mqtt_client_t *client)
+{
+  LWIP_ASSERT("mqtt_client_is_connected: client != NULL", client);
+  return client->conn_state == MQTT_CONNECTED;
+}
diff --git a/src/apps/snmp/snmp_mib2_ip.c b/src/apps/snmp/snmp_mib2_ip.c
index 913d97f..4f05180 100644
--- a/src/apps/snmp/snmp_mib2_ip.c
+++ b/src/apps/snmp/snmp_mib2_ip.c
@@ -581,7 +581,7 @@
       snmp_ip4_to_oid(ip, &test_oid[1]);
 
       /* check generated OID: is it a candidate for the next one? */
-      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), (void*)(size_t)i);
+      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), LWIP_PTR_NUMERIC_CAST(void*, i));
     }
   }
 
@@ -589,7 +589,7 @@
   if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
     snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
     /* fill in object properties */
-    return ip_NetToMediaTable_get_cell_value_core((u8_t)(size_t)state.reference, column, value, value_len);
+    return ip_NetToMediaTable_get_cell_value_core(LWIP_PTR_NUMERIC_CAST(u8_t, state.reference), column, value, value_len);
   }
 
   /* not found */
diff --git a/src/apps/snmp/snmp_netconn.c b/src/apps/snmp/snmp_netconn.c
index 070b41c..24c3e26 100644
--- a/src/apps/snmp/snmp_netconn.c
+++ b/src/apps/snmp/snmp_netconn.c
@@ -36,6 +36,7 @@
 
 #if LWIP_SNMP && SNMP_USE_NETCONN
 
+#include <string.h>
 #include "lwip/api.h"
 #include "lwip/ip.h"
 #include "lwip/udp.h"
@@ -52,7 +53,7 @@
   LWIP_UNUSED_ARG(arg);
   
   /* Bind to SNMP port with default IP address */
- #if LWIP_IPV6
+#if LWIP_IPV6
   conn = netconn_new(NETCONN_UDP_IPV6);
   netconn_bind(conn, IP6_ADDR_ANY, SNMP_IN_PORT);
 #else /* LWIP_IPV6 */
diff --git a/src/apps/snmp/snmp_threadsync.c b/src/apps/snmp/snmp_threadsync.c
index 858c40e..204f265 100644
--- a/src/apps/snmp/snmp_threadsync.c
+++ b/src/apps/snmp/snmp_threadsync.c
@@ -211,6 +211,7 @@
   err_t err = sys_mutex_new(&instance->sem_usage_mutex);
   LWIP_ASSERT("Failed to set up mutex", err == ERR_OK);
   err = sys_sem_new(&instance->sem, 0);
+  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
   LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK);
   instance->sync_fn = sync_fn;
 }
diff --git a/src/apps/sntp/sntp.c b/src/apps/sntp/sntp.c
index fb952111..71b2abe 100644
--- a/src/apps/sntp/sntp.c
+++ b/src/apps/sntp/sntp.c
@@ -573,6 +573,7 @@
 {
   if (sntp_pcb != NULL) {
     sys_untimeout(sntp_request, NULL);
+    sys_untimeout(sntp_try_next_server, NULL);
     udp_remove(sntp_pcb);
     sntp_pcb = NULL;
   }
@@ -688,7 +689,7 @@
   if (idx < SNTP_MAX_SERVERS) {
     return &sntp_servers[idx].addr;
   }
-  return IP4_ADDR_ANY;
+  return IP_ADDR_ANY;
 }
 
 #if SNTP_SERVER_DNS
diff --git a/src/core/def.c b/src/core/def.c
index 99f3d62..8125313 100644
--- a/src/core/def.c
+++ b/src/core/def.c
@@ -20,6 +20,10 @@
  * @ingroup sys_layer
  * lwIP provides default implementations for non-standard functions.
  * These can be mapped to OS functions to reduce code footprint if desired.
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
  */
 
 /*
@@ -101,13 +105,13 @@
 lwip_strnstr(const char* buffer, const char* token, size_t n)
 {
   const char* p;
-  int tokenlen = (int)strlen(token);
+  size_t tokenlen = strlen(token);
   if (tokenlen == 0) {
-    return (char *)(size_t)buffer;
+    return LWIP_CONST_CAST(char *, buffer);
   }
   for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
     if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) {
-      return (char *)(size_t)p;
+      return LWIP_CONST_CAST(char *, p);
     }
   }
   return NULL;
diff --git a/src/core/dns.c b/src/core/dns.c
index 2aa1dd9..056672b 100644
--- a/src/core/dns.c
+++ b/src/core/dns.c
@@ -24,6 +24,11 @@
  * the resolver code calls a specified callback function (which
  * must be implemented by the module that uses the resolver).
  * 
+ * Multicast DNS queries are supported for names ending on ".local".
+ * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762
+ * chapter 5.1), this is not a fully compliant implementation of continuous
+ * mDNS querying!
+ *
  * All functions must be called from TCPIP thread.
  * 
  * @see @ref netconn_common for thread-safe access.
@@ -71,6 +76,7 @@
 /** @todo: define good default values (rfc compliance) */
 /** @todo: improve answer parsing, more checkings... */
 /** @todo: check RFC1035 - 7.3. Processing responses */
+/** @todo: one-shot mDNS: dual-stack fallback to another IP version */
 
 /*-----------------------------------------------------------------------------
  * Includes
@@ -116,6 +122,13 @@
 #error DNS_MAX_TTL must be a positive 32-bit value
 #endif
 
+#if DNS_TABLE_SIZE > 255
+#error DNS_TABLE_SIZE must fit into an u8_t
+#endif
+#if DNS_MAX_SERVERS > 255
+#error DNS_MAX_SERVERS must fit into an u8_t
+#endif
+
 /* The number of parallel requests (i.e. calls to dns_gethostbyname
  * that cannot be answered from the DNS table.
  * This is set to the table size by default.
@@ -123,6 +136,10 @@
 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
 #ifndef DNS_MAX_REQUESTS
 #define DNS_MAX_REQUESTS          DNS_TABLE_SIZE
+#else
+#if DNS_MAX_REQUESTS > 255
+#error DNS_MAX_REQUESTS must fit into an u8_t
+#endif
 #endif
 #else
 /* In this configuration, both arrays have to have the same size and are used
@@ -134,6 +151,10 @@
 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
 #ifndef DNS_MAX_SOURCE_PORTS
 #define DNS_MAX_SOURCE_PORTS      DNS_MAX_REQUESTS
+#else
+#if DNS_MAX_SOURCE_PORTS > 255
+#error DNS_MAX_SOURCE_PORTS must fit into an u8_t
+#endif
 #endif
 #else
 #ifdef DNS_MAX_SOURCE_PORTS
@@ -160,6 +181,12 @@
 #define LWIP_DNS_SET_ADDRTYPE(x, y)
 #endif /* LWIP_IPV4 && LWIP_IPV6 */
 
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+#define LWIP_DNS_ISMDNS_ARG(x) , x
+#else
+#define LWIP_DNS_ISMDNS_ARG(x)
+#endif
+
 /** DNS query message structure.
     No packing needed: only used locally on the stack. */
 struct dns_query {
@@ -209,6 +236,9 @@
 #if LWIP_IPV4 && LWIP_IPV6
   u8_t reqaddrtype;
 #endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  u8_t is_mdns;
+#endif
 };
 
 /** DNS request table entry: used when dns_gehostbyname cannot answer the
@@ -272,6 +302,13 @@
 static struct dns_req_entry   dns_requests[DNS_MAX_REQUESTS];
 static ip_addr_t              dns_servers[DNS_MAX_SERVERS];
 
+#if LWIP_IPV4
+const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif /* LWIP_IPV6 */
+
 /**
  * Initialize the resolver: set up the UDP pcb and configure the default server
  * (if DNS_SERVER_ADDRESS is set).
@@ -329,7 +366,7 @@
     if (dnsserver != NULL) {
       dns_servers[numdns] = (*dnsserver);
     } else {
-      dns_servers[numdns] = *IP4_ADDR_ANY;
+      dns_servers[numdns] = *IP_ADDR_ANY;
     }
   }
 }
@@ -348,7 +385,7 @@
   if (numdns < DNS_MAX_SERVERS) {
     return &dns_servers[numdns];
   } else {
-    return IP4_ADDR_ANY;
+    return IP_ADDR_ANY;
   }
 }
 
@@ -665,7 +702,11 @@
   LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
               (u16_t)(entry->server_idx), entry->name));
   LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
-  if (ip_addr_isany_val(dns_servers[entry->server_idx])) {
+  if (ip_addr_isany_val(dns_servers[entry->server_idx])
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+      && !entry->is_mdns
+#endif
+    ) {
     /* DNS server not valid anymore, e.g. PPP netif has been shut down */
     /* call specified callback function if provided */
     dns_call_found(idx, NULL);
@@ -678,6 +719,8 @@
   p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
                  SIZEOF_DNS_QUERY), PBUF_RAM);
   if (p != NULL) {
+    const ip_addr_t* dst;
+    u16_t dst_port;
     /* fill dns header */
     memset(&hdr, 0, SIZEOF_DNS_HDR);
     hdr.id = lwip_htons(entry->txid);
@@ -720,7 +763,30 @@
     /* send dns packet */
     LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n",
       entry->txid, entry->name, entry->server_idx));
-    err = udp_sendto(dns_pcbs[pcb_idx], p, &dns_servers[entry->server_idx], DNS_SERVER_PORT);
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+    if (entry->is_mdns) {
+      dst_port = DNS_MQUERY_PORT;
+#if LWIP_IPV6
+      if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+      {
+        dst = &dns_mquery_v6group;
+      }
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+      else
+#endif
+#if LWIP_IPV4
+      {
+        dst = &dns_mquery_v4group;
+      }
+#endif
+    } else
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+    {
+      dst_port = DNS_SERVER_PORT;
+      dst = &dns_servers[entry->server_idx];
+    }
+    err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
 
     /* free pbuf */
     pbuf_free(p);
@@ -923,7 +989,11 @@
     case DNS_STATE_ASKING:
       if (--entry->tmr == 0) {
         if (++entry->retries == DNS_MAX_RETRIES) {
-          if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])) {
+          if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+            && !entry->is_mdns
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+            ) {
             /* change of server */
             entry->server_idx++;
             entry->tmr = 1;
@@ -1060,10 +1130,15 @@
           goto memerr; /* ignore this packet */
         }
 
-        /* Check whether response comes from the same network address to which the
-           question was sent. (RFC 5452) */
-        if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
-          goto memerr; /* ignore this packet */
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+        if (!entry->is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+        {
+          /* Check whether response comes from the same network address to which the
+             question was sent. (RFC 5452) */
+          if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
+            goto memerr; /* ignore this packet */
+          }
         }
 
         /* Check if the name in the "question" part match with the name in the entry and
@@ -1195,7 +1270,7 @@
  */
 static err_t
 dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
-            void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+            void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns))
 {
   u8_t i;
   u8_t lseq, lseqi;
@@ -1244,8 +1319,9 @@
     }
     /* check if this is the oldest completed entry */
     if (entry->state == DNS_STATE_DONE) {
-      if ((u8_t)(dns_seqno - entry->seqno) > lseq) {
-        lseq = dns_seqno - entry->seqno;
+      u8_t age = dns_seqno - entry->seqno;
+      if (age > lseq) {
+        lseq = age;
         lseqi = i;
       }
     }
@@ -1310,6 +1386,10 @@
   LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx)));
 #endif
 
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  entry->is_mdns = is_mdns;
+#endif
+
   dns_seqno++;
 
   /* force to send query without waiting timer */
@@ -1365,6 +1445,9 @@
                            void *callback_arg, u8_t dns_addrtype)
 {
   size_t hostnamelen;
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  u8_t is_mdns;
+#endif
   /* not initialized or no valid server yet, or invalid addr pointer
    * or invalid hostname or invalid hostname length */
   if ((addr == NULL) ||
@@ -1421,13 +1504,25 @@
   LWIP_UNUSED_ARG(dns_addrtype);
 #endif /* LWIP_IPV4 && LWIP_IPV6 */
 
-  /* prevent calling found callback if no server is set, return error instead */
-  if (ip_addr_isany_val(dns_servers[0])) {
-    return ERR_VAL;
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) {
+    is_mdns = 1;
+  } else {
+    is_mdns = 0;
+  }
+
+  if (!is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+  {
+    /* prevent calling found callback if no server is set, return error instead */
+    if (ip_addr_isany_val(dns_servers[0])) {
+      return ERR_VAL;
+    }
   }
 
   /* queue query with specified callback */
-  return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype));
+  return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)
+     LWIP_DNS_ISMDNS_ARG(is_mdns));
 }
 
 #endif /* LWIP_DNS */
diff --git a/src/core/inet_chksum.c b/src/core/inet_chksum.c
index 8028952..917f3e4 100644
--- a/src/core/inet_chksum.c
+++ b/src/core/inet_chksum.c
@@ -51,7 +51,6 @@
 #include "lwip/def.h"
 #include "lwip/ip_addr.h"
 
-#include <stddef.h>
 #include <string.h>
 
 #ifndef LWIP_CHKSUM
diff --git a/src/core/init.c b/src/core/init.c
index 570d54a..ee415a9 100644
--- a/src/core/init.c
+++ b/src/core/init.c
@@ -339,6 +339,11 @@
 void
 lwip_init(void)
 {
+#ifndef LWIP_SKIP_CONST_CHECK
+  int a;
+  LWIP_UNUSED_ARG(a);
+  LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void*, &a) == &a);
+#endif
 #ifndef LWIP_SKIP_PACKING_CHECK
   LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);
 #endif
diff --git a/src/core/ipv4/autoip.c b/src/core/ipv4/autoip.c
index 86a4aed..10db8a3 100644
--- a/src/core/ipv4/autoip.c
+++ b/src/core/ipv4/autoip.c
@@ -68,7 +68,6 @@
 #include "lwip/etharp.h"
 #include "lwip/prot/autoip.h"
 
-#include <stdlib.h>
 #include <string.h>
 
 /** Pseudo random macro based on netif informations.
@@ -95,8 +94,6 @@
 static err_t autoip_arp_announce(struct netif *netif);
 static void autoip_start_probing(struct netif *netif);
 
-#define netif_autoip_data(netif) ((struct autoip*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP))
-
 /**
  * @ingroup autoip 
  * Set a statically allocated struct autoip to work with.
@@ -482,7 +479,8 @@
       * ip.dst == llipaddr && hw.src != own hwaddr
       */
       if ((ip4_addr_cmp(&sipaddr, &autoip->llipaddr)) ||
-          (ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
+          (ip4_addr_isany_val(sipaddr) &&
+           ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
            !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
         LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
           ("autoip_arp_reply(): Probe Conflict detected\n"));
diff --git a/src/core/ipv4/dhcp.c b/src/core/ipv4/dhcp.c
index de5fc73..dd35471 100644
--- a/src/core/ipv4/dhcp.c
+++ b/src/core/ipv4/dhcp.c
@@ -103,26 +103,40 @@
 
 #define REBOOT_TRIES                2
 
+#if LWIP_DNS && LWIP_DHCP_MAX_DNS_SERVERS
+#if DNS_MAX_SERVERS > LWIP_DHCP_MAX_DNS_SERVERS
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS LWIP_DHCP_MAX_DNS_SERVERS
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
+#endif
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS 0
+#endif
+
 /** Option handling: options are parsed in dhcp_parse_reply
  * and saved in an array where other functions can load them from.
  * This might be moved into the struct dhcp (not necessarily since
  * lwIP is single-threaded and the array is only used while in recv
  * callback). */
-#define DHCP_OPTION_IDX_OVERLOAD    0
-#define DHCP_OPTION_IDX_MSG_TYPE    1
-#define DHCP_OPTION_IDX_SERVER_ID   2
-#define DHCP_OPTION_IDX_LEASE_TIME  3
-#define DHCP_OPTION_IDX_T1          4
-#define DHCP_OPTION_IDX_T2          5
-#define DHCP_OPTION_IDX_SUBNET_MASK 6
-#define DHCP_OPTION_IDX_ROUTER      7
-#define DHCP_OPTION_IDX_DNS_SERVER  8
+enum dhcp_option_idx {
+  DHCP_OPTION_IDX_OVERLOAD = 0,
+  DHCP_OPTION_IDX_MSG_TYPE,
+  DHCP_OPTION_IDX_SERVER_ID,
+  DHCP_OPTION_IDX_LEASE_TIME,
+  DHCP_OPTION_IDX_T1,
+  DHCP_OPTION_IDX_T2,
+  DHCP_OPTION_IDX_SUBNET_MASK,
+  DHCP_OPTION_IDX_ROUTER,
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+  DHCP_OPTION_IDX_DNS_SERVER,
+  DHCP_OPTION_IDX_DNS_SERVER_LAST = DHCP_OPTION_IDX_DNS_SERVER + LWIP_DHCP_PROVIDE_DNS_SERVERS - 1,
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
 #if LWIP_DHCP_GET_NTP_SRV
-#define DHCP_OPTION_IDX_NTP_SERVER  (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
-#define DHCP_OPTION_IDX_MAX         (DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS)
-#else /* LWIP_DHCP_GET_NTP_SRV */
-#define DHCP_OPTION_IDX_MAX         (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
+  DHCP_OPTION_IDX_NTP_SERVER,
+  DHCP_OPTION_IDX_NTP_SERVER_LAST = DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS - 1,
 #endif /* LWIP_DHCP_GET_NTP_SRV */
+  DHCP_OPTION_IDX_MAX
+};
 
 /** Holds the decoded option values, only valid while in dhcp_recv.
     @todo: move this into struct dhcp? */
@@ -135,8 +149,10 @@
 static u8_t dhcp_discover_request_options[] = {
   DHCP_OPTION_SUBNET_MASK,
   DHCP_OPTION_ROUTER,
-  DHCP_OPTION_BROADCAST,
-  DHCP_OPTION_DNS_SERVER
+  DHCP_OPTION_BROADCAST
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+  , DHCP_OPTION_DNS_SERVER
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
 #if LWIP_DHCP_GET_NTP_SRV
   , DHCP_OPTION_NTP
 #endif /* LWIP_DHCP_GET_NTP_SRV */
@@ -193,8 +209,6 @@
 /* always add the DHCP options trailer to end and pad */
 static void dhcp_option_trailer(struct dhcp *dhcp);
 
-#define netif_dhcp_data(netif) ((struct dhcp*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP))
-
 /** Ensure DHCP PCB is allocated and bound */
 static err_t
 dhcp_inc_pcb_refcount(void)
@@ -572,9 +586,9 @@
 {
   struct dhcp *dhcp = netif_dhcp_data(netif);
 
-#if LWIP_DNS || LWIP_DHCP_GET_NTP_SRV
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV
   u8_t n;
-#endif /* LWIP_DNS || LWIP_DHCP_GET_NTP_SRV */
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV */
 #if LWIP_DHCP_GET_NTP_SRV
   ip4_addr_t ntp_server_addrs[LWIP_DHCP_MAX_NTP_SERVERS];
 #endif
@@ -640,14 +654,14 @@
   dhcp_set_ntp_servers(n, ntp_server_addrs);
 #endif /* LWIP_DHCP_GET_NTP_SRV */
 
-#if LWIP_DNS
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
   /* DNS servers */
-  for (n = 0; (n < DNS_MAX_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
+  for (n = 0; (n < LWIP_DHCP_PROVIDE_DNS_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
     ip_addr_t dns_addr;
     ip_addr_set_ip4_u32(&dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
     dns_setserver(n, &dns_addr);
   }
-#endif /* LWIP_DNS */
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
 }
 
 /**
@@ -1517,6 +1531,7 @@
         LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_ROUTER;
         break;
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
       case(DHCP_OPTION_DNS_SERVER):
         /* special case: there might be more than one server */
         LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
@@ -1525,6 +1540,7 @@
         LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
         break;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
       case(DHCP_OPTION_LEASE_TIME):
         LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
@@ -1541,6 +1557,8 @@
 #endif /* LWIP_DHCP_GET_NTP_SRV*/
       case(DHCP_OPTION_OVERLOAD):
         LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+        /* decode overload only in options, not in file/sname: invalid packet */
+        LWIP_ERROR("overload in file/sname", options_idx == DHCP_OPTIONS_OFS, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_OVERLOAD;
         break;
       case(DHCP_OPTION_MESSAGE_TYPE):
diff --git a/src/core/ipv4/etharp.c b/src/core/ipv4/etharp.c
index bf4dad5..b599bcc 100644
--- a/src/core/ipv4/etharp.c
+++ b/src/core/ipv4/etharp.c
@@ -840,7 +840,7 @@
     if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
         !ip4_addr_islinklocal(ipaddr)) {
 #if LWIP_AUTOIP
-      struct ip_hdr *iphdr = (struct ip_hdr*)(size_t)q->payload;
+      struct ip_hdr *iphdr = LWIP_ALIGNMENT_CAST(struct ip_hdr*, q->payload);
       /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
          a link-local source address must always be "directly to its destination
          on the same physical link. The host MUST NOT send the packet to any
diff --git a/src/core/ipv4/icmp.c b/src/core/ipv4/icmp.c
index e60e448..e030ecc 100644
--- a/src/core/ipv4/icmp.c
+++ b/src/core/ipv4/icmp.c
@@ -81,7 +81,7 @@
 #endif /* LWIP_DEBUG */
   struct icmp_echo_hdr *iecho;
   const struct ip_hdr *iphdr_in;
-  s16_t hlen;
+  u16_t hlen;
   const ip4_addr_t* src;
 
   ICMP_STATS_INC(icmp.recv);
@@ -148,7 +148,7 @@
     }
 #endif
 #if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
-    if (pbuf_header(p, (hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
+    if (pbuf_header(p, (s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
       /* p is not big enough to contain link headers
        * allocate a new one and copy p into it
        */
@@ -167,7 +167,7 @@
       /* copy the ip header */
       MEMCPY(r->payload, iphdr_in, hlen);
       /* switch r->payload back to icmp header (cannot fail) */
-      if (pbuf_header(r, -hlen)) {
+      if (pbuf_header(r, (s16_t)-hlen)) {
         LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0);
         pbuf_free(r);
         goto icmperr;
@@ -194,7 +194,7 @@
     /* We generate an answer by switching the dest and src ip addresses,
      * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
     iecho = (struct icmp_echo_hdr *)p->payload;
-    if (pbuf_header(p, hlen)) {
+    if (pbuf_header(p, (s16_t)hlen)) {
       LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet"));
     } else {
       err_t ret;
@@ -247,7 +247,7 @@
     if (type == ICMP_DUR) {
       MIB2_STATS_INC(mib2.icmpindestunreachs);
     } else if (type == ICMP_TE) {
-      MIB2_STATS_INC(mib2.icmpindestunreachs);
+      MIB2_STATS_INC(mib2.icmpintimeexcds);
     } else if (type == ICMP_PP) {
       MIB2_STATS_INC(mib2.icmpinparmprobs);
     } else if (type == ICMP_SQ) {
diff --git a/src/core/ipv4/igmp.c b/src/core/ipv4/igmp.c
index 5ae7d9c..561448e 100644
--- a/src/core/ipv4/igmp.c
+++ b/src/core/ipv4/igmp.c
@@ -244,6 +244,7 @@
 igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
 {
   struct igmp_group *group;
+  struct igmp_group *list_head = netif_igmp_data(ifp);
 
   /* Search if the group already exists */
   group = igmp_lookfor_group(ifp, addr);
@@ -251,7 +252,7 @@
     /* Group already exists. */
     return group;
   }
-
+  
   /* Group doesn't exist yet, create a new one */
   group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
   if (group != NULL) {
@@ -260,9 +261,21 @@
     group->group_state        = IGMP_GROUP_NON_MEMBER;
     group->last_reporter_flag = 0;
     group->use                = 0;
-    group->next               = netif_igmp_data(ifp);
 
-    netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
+    /* Ensure allsystems group is always first in list */    
+    if (list_head == NULL) {
+      /* this is the first entry in linked list */
+      LWIP_ASSERT("igmp_lookup_group: first group must be allsystems",
+        (ip4_addr_cmp(addr, &allsystems) != 0));
+      group->next = NULL;
+      netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
+    } else {
+      /* append _after_ first entry */
+      LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems",
+        (ip4_addr_cmp(addr, &allsystems) == 0));
+      group->next = list_head->next;
+      list_head->next = group;
+    }
   }
 
   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
@@ -282,23 +295,18 @@
 igmp_remove_group(struct netif* netif, struct igmp_group *group)
 {
   err_t err = ERR_OK;
+  struct igmp_group *tmp_group;
 
-  /* Is it the first group? */
-  if (netif_igmp_data(netif) == group) {
-    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group->next);
-  } else {
-    /* look for group further down the list */
-    struct igmp_group *tmpGroup;
-    for (tmpGroup = netif_igmp_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) {
-      if (tmpGroup->next == group) {
-        tmpGroup->next = group->next;
-        break;
-      }
+  /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+  for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) {
+    if (tmp_group->next == group) {
+      tmp_group->next = group->next;
+      break;
     }
-    /* Group not found in the global igmp_group_list */
-    if (tmpGroup == NULL) {
-      err = ERR_ARG;
-    }
+  }
+  /* Group not found in the global igmp_group_list */
+  if (tmp_group == NULL) {
+    err = ERR_ARG;
   }
 
   return err;
diff --git a/src/core/ipv4/ip4.c b/src/core/ipv4/ip4.c
index c783677..2e2ce4b 100644
--- a/src/core/ipv4/ip4.c
+++ b/src/core/ipv4/ip4.c
@@ -177,7 +177,7 @@
   /* loopif is disabled, looopback traffic is passed through any netif */
   if (ip4_addr_isloopback(dest)) {
     /* don't check for link on loopback traffic */
-    if (netif_is_up(netif_default)) {
+    if (netif_default != NULL && netif_is_up(netif_default)) {
       return netif_default;
     }
     /* default netif is not up, just use any netif for loopback traffic */
@@ -518,6 +518,15 @@
 #endif /* LWIP_AUTOIP */
       }
       if (first) {
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+        /* Packets sent to the loopback address must not be accepted on an
+         * interface that does not have the loopback address assigned to it,
+         * unless a non-loopback interface is used for loopback traffic. */
+        if (ip4_addr_isloopback(ip4_current_dest_addr())) {
+          netif = NULL;
+          break;
+        }
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
         first = 0;
         netif = netif_list;
       } else {
@@ -589,6 +598,7 @@
     } else
 #endif /* IP_FORWARD */
     {
+      IP_STATS_INC(ip.drop);
       MIB2_STATS_INC(mib2.ipinaddrerrors);
       MIB2_STATS_INC(mib2.ipindiscards);
     }
@@ -853,7 +863,7 @@
     IPH_TTL_SET(iphdr, ttl);
     IPH_PROTO_SET(iphdr, proto);
 #if CHECKSUM_GEN_IP_INLINE
-    chk_sum += LWIP_MAKE_U16(proto, ttl);
+    chk_sum += PP_NTOHS(proto | (ttl << 8));
 #endif /* CHECKSUM_GEN_IP_INLINE */
 
     /* dest cannot be NULL here */
@@ -866,7 +876,7 @@
     IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
     IPH_TOS_SET(iphdr, tos);
 #if CHECKSUM_GEN_IP_INLINE
-    chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl);
+    chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8));
 #endif /* CHECKSUM_GEN_IP_INLINE */
     IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
 #if CHECKSUM_GEN_IP_INLINE
diff --git a/src/core/ipv4/ip4_addr.c b/src/core/ipv4/ip4_addr.c
index eb812af..2d47992 100644
--- a/src/core/ipv4/ip4_addr.c
+++ b/src/core/ipv4/ip4_addr.c
@@ -183,10 +183,10 @@
     }
     for (;;) {
       if (isdigit(c)) {
-        val = (val * base) + (int)(c - '0');
+        val = (val * base) + (u32_t)(c - '0');
         c = *++cp;
       } else if (base == 16 && isxdigit(c)) {
-        val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A'));
+        val = (val << 4) | (u32_t)(c + 10 - (islower(c) ? 'a' : 'A'));
         c = *++cp;
       } else {
         break;
@@ -310,7 +310,7 @@
     do {
       rem = *ap % (u8_t)10;
       *ap /= (u8_t)10;
-      inv[i++] = '0' + rem;
+      inv[i++] = (char)('0' + rem);
     } while (*ap);
     while (i--) {
       if (len++ >= buflen) {
diff --git a/src/core/ipv4/ip4_frag.c b/src/core/ipv4/ip4_frag.c
index b6f9094..57fb44c 100644
--- a/src/core/ipv4/ip4_frag.c
+++ b/src/core/ipv4/ip4_frag.c
@@ -687,6 +687,8 @@
   struct pbuf *rambuf;
 #if !LWIP_NETIF_TX_SINGLE_PBUF
   struct pbuf *newpbuf;
+  u16_t newpbuflen = 0;
+  u16_t left_to_copy;
 #endif
   struct ip_hdr *original_iphdr;
   struct ip_hdr *iphdr;
@@ -696,10 +698,6 @@
   int last;
   u16_t poff = IP_HLEN;
   u16_t tmp;
-#if !LWIP_NETIF_TX_SINGLE_PBUF
-  u16_t newpbuflen = 0;
-  u16_t left_to_copy;
-#endif
 
   original_iphdr = (struct ip_hdr *)p->payload;
   iphdr = original_iphdr;
diff --git a/src/core/ipv6/ethip6.c b/src/core/ipv6/ethip6.c
index 509fc1c..8f9a91b 100644
--- a/src/core/ipv6/ethip6.c
+++ b/src/core/ipv6/ethip6.c
@@ -62,7 +62,9 @@
  * For IPv6 multicast, corresponding Ethernet addresses
  * are selected and the packet is transmitted on the link.
  *
- * For unicast addresses, ...
+ * For unicast addresses, ask the ND6 module what to do. It will either let us
+ * send the the packet right away, or queue the packet for later itself, unless
+ * an error occurs.
  *
  * @todo anycast addresses
  *
@@ -71,14 +73,14 @@
  * @param ip6addr The IP address of the packet destination.
  *
  * @return
- * - ERR_RTE No route to destination (no gateway to external networks),
- * or the return type of either nd6_queue_packet() or ethernet_output().
+ * - ERR_OK or the return value of @ref nd6_get_next_hop_addr_or_queue.
  */
 err_t
 ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
 {
   struct eth_addr dest;
-  s8_t i;
+  const u8_t *hwaddr;
+  err_t result;
 
   /* multicast destination IP address? */
   if (ip6_addr_ismulticast(ip6addr)) {
@@ -91,36 +93,26 @@
     dest.addr[5] = ((const u8_t *)(&(ip6addr->addr[3])))[3];
 
     /* Send out. */
-    return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+    return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
   }
 
   /* We have a unicast destination IP address */
   /* @todo anycast? */
-  /* Get next hop record. */
-  i = nd6_get_next_hop_entry(ip6addr, netif);
-  if (i < 0) {
-    /* failed to get a next hop neighbor record. */
-    return ERR_MEM;
+
+  /* Ask ND6 what to do with the packet. */
+  result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+  if (result != ERR_OK) {
+    return result;
   }
 
-  /* Now that we have a destination record, send or queue the packet. */
-  if (neighbor_cache[i].state == ND6_STALE) {
-    /* Switch to delay state. */
-    neighbor_cache[i].state = ND6_DELAY;
-    neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
-  }
-  /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
-  if ((neighbor_cache[i].state == ND6_REACHABLE) ||
-      (neighbor_cache[i].state == ND6_DELAY) ||
-      (neighbor_cache[i].state == ND6_PROBE)) {
-
-    /* Send out. */
-    SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6);
-    return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+  /* If no hardware address is returned, nd6 has queued the packet for later. */
+  if (hwaddr == NULL) {
+    return ERR_OK;
   }
 
-  /* We should queue packet on this interface. */
-  return nd6_queue_packet(i, q);
+  /* Send out the packet using the returned hardware address. */
+  SMEMCPY(dest.addr, hwaddr, 6);
+  return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
 }
 
 #endif /* LWIP_IPV6 && LWIP_ETHERNET */
diff --git a/src/core/ipv6/ip6.c b/src/core/ipv6/ip6.c
index 8cc9c01..a542a86 100644
--- a/src/core/ipv6/ip6.c
+++ b/src/core/ipv6/ip6.c
@@ -94,7 +94,8 @@
   if (ip6_addr_islinklocal(dest)) {
     if (ip6_addr_isany(src)) {
       /* Use default netif, if Up. */
-      if (!netif_is_up(netif_default) || !netif_is_link_up(netif_default)) {
+      if (netif_default == NULL || !netif_is_up(netif_default) ||
+          !netif_is_link_up(netif_default)) {
         return NULL;
       }
       return netif_default;
@@ -114,7 +115,8 @@
     }
 
     /* netif not found, use default netif, if up */
-    if (!netif_is_up(netif_default) || !netif_is_link_up(netif_default)) {
+    if (netif_default == NULL || !netif_is_up(netif_default) ||
+        !netif_is_link_up(netif_default)) {
       return NULL;
     }
     return netif_default;
@@ -142,15 +144,9 @@
   }
 
   /* Get the netif for a suitable router. */
-  i = nd6_select_router(dest, NULL);
-  if (i >= 0) {
-    if (default_router_list[i].neighbor_entry != NULL) {
-      if (default_router_list[i].neighbor_entry->netif != NULL) {
-        if (netif_is_up(default_router_list[i].neighbor_entry->netif) && netif_is_link_up(default_router_list[i].neighbor_entry->netif)) {
-          return default_router_list[i].neighbor_entry->netif;
-        }
-      }
-    }
+  netif = nd6_find_route(dest);
+  if ((netif != NULL) && netif_is_up(netif) && netif_is_link_up(netif)) {
+    return netif;
   }
 
   /* try with the netif that matches the source address. */
@@ -172,7 +168,7 @@
   /* loopif is disabled, loopback traffic is passed through any netif */
   if (ip6_addr_isloopback(dest)) {
     /* don't check for link on loopback traffic */
-    if (netif_is_up(netif_default)) {
+    if (netif_default != NULL && netif_is_up(netif_default)) {
       return netif_default;
     }
     /* default netif is not up, just use any netif for loopback traffic */
@@ -290,8 +286,9 @@
 {
   struct netif *netif;
 
-  /* do not forward link-local addresses */
-  if (ip6_addr_islinklocal(ip6_current_dest_addr())) {
+  /* do not forward link-local or loopback addresses */
+  if (ip6_addr_islinklocal(ip6_current_dest_addr()) ||
+      ip6_addr_isloopback(ip6_current_dest_addr())) {
     LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n"));
     IP6_STATS_INC(ip6.rterr);
     IP6_STATS_INC(ip6.drop);
@@ -509,12 +506,21 @@
           }
         }
       }
-      if (ip6_addr_islinklocal(ip6_current_dest_addr())) {
-        /* Do not match link-local addresses to other netifs. */
-        netif = NULL;
-        break;
-      }
       if (first) {
+        if (ip6_addr_islinklocal(ip6_current_dest_addr())
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+            || ip6_addr_isloopback(ip6_current_dest_addr())
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+        ) {
+          /* Do not match link-local addresses to other netifs. The loopback
+           * address is to be considered link-local and packets to it should be
+           * dropped on other interfaces, as per RFC 4291 Sec. 2.5.3. This
+           * requirement cannot be implemented in the case that loopback
+           * traffic is sent across a non-loopback interface, however.
+           */
+          netif = NULL;
+          break;
+        }
         first = 0;
         netif = netif_list;
       } else {
@@ -709,7 +715,7 @@
 options_done:
 
   /* p points to IPv6 header again. */
-  pbuf_header_force(p, ip_data.current_ip_header_tot_len);
+  pbuf_header_force(p, (s16_t)ip_data.current_ip_header_tot_len);
 
   /* send to upper layers */
   LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n"));
diff --git a/src/core/ipv6/ip6_addr.c b/src/core/ipv6/ip6_addr.c
index 964291e..aa06659 100644
--- a/src/core/ipv6/ip6_addr.c
+++ b/src/core/ipv6/ip6_addr.c
@@ -132,8 +132,8 @@
     } else if (isxdigit(*s)) {
       /* add current digit */
       current_block_value = (current_block_value << 4) +
-          (isdigit(*s) ? *s - '0' :
-          10 + (islower(*s) ? *s - 'a' : *s - 'A'));
+          (isdigit(*s) ? (u32_t)(*s - '0') :
+          (u32_t)(10 + (islower(*s) ? *s - 'a' : *s - 'A')));
     } else {
       /* unexpected digit, space? CRLF? */
       break;
diff --git a/src/core/ipv6/ip6_frag.c b/src/core/ipv6/ip6_frag.c
index b374f69..ff07f71 100644
--- a/src/core/ipv6/ip6_frag.c
+++ b/src/core/ipv6/ip6_frag.c
@@ -379,6 +379,7 @@
     /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
        This cannot fail since we already checked when receiving this fragment. */
     u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
+    LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
     LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
   }
 #else /* IPV6_FRAG_COPYHEADER */
@@ -530,6 +531,7 @@
         if (IPV6_FRAG_REQROOM > 0) {
           /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
           u8_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM));
+          LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
           LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
         }
 #endif
@@ -546,6 +548,7 @@
     if (IPV6_FRAG_REQROOM > 0) {
       /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
       u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM));
+      LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
       LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
     }
     iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN);
@@ -610,6 +613,7 @@
 
 #if LWIP_IPV6 && LWIP_IPV6_FRAG
 
+#if !LWIP_NETIF_TX_SINGLE_PBUF
 /** Allocate a new struct pbuf_custom_ref */
 static struct pbuf_custom_ref*
 ip6_frag_alloc_pbuf_custom_ref(void)
@@ -638,6 +642,7 @@
   }
   ip6_frag_free_pbuf_custom_ref(pcr);
 }
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
 
 /**
  * Fragment an IPv6 datagram if too large for the netif or path MTU.
@@ -658,7 +663,11 @@
   struct ip6_hdr *ip6hdr;
   struct ip6_frag_hdr *frag_hdr;
   struct pbuf *rambuf;
+#if !LWIP_NETIF_TX_SINGLE_PBUF
   struct pbuf *newpbuf;
+  u16_t newpbuflen = 0;
+  u16_t left_to_copy;
+#endif
   static u32_t identification;
   u16_t nfb;
   u16_t left, cop;
@@ -666,8 +675,6 @@
   u16_t fragment_offset = 0;
   u16_t last;
   u16_t poff = IP6_HLEN;
-  u16_t newpbuflen = 0;
-  u16_t left_to_copy;
 
   identification++;
 
@@ -686,6 +693,26 @@
     /* Fill this fragment */
     cop = last ? left : nfb;
 
+#if LWIP_NETIF_TX_SINGLE_PBUF
+    rambuf = pbuf_alloc(PBUF_IP, cop + IP6_FRAG_HLEN, PBUF_RAM);
+    if (rambuf == NULL) {
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      return ERR_MEM;
+    }
+    LWIP_ASSERT("this needs a pbuf in one piece!",
+      (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+    poff += pbuf_copy_partial(p, (u8_t*)rambuf->payload + IP6_FRAG_HLEN, cop, poff);
+    /* make room for the IP header */
+    if (pbuf_header(rambuf, IP6_HLEN)) {
+      pbuf_free(rambuf);
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      return ERR_MEM;
+    }
+    /* fill in the IP header */
+    SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
+    ip6hdr = (struct ip6_hdr *)rambuf->payload;
+    frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
+#else
     /* When not using a static buffer, create a chain of pbufs.
      * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
      * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
@@ -744,6 +771,7 @@
       }
     }
     poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
 
     /* Set headers */
     frag_hdr->_nexth = original_ip6hdr->_nexth;
diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c
index 82394f6..7d7a957 100644
--- a/src/core/ipv6/nd6.c
+++ b/src/core/ipv6/nd6.c
@@ -46,6 +46,7 @@
 #if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
 
 #include "lwip/nd6.h"
+#include "lwip/priv/nd6_priv.h"
 #include "lwip/prot/nd6.h"
 #include "lwip/prot/icmp6.h"
 #include "lwip/pbuf.h"
@@ -59,6 +60,7 @@
 #include "lwip/mld6.h"
 #include "lwip/ip.h"
 #include "lwip/stats.h"
+#include "lwip/dns.h"
 
 #include <string.h>
 
@@ -93,10 +95,13 @@
 static s8_t nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr);
 static s8_t nd6_new_destination_cache_entry(void);
 static s8_t nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif);
+static s8_t nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif);
 static s8_t nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif);
 static s8_t nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif);
 static s8_t nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif);
 static s8_t nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif);
+static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q);
 
 #define ND6_SEND_FLAG_MULTICAST_DEST 0x01
 #define ND6_SEND_FLAG_ALLNODES_DEST 0x02
@@ -155,25 +160,6 @@
        * link-layer changed?
        * part of DAD mechanism? */
 
-      /* Check that link-layer address option also fits in packet. */
-      if (p->len < (sizeof(struct na_header) + 2)) {
-        /* @todo debug message */
-        pbuf_free(p);
-        ND6_STATS_INC(nd6.lenerr);
-        ND6_STATS_INC(nd6.drop);
-        return;
-      }
-
-      lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
-
-      if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
-        /* @todo debug message */
-        pbuf_free(p);
-        ND6_STATS_INC(nd6.lenerr);
-        ND6_STATS_INC(nd6.drop);
-        return;
-      }
-
       /* Create an aligned copy. */
       ip6_addr_set(&target_address, &(na_hdr->target_address));
 
@@ -185,12 +171,6 @@
           /* We are using a duplicate address. */
           netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
 
-#if LWIP_IPV6_MLD
-          /* Leave solicited node multicast group. */
-          ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]);
-          mld6_leavegroup_netif(inp, &multicast_address);
-#endif /* LWIP_IPV6_MLD */
-
 #if LWIP_IPV6_AUTOCONFIG
           /* Check to see if this address was autoconfigured. */
           if (!ip6_addr_islinklocal(&target_address)) {
@@ -209,6 +189,25 @@
       }
 #endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */
 
+      /* Check that link-layer address option also fits in packet. */
+      if (p->len < (sizeof(struct na_header) + 2)) {
+        /* @todo debug message */
+        pbuf_free(p);
+        ND6_STATS_INC(nd6.lenerr);
+        ND6_STATS_INC(nd6.drop);
+        return;
+      }
+
+      lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+      if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
+        /* @todo debug message */
+        pbuf_free(p);
+        ND6_STATS_INC(nd6.lenerr);
+        ND6_STATS_INC(nd6.drop);
+        return;
+      }
+
       /* This is an unsolicited NA, most likely there was a LLADDR change. */
       i = nd6_find_neighbor_cache_entry(&target_address);
       if (i >= 0) {
@@ -390,6 +389,10 @@
     struct ra_header *ra_hdr;
     u8_t *buffer; /* Used to copy options. */
     u16_t offset;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+    /* There can by multiple RDNSS options per RA */
+    u8_t rdnss_server_idx = 0;
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
 
     /* Check that RA header fits in packet. */
     if (p->len < sizeof(struct ra_header)) {
@@ -534,6 +537,37 @@
         route_opt = (struct route_option *)buffer;*/
 
         break;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+      case ND6_OPTION_TYPE_RDNSS:
+      {
+        u8_t num, n;
+        struct rdnss_option * rdnss_opt;
+
+        rdnss_opt = (struct rdnss_option *)buffer;
+        num = (rdnss_opt->length - 1) / 2;
+        for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++) {
+          ip_addr_t rdnss_address;
+
+          /* Get a memory-aligned copy of the prefix. */
+          ip_addr_copy_from_ip6(rdnss_address, rdnss_opt->rdnss_address[n]);
+
+          if (htonl(rdnss_opt->lifetime) > 0) {
+            /* TODO implement Lifetime > 0 */
+            dns_setserver(rdnss_server_idx++, &rdnss_address);
+          } else {
+            /* TODO implement DNS removal in dns.c */
+            u8_t s;
+            for (s = 0; s < DNS_MAX_SERVERS; s++) {
+              const ip_addr_t *addr = dns_getserver(s);
+              if(ip_addr_cmp(addr, &rdnss_address)) {
+                dns_setserver(s, NULL);
+              }
+            }
+          }
+        }
+        break;
+      }
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
       default:
         /* Unrecognized option, abort. */
         ND6_STATS_INC(nd6.proterr);
@@ -549,6 +583,7 @@
   {
     struct redirect_header *redir_hdr;
     struct lladdr_option *lladdr_opt;
+    ip6_addr_t tmp;
 
     /* Check that Redir header fits in packet. */
     if (p->len < sizeof(struct redirect_header)) {
@@ -571,10 +606,10 @@
     }
 
     /* Copy original destination address to current source address, to have an aligned copy. */
-    ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address));
+    ip6_addr_set(&tmp, &(redir_hdr->destination_address));
 
     /* Find dest address in cache */
-    i = nd6_find_destination_cache_entry(ip6_current_src_addr());
+    i = nd6_find_destination_cache_entry(&tmp);
     if (i < 0) {
       /* Destination not in cache, drop packet. */
       pbuf_free(p);
@@ -588,15 +623,15 @@
     if (lladdr_opt != NULL) {
       if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) {
         /* Copy target address to current source address, to have an aligned copy. */
-        ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->target_address));
+        ip6_addr_set(&tmp, &(redir_hdr->target_address));
 
-        i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
+        i = nd6_find_neighbor_cache_entry(&tmp);
         if (i < 0) {
           i = nd6_new_neighbor_cache_entry();
           if (i >= 0) {
             neighbor_cache[i].netif = inp;
             MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
-            ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr());
+            ip6_addr_set(&(neighbor_cache[i].next_hop_address), &tmp);
 
             /* Receiving a message does not prove reachability: only in one direction.
              * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
@@ -622,6 +657,7 @@
     struct icmp6_hdr *icmp6hdr; /* Packet too big message */
     struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */
     u32_t pmtu;
+    ip6_addr_t tmp;
 
     /* Check that ICMPv6 header + IPv6 header fit in payload */
     if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) {
@@ -636,10 +672,10 @@
     ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr));
 
     /* Copy original destination address to current source address, to have an aligned copy. */
-    ip6_addr_set(ip6_current_src_addr(), &(ip6hdr->dest));
+    ip6_addr_set(&tmp, &(ip6hdr->dest));
 
     /* Look for entry in destination cache. */
-    i = nd6_find_destination_cache_entry(ip6_current_src_addr());
+    i = nd6_find_destination_cache_entry(&tmp);
     if (i < 0) {
       /* Destination not in cache, drop packet. */
       pbuf_free(p);
@@ -829,13 +865,6 @@
           netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED);
           /* @todo implement preferred and valid lifetimes. */
         } else if (netif->flags & NETIF_FLAG_UP) {
-#if LWIP_IPV6_MLD
-          if ((addr_state & IP6_ADDR_TENTATIVE_COUNT_MASK) == 0) {
-            /* Join solicited node multicast group. */
-            ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]);
-            mld6_joingroup_netif(netif, &multicast_address);
-          }
-#endif /* LWIP_IPV6_MLD */
           /* Send a NS for this address. */
           nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST);
           /* tentative: set next state by increasing by one */
@@ -1294,6 +1323,22 @@
 }
 
 /**
+ * Clear the destination cache.
+ *
+ * This operation may be necessary for consistency in the light of changing
+ * local addresses and/or use of the gateway hook.
+ */
+void
+nd6_clear_destination_cache(void)
+{
+  int i;
+
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    ip6_addr_set_any(&destination_cache[i].destination_addr);
+  }
+}
+
+/**
  * Determine whether an address matches an on-link prefix.
  *
  * @param ip6addr the IPv6 address to match
@@ -1328,7 +1373,7 @@
  * @return the default router entry index, or -1 if no suitable
  *         router is found
  */
-s8_t
+static s8_t
 nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif)
 {
   s8_t i;
@@ -1381,6 +1426,30 @@
 }
 
 /**
+ * Find a router-announced route to the given destination.
+ *
+ * The caller is responsible for checking whether the returned netif, if any,
+ * is in a suitable state (up, link up) to be used for packet transmission.
+ *
+ * @param ip6addr the destination IPv6 address
+ * @return the netif to use for the destination, or NULL if none found
+ */
+struct netif *
+nd6_find_route(const ip6_addr_t *ip6addr)
+{
+  s8_t i;
+
+  i = nd6_select_router(ip6addr, NULL);
+  if (i >= 0) {
+    if (default_router_list[i].neighbor_entry != NULL) {
+      return default_router_list[i].neighbor_entry->netif; /* may be NULL */
+    }
+  }
+
+  return NULL;
+}
+
+/**
  * Find an entry for a default router.
  *
  * @param router_addr the IPv6 address of the router
@@ -1416,6 +1485,7 @@
 nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif)
 {
   s8_t router_index;
+  s8_t free_router_index;
   s8_t neighbor_index;
 
   /* Do we have a neighbor entry for this router? */
@@ -1439,12 +1509,22 @@
   neighbor_cache[neighbor_index].isrouter = 1;
 
   /* Look for empty entry. */
-  for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) {
+  free_router_index = LWIP_ND6_NUM_ROUTERS;
+  for (router_index = LWIP_ND6_NUM_ROUTERS - 1; router_index >= 0; router_index--) {
+    /* check if router already exists (this is a special case for 2 netifs on the same subnet
+       - e.g. wifi and cable) */
+    if(default_router_list[router_index].neighbor_entry == &(neighbor_cache[neighbor_index])){ 
+      return router_index; 
+    } 
     if (default_router_list[router_index].neighbor_entry == NULL) {
-      default_router_list[router_index].neighbor_entry = &(neighbor_cache[neighbor_index]);
-      return router_index;
+      /* remember lowest free index to create a new entry */
+      free_router_index = router_index;
     }
   }
+  if (free_router_index < LWIP_ND6_NUM_ROUTERS) {
+    default_router_list[free_router_index].neighbor_entry = &(neighbor_cache[neighbor_index]);
+    return free_router_index;
+  }
 
   /* Could not create a router entry. */
 
@@ -1521,9 +1601,12 @@
  *         suitable next hop was found, ERR_MEM if no cache entry
  *         could be created
  */
-s8_t
+static s8_t
 nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif)
 {
+#ifdef LWIP_HOOK_ND6_GET_GW
+  const ip6_addr_t *next_hop_addr;
+#endif /* LWIP_HOOK_ND6_GET_GW */
   s8_t i;
 
 #if LWIP_NETIF_HWADDRHINT
@@ -1567,6 +1650,12 @@
         /* Destination in local link. */
         destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
         ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr);
+#ifdef LWIP_HOOK_ND6_GET_GW
+      } else if ((next_hop_addr = LWIP_HOOK_ND6_GET_GW(netif, ip6addr)) != NULL) {
+        /* Next hop for destination provided by hook function. */
+        destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
+        ip6_addr_set(&destination_cache[nd6_cached_destination_index].next_hop_addr, next_hop_addr);
+#endif /* LWIP_HOOK_ND6_GET_GW */
       } else {
         /* We need to select a router. */
         i = nd6_select_router(ip6addr, netif);
@@ -1634,7 +1723,7 @@
  * @param q packet to be queued
  * @return ERR_OK if succeeded, ERR_MEM if out of memory
  */
-err_t
+static err_t
 nd6_queue_packet(s8_t neighbor_index, struct pbuf *q)
 {
   err_t result = ERR_MEM;
@@ -1811,6 +1900,61 @@
 #endif /* LWIP_ND6_QUEUEING */
 }
 
+/**
+ * A packet is to be transmitted to a specific IPv6 destination on a specific
+ * interface. Check if we can find the hardware address of the next hop to use
+ * for the packet. If so, give the hardware address to the caller, which should
+ * use it to send the packet right away. Otherwise, enqueue the packet for
+ * later transmission while looking up the hardware address, if possible.
+ *
+ * As such, this function returns one of three different possible results:
+ *
+ * - ERR_OK with a non-NULL 'hwaddrp': the caller should send the packet now.
+ * - ERR_OK with a NULL 'hwaddrp': the packet has been enqueued for later.
+ * - not ERR_OK: something went wrong; forward the error upward in the stack.
+ *
+ * @param netif The lwIP network interface on which the IP packet will be sent.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The destination IPv6 address of the packet.
+ * @param hwaddrp On success, filled with a pointer to a HW address or NULL (meaning
+ *        the packet has been queued).
+ * @return
+ * - ERR_OK on success, ERR_RTE if no route was found for the packet,
+ * or ERR_MEM if low memory conditions prohibit sending the packet at all.
+ */
+err_t
+nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp)
+{
+  s8_t i;
+
+  /* Get next hop record. */
+  i = nd6_get_next_hop_entry(ip6addr, netif);
+  if (i < 0) {
+    /* failed to get a next hop neighbor record. */
+    return i;
+  }
+
+  /* Now that we have a destination record, send or queue the packet. */
+  if (neighbor_cache[i].state == ND6_STALE) {
+    /* Switch to delay state. */
+    neighbor_cache[i].state = ND6_DELAY;
+    neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+  }
+  /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
+  if ((neighbor_cache[i].state == ND6_REACHABLE) ||
+      (neighbor_cache[i].state == ND6_DELAY) ||
+      (neighbor_cache[i].state == ND6_PROBE)) {
+
+    /* Tell the caller to send out the packet now. */
+    *hwaddrp = neighbor_cache[i].lladdr;
+    return ERR_OK;
+  }
+
+  /* We should queue packet on this interface. */
+  *hwaddrp = NULL;
+  return nd6_queue_packet(i, q);
+}
+
 
 /**
  * Get the Path MTU for a destination.
@@ -1917,4 +2061,38 @@
   }
 }
 
+#if LWIP_IPV6_MLD
+/**
+ * The state of a local IPv6 address entry is about to change. If needed, join
+ * or leave the solicited-node multicast group for the address.
+ *
+ * @param netif The netif that owns the address.
+ * @param addr_idx The index of the address.
+ * @param new_state The new (IP6_ADDR_) state for the address.
+ */
+void
+nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state)
+{
+  u8_t old_state, old_member, new_member;
+
+  old_state = netif_ip6_addr_state(netif, addr_idx);
+
+  /* Determine whether we were, and should be, a member of the solicited-node
+   * multicast group for this address. For tentative addresses, the group is
+   * not joined until the address enters the TENTATIVE_1 (or VALID) state. */
+  old_member = (old_state != IP6_ADDR_INVALID && old_state != IP6_ADDR_TENTATIVE);
+  new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_TENTATIVE);
+
+  if (old_member != new_member) {
+    ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]);
+
+    if (new_member) {
+      mld6_joingroup_netif(netif, &multicast_address);
+    } else {
+      mld6_leavegroup_netif(netif, &multicast_address);
+    }
+  }
+}
+#endif /* LWIP_IPV6_MLD */
+
 #endif /* LWIP_IPV6 */
diff --git a/src/core/mem.c b/src/core/mem.c
index 40f4cb9..db3b7cc 100644
--- a/src/core/mem.c
+++ b/src/core/mem.c
@@ -61,9 +61,13 @@
 #include "lwip/err.h"
 
 #include <string.h>
-#include <stdlib.h>
+
+#if MEM_LIBC_MALLOC
+#include <stdlib.h> /* for malloc()/free() */
+#endif
 
 #if MEM_LIBC_MALLOC || MEM_USE_POOLS
+
 /** mem_init is not used when using pools instead of a heap or using
  * C library malloc().
  */
@@ -162,7 +166,7 @@
 mem_malloc(mem_size_t size)
 {
   void *ret;
-  struct memp_malloc_helper *element;
+  struct memp_malloc_helper *element = NULL;
   memp_t poolnr;
   mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
 
diff --git a/src/core/memp.c b/src/core/memp.c
index 2e3db28..3162449 100644
--- a/src/core/memp.c
+++ b/src/core/memp.c
@@ -71,7 +71,7 @@
 #include "netif/ppp/ppp_opts.h"
 #include "lwip/netdb.h"
 #include "lwip/dns.h"
-#include "lwip/nd6.h"
+#include "lwip/priv/nd6_priv.h"
 #include "lwip/ip6_frag.h"
 #include "lwip/mld6.h"
 
@@ -209,7 +209,7 @@
     for (j = 0; j < memp_pools[i]->num; ++j) {
       memp_overflow_check_element_overflow(p, memp_pools[i]);
       memp_overflow_check_element_underflow(p, memp_pools[i]);
-      p = (struct memp*)(size_t)((u8_t*)p + MEMP_SIZE + memp_pools[i]->size + MEMP_SANITY_REGION_AFTER_ALIGNED);
+      p = LWIP_ALIGNMENT_CAST(struct memp*, ((u8_t*)p + MEMP_SIZE + memp_pools[i]->size + MEMP_SANITY_REGION_AFTER_ALIGNED));
     }
   }
   SYS_ARCH_UNPROTECT(old_level);
@@ -301,15 +301,15 @@
   SYS_ARCH_PROTECT(old_level);
 
   memp = *desc->tab;
-
-#if MEMP_OVERFLOW_CHECK == 1
-  memp_overflow_check_element_overflow(memp, desc);
-  memp_overflow_check_element_underflow(memp, desc);
-#endif /* MEMP_OVERFLOW_CHECK */
 #endif /* MEMP_MEM_MALLOC */
 
   if (memp != NULL) {
 #if !MEMP_MEM_MALLOC
+#if MEMP_OVERFLOW_CHECK == 1
+    memp_overflow_check_element_overflow(memp, desc);
+    memp_overflow_check_element_underflow(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+
     *desc->tab = memp->next;
 #if MEMP_OVERFLOW_CHECK
     memp->next = NULL;
diff --git a/src/core/netif.c b/src/core/netif.c
index fc9e50b..428b148 100644
--- a/src/core/netif.c
+++ b/src/core/netif.c
@@ -180,7 +180,7 @@
 #endif /* NO_SYS */
 
 #if LWIP_IPV6
-  IP_ADDR6(loop_netif.ip6_addr, 0, 0, 0, PP_HTONL(0x00000001UL));
+  IP_ADDR6_HOST(loop_netif.ip6_addr, 0, 0, 0, 0x00000001UL);
   loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID;
 #endif /* LWIP_IPV6 */
 
@@ -415,7 +415,7 @@
       udp_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
 #endif /* LWIP_UDP */
 #if LWIP_RAW
-    raw_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
+      raw_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
 #endif /* LWIP_RAW */
     }
   }
@@ -478,7 +478,7 @@
     return NULL;
   }
 
-  num = name[2] - '0';
+  num = (u8_t)(name[2] - '0');
 
   for (netif = netif_list; netif != NULL; netif = netif->next) {
     if (num == netif->num &&
@@ -904,7 +904,6 @@
 void
 netif_poll(struct netif *netif)
 {
-  struct pbuf *in;
   /* If we have a loopif, SNMP counters are adjusted for it,
    * if not they are adjusted for 'netif'. */
 #if MIB2_STATS
@@ -916,56 +915,52 @@
 #endif /* MIB2_STATS */
   SYS_ARCH_DECL_PROTECT(lev);
 
-  do {
-    /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
-    SYS_ARCH_PROTECT(lev);
-    in = netif->loop_first;
-    if (in != NULL) {
-      struct pbuf *in_end = in;
+  /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
+  SYS_ARCH_PROTECT(lev);
+  while (netif->loop_first != NULL) {
+    struct pbuf *in, *in_end;
 #if LWIP_LOOPBACK_MAX_PBUFS
-      u8_t clen = 1;
-#endif /* LWIP_LOOPBACK_MAX_PBUFS */
-      while (in_end->len != in_end->tot_len) {
-        LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
-        in_end = in_end->next;
-#if LWIP_LOOPBACK_MAX_PBUFS
-        clen++;
-#endif /* LWIP_LOOPBACK_MAX_PBUFS */
-      }
-#if LWIP_LOOPBACK_MAX_PBUFS
-      /* adjust the number of pbufs on queue */
-      LWIP_ASSERT("netif->loop_cnt_current underflow",
-        ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
-      netif->loop_cnt_current -= clen;
+    u8_t clen = 1;
 #endif /* LWIP_LOOPBACK_MAX_PBUFS */
 
-      /* 'in_end' now points to the last pbuf from 'in' */
-      if (in_end == netif->loop_last) {
-        /* this was the last pbuf in the list */
-        netif->loop_first = netif->loop_last = NULL;
-      } else {
-        /* pop the pbuf off the list */
-        netif->loop_first = in_end->next;
-        LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
-      }
-      /* De-queue the pbuf from its successors on the 'loop_' list. */
-      in_end->next = NULL;
+    in = in_end = netif->loop_first;
+    while (in_end->len != in_end->tot_len) {
+      LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
+      in_end = in_end->next;
+#if LWIP_LOOPBACK_MAX_PBUFS
+      clen++;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
     }
+#if LWIP_LOOPBACK_MAX_PBUFS
+    /* adjust the number of pbufs on queue */
+    LWIP_ASSERT("netif->loop_cnt_current underflow",
+      ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
+    netif->loop_cnt_current -= clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+    /* 'in_end' now points to the last pbuf from 'in' */
+    if (in_end == netif->loop_last) {
+      /* this was the last pbuf in the list */
+      netif->loop_first = netif->loop_last = NULL;
+    } else {
+      /* pop the pbuf off the list */
+      netif->loop_first = in_end->next;
+      LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
+    }
+    /* De-queue the pbuf from its successors on the 'loop_' list. */
+    in_end->next = NULL;
     SYS_ARCH_UNPROTECT(lev);
 
-    if (in != NULL) {
-      LINK_STATS_INC(link.recv);
-      MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len);
-      MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts);
-      /* loopback packets are always IP packets! */
-      if (ip_input(in, netif) != ERR_OK) {
-        pbuf_free(in);
-      }
-      /* Don't reference the packet any more! */
-      in = NULL;
+    LINK_STATS_INC(link.recv);
+    MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len);
+    MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts);
+    /* loopback packets are always IP packets! */
+    if (ip_input(in, netif) != ERR_OK) {
+      pbuf_free(in);
     }
-  /* go on while there is a packet on the list */
-  } while (netif->loop_first != NULL);
+    SYS_ARCH_PROTECT(lev);
+  }
+  SYS_ARCH_UNPROTECT(lev);
 }
 
 #if !LWIP_NETIF_LOOPBACK_MULTITHREADING
@@ -1058,7 +1053,7 @@
       udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
 #endif /* LWIP_UDP */
 #if LWIP_RAW
-    raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
+      raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
 #endif /* LWIP_RAW */
     }
     /* @todo: remove/readd mib2 ip6 entries? */
@@ -1101,6 +1096,13 @@
     u8_t new_valid = state & IP6_ADDR_VALID;
     LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set_state: netif address state being changed\n"));
 
+#if LWIP_IPV6_MLD
+    /* Reevaluate solicited-node multicast group membership. */
+    if (netif->flags & NETIF_FLAG_MLD6) {
+      nd6_adjust_mld_membership(netif, addr_idx, state);
+    }
+#endif /* LWIP_IPV6_MLD */
+
     if (old_valid && !new_valid) {
       /* address about to be removed by setting invalid */
 #if LWIP_TCP
@@ -1110,7 +1112,7 @@
       udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
 #endif /* LWIP_UDP */
 #if LWIP_RAW
-    raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
+      raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
 #endif /* LWIP_RAW */
       /* @todo: remove mib2 ip6 entries? */
     }
@@ -1200,10 +1202,10 @@
   /* Set address state. */
 #if LWIP_IPV6_DUP_DETECT_ATTEMPTS
   /* Will perform duplicate address detection (DAD). */
-  netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE;
+  netif_ip6_addr_set_state(netif, 0, IP6_ADDR_TENTATIVE);
 #else
   /* Consider address valid. */
-  netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED;
+  netif_ip6_addr_set_state(netif, 0, IP6_ADDR_PREFERRED);
 #endif /* LWIP_IPV6_AUTOCONFIG */
 }
 
@@ -1233,7 +1235,7 @@
 
   /* Find a free slot -- musn't be the first one (reserved for link local) */
   for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
-    if (!ip6_addr_isvalid(netif->ip6_addr_state[i])) {
+    if (ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) {
       ip_addr_copy_from_ip6(netif->ip6_addr[i], *ip6addr);
       netif_ip6_addr_set_state(netif, i, IP6_ADDR_TENTATIVE);
       if (chosen_idx != NULL) {
diff --git a/src/core/pbuf.c b/src/core/pbuf.c
index 887dc8a..af63e60 100644
--- a/src/core/pbuf.c
+++ b/src/core/pbuf.c
@@ -568,11 +568,11 @@
   }
 
   if (header_size_increment < 0) {
-    increment_magnitude = -header_size_increment;
+    increment_magnitude = (u16_t)-header_size_increment;
     /* Check that we aren't going to move off the end of the pbuf */
     LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
   } else {
-    increment_magnitude = header_size_increment;
+    increment_magnitude = (u16_t)header_size_increment;
 #if 0
     /* Can't assert these as some callers speculatively call
          pbuf_header() to see if it's OK.  Will return 1 below instead. */
@@ -1119,7 +1119,8 @@
 struct pbuf*
 pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset)
 {
-  return (struct pbuf*)(size_t)pbuf_skip_const(in, in_offset, out_offset);
+  const struct pbuf* out = pbuf_skip_const(in, in_offset, out_offset);
+  return LWIP_CONST_CAST(struct pbuf*, out);
 }
 
 /**
@@ -1227,6 +1228,7 @@
     return p;
   }
   err = pbuf_copy(q, p);
+  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
   LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
   pbuf_free(p);
   return q;
diff --git a/src/core/raw.c b/src/core/raw.c
index f692c9c..93c6547 100644
--- a/src/core/raw.c
+++ b/src/core/raw.c
@@ -209,7 +209,7 @@
 err_t
 raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
 {
-  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
     return ERR_VAL;
   }
   ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
@@ -233,7 +233,7 @@
 err_t
 raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
 {
-  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
     return ERR_VAL;
   }
   ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
@@ -320,7 +320,13 @@
     }
   }
 
-  netif = ip_route(&pcb->local_ip, ipaddr);
+  if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+    /* Don't call ip_route() with IP_ANY_TYPE */
+    netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(ipaddr)), ipaddr);
+  } else {
+    netif = ip_route(&pcb->local_ip, ipaddr);
+  }
+
   if (netif == NULL) {
     LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
     ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ipaddr);
diff --git a/src/core/sys.c b/src/core/sys.c
index f177737..7059b4d 100644
--- a/src/core/sys.c
+++ b/src/core/sys.c
@@ -36,6 +36,44 @@
  *
  */
 
+/**
+ * @defgroup sys_layer Porting (system abstraction layer)
+ * @ingroup lwip
+ * @verbinclude "sys_arch.txt"
+ *
+ * @defgroup sys_os OS abstraction layer
+ * @ingroup sys_layer
+ * No need to implement functions in this section in NO_SYS mode.
+ *
+ * @defgroup sys_sem Semaphores
+ * @ingroup sys_os
+ *
+ * @defgroup sys_mutex Mutexes
+ * @ingroup sys_os
+ * Mutexes are recommended to correctly handle priority inversion,
+ * especially if you use LWIP_CORE_LOCKING .
+ *
+ * @defgroup sys_mbox Mailboxes
+ * @ingroup sys_os
+ *
+ * @defgroup sys_time Time
+ * @ingroup sys_layer
+ *
+ * @defgroup sys_prot Critical sections
+ * @ingroup sys_layer
+ * Used to protect short regions of code against concurrent access.
+ * - Your system is a bare-metal system (probably with an RTOS)
+ *   and interrupts are under your control:
+ *   Implement this as LockInterrupts() / UnlockInterrupts()
+ * - Your system uses an RTOS with deferred interrupt handling from a
+ *   worker thread: Implement as a global mutex or lock/unlock scheduler
+ * - Your system uses a high-level OS with e.g. POSIX signals:
+ *   Implement as a global mutex
+ *
+ * @defgroup sys_misc Misc
+ * @ingroup sys_os
+ */
+
 #include "lwip/opt.h"
 
 #include "lwip/sys.h"
diff --git a/src/core/tcp.c b/src/core/tcp.c
index 1d70098..75be86b 100644
--- a/src/core/tcp.c
+++ b/src/core/tcp.c
@@ -551,7 +551,7 @@
 #endif /* LWIP_IPV4 */
 
   /* still need to check for ipaddr == NULL in IPv6 only case */
-  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
     return ERR_VAL;
   }
 
@@ -636,19 +636,44 @@
  *
  * @note The original tcp_pcb is freed. This function therefore has to be
  *       called like this:
- *             tpcb = tcp_listen(tpcb);
+ *             tpcb = tcp_listen_with_backlog(tpcb, backlog);
  */
 struct tcp_pcb *
 tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
 {
-  struct tcp_pcb_listen *lpcb;
+  return tcp_listen_with_backlog_and_err(pcb, backlog, NULL);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @param err when NULL is returned, this contains the error reason
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ *       called like this:
+ *             tpcb = tcp_listen_with_backlog_and_err(tpcb, backlog, &err);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err)
+{
+  struct tcp_pcb_listen *lpcb = NULL;
+  err_t res;
 
   LWIP_UNUSED_ARG(backlog);
-  LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL);
+  LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, res = ERR_CLSD; goto done);
 
   /* already listening? */
   if (pcb->state == LISTEN) {
-    return pcb;
+    lpcb = (struct tcp_pcb_listen*)pcb;
+    res = ERR_ALREADY;
+    goto done;
   }
 #if SO_REUSE
   if (ip_get_option(pcb, SOF_REUSEADDR)) {
@@ -659,14 +684,17 @@
       if ((lpcb->local_port == pcb->local_port) &&
           ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
         /* this address/port is already used */
-        return NULL;
+        lpcb = NULL;
+        res = ERR_USE;
+        goto done;
       }
     }
   }
 #endif /* SO_REUSE */
   lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
   if (lpcb == NULL) {
-    return NULL;
+    res = ERR_MEM;
+    goto done;
   }
   lpcb->callback_arg = pcb->callback_arg;
   lpcb->local_port = pcb->local_port;
@@ -691,6 +719,11 @@
   tcp_backlog_set(lpcb, backlog);
 #endif /* TCP_LISTEN_BACKLOG */
   TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
+  res = ERR_OK;
+done:
+  if (err != NULL) {
+    *err = res;
+  }
   return (struct tcp_pcb *)lpcb;
 }
 
@@ -826,7 +859,7 @@
   u32_t iss;
   u16_t old_local_port;
 
-  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
     return ERR_VAL;
   }
 
@@ -880,10 +913,11 @@
 #endif /* SO_REUSE */
   }
 
-  iss = tcp_next_iss();
+  iss = tcp_next_iss(pcb);
   pcb->rcv_nxt = 0;
   pcb->snd_nxt = iss;
   pcb->lastack = iss - 1;
+  pcb->snd_wl2 = iss - 1;
   pcb->snd_lbb = iss - 1;
   /* Start with a window that does not need scaling. When window scaling is
      enabled and used, the window is enlarged when both sides agree on scaling. */
@@ -1491,7 +1525,6 @@
 tcp_alloc(u8_t prio)
 {
   struct tcp_pcb *pcb;
-  u32_t iss;
 
   pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
   if (pcb == NULL) {
@@ -1554,11 +1587,6 @@
     pcb->sv = 3000 / TCP_SLOW_INTERVAL;
     pcb->rtime = -1;
     pcb->cwnd = 1;
-    iss = tcp_next_iss();
-    pcb->snd_wl2 = iss;
-    pcb->snd_nxt = iss;
-    pcb->lastack = iss;
-    pcb->snd_lbb = iss;
     pcb->tmr = tcp_ticks;
     pcb->last_timer = tcp_timer_ctr;
 
@@ -1826,12 +1854,18 @@
  * @return u32_t pseudo random sequence number
  */
 u32_t
-tcp_next_iss(void)
+tcp_next_iss(struct tcp_pcb *pcb)
 {
+#ifdef LWIP_HOOK_TCP_ISN
+  return LWIP_HOOK_TCP_ISN(&pcb->local_ip, pcb->local_port, &pcb->remote_ip, pcb->remote_port);
+#else /* LWIP_HOOK_TCP_ISN */
   static u32_t iss = 6510;
 
+  LWIP_UNUSED_ARG(pcb);
+
   iss += tcp_ticks;       /* XXX */
   return iss;
+#endif /* LWIP_HOOK_TCP_ISN */
 }
 
 #if TCP_CALCULATE_EFF_SEND_MSS
diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c
index 93268b3..fbbdc72 100644
--- a/src/core/tcp_in.c
+++ b/src/core/tcp_in.c
@@ -358,6 +358,11 @@
         ((pcb->refused_data != NULL) && (tcplen > 0))) {
         /* pcb has been aborted or refused data is still refused and the new
            segment contains data */
+        if (pcb->rcv_ann_wnd == 0) {
+          /* this is a zero-window probe, we respond to it with current RCV.NXT
+          and drop the data segment */
+          tcp_send_empty_ack(pcb);
+        }
         TCP_STATS_INC(tcp.drop);
         MIB2_STATS_INC(mib2.tcpinerrs);
         goto aborted;
@@ -542,6 +547,7 @@
 tcp_listen_input(struct tcp_pcb_listen *pcb)
 {
   struct tcp_pcb *npcb;
+  u32_t iss;
   err_t rc;
 
   if (flags & TCP_RST) {
@@ -589,6 +595,11 @@
     npcb->state = SYN_RCVD;
     npcb->rcv_nxt = seqno + 1;
     npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+    iss = tcp_next_iss(npcb);
+    npcb->snd_wl2 = iss;
+    npcb->snd_nxt = iss;
+    npcb->lastack = iss;
+    npcb->snd_lbb = iss;
     npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
     npcb->callback_arg = pcb->callback_arg;
 #if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
@@ -602,7 +613,7 @@
 
     /* Parse any options in the SYN. */
     tcp_parseopt(npcb);
-    npcb->snd_wnd = SND_WND_SCALE(npcb, tcphdr->wnd);
+    npcb->snd_wnd = tcphdr->wnd;
     npcb->snd_wnd_max = npcb->snd_wnd;
     npcb->ssthresh = LWIP_TCP_INITIAL_SSTHRESH(npcb);
 
@@ -751,7 +762,7 @@
       pcb->rcv_nxt = seqno + 1;
       pcb->rcv_ann_right_edge = pcb->rcv_nxt;
       pcb->lastack = ackno;
-      pcb->snd_wnd = SND_WND_SCALE(pcb, tcphdr->wnd);
+      pcb->snd_wnd = tcphdr->wnd;
       pcb->snd_wnd_max = pcb->snd_wnd;
       pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
       pcb->state = ESTABLISHED;
diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c
index 3c2bc81..8ccdffb 100644
--- a/src/core/tcp_out.c
+++ b/src/core/tcp_out.c
@@ -377,6 +377,7 @@
   u16_t oversize = 0;
   u16_t oversize_used = 0;
 #endif /* TCP_OVERSIZE */
+  u16_t extendlen = 0;
 #if TCP_CHECKSUM_ON_COPY
   u16_t concat_chksum = 0;
   u8_t concat_chksum_swapped = 0;
@@ -480,6 +481,10 @@
     /*
      * Phase 2: Chain a new pbuf to the end of pcb->unsent.
      *
+     * As an exception when NOT copying the data, if the given data buffer
+     * directly follows the last unsent data buffer in memory, extend the last
+     * ROM pbuf reference to the buffer, thus saving a ROM pbuf allocation.
+     *
      * We don't extend segments containing SYN/FIN flags or options
      * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
      * the end.
@@ -506,12 +511,24 @@
 #if TCP_CHECKSUM_ON_COPY
         concat_chksummed += seglen;
 #endif /* TCP_CHECKSUM_ON_COPY */
+        queuelen += pbuf_clen(concat_p);
       } else {
         /* Data is not copied */
-        if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
-          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
-                      ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
-          goto memerr;
+        /* If the last unsent pbuf is of type PBUF_ROM, try to extend it. */
+        struct pbuf *p;
+        for (p = last_unsent->p; p->next != NULL; p = p->next);
+        if (p->type == PBUF_ROM && (const u8_t *)p->payload + p->len == (const u8_t *)arg) {
+          LWIP_ASSERT("tcp_write: ROM pbufs cannot be oversized", pos == 0);
+          extendlen = seglen;
+        } else {
+          if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+            LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                        ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+            goto memerr;
+          }
+          /* reference the non-volatile payload data */
+          ((struct pbuf_rom*)concat_p)->payload = (const u8_t*)arg + pos;
+          queuelen += pbuf_clen(concat_p);
         }
 #if TCP_CHECKSUM_ON_COPY
         /* calculate the checksum of nocopy-data */
@@ -519,12 +536,9 @@
           &concat_chksum, &concat_chksum_swapped);
         concat_chksummed += seglen;
 #endif /* TCP_CHECKSUM_ON_COPY */
-        /* reference the non-volatile payload data */
-        ((struct pbuf_rom*)concat_p)->payload = (const u8_t*)arg + pos;
       }
 
       pos += seglen;
-      queuelen += pbuf_clen(concat_p);
     }
   } else {
 #if TCP_OVERSIZE
@@ -669,26 +683,40 @@
 #endif /* TCP_OVERSIZE */
 
   /*
-   * Phase 2: concat_p can be concatenated onto last_unsent->p
+   * Phase 2: concat_p can be concatenated onto last_unsent->p, unless we
+   * determined that the last ROM pbuf can be extended to include the new data.
    */
   if (concat_p != NULL) {
     LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
       (last_unsent != NULL));
     pbuf_cat(last_unsent->p, concat_p);
     last_unsent->len += concat_p->tot_len;
-#if TCP_CHECKSUM_ON_COPY
-    if (concat_chksummed) {
-      /*if concat checksumm swapped - swap it back */
-      if (concat_chksum_swapped) {
-        concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum);
-      }
-      tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
-        &last_unsent->chksum_swapped);
-      last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+  } else if (extendlen > 0) {
+    struct pbuf *p;
+    LWIP_ASSERT("tcp_write: extension of reference requires reference",
+      last_unsent != NULL && last_unsent->p != NULL);
+    for (p = last_unsent->p; p->next != NULL; p = p->next) {
+      p->tot_len += extendlen;
     }
-#endif /* TCP_CHECKSUM_ON_COPY */
+    p->tot_len += extendlen;
+    p->len += extendlen;
+    last_unsent->len += extendlen;
   }
 
+#if TCP_CHECKSUM_ON_COPY
+  if (concat_chksummed) {
+    LWIP_ASSERT("tcp_write: concat checksum needs concatenated data",
+        concat_p != NULL || extendlen > 0);
+    /*if concat checksumm swapped - swap it back */
+    if (concat_chksum_swapped) {
+      concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum);
+    }
+    tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+      &last_unsent->chksum_swapped);
+    last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+  }
+#endif /* TCP_CHECKSUM_ON_COPY */
+
   /*
    * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
    * is harmless
@@ -1033,6 +1061,24 @@
                  lwip_ntohl(seg->tcphdr->seqno), pcb->lastack));
   }
 #endif /* TCP_CWND_DEBUG */
+  /* Check if we need to start the persistent timer when the next unsent segment
+   * does not fit within the remaining send window and RTO timer is not running (we
+   * have no in-flight data). A traditional approach would fill the remaining window
+   * with part of the unsent segment (which will engage zero-window probing upon
+   * reception of the zero window update from the receiver). This ensures the
+   * subsequent window update is reliably received. With the goal of being lightweight,
+   * we avoid splitting the unsent segment and treat the window as already zero.
+   */
+  if (seg != NULL &&
+      lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd &&
+      wnd > 0 && wnd == pcb->snd_wnd && pcb->unacked == NULL) {
+    /* Start the persist timer */
+    if (pcb->persist_backoff == 0) {
+      pcb->persist_cnt = 0;
+      pcb->persist_backoff = 1;
+    }
+    goto output_done;
+  }
   /* data available and window allows it to be sent? */
   while (seg != NULL &&
          lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
@@ -1112,6 +1158,7 @@
     }
     seg = pcb->unsent;
   }
+output_done:
 #if TCP_OVERSIZE
   if (pcb->unsent == NULL) {
     /* last unsent has been removed, reset unsent_oversize */
diff --git a/src/core/timeouts.c b/src/core/timeouts.c
index e2dc0fc..227d71f 100644
--- a/src/core/timeouts.c
+++ b/src/core/timeouts.c
@@ -179,7 +179,7 @@
   for (i = 1; i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) {
     /* we have to cast via size_t to get rid of const warning
       (this is OK as cyclic_timer() casts back to const* */
-    sys_timeout(lwip_cyclic_timers[i].interval_ms, cyclic_timer, (void*)(size_t)&lwip_cyclic_timers[i]);
+    sys_timeout(lwip_cyclic_timers[i].interval_ms, cyclic_timer, LWIP_CONST_CAST(void*, &lwip_cyclic_timers[i]));
   }
 
   /* Initialise timestamp for sys_check_timeouts */
diff --git a/src/core/udp.c b/src/core/udp.c
index e0ab186..a916414 100644
--- a/src/core/udp.c
+++ b/src/core/udp.c
@@ -116,24 +116,6 @@
     }
   }
   return udp_port;
-#if 0
-  struct udp_pcb *ipcb = udp_pcbs;
-  while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) {
-    if (ipcb->local_port == udp_port) {
-      /* port is already used by another udp_pcb */
-      udp_port++;
-      /* restart scanning all udp pcbs */
-      ipcb = udp_pcbs;
-    } else {
-      /* go on with next udp pcb */
-      ipcb = ipcb->next;
-    }
-  }
-  if (ipcb != NULL) {
-    return 0;
-  }
-  return udp_port;
-#endif
 }
 
 /** Common code to see if the current input packet matches the pcb
@@ -178,15 +160,8 @@
       }
     } else
 #endif /* LWIP_IPV4 */
-    /* Handle IPv4 and IPv6: all, multicast or exact match */
-    if (ip_addr_isany(&pcb->local_ip) ||
-#if LWIP_IPV6_MLD
-       (ip_current_is_v6() && ip6_addr_ismulticast(ip6_current_dest_addr())) ||
-#endif /* LWIP_IPV6_MLD */
-#if LWIP_IGMP
-       (!ip_current_is_v6() && ip4_addr_ismulticast(ip4_current_dest_addr())) ||
-#endif /* LWIP_IGMP */
-       ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+    /* Handle IPv4 and IPv6: all or exact match */
+    if (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
       return 1;
     }
   }
@@ -429,7 +404,7 @@
          destination address was broadcast/multicast. */
       if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) {
         /* move payload pointer back to ip header */
-        pbuf_header_force(p, ip_current_header_tot_len() + UDP_HLEN);
+        pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN));
         icmp_port_unreach(ip_current_is_v6(), p);
       }
 #endif /* LWIP_ICMP || LWIP_ICMP6 */
@@ -571,7 +546,12 @@
 #endif /* LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) */
 
   /* find the outgoing network interface for this packet */
-  netif = ip_route(&pcb->local_ip, dst_ip_route);
+  if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+    /* Don't call ip_route() with IP_ANY_TYPE */
+    netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(dst_ip_route)), dst_ip_route);
+  } else {
+    netif = ip_route(&pcb->local_ip, dst_ip_route);
+  }
 
   /* no outgoing network interface could be found? */
   if (netif == NULL) {
@@ -912,7 +892,7 @@
 #endif /* LWIP_IPV4 */
 
   /* still need to check for ipaddr == NULL in IPv6 only case */
-  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) {
+  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
     return ERR_VAL;
   }
 
diff --git a/src/include/lwip/api.h b/src/include/lwip/api.h
index e8620dd..516bd16 100644
--- a/src/include/lwip/api.h
+++ b/src/include/lwip/api.h
@@ -43,8 +43,7 @@
 /* Note: Netconn API is always available when sockets are enabled -
  * sockets are implemented on top of them */
 
-#include <stddef.h> /* for size_t */
-
+#include "lwip/arch.h"
 #include "lwip/netbuf.h"
 #include "lwip/sys.h"
 #include "lwip/ip_addr.h"
@@ -90,6 +89,7 @@
 #define NETCONNTYPE_ISUDPLITE(t)     (((t)&0xF3) == NETCONN_UDPLITE)
 #define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF3) == NETCONN_UDPNOCHKSUM)
 #else /* LWIP_IPV6 */
+#define NETCONNTYPE_ISIPV6(t)        (0)
 #define NETCONNTYPE_ISUDPLITE(t)     ((t) == NETCONN_UDPLITE)
 #define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM)
 #endif /* LWIP_IPV6 */
diff --git a/src/include/lwip/apps/mqtt.h b/src/include/lwip/apps/mqtt.h
new file mode 100644
index 0000000..ee88040
--- /dev/null
+++ b/src/include/lwip/apps/mqtt.h
@@ -0,0 +1,243 @@
+/**
+ * @file
+ * MQTT client
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_CLIENT_H
+#define LWIP_HDR_APPS_MQTT_CLIENT_H
+
+#include "lwip/apps/mqtt_opts.h"
+#include "lwip/err.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct mqtt_client_t mqtt_client_t;
+
+/** @ingroup mqtt
+ * Default MQTT port */
+#define MQTT_PORT 1883
+
+/*---------------------------------------------------------------------------------------------- */
+/* Connection with server */
+
+/**
+ * @ingroup mqtt
+ * Client information and connection parameters */
+struct mqtt_connect_client_info_t {
+  /** Client identifier, must be set by caller */
+  const char *client_id;
+  /** User name and password, set to NULL if not used */
+  const char* client_user;
+  const char* client_pass;
+  /** keep alive time in seconds, 0 to disable keep alive functionality*/
+  u16_t keep_alive;
+  /** will topic, set to NULL if will is not to be used,
+      will_msg, will_qos and will retain are then ignored */
+  const char* will_topic;
+  const char* will_msg;
+  u8_t will_qos;
+  u8_t will_retain;
+};
+
+/**
+ * @ingroup mqtt
+ * Connection status codes */
+typedef enum
+{
+  MQTT_CONNECT_ACCEPTED                 = 0,
+  MQTT_CONNECT_REFUSED_PROTOCOL_VERSION = 1,
+  MQTT_CONNECT_REFUSED_IDENTIFIER       = 2,
+  MQTT_CONNECT_REFUSED_SERVER           = 3,
+  MQTT_CONNECT_REFUSED_USERNAME_PASS    = 4,
+  MQTT_CONNECT_REFUSED_NOT_AUTHORIZED_  = 5,
+  MQTT_CONNECT_DISCONNECTED             = 256,
+  MQTT_CONNECT_TIMEOUT                  = 257
+} mqtt_connection_status_t;
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt connection status callback. Called when
+ * client has connected to the server after initiating a mqtt connection attempt by
+ * calling mqtt_connect() or when connection is closed by server or an error
+ *
+ * @param client MQTT client itself
+ * @param arg Additional argument to pass to the callback function
+ * @param status Connect result code or disconnection notification @see mqtt_connection_status_t
+ *
+ */
+typedef void (*mqtt_connection_cb_t)(mqtt_client_t *client, void *arg, mqtt_connection_status_t status);
+
+
+/**
+ * @ingroup mqtt
+ * Data callback flags */
+enum {
+  /** Flag set when last fragment of data arrives in data callback */
+  MQTT_DATA_FLAG_LAST = 1
+};
+
+/** 
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish data callback function. Called when data
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param data User data, pointed object, data may not be referenced after callback return,
+          NULL is passed when all publish data are delivered
+ * @param len Length of publish data fragment
+ * @param flags MQTT_DATA_FLAG_LAST set when this call contains the last part of data from publish message
+ *
+ */
+typedef void (*mqtt_incoming_data_cb_t)(void *arg, const u8_t *data, u16_t len, u8_t flags);
+
+
+/** 
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish function. Called when an incoming publish
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param topic Zero terminated Topic text string, topic may not be referenced after callback return
+ * @param tot_len Total length of publish data, if set to 0 (no publish payload) data callback will not be invoked
+ */
+typedef void (*mqtt_incoming_publish_cb_t)(void *arg, const char *topic, u32_t tot_len);
+
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt request callback. Called when a subscribe, unsubscribe
+ * or publish request has completed
+ * @param arg Pointer to user data supplied when invoking request
+ * @param err ERR_OK on success
+ *            ERR_TIMEOUT if no response was received within timeout,
+ *            ERR_ABRT if (un)subscribe was denied
+ */
+typedef void (*mqtt_request_cb_t)(void *arg, err_t err);
+
+
+/**
+ * Pending request item, binds application callback to pending server requests
+ */
+struct mqtt_request_t
+{
+  /** Next item in list, NULL means this is the last in chain,
+      next pointing at itself means request is unallocated */
+  struct mqtt_request_t *next;
+  /** Callback to upper layer */
+  mqtt_request_cb_t cb;
+  void *arg;
+  /** MQTT packet identifier */
+  u16_t pkt_id;
+  /** Expire time relative to element before this  */
+  u16_t timeout_diff;
+};
+
+/** Ring buffer */
+struct mqtt_ringbuf_t {
+  u16_t put;
+  u16_t get;
+  u8_t buf[MQTT_OUTPUT_RINGBUF_SIZE];
+};
+
+/** MQTT client */
+struct mqtt_client_t
+{
+  /** Timers and timeouts */
+  u16_t cyclic_tick;
+  u16_t keep_alive;
+  u16_t server_watchdog;
+  /** Packet identifier generator*/
+  u16_t pkt_id_seq;
+  /** Packet identifier of pending incoming publish */
+  u16_t inpub_pkt_id;
+  /** Connection state */
+  u8_t conn_state;
+  struct tcp_pcb *conn;
+  /** Connection callback */
+  void *connect_arg;
+  mqtt_connection_cb_t connect_cb;
+  /** Pending requests to server */
+  struct mqtt_request_t *pend_req_queue;
+  struct mqtt_request_t req_list[MQTT_REQ_MAX_IN_FLIGHT];
+  void *inpub_arg;
+  /** Incoming data callback */
+  mqtt_incoming_data_cb_t data_cb;
+  mqtt_incoming_publish_cb_t pub_cb;
+  /** Input */
+  u32_t msg_idx;
+  u8_t rx_buffer[MQTT_VAR_HEADER_BUFFER_LEN];
+  /** Output ring-buffer */
+  struct mqtt_ringbuf_t output;
+};
+
+
+/** Connect to server */
+err_t mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ipaddr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+                   const struct mqtt_connect_client_info_t *client_info);
+
+/** Disconnect from server */
+void mqtt_disconnect(mqtt_client_t *client);
+
+/** Create new client */
+mqtt_client_t *mqtt_client_new(void);
+
+/** Check connection status */
+u8_t mqtt_client_is_connected(mqtt_client_t *client);
+
+/** Set callback to call for incoming publish */
+void mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t,
+                             mqtt_incoming_data_cb_t data_cb, void *arg);
+
+/** Common function for subscribe and unsubscribe */
+err_t mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub);
+
+/** @ingroup mqtt
+ *Subscribe to topic */
+#define mqtt_subscribe(client, topic, qos, cb, arg) mqtt_sub_unsub(client, topic, qos, cb, arg, 1)
+/** @ingroup mqtt
+ *  Unsubscribe to topic */
+#define mqtt_unsubscribe(client, topic, cb, arg) mqtt_sub_unsub(client, topic, 0, cb, arg, 0)
+
+
+/** Publish data to topic */
+err_t mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+                                    mqtt_request_cb_t cb, void *arg);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_CLIENT_H */
diff --git a/src/include/lwip/apps/mqtt_opts.h b/src/include/lwip/apps/mqtt_opts.h
new file mode 100644
index 0000000..ffefacd
--- /dev/null
+++ b/src/include/lwip/apps/mqtt_opts.h
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * MQTT client options
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_OPTS_H
+#define LWIP_HDR_APPS_MQTT_OPTS_H
+
+#include "lwip/opt.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup mqtt_opts Options
+ * @ingroup mqtt
+ * @{
+ */
+
+/**
+ * Output ring-buffer size, must be able to fit largest outgoing publish message topic+payloads
+ */
+#ifndef MQTT_OUTPUT_RINGBUF_SIZE
+#define MQTT_OUTPUT_RINGBUF_SIZE 256
+#endif
+
+/**
+ * Number of bytes in receive buffer, must be at least the size of the longest incoming topic + 8
+ * If one wants to avoid fragmented incoming publish, set length to max incoming topic length + max payload length + 8
+ */
+#ifndef MQTT_VAR_HEADER_BUFFER_LEN
+#define MQTT_VAR_HEADER_BUFFER_LEN 128
+#endif
+
+/**
+ * Maximum number of pending subscribe, unsubscribe and publish requests to server .
+ */
+#ifndef MQTT_REQ_MAX_IN_FLIGHT
+#define MQTT_REQ_MAX_IN_FLIGHT 4
+#endif
+
+/**
+ * Seconds between each cyclic timer call.
+ */
+#ifndef MQTT_CYCLIC_TIMER_INTERVAL
+#define MQTT_CYCLIC_TIMER_INTERVAL 5
+#endif
+
+/**
+ * Publish, subscribe and unsubscribe request timeout in seconds.
+ */
+#ifndef MQTT_REQ_TIMEOUT
+#define MQTT_REQ_TIMEOUT 30
+#endif
+
+/**
+ * Seconds for MQTT connect response timeout after sending connect request
+ */
+#ifndef MQTT_CONNECT_TIMOUT
+#define MQTT_CONNECT_TIMOUT 100
+#endif
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_OPTS_H */
diff --git a/src/include/lwip/arch.h b/src/include/lwip/arch.h
index 473e4a4..b59f521 100644
--- a/src/include/lwip/arch.h
+++ b/src/include/lwip/arch.h
@@ -47,12 +47,67 @@
 
 #include "arch/cc.h"
 
+/**
+ * @defgroup compiler_abstraction Compiler/platform abstraction
+ * @ingroup sys_layer
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
+ * @{
+ */
+
+/** Define the byte order of the system.
+ * Needed for conversion of network data to host byte order.
+ * Allowed values: LITTLE_ENDIAN and BIG_ENDIAN
+ */
+#ifndef BYTE_ORDER
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+
+/** Define random number generator function of your system */
+#ifndef LWIP_RAND
+#define LWIP_RAND() ((u32_t)rand())
+#endif
+
+/** Platform specific diagnostic output.\n
+ * Note the default implementation pulls in printf, which may
+ * in turn pull in a lot of standard libary code. In resource-constrained 
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_DIAG
+#define LWIP_PLATFORM_DIAG(x)	do {printf x;} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Platform specific assertion handling.\n
+ * Note the default implementation pulls in printf, fflush and abort, which may
+ * in turn pull in a lot of standard libary code. In resource-constrained 
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_ASSERT
+#define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
+                                     x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if you do not want to
+ * include stddef.h header to get size_t. You need to typedef size_t
+ * by yourself in this case.
+ */
+#ifndef LWIP_NO_STDDEF_H
+#define LWIP_NO_STDDEF_H 0
+#endif
+
+#if !LWIP_NO_STDDEF_H
+#include <stddef.h> /* for size_t */
+#endif
+
 /** Define this to 1 in arch/cc.h of your port if your compiler does not provide
- * the stdint.h header. This cannot be \#defined in lwipopts.h since
- * this is not an option of lwIP itself, but an option of the lwIP port
- * to your system.
- * Additionally, this header is meant to be \#included in lwipopts.h
- * (you may need to declare function prototypes in there).
+ * the stdint.h header. You need to typedef the generic types listed in
+ * lwip/arch.h yourself in this case (u8_t, u16_t...).
  */
 #ifndef LWIP_NO_STDINT_H
 #define LWIP_NO_STDINT_H 0
@@ -71,11 +126,8 @@
 #endif
 
 /** Define this to 1 in arch/cc.h of your port if your compiler does not provide
- * the inttypes.h header. This cannot be \#defined in lwipopts.h since
- * this is not an option of lwIP itself, but an option of the lwIP port
- * to your system.
- * Additionally, this header is meant to be \#included in lwipopts.h
- * (you may need to declare function prototypes in there).
+ * the inttypes.h header. You need to define the format strings listed in
+ * lwip/arch.h yourself in this case (X8_F, U16_F...).
  */
 #ifndef LWIP_NO_INTTYPES_H
 #define LWIP_NO_INTTYPES_H 0
@@ -110,14 +162,31 @@
 #endif
 #endif
 
+/** C++ const_cast<target_type>(val) equivalent to remove constness from a value (GCC -Wcast-qual) */
+#ifndef LWIP_CONST_CAST
+#define LWIP_CONST_CAST(target_type, val) ((target_type)((ptrdiff_t)val))
+#endif
+
+/** Get rid of alignment cast warnings (GCC -Wcast-align) */
+#ifndef LWIP_ALIGNMENT_CAST
+#define LWIP_ALIGNMENT_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
+/** Get rid of warnings related to pointer-to-numeric and vice-versa casts,
+ * e.g. "conversion from 'u8_t' to 'void *' of greater size"
+ */
+#ifndef LWIP_PTR_NUMERIC_CAST
+#define LWIP_PTR_NUMERIC_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
 /** Allocates a memory buffer of specified size that is of sufficient size to align
  * its start address using LWIP_MEM_ALIGN.
  * You can declare your own version here e.g. to enforce alignment without adding
  * trailing padding bytes (see LWIP_MEM_ALIGN_BUFFER) or your own section placement
- * requirements.
- * e.g. if you use gcc and need 32 bit alignment:
- * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[size] __attribute__((aligned(4)))
- * or more portable:
+ * requirements.\n
+ * e.g. if you use gcc and need 32 bit alignment:\n
+ * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[size] \_\_attribute\_\_((aligned(4)))\n
+ * or more portable:\n
  * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u32_t variable_name[(size + sizeof(u32_t) - 1) / sizeof(u32_t)]
  */
 #ifndef LWIP_DECLARE_MEMORY_ALIGNED
@@ -151,39 +220,84 @@
 extern "C" {
 #endif
 
+/** Packed structs support.
+  * Placed BEFORE declaration of a packed struct.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
 #ifndef PACK_STRUCT_BEGIN
 #define PACK_STRUCT_BEGIN
 #endif /* PACK_STRUCT_BEGIN */
 
+/** Packed structs support.
+  * Placed AFTER declaration of a packed struct.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
 #ifndef PACK_STRUCT_END
 #define PACK_STRUCT_END
 #endif /* PACK_STRUCT_END */
 
+/** Packed structs support.
+  * Placed between end of declaration of a packed struct and trailing semicolon.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
 #ifndef PACK_STRUCT_STRUCT
+#if defined(__GNUC__) || defined(__clang__)
+#define PACK_STRUCT_STRUCT __attribute__((packed))
+#else
 #define PACK_STRUCT_STRUCT
+#endif
 #endif /* PACK_STRUCT_STRUCT */
 
+/** Packed structs support.
+  * Wraps u32_t and u16_t members.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
 #ifndef PACK_STRUCT_FIELD
 #define PACK_STRUCT_FIELD(x) x
 #endif /* PACK_STRUCT_FIELD */
 
-/* Used for struct fields of u8_t,
- * where some compilers warn that packing is not necessary */
+/** Packed structs support.
+  * Wraps u8_t members, where some compilers warn that packing is not necessary.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
 #ifndef PACK_STRUCT_FLD_8
 #define PACK_STRUCT_FLD_8(x) PACK_STRUCT_FIELD(x)
 #endif /* PACK_STRUCT_FLD_8 */
 
-/* Used for struct fields of that are packed structs themself,
- * where some compilers warn that packing is not necessary */
+/** Packed structs support.
+  * Wraps members that are packed structs themselves, where some compilers warn that packing is not necessary.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
 #ifndef PACK_STRUCT_FLD_S
 #define PACK_STRUCT_FLD_S(x) PACK_STRUCT_FIELD(x)
 #endif /* PACK_STRUCT_FLD_S */
 
+/** Packed structs support using \#include files before and after struct to be packed.\n
+ * The file included BEFORE the struct is "arch/bpstruct.h".\n
+ * The file included AFTER the struct is "arch/epstruct.h".\n
+ * This can be used to implement struct packing on MS Visual C compilers, see
+ * the Win32 port in the lwIP contrib repository for reference.
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifdef __DOXYGEN__
+#define PACK_STRUCT_USE_INCLUDES
+#endif
 
+/** Eliminates compiler warning about unused arguments (GCC -Wextra -Wunused). */
 #ifndef LWIP_UNUSED_ARG
 #define LWIP_UNUSED_ARG(x) (void)x
 #endif /* LWIP_UNUSED_ARG */
 
+/**
+ * @}
+ */
 
 #ifdef __cplusplus
 }
diff --git a/src/include/lwip/autoip.h b/src/include/lwip/autoip.h
index 2f66698..1d85bcc 100644
--- a/src/include/lwip/autoip.h
+++ b/src/include/lwip/autoip.h
@@ -88,6 +88,8 @@
 /* for lwIP internal use by ip4.c */
 u8_t autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr);
 
+#define netif_autoip_data(netif) ((struct autoip*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP))
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/lwip/debug.h b/src/include/lwip/debug.h
index c676612..a142f1c 100644
--- a/src/include/lwip/debug.h
+++ b/src/include/lwip/debug.h
@@ -40,24 +40,45 @@
 #include "lwip/arch.h"
 #include "lwip/opt.h"
 
-/** lower two bits indicate debug level
- * - 0 all
- * - 1 warning
- * - 2 serious
- * - 3 severe
+/**
+ * @defgroup debugging_levels LWIP_DBG_MIN_LEVEL and LWIP_DBG_TYPES_ON values
+ * @ingroup lwip_opts_debugmsg
+ * @{
  */
-#define LWIP_DBG_LEVEL_ALL     0x00
-#define LWIP_DBG_LEVEL_OFF     LWIP_DBG_LEVEL_ALL /* compatibility define only */
-#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */
-#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */
-#define LWIP_DBG_LEVEL_SEVERE  0x03
-#define LWIP_DBG_MASK_LEVEL    0x03
 
+/** @name Debug level (LWIP_DBG_MIN_LEVEL)
+ * @{
+ */
+/** Debug level: ALL messages*/
+#define LWIP_DBG_LEVEL_ALL     0x00
+/** Debug level: Warnings. bad checksums, dropped packets, ... */
+#define LWIP_DBG_LEVEL_WARNING 0x01
+/** Debug level: Serious. memory allocation failures, ... */
+#define LWIP_DBG_LEVEL_SERIOUS 0x02
+/** Debug level: Severe */
+#define LWIP_DBG_LEVEL_SEVERE  0x03
+/**
+ * @}
+ */
+
+#define LWIP_DBG_MASK_LEVEL    0x03
+/* compatibility define only */
+#define LWIP_DBG_LEVEL_OFF     LWIP_DBG_LEVEL_ALL
+
+/** @name Enable/disable debug messages completely (LWIP_DBG_TYPES_ON)
+ * @{
+ */
 /** flag for LWIP_DEBUGF to enable that debug message */
 #define LWIP_DBG_ON            0x80U
 /** flag for LWIP_DEBUGF to disable that debug message */
 #define LWIP_DBG_OFF           0x00U
+/**
+ * @}
+ */
 
+/** @name Debug message types (LWIP_DBG_TYPES_ON)
+ * @{
+ */
 /** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */
 #define LWIP_DBG_TRACE         0x40U
 /** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */
@@ -66,11 +87,31 @@
 #define LWIP_DBG_FRESH         0x10U
 /** flag for LWIP_DEBUGF to halt after printing this debug message */
 #define LWIP_DBG_HALT          0x08U
+/**
+ * @}
+ */
 
 /**
- * LWIP_NOASSERT: Disable LWIP_ASSERT checks.
- * -- To disable assertions define LWIP_NOASSERT in arch/cc.h.
+ * @}
  */
+
+/**
+ * @defgroup lwip_assertions Assertion handling
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
+ * LWIP_NOASSERT: Disable LWIP_ASSERT checks:
+ * To disable assertions define LWIP_NOASSERT in arch/cc.h.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_NOASSERT
+#undef LWIP_NOASSERT
+#endif
+/**
+ * @}
+ */
+
 #ifndef LWIP_NOASSERT
 #define LWIP_ASSERT(message, assertion) do { if (!(assertion)) { \
   LWIP_PLATFORM_ASSERT(message); }} while(0)
@@ -81,7 +122,6 @@
 #define LWIP_ASSERT(message, assertion)
 #endif /* LWIP_NOASSERT */
 
-/** if "expression" isn't true, then print "message" and execute "handler" expression */
 #ifndef LWIP_ERROR
 #ifndef LWIP_NOASSERT
 #define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_ASSERT(message)
@@ -91,17 +131,23 @@
 #define LWIP_PLATFORM_ERROR(message)
 #endif
 
+/* if "expression" isn't true, then print "message" and execute "handler" expression */
 #define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
   LWIP_PLATFORM_ERROR(message); handler;}} while(0)
 #endif /* LWIP_ERROR */
 
+/** Enable debug message printing, but only if debug message type is enabled
+ *  AND is of correct type AND is at least LWIP_DBG_LEVEL.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_DEBUG
+#undef LWIP_DEBUG
+#endif
+
 #ifdef LWIP_DEBUG
 #ifndef LWIP_PLATFORM_DIAG
 #error "If you want to use LWIP_DEBUG, LWIP_PLATFORM_DIAG(message) needs to be defined in your arch/cc.h"
 #endif
-/** print debug message only if debug message type is enabled...
- *  AND is of correct type AND is at least LWIP_DBG_LEVEL
- */
 #define LWIP_DEBUGF(debug, message) do { \
                                if ( \
                                    ((debug) & LWIP_DBG_ON) && \
@@ -119,4 +165,3 @@
 #endif /* LWIP_DEBUG */
 
 #endif /* LWIP_HDR_DEBUG_H */
-
diff --git a/src/include/lwip/def.h b/src/include/lwip/def.h
index bb07009..aaa64c7 100644
--- a/src/include/lwip/def.h
+++ b/src/include/lwip/def.h
@@ -57,6 +57,12 @@
 /* Get the number of entries in an array ('x' must NOT be a pointer!) */
 #define LWIP_ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
 
+/** Create u32_t value from bytes */
+#define LWIP_MAKEU32(a,b,c,d) (((u32_t)((a) & 0xff) << 24) | \
+                               ((u32_t)((b) & 0xff) << 16) | \
+                               ((u32_t)((c) & 0xff) << 8)  | \
+                                (u32_t)((d) & 0xff))
+
 #ifndef NULL
 #ifdef __cplusplus
 #define NULL 0
@@ -65,13 +71,6 @@
 #endif
 #endif
 
-/* Endianess-optimized shifting of two u8_t to create one u16_t */
-#if BYTE_ORDER == LITTLE_ENDIAN
-#define LWIP_MAKE_U16(a, b) ((a << 8) | b)
-#else
-#define LWIP_MAKE_U16(a, b) ((b << 8) | a)
-#endif
-
 #if BYTE_ORDER == BIG_ENDIAN
 #define lwip_htons(x) (x)
 #define lwip_ntohs(x) (x)
@@ -103,11 +102,11 @@
 /* These macros should be calculated by the preprocessor and are used
    with compile-time constants only (so that there is no little-endian
    overhead at runtime). */
-#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
+#define PP_HTONS(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8))
 #define PP_NTOHS(x) PP_HTONS(x)
-#define PP_HTONL(x) ((((x) & 0xff) << 24) | \
-                     (((x) & 0xff00) << 8) | \
-                     (((x) & 0xff0000UL) >> 8) | \
+#define PP_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \
+                     (((x) & 0x0000ff00UL) <<  8) | \
+                     (((x) & 0x00ff0000UL) >>  8) | \
                      (((x) & 0xff000000UL) >> 24))
 #define PP_NTOHL(x) PP_HTONL(x)
 
diff --git a/src/include/lwip/dhcp.h b/src/include/lwip/dhcp.h
index f750328..ac1b18e 100644
--- a/src/include/lwip/dhcp.h
+++ b/src/include/lwip/dhcp.h
@@ -132,6 +132,8 @@
 extern void dhcp_set_ntp_servers(u8_t num_ntp_servers, const ip4_addr_t* ntp_server_addrs);
 #endif /* LWIP_DHCP_GET_NTP_SRV */
 
+#define netif_dhcp_data(netif) ((struct dhcp*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP))
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/lwip/dns.h b/src/include/lwip/dns.h
index 00d5a78..2d147af 100644
--- a/src/include/lwip/dns.h
+++ b/src/include/lwip/dns.h
@@ -61,7 +61,7 @@
 #ifndef LWIP_DNS_ADDRTYPE_DEFAULT
 #define LWIP_DNS_ADDRTYPE_DEFAULT   LWIP_DNS_ADDRTYPE_IPV4_IPV6
 #endif
-#elif defined(LWIP_IPV4)
+#elif LWIP_IPV4
 #define LWIP_DNS_ADDRTYPE_DEFAULT   LWIP_DNS_ADDRTYPE_IPV4
 #else
 #define LWIP_DNS_ADDRTYPE_DEFAULT   LWIP_DNS_ADDRTYPE_IPV6
@@ -84,6 +84,13 @@
 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
 #endif /* DNS_LOCAL_HOSTLIST */
 
+#if LWIP_IPV4
+extern const ip_addr_t dns_mquery_v4group;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+extern const ip_addr_t dns_mquery_v6group;
+#endif /* LWIP_IPV6 */
+
 /** Callback which is invoked when a hostname is found.
  * A function of this type must be implemented by the application using the DNS resolver.
  * @param name pointer to the name that was looked up.
diff --git a/src/include/lwip/errno.h b/src/include/lwip/errno.h
index 25bc1f8..641cffb 100644
--- a/src/include/lwip/errno.h
+++ b/src/include/lwip/errno.h
@@ -45,100 +45,100 @@
 
 #ifdef LWIP_PROVIDE_ERRNO
 
-#define  EPERM         1  /* Operation not permitted */
-#define  ENOENT        2  /* No such file or directory */
-#define  ESRCH         3  /* No such process */
-#define  EINTR         4  /* Interrupted system call */
-#define  EIO           5  /* I/O error */
-#define  ENXIO         6  /* No such device or address */
-#define  E2BIG         7  /* Arg list too long */
-#define  ENOEXEC       8  /* Exec format error */
-#define  EBADF         9  /* Bad file number */
-#define  ECHILD       10  /* No child processes */
-#define  EAGAIN       11  /* Try again */
-#define  ENOMEM       12  /* Out of memory */
-#define  EACCES       13  /* Permission denied */
-#define  EFAULT       14  /* Bad address */
-#define  ENOTBLK      15  /* Block device required */
-#define  EBUSY        16  /* Device or resource busy */
-#define  EEXIST       17  /* File exists */
-#define  EXDEV        18  /* Cross-device link */
-#define  ENODEV       19  /* No such device */
-#define  ENOTDIR      20  /* Not a directory */
-#define  EISDIR       21  /* Is a directory */
-#define  EINVAL       22  /* Invalid argument */
-#define  ENFILE       23  /* File table overflow */
-#define  EMFILE       24  /* Too many open files */
-#define  ENOTTY       25  /* Not a typewriter */
-#define  ETXTBSY      26  /* Text file busy */
-#define  EFBIG        27  /* File too large */
-#define  ENOSPC       28  /* No space left on device */
-#define  ESPIPE       29  /* Illegal seek */
-#define  EROFS        30  /* Read-only file system */
-#define  EMLINK       31  /* Too many links */
-#define  EPIPE        32  /* Broken pipe */
-#define  EDOM         33  /* Math argument out of domain of func */
-#define  ERANGE       34  /* Math result not representable */
-#define  EDEADLK      35  /* Resource deadlock would occur */
-#define  ENAMETOOLONG 36  /* File name too long */
-#define  ENOLCK       37  /* No record locks available */
-#define  ENOSYS       38  /* Function not implemented */
-#define  ENOTEMPTY    39  /* Directory not empty */
-#define  ELOOP        40  /* Too many symbolic links encountered */
-#define  EWOULDBLOCK  EAGAIN  /* Operation would block */
-#define  ENOMSG       42  /* No message of desired type */
-#define  EIDRM        43  /* Identifier removed */
-#define  ECHRNG       44  /* Channel number out of range */
-#define  EL2NSYNC     45  /* Level 2 not synchronized */
-#define  EL3HLT       46  /* Level 3 halted */
-#define  EL3RST       47  /* Level 3 reset */
-#define  ELNRNG       48  /* Link number out of range */
-#define  EUNATCH      49  /* Protocol driver not attached */
-#define  ENOCSI       50  /* No CSI structure available */
-#define  EL2HLT       51  /* Level 2 halted */
-#define  EBADE        52  /* Invalid exchange */
-#define  EBADR        53  /* Invalid request descriptor */
-#define  EXFULL       54  /* Exchange full */
-#define  ENOANO       55  /* No anode */
-#define  EBADRQC      56  /* Invalid request code */
-#define  EBADSLT      57  /* Invalid slot */
+#define  EPERM            1  /* Operation not permitted */
+#define  ENOENT           2  /* No such file or directory */
+#define  ESRCH            3  /* No such process */
+#define  EINTR            4  /* Interrupted system call */
+#define  EIO              5  /* I/O error */
+#define  ENXIO            6  /* No such device or address */
+#define  E2BIG            7  /* Arg list too long */
+#define  ENOEXEC          8  /* Exec format error */
+#define  EBADF            9  /* Bad file number */
+#define  ECHILD          10  /* No child processes */
+#define  EAGAIN          11  /* Try again */
+#define  ENOMEM          12  /* Out of memory */
+#define  EACCES          13  /* Permission denied */
+#define  EFAULT          14  /* Bad address */
+#define  ENOTBLK         15  /* Block device required */
+#define  EBUSY           16  /* Device or resource busy */
+#define  EEXIST          17  /* File exists */
+#define  EXDEV           18  /* Cross-device link */
+#define  ENODEV          19  /* No such device */
+#define  ENOTDIR         20  /* Not a directory */
+#define  EISDIR          21  /* Is a directory */
+#define  EINVAL          22  /* Invalid argument */
+#define  ENFILE          23  /* File table overflow */
+#define  EMFILE          24  /* Too many open files */
+#define  ENOTTY          25  /* Not a typewriter */
+#define  ETXTBSY         26  /* Text file busy */
+#define  EFBIG           27  /* File too large */
+#define  ENOSPC          28  /* No space left on device */
+#define  ESPIPE          29  /* Illegal seek */
+#define  EROFS           30  /* Read-only file system */
+#define  EMLINK          31  /* Too many links */
+#define  EPIPE           32  /* Broken pipe */
+#define  EDOM            33  /* Math argument out of domain of func */
+#define  ERANGE          34  /* Math result not representable */
+#define  EDEADLK         35  /* Resource deadlock would occur */
+#define  ENAMETOOLONG    36  /* File name too long */
+#define  ENOLCK          37  /* No record locks available */
+#define  ENOSYS          38  /* Function not implemented */
+#define  ENOTEMPTY       39  /* Directory not empty */
+#define  ELOOP           40  /* Too many symbolic links encountered */
+#define  EWOULDBLOCK     EAGAIN  /* Operation would block */
+#define  ENOMSG          42  /* No message of desired type */
+#define  EIDRM           43  /* Identifier removed */
+#define  ECHRNG          44  /* Channel number out of range */
+#define  EL2NSYNC        45  /* Level 2 not synchronized */
+#define  EL3HLT          46  /* Level 3 halted */
+#define  EL3RST          47  /* Level 3 reset */
+#define  ELNRNG          48  /* Link number out of range */
+#define  EUNATCH         49  /* Protocol driver not attached */
+#define  ENOCSI          50  /* No CSI structure available */
+#define  EL2HLT          51  /* Level 2 halted */
+#define  EBADE           52  /* Invalid exchange */
+#define  EBADR           53  /* Invalid request descriptor */
+#define  EXFULL          54  /* Exchange full */
+#define  ENOANO          55  /* No anode */
+#define  EBADRQC         56  /* Invalid request code */
+#define  EBADSLT         57  /* Invalid slot */
 
-#define  EDEADLOCK    EDEADLK
+#define  EDEADLOCK       EDEADLK
 
-#define  EBFONT       59  /* Bad font file format */
-#define  ENOSTR       60  /* Device not a stream */
-#define  ENODATA      61  /* No data available */
-#define  ETIME        62  /* Timer expired */
-#define  ENOSR        63  /* Out of streams resources */
-#define  ENONET       64  /* Machine is not on the network */
-#define  ENOPKG       65  /* Package not installed */
-#define  EREMOTE      66  /* Object is remote */
-#define  ENOLINK      67  /* Link has been severed */
-#define  EADV         68  /* Advertise error */
-#define  ESRMNT       69  /* Srmount error */
-#define  ECOMM        70  /* Communication error on send */
-#define  EPROTO       71  /* Protocol error */
-#define  EMULTIHOP    72  /* Multihop attempted */
-#define  EDOTDOT      73  /* RFS specific error */
-#define  EBADMSG      74  /* Not a data message */
-#define  EOVERFLOW    75  /* Value too large for defined data type */
-#define  ENOTUNIQ     76  /* Name not unique on network */
-#define  EBADFD       77  /* File descriptor in bad state */
-#define  EREMCHG      78  /* Remote address changed */
-#define  ELIBACC      79  /* Can not access a needed shared library */
-#define  ELIBBAD      80  /* Accessing a corrupted shared library */
-#define  ELIBSCN      81  /* .lib section in a.out corrupted */
-#define  ELIBMAX      82  /* Attempting to link in too many shared libraries */
-#define  ELIBEXEC     83  /* Cannot exec a shared library directly */
-#define  EILSEQ       84  /* Illegal byte sequence */
-#define  ERESTART     85  /* Interrupted system call should be restarted */
-#define  ESTRPIPE     86  /* Streams pipe error */
-#define  EUSERS       87  /* Too many users */
-#define  ENOTSOCK     88  /* Socket operation on non-socket */
-#define  EDESTADDRREQ 89  /* Destination address required */
-#define  EMSGSIZE     90  /* Message too long */
-#define  EPROTOTYPE   91  /* Protocol wrong type for socket */
-#define  ENOPROTOOPT  92  /* Protocol not available */
+#define  EBFONT          59  /* Bad font file format */
+#define  ENOSTR          60  /* Device not a stream */
+#define  ENODATA         61  /* No data available */
+#define  ETIME           62  /* Timer expired */
+#define  ENOSR           63  /* Out of streams resources */
+#define  ENONET          64  /* Machine is not on the network */
+#define  ENOPKG          65  /* Package not installed */
+#define  EREMOTE         66  /* Object is remote */
+#define  ENOLINK         67  /* Link has been severed */
+#define  EADV            68  /* Advertise error */
+#define  ESRMNT          69  /* Srmount error */
+#define  ECOMM           70  /* Communication error on send */
+#define  EPROTO          71  /* Protocol error */
+#define  EMULTIHOP       72  /* Multihop attempted */
+#define  EDOTDOT         73  /* RFS specific error */
+#define  EBADMSG         74  /* Not a data message */
+#define  EOVERFLOW       75  /* Value too large for defined data type */
+#define  ENOTUNIQ        76  /* Name not unique on network */
+#define  EBADFD          77  /* File descriptor in bad state */
+#define  EREMCHG         78  /* Remote address changed */
+#define  ELIBACC         79  /* Can not access a needed shared library */
+#define  ELIBBAD         80  /* Accessing a corrupted shared library */
+#define  ELIBSCN         81  /* .lib section in a.out corrupted */
+#define  ELIBMAX         82  /* Attempting to link in too many shared libraries */
+#define  ELIBEXEC        83  /* Cannot exec a shared library directly */
+#define  EILSEQ          84  /* Illegal byte sequence */
+#define  ERESTART        85  /* Interrupted system call should be restarted */
+#define  ESTRPIPE        86  /* Streams pipe error */
+#define  EUSERS          87  /* Too many users */
+#define  ENOTSOCK        88  /* Socket operation on non-socket */
+#define  EDESTADDRREQ    89  /* Destination address required */
+#define  EMSGSIZE        90  /* Message too long */
+#define  EPROTOTYPE      91  /* Protocol wrong type for socket */
+#define  ENOPROTOOPT     92  /* Protocol not available */
 #define  EPROTONOSUPPORT 93  /* Protocol not supported */
 #define  ESOCKTNOSUPPORT 94  /* Socket type not supported */
 #define  EOPNOTSUPP      95  /* Operation not supported on transport endpoint */
diff --git a/src/include/lwip/icmp6.h b/src/include/lwip/icmp6.h
index e66557d..a29dc8c 100644
--- a/src/include/lwip/icmp6.h
+++ b/src/include/lwip/icmp6.h
@@ -45,99 +45,12 @@
 #include "lwip/pbuf.h"
 #include "lwip/ip6_addr.h"
 #include "lwip/netif.h"
-
+#include "lwip/prot/icmp6.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/** ICMP type */
-enum icmp6_type {
-  /** Destination unreachable */
-  ICMP6_TYPE_DUR = 1,
-  /** Packet too big */
-  ICMP6_TYPE_PTB = 2,
-  /** Time exceeded */
-  ICMP6_TYPE_TE = 3,
-  /** Parameter problem */
-  ICMP6_TYPE_PP = 4,
-  /** Private experimentation */
-  ICMP6_TYPE_PE1 = 100,
-  /** Private experimentation */
-  ICMP6_TYPE_PE2 = 101,
-  /** Reserved for expansion of error messages */
-  ICMP6_TYPE_RSV_ERR = 127,
-
-  /** Echo request */
-  ICMP6_TYPE_EREQ = 128,
-  /** Echo reply */
-  ICMP6_TYPE_EREP = 129,
-  /** Multicast listener query */
-  ICMP6_TYPE_MLQ = 130,
-  /** Multicast listener report */
-  ICMP6_TYPE_MLR = 131,
-  /** Multicast listener done */
-  ICMP6_TYPE_MLD = 132,
-  /** Router solicitation */
-  ICMP6_TYPE_RS = 133,
-  /** Router advertisement */
-  ICMP6_TYPE_RA = 134,
-  /** Neighbor solicitation */
-  ICMP6_TYPE_NS = 135,
-  /** Neighbor advertisement */
-  ICMP6_TYPE_NA = 136,
-  /** Redirect */
-  ICMP6_TYPE_RD = 137,
-  /** Multicast router advertisement */
-  ICMP6_TYPE_MRA = 151,
-  /** Multicast router solicitation */
-  ICMP6_TYPE_MRS = 152,
-  /** Multicast router termination */
-  ICMP6_TYPE_MRT = 153,
-  /** Private experimentation */
-  ICMP6_TYPE_PE3 = 200,
-  /** Private experimentation */
-  ICMP6_TYPE_PE4 = 201,
-  /** Reserved for expansion of informational messages */
-  ICMP6_TYPE_RSV_INF = 255
-};
-
-/** ICMP destination unreachable codes */
-enum icmp6_dur_code {
-  /** No route to destination */
-  ICMP6_DUR_NO_ROUTE = 0,
-  /** Communication with destination administratively prohibited */
-  ICMP6_DUR_PROHIBITED = 1,
-  /** Beyond scope of source address */
-  ICMP6_DUR_SCOPE = 2,
-  /** Address unreachable */
-  ICMP6_DUR_ADDRESS = 3,
-  /** Port unreachable */
-  ICMP6_DUR_PORT = 4,
-  /** Source address failed ingress/egress policy */
-  ICMP6_DUR_POLICY = 5,
-  /** Reject route to destination */
-  ICMP6_DUR_REJECT_ROUTE = 6
-};
-
-/** ICMP time exceeded codes */
-enum icmp6_te_code {
-  /** Hop limit exceeded in transit */
-  ICMP6_TE_HL = 0,
-  /** Fragment reassembly time exceeded */
-  ICMP6_TE_FRAG = 1
-};
-
-/** ICMP parameter code */
-enum icmp6_pp_code {
-  /** Erroneous header field encountered */
-  ICMP6_PP_FIELD = 0,
-  /** Unrecognized next header type encountered */
-  ICMP6_PP_HEADER = 1,
-  /** Unrecognized IPv6 option encountered */
-  ICMP6_PP_OPTION = 2
-};
-
 #if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
 
 void icmp6_input(struct pbuf *p, struct netif *inp);
diff --git a/src/include/lwip/inet.h b/src/include/lwip/inet.h
index 17edef3..4a34f02 100644
--- a/src/include/lwip/inet.h
+++ b/src/include/lwip/inet.h
@@ -132,10 +132,10 @@
 
 #if LWIP_IPV4
 
-#define inet_addr_from_ipaddr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr))
-#define inet_addr_to_ipaddr(target_ipaddr, source_inaddr)   (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr))
+#define inet_addr_from_ip4addr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr))
+#define inet_addr_to_ip4addr(target_ipaddr, source_inaddr)   (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr))
 /* ATTENTION: the next define only works because both s_addr and ip4_addr_t are an u32_t effectively! */
-#define inet_addr_to_ipaddr_p(target_ip4addr_p, source_inaddr)   ((target_ip4addr_p) = (ip4_addr_t*)&((source_inaddr)->s_addr))
+#define inet_addr_to_ip4addr_p(target_ip4addr_p, source_inaddr)   ((target_ip4addr_p) = (ip4_addr_t*)&((source_inaddr)->s_addr))
 
 /* directly map this to the lwip internal functions */
 #define inet_addr(cp)                   ipaddr_addr(cp)
diff --git a/src/include/lwip/ip4_addr.h b/src/include/lwip/ip4_addr.h
index 166acfa..51b46b8 100644
--- a/src/include/lwip/ip4_addr.h
+++ b/src/include/lwip/ip4_addr.h
@@ -52,24 +52,9 @@
   u32_t addr;
 };
 
-/** This is the packed version of ip4_addr_t,
-   used in network headers that are itself packed */
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct ip4_addr_packed {
-  PACK_STRUCT_FIELD(u32_t addr);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/epstruct.h"
-#endif
-
 /** ip4_addr_t uses a struct for convenience only, so that the same defines can
  * operate both on ip4_addr_t as well as on ip4_addr_p_t. */
 typedef struct ip4_addr ip4_addr_t;
-typedef struct ip4_addr_packed ip4_addr_p_t;
 
 /**
  * struct ipaddr2 is used in the definition of the ARP packet format in
@@ -131,23 +116,8 @@
 
 #define IP_LOOPBACKNET      127                 /* official! */
 
-
-#if BYTE_ORDER == BIG_ENDIAN
 /** Set an IP address given by the four byte-parts */
-#define IP4_ADDR(ipaddr, a,b,c,d) \
-        (ipaddr)->addr = ((u32_t)((a) & 0xff) << 24) | \
-                         ((u32_t)((b) & 0xff) << 16) | \
-                         ((u32_t)((c) & 0xff) << 8)  | \
-                          (u32_t)((d) & 0xff)
-#else
-/** Set an IP address given by the four byte-parts.
-    Little-endian version that prevents the use of lwip_htonl. */
-#define IP4_ADDR(ipaddr, a,b,c,d) \
-        (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \
-                         ((u32_t)((c) & 0xff) << 16) | \
-                         ((u32_t)((b) & 0xff) << 8)  | \
-                          (u32_t)((a) & 0xff)
-#endif
+#define IP4_ADDR(ipaddr, a,b,c,d)  (ipaddr)->addr = PP_HTONL(LWIP_MAKEU32(a,b,c,d))
 
 /** MEMCPY-like copying of IP addresses where addresses are known to be
  * 16-bit-aligned if the port is correctly configured (so a port could define
diff --git a/src/include/lwip/ip6_addr.h b/src/include/lwip/ip6_addr.h
index 14d0f7c..d04472c 100644
--- a/src/include/lwip/ip6_addr.h
+++ b/src/include/lwip/ip6_addr.h
@@ -43,6 +43,7 @@
 #define LWIP_HDR_IP6_ADDR_H
 
 #include "lwip/opt.h"
+#include "def.h"
 
 #if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
 
@@ -58,41 +59,12 @@
   u32_t addr[4];
 };
 
-/** This is the packed version of ip6_addr_t,
-    used in network headers that are itself packed */
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct ip6_addr_packed {
-  PACK_STRUCT_FIELD(u32_t addr[4]);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/epstruct.h"
-#endif
-
 /** IPv6 address */
 typedef struct ip6_addr ip6_addr_t;
-typedef struct ip6_addr_packed ip6_addr_p_t;
 
-
-#if BYTE_ORDER == BIG_ENDIAN
-/** Set an IPv6 partial address given by byte-parts. */
+/** Set an IPv6 partial address given by byte-parts */
 #define IP6_ADDR_PART(ip6addr, index, a,b,c,d) \
-  (ip6addr)->addr[index] = ((u32_t)((a) & 0xff) << 24) | \
-                           ((u32_t)((b) & 0xff) << 16) | \
-                           ((u32_t)((c) & 0xff) << 8)  | \
-                            (u32_t)((d) & 0xff)
-#else
-/** Set an IPv6 partial address given by byte-parts.
-Little-endian version, stored in network order (no lwip_htonl). */
-#define IP6_ADDR_PART(ip6addr, index, a,b,c,d) \
-  (ip6addr)->addr[index] = ((u32_t)((d) & 0xff) << 24) | \
-                           ((u32_t)((c) & 0xff) << 16) | \
-                           ((u32_t)((b) & 0xff) << 8)  | \
-                            (u32_t)((a) & 0xff)
-#endif
+  (ip6addr)->addr[index] = PP_HTONL(LWIP_MAKEU32(a,b,c,d))
 
 /** Set a full IPv6 address by passing the 4 u32_t indices in network byte order
     (use PP_HTONL() for constants) */
diff --git a/src/include/lwip/ip_addr.h b/src/include/lwip/ip_addr.h
index c1df746..def2d08 100644
--- a/src/include/lwip/ip_addr.h
+++ b/src/include/lwip/ip_addr.h
@@ -66,7 +66,7 @@
  * A union struct for both IP version's addresses.
  * ATTENTION: watch out for its size when adding IPv6 address scope!
  */
-typedef struct _ip_addr {
+typedef struct ip_addr {
   union {
     ip6_addr_t ip6;
     ip4_addr_t ip4;
@@ -79,8 +79,12 @@
 
 /** @ingroup ip4addr */
 #define IPADDR4_INIT(u32val)          { { { { u32val, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_V4 }
+/** @ingroup ip4addr */
+#define IPADDR4_INIT_BYTES(a,b,c,d)   IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d)))
 /** @ingroup ip6addr */
 #define IPADDR6_INIT(a, b, c, d)      { { { { a, b, c, d } } }, IPADDR_TYPE_V6 }
+/** @ingroup ip6addr */
+#define IPADDR6_INIT_HOST(a, b, c, d) { { { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } } }, IPADDR_TYPE_V6 }
 
 /** @ingroup ipaddr */
 #define IP_IS_ANY_TYPE_VAL(ipaddr)    (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_ANY)
@@ -118,6 +122,8 @@
 /** @ingroup ip6addr */
 #define IP_ADDR6(ipaddr,i0,i1,i2,i3)  do { IP6_ADDR(ip_2_ip6(ipaddr),i0,i1,i2,i3); \
                                            IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V6); } while(0)
+/** @ingroup ip6addr */
+#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3)  IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3))
 
 /** @ingroup ipaddr */
 #define ip_addr_copy(dest, src)      do{ IP_SET_TYPE_VAL(dest, IP_GET_TYPE(&src)); if(IP_IS_V6_VAL(src)){ \
@@ -215,6 +221,19 @@
 /** @ingroup ipaddr */
 #define IPADDR_STRLEN_MAX   IP6ADDR_STRLEN_MAX
 
+/** @ingroup ipaddr */
+#define ip4_2_ipv6_mapped_ipv4(ip6addr, ip4addr) do { \
+  (ip6addr)->addr[3] = (ip4addr)->addr; \
+  (ip6addr)->addr[2] = PP_HTONL(0x0000FFFFUL); \
+  (ip6addr)->addr[1] = 0; \
+  (ip6addr)->addr[0] = 0; } while(0);
+
+/** @ingroup ipaddr */
+#define unmap_ipv6_mapped_ipv4(ip4addr, ip6addr) \
+  (ip4addr)->addr = (ip6addr)->addr[3];
+
+#define IP46_ADDR_ANY(type) (((type) == IPADDR_TYPE_V6)? IP6_ADDR_ANY : IP4_ADDR_ANY)
+
 #else /* LWIP_IPV4 && LWIP_IPV6 */
 
 #define IP_ADDR_PCB_VERSION_MATCH(addr, pcb)         1
@@ -224,6 +243,7 @@
 
 typedef ip4_addr_t ip_addr_t;
 #define IPADDR4_INIT(u32val)                    { u32val }
+#define IPADDR4_INIT_BYTES(a,b,c,d)             IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d)))
 #define IP_IS_V4_VAL(ipaddr)                    1
 #define IP_IS_V6_VAL(ipaddr)                    0
 #define IP_IS_V4(ipaddr)                        1
@@ -263,10 +283,13 @@
 
 #define IPADDR_STRLEN_MAX   IP4ADDR_STRLEN_MAX
 
+#define IP46_ADDR_ANY(type) (IP4_ADDR_ANY)
+
 #else /* LWIP_IPV4 */
 
 typedef ip6_addr_t ip_addr_t;
 #define IPADDR6_INIT(a, b, c, d)                { { a, b, c, d } }
+#define IPADDR6_INIT_HOST(a, b, c, d)           { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } }
 #define IP_IS_V4_VAL(ipaddr)                    0
 #define IP_IS_V6_VAL(ipaddr)                    1
 #define IP_IS_V4(ipaddr)                        0
@@ -277,6 +300,7 @@
 #define IP_GET_TYPE(ipaddr)                     IPADDR_TYPE_V6
 #define ip_2_ip6(ipaddr)                        (ipaddr)
 #define IP_ADDR6(ipaddr,i0,i1,i2,i3)            IP6_ADDR(ipaddr,i0,i1,i2,i3)
+#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3)       IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3))
 
 #define ip_addr_copy(dest, src)                 ip6_addr_copy(dest, src)
 #define ip_addr_copy_from_ip6(dest, src)        ip6_addr_copy(dest, src)
@@ -304,6 +328,8 @@
 
 #define IPADDR_STRLEN_MAX   IP6ADDR_STRLEN_MAX
 
+#define IP46_ADDR_ANY(type) (IP6_ADDR_ANY)
+
 #endif /* LWIP_IPV4 */
 #endif /* LWIP_IPV4 && LWIP_IPV6 */
 
@@ -314,7 +340,13 @@
 
 /**
  * @ingroup ip4addr
- * Provided for compatibility. Use IP4_ADDR_ANY for better readability.
+ * Can be used as a fixed/const ip_addr_t
+ * for the IP wildcard.
+ * Defined to @ref IP4_ADDR_ANY when IPv4 is enabled.
+ * Defined to @ref IP6_ADDR_ANY in IPv6 only systems.
+ * Use this if you can handle IPv4 _AND_ IPv6 addresses.
+ * Use @ref IP4_ADDR_ANY or @ref IP6_ADDR_ANY when the IP
+ * type matters.
  */
 #define IP_ADDR_ANY         IP4_ADDR_ANY
 /**
@@ -355,7 +387,7 @@
 #define IP6_ADDR_ANY6  (ip_2_ip6(&ip6_addr_any))
 
 #if !LWIP_IPV4
-/** Just a little upgrade-helper for IPv6-only configurations: */
+/** IPv6-only configurations */
 #define IP_ADDR_ANY IP6_ADDR_ANY
 #endif /* !LWIP_IPV4 */
 
diff --git a/src/include/lwip/mem.h b/src/include/lwip/mem.h
index e4f6a64..ff208d2 100644
--- a/src/include/lwip/mem.h
+++ b/src/include/lwip/mem.h
@@ -45,7 +45,8 @@
 
 #if MEM_LIBC_MALLOC
 
-#include <stddef.h> /* for size_t */
+#include "lwip/arch.h"
+
 typedef size_t mem_size_t;
 #define MEM_SIZE_F SZT_F
 
diff --git a/src/include/lwip/nd6.h b/src/include/lwip/nd6.h
index e4715ad..8204fa4 100644
--- a/src/include/lwip/nd6.h
+++ b/src/include/lwip/nd6.h
@@ -48,107 +48,32 @@
 
 #if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
 
-#include "lwip/pbuf.h"
-#include "lwip/ip6.h"
 #include "lwip/ip6_addr.h"
-#include "lwip/netif.h"
-
+#include "lwip/err.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/** Struct for tables. */
-struct nd6_neighbor_cache_entry {
-  ip6_addr_t next_hop_address;
-  struct netif *netif;
-  u8_t lladdr[NETIF_MAX_HWADDR_LEN];
-  /*u32_t pmtu;*/
-#if LWIP_ND6_QUEUEING
-  /** Pointer to queue of pending outgoing packets on this entry. */
-  struct nd6_q_entry *q;
-#else /* LWIP_ND6_QUEUEING */
-  /** Pointer to a single pending outgoing packet on this entry. */
-  struct pbuf *q;
-#endif /* LWIP_ND6_QUEUEING */
-  u8_t state;
-  u8_t isrouter;
-  union {
-    u32_t reachable_time; /* in ms since value may originate from network packet */
-    u32_t delay_time;     /* ticks (ND6_TMR_INTERVAL) */
-    u32_t probes_sent;
-    u32_t stale_time;     /* ticks (ND6_TMR_INTERVAL) */
-  } counter;
-};
-
-struct nd6_destination_cache_entry {
-  ip6_addr_t destination_addr;
-  ip6_addr_t next_hop_addr;
-  u16_t pmtu;
-  u32_t age;
-};
-
-struct nd6_prefix_list_entry {
-  ip6_addr_t prefix;
-  struct netif *netif;
-  u32_t invalidation_timer; /* in ms since value may originate from network packet */
-#if LWIP_IPV6_AUTOCONFIG
-  u8_t flags;
-#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01
-#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02
-#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04
-#endif /* LWIP_IPV6_AUTOCONFIG */
-};
-
-struct nd6_router_list_entry {
-  struct nd6_neighbor_cache_entry *neighbor_entry;
-  u32_t invalidation_timer; /* in ms since value may originate from network packet */
-  u8_t flags;
-};
-
-enum nd6_neighbor_cache_entry_state {
-  ND6_NO_ENTRY = 0,
-  ND6_INCOMPLETE,
-  ND6_REACHABLE,
-  ND6_STALE,
-  ND6_DELAY,
-  ND6_PROBE
-};
-
-#if LWIP_ND6_QUEUEING
-/** struct for queueing outgoing packets for unknown address
-  * defined here to be accessed by memp.h
-  */
-struct nd6_q_entry {
-  struct nd6_q_entry *next;
-  struct pbuf *p;
-};
-#endif /* LWIP_ND6_QUEUEING */
-
 /** 1 second period */
 #define ND6_TMR_INTERVAL 1000
 
-/* Router tables. */
-/* @todo make these static? and entries accessible through API? */
-extern struct nd6_neighbor_cache_entry neighbor_cache[];
-extern struct nd6_destination_cache_entry destination_cache[];
-extern struct nd6_prefix_list_entry prefix_list[];
-extern struct nd6_router_list_entry default_router_list[];
-
-/* Default values, can be updated by a RA message. */
-extern u32_t reachable_time;
-extern u32_t retrans_timer;
+struct pbuf;
+struct netif;
 
 void nd6_tmr(void);
 void nd6_input(struct pbuf *p, struct netif *inp);
-s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif);
-s8_t nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif);
+void nd6_clear_destination_cache(void);
+struct netif *nd6_find_route(const ip6_addr_t *ip6addr);
+err_t nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp);
 u16_t nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif);
-err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *p);
 #if LWIP_ND6_TCP_REACHABILITY_HINTS
 void nd6_reachability_hint(const ip6_addr_t *ip6addr);
 #endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
 void nd6_cleanup_netif(struct netif *netif);
+#if LWIP_IPV6_MLD
+void nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state);
+#endif /* LWIP_IPV6_MLD */
 
 #ifdef __cplusplus
 }
diff --git a/src/include/lwip/netdb.h b/src/include/lwip/netdb.h
index 21688c6..d3d15df 100644
--- a/src/include/lwip/netdb.h
+++ b/src/include/lwip/netdb.h
@@ -38,8 +38,7 @@
 
 #if LWIP_DNS && LWIP_SOCKET
 
-#include <stddef.h> /* for size_t */
-
+#include "lwip/arch.h"
 #include "lwip/inet.h"
 #include "lwip/sockets.h"
 
diff --git a/src/include/lwip/netifapi.h b/src/include/lwip/netifapi.h
index 20f8bca..8bd2b4f 100644
--- a/src/include/lwip/netifapi.h
+++ b/src/include/lwip/netifapi.h
@@ -93,13 +93,17 @@
                             netifapi_errt_fn errtfunc);
 
 /** @ingroup netifapi_netif */
-#define netifapi_netif_remove(n)      netifapi_netif_common(n, netif_remove, NULL)
+#define netifapi_netif_remove(n)        netifapi_netif_common(n, netif_remove, NULL)
 /** @ingroup netifapi_netif */
-#define netifapi_netif_set_up(n)      netifapi_netif_common(n, netif_set_up, NULL)
+#define netifapi_netif_set_up(n)        netifapi_netif_common(n, netif_set_up, NULL)
 /** @ingroup netifapi_netif */
-#define netifapi_netif_set_down(n)    netifapi_netif_common(n, netif_set_down, NULL)
+#define netifapi_netif_set_down(n)      netifapi_netif_common(n, netif_set_down, NULL)
 /** @ingroup netifapi_netif */
-#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL)
+#define netifapi_netif_set_default(n)   netifapi_netif_common(n, netif_set_default, NULL)
+/** @ingroup netifapi_netif */
+#define netifapi_netif_set_link_up(n)   netifapi_netif_common(n, netif_set_link_up, NULL)
+/** @ingroup netifapi_netif */
+#define netifapi_netif_set_link_down(n) netifapi_netif_common(n, netif_set_link_down, NULL)
 
 /**
  * @defgroup netifapi_dhcp4 DHCPv4
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 6130501..5abac30 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -882,6 +882,15 @@
 #if !defined LWIP_DHCP_MAX_NTP_SERVERS || defined __DOXYGEN__
 #define LWIP_DHCP_MAX_NTP_SERVERS       1
 #endif
+
+/**
+ * LWIP_DHCP_MAX_DNS_SERVERS > 0: Request DNS servers with discover/select.
+ * DHCP servers received in the response are passed to DNS via @ref dns_setserver()
+ * (up to the maximum limit defined here).
+ */
+#if !defined LWIP_DHCP_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_DHCP_MAX_DNS_SERVERS       DNS_MAX_SERVERS
+#endif
 /**
  * @}
  */
@@ -1057,6 +1066,12 @@
 #if !defined DNS_LOCAL_HOSTLIST_IS_DYNAMIC || defined __DOXYGEN__
 #define DNS_LOCAL_HOSTLIST_IS_DYNAMIC   0
 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Set this to 1 to enable querying ".local" names via mDNS
+ *  using a One-Shot Multicast DNS Query */
+#if !defined LWIP_DNS_SUPPORT_MDNS_QUERIES || defined __DOXYGEN__
+#define LWIP_DNS_SUPPORT_MDNS_QUERIES  0
+#endif
 /**
  * @}
  */
@@ -1348,7 +1363,7 @@
  * for an additional encapsulation header before ethernet headers (e.g. 802.11)
  */
 #if !defined PBUF_LINK_ENCAPSULATION_HLEN || defined __DOXYGEN__
-#define PBUF_LINK_ENCAPSULATION_HLEN    0
+#define PBUF_LINK_ENCAPSULATION_HLEN    0u
 #endif
 
 /**
@@ -2164,7 +2179,7 @@
 /**
  * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented
  */
-#if !defined LWIP_IPV6_REASS || defined __DOXYGEN__ || defined __DOXYGEN__
+#if !defined LWIP_IPV6_REASS || defined __DOXYGEN__
 #define LWIP_IPV6_REASS                 (LWIP_IPV6)
 #endif
 
@@ -2230,13 +2245,18 @@
  */
 /**
  * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol.
+ * If LWIP_IPV6 is enabled but this setting is disabled, the MAC layer must
+ * indiscriminately pass all inbound IPv6 multicast traffic to lwIP.
  */
 #if !defined LWIP_IPV6_MLD || defined __DOXYGEN__
 #define LWIP_IPV6_MLD                   (LWIP_IPV6)
 #endif
 
 /**
- * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast that can be joined.
+ * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast groups that can be joined.
+ * There must be enough groups so that each netif can join the solicited-node
+ * multicast group for each of its local addresses, plus one for MDNS if
+ * applicable, plus any number of groups to be joined on UDP sockets.
  */
 #if !defined MEMP_NUM_MLD6_GROUP || defined __DOXYGEN__
 #define MEMP_NUM_MLD6_GROUP             4
@@ -2342,7 +2362,7 @@
  * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation
  * message is sent, during neighbor reachability detection.
  */
-#if !defined LWIP_ND6_DELAY_FIRST_PROBE_TIME || defined __DOXYGEN__s
+#if !defined LWIP_ND6_DELAY_FIRST_PROBE_TIME || defined __DOXYGEN__
 #define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000
 #endif
 
@@ -2359,9 +2379,18 @@
  * with reachability hints for connected destinations. This helps avoid sending
  * unicast neighbor solicitation messages.
  */
-#if !defined LWIP_ND6_TCP_REACHABILITY_HINTS || defined __DOXYGEN__ || defined __DOXYGEN__
+#if !defined LWIP_ND6_TCP_REACHABILITY_HINTS || defined __DOXYGEN__
 #define LWIP_ND6_TCP_REACHABILITY_HINTS 1
 #endif
+
+/**
+ * LWIP_ND6_RDNSS_MAX_DNS_SERVERS > 0: Use IPv6 Router Advertisement Recursive
+ * DNS Server Option (as per RFC 6106) to copy a defined maximum number of DNS
+ * servers to the DNS module.
+ */
+#if !defined LWIP_ND6_RDNSS_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS  0
+#endif
 /**
  * @}
  */
@@ -2387,6 +2416,29 @@
  */
 
 /**
+ * LWIP_HOOK_TCP_ISN:
+ * Hook for generation of the Initial Sequence Number (ISN) for a new TCP
+ * connection. The default lwIP ISN generation algorithm is very basic and may
+ * allow for TCP spoofing attacks. This hook provides the means to implement
+ * the standardized ISN generation algorithm from RFC 6528 (see contrib/adons/tcp_isn),
+ * or any other desired algorithm as a replacement.
+ * Called from tcp_connect() and tcp_listen_input() when an ISN is needed for
+ * a new TCP connection, if TCP support (@ref LWIP_TCP) is enabled.\n
+ * Signature: u32_t my_hook_tcp_isn(const ip_addr_t* local_ip, u16_t local_port, const ip_addr_t* remote_ip, u16_t remote_port);
+ * - it may be necessary to use "struct ip_addr" (ip4_addr, ip6_addr) instead of "ip_addr_t" in function declarations\n
+ * Arguments:
+ * - local_ip: pointer to the local IP address of the connection
+ * - local_port: local port number of the connection (host-byte order)
+ * - remote_ip: pointer to the remote IP address of the connection
+ * - remote_port: remote port number of the connection (host-byte order)\n
+ * Return value:
+ * - the 32-bit Initial Sequence Number to use for the new TCP connection.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_TCP_ISN(local_ip, local_port, remote_ip, remote_port)
+#endif
+
+/**
  * LWIP_HOOK_IP4_INPUT(pbuf, input_netif):
  * - called from ip_input() (IPv4)
  * - pbuf: received struct pbuf passed to ip_input()
@@ -2427,7 +2479,7 @@
  * - dest: the destination IPv4 address
  * Returns the IPv4 address of the gateway to handle the specified destination
  * IPv4 address. If NULL is returned, the netif's default gateway is used.
- * The returned address MUST be reachable on the specified netif!
+ * The returned address MUST be directly reachable on the specified netif!
  * This function is meant to implement advanced IPv4 routing together with
  * LWIP_HOOK_IP4_ROUTE(). The actual routing/gateway table implementation is
  * not part of lwIP but can e.g. be hidden in the netif's state argument.
@@ -2464,6 +2516,22 @@
 #endif
 
 /**
+ * LWIP_HOOK_ND6_GET_GW(netif, dest):
+ * - called from nd6_get_next_hop_entry() (IPv6)
+ * - netif: the netif used for sending
+ * - dest: the destination IPv6 address
+ * Returns the IPv6 address of the next hop to handle the specified destination
+ * IPv6 address. If NULL is returned, a NDP-discovered router is used instead.
+ * The returned address MUST be directly reachable on the specified netif!
+ * This function is meant to implement advanced IPv6 routing together with
+ * LWIP_HOOK_IP6_ROUTE(). The actual routing/gateway table implementation is
+ * not part of lwIP but can e.g. be hidden in the netif's state argument.
+*/
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_ND6_GET_GW(netif, dest)
+#endif
+
+/**
  * LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr):
  * - called from ethernet_input() if VLAN support is enabled
  * - netif: struct netif on which the packet has been received
@@ -2526,7 +2594,7 @@
    ---------------------------------------
 */
 /**
- * @defgroup lwip_opts_debugmsg Debugging
+ * @defgroup lwip_opts_debugmsg Debug messages
  * @ingroup lwip_opts_debug
  * @{
  */
@@ -2534,14 +2602,16 @@
  * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is
  * compared against this value. If it is smaller, then debugging
  * messages are written.
+ * @see debugging_levels
  */
-#if !defined LWIP_DBG_MIN_LEVEL || defined __DOXYGEN__ || defined __DOXYGEN__
+#if !defined LWIP_DBG_MIN_LEVEL || defined __DOXYGEN__
 #define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_ALL
 #endif
 
 /**
  * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable
  * debug messages of certain types.
+ * @see debugging_levels
  */
 #if !defined LWIP_DBG_TYPES_ON || defined __DOXYGEN__
 #define LWIP_DBG_TYPES_ON               LWIP_DBG_ON
diff --git a/src/include/lwip/priv/api_msg.h b/src/include/lwip/priv/api_msg.h
index ad38345..f12b8b7 100644
--- a/src/include/lwip/priv/api_msg.h
+++ b/src/include/lwip/priv/api_msg.h
@@ -43,8 +43,7 @@
 /* Note: Netconn API is always available when sockets are enabled -
  * sockets are implemented on top of them */
 
-#include <stddef.h> /* for size_t */
-
+#include "lwip/arch.h"
 #include "lwip/ip_addr.h"
 #include "lwip/err.h"
 #include "lwip/sys.h"
diff --git a/src/include/lwip/priv/nd6_priv.h b/src/include/lwip/priv/nd6_priv.h
new file mode 100644
index 0000000..4bda0b7
--- /dev/null
+++ b/src/include/lwip/priv/nd6_priv.h
@@ -0,0 +1,144 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_ND6_PRIV_H
+#define LWIP_HDR_ND6_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_ND6_QUEUEING
+/** struct for queueing outgoing packets for unknown address
+  * defined here to be accessed by memp.h
+  */
+struct nd6_q_entry {
+  struct nd6_q_entry *next;
+  struct pbuf *p;
+};
+#endif /* LWIP_ND6_QUEUEING */
+
+/** Struct for tables. */
+struct nd6_neighbor_cache_entry {
+  ip6_addr_t next_hop_address;
+  struct netif *netif;
+  u8_t lladdr[NETIF_MAX_HWADDR_LEN];
+  /*u32_t pmtu;*/
+#if LWIP_ND6_QUEUEING
+  /** Pointer to queue of pending outgoing packets on this entry. */
+  struct nd6_q_entry *q;
+#else /* LWIP_ND6_QUEUEING */
+  /** Pointer to a single pending outgoing packet on this entry. */
+  struct pbuf *q;
+#endif /* LWIP_ND6_QUEUEING */
+  u8_t state;
+  u8_t isrouter;
+  union {
+    u32_t reachable_time; /* in ms since value may originate from network packet */
+    u32_t delay_time;     /* ticks (ND6_TMR_INTERVAL) */
+    u32_t probes_sent;
+    u32_t stale_time;     /* ticks (ND6_TMR_INTERVAL) */
+  } counter;
+};
+
+struct nd6_destination_cache_entry {
+  ip6_addr_t destination_addr;
+  ip6_addr_t next_hop_addr;
+  u16_t pmtu;
+  u32_t age;
+};
+
+struct nd6_prefix_list_entry {
+  ip6_addr_t prefix;
+  struct netif *netif;
+  u32_t invalidation_timer; /* in ms since value may originate from network packet */
+#if LWIP_IPV6_AUTOCONFIG
+  u8_t flags;
+#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01
+#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02
+#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04
+#endif /* LWIP_IPV6_AUTOCONFIG */
+};
+
+struct nd6_router_list_entry {
+  struct nd6_neighbor_cache_entry *neighbor_entry;
+  u32_t invalidation_timer; /* in ms since value may originate from network packet */
+  u8_t flags;
+};
+
+enum nd6_neighbor_cache_entry_state {
+  ND6_NO_ENTRY = 0,
+  ND6_INCOMPLETE,
+  ND6_REACHABLE,
+  ND6_STALE,
+  ND6_DELAY,
+  ND6_PROBE
+};
+
+/* Router tables. */
+/* @todo make these static? and entries accessible through API? */
+extern struct nd6_neighbor_cache_entry neighbor_cache[];
+extern struct nd6_destination_cache_entry destination_cache[];
+extern struct nd6_prefix_list_entry prefix_list[];
+extern struct nd6_router_list_entry default_router_list[];
+
+/* Default values, can be updated by a RA message. */
+extern u32_t reachable_time;
+extern u32_t retrans_timer;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_ND6_PRIV_H */
diff --git a/src/include/lwip/priv/tcp_priv.h b/src/include/lwip/priv/tcp_priv.h
index 51747e0..f68a856 100644
--- a/src/include/lwip/priv/tcp_priv.h
+++ b/src/include/lwip/priv/tcp_priv.h
@@ -34,8 +34,8 @@
  * Author: Adam Dunkels <adam@sics.se>
  *
  */
-#ifndef LWIP_HDR_TCP_IMPL_H
-#define LWIP_HDR_TCP_IMPL_H
+#ifndef LWIP_HDR_TCP_PRIV_H
+#define LWIP_HDR_TCP_PRIV_H
 
 #include "lwip/opt.h"
 
@@ -452,7 +452,7 @@
        const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
        u16_t local_port, u16_t remote_port);
 
-u32_t tcp_next_iss(void);
+u32_t tcp_next_iss(struct tcp_pcb *pcb);
 
 err_t tcp_keepalive(struct tcp_pcb *pcb);
 err_t tcp_zero_window_probe(struct tcp_pcb *pcb);
@@ -501,4 +501,4 @@
 
 #endif /* LWIP_TCP */
 
-#endif /* LWIP_HDR_TCP_H */
+#endif /* LWIP_HDR_TCP_PRIV_H */
diff --git a/src/include/lwip/prot/dns.h b/src/include/lwip/prot/dns.h
index 0a99ab0..94782d6 100644
--- a/src/include/lwip/prot/dns.h
+++ b/src/include/lwip/prot/dns.h
@@ -115,6 +115,24 @@
 #endif
 #define SIZEOF_DNS_HDR 12
 
+
+/* Multicast DNS definitions */
+
+/** UDP port for multicast DNS queries */
+#ifndef DNS_MQUERY_PORT
+#define DNS_MQUERY_PORT             5353
+#endif
+
+/* IPv4 group for multicast DNS queries: 224.0.0.251 */
+#ifndef DNS_MQUERY_IPV4_GROUP_INIT
+#define DNS_MQUERY_IPV4_GROUP_INIT  IPADDR4_INIT_BYTES(224,0,0,251)
+#endif
+
+/* IPv6 group for multicast DNS queries: FF02::FB */
+#ifndef DNS_MQUERY_IPV6_GROUP_INIT
+#define DNS_MQUERY_IPV6_GROUP_INIT  IPADDR6_INIT_HOST(0xFF020000,0,0,0xFB)
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/lwip/prot/icmp6.h b/src/include/lwip/prot/icmp6.h
index a0a713b..3461120 100644
--- a/src/include/lwip/prot/icmp6.h
+++ b/src/include/lwip/prot/icmp6.h
@@ -43,6 +43,93 @@
 extern "C" {
 #endif
 
+/** ICMP type */
+enum icmp6_type {
+  /** Destination unreachable */
+  ICMP6_TYPE_DUR = 1,
+  /** Packet too big */
+  ICMP6_TYPE_PTB = 2,
+  /** Time exceeded */
+  ICMP6_TYPE_TE = 3,
+  /** Parameter problem */
+  ICMP6_TYPE_PP = 4,
+  /** Private experimentation */
+  ICMP6_TYPE_PE1 = 100,
+  /** Private experimentation */
+  ICMP6_TYPE_PE2 = 101,
+  /** Reserved for expansion of error messages */
+  ICMP6_TYPE_RSV_ERR = 127,
+
+  /** Echo request */
+  ICMP6_TYPE_EREQ = 128,
+  /** Echo reply */
+  ICMP6_TYPE_EREP = 129,
+  /** Multicast listener query */
+  ICMP6_TYPE_MLQ = 130,
+  /** Multicast listener report */
+  ICMP6_TYPE_MLR = 131,
+  /** Multicast listener done */
+  ICMP6_TYPE_MLD = 132,
+  /** Router solicitation */
+  ICMP6_TYPE_RS = 133,
+  /** Router advertisement */
+  ICMP6_TYPE_RA = 134,
+  /** Neighbor solicitation */
+  ICMP6_TYPE_NS = 135,
+  /** Neighbor advertisement */
+  ICMP6_TYPE_NA = 136,
+  /** Redirect */
+  ICMP6_TYPE_RD = 137,
+  /** Multicast router advertisement */
+  ICMP6_TYPE_MRA = 151,
+  /** Multicast router solicitation */
+  ICMP6_TYPE_MRS = 152,
+  /** Multicast router termination */
+  ICMP6_TYPE_MRT = 153,
+  /** Private experimentation */
+  ICMP6_TYPE_PE3 = 200,
+  /** Private experimentation */
+  ICMP6_TYPE_PE4 = 201,
+  /** Reserved for expansion of informational messages */
+  ICMP6_TYPE_RSV_INF = 255
+};
+
+/** ICMP destination unreachable codes */
+enum icmp6_dur_code {
+  /** No route to destination */
+  ICMP6_DUR_NO_ROUTE = 0,
+  /** Communication with destination administratively prohibited */
+  ICMP6_DUR_PROHIBITED = 1,
+  /** Beyond scope of source address */
+  ICMP6_DUR_SCOPE = 2,
+  /** Address unreachable */
+  ICMP6_DUR_ADDRESS = 3,
+  /** Port unreachable */
+  ICMP6_DUR_PORT = 4,
+  /** Source address failed ingress/egress policy */
+  ICMP6_DUR_POLICY = 5,
+  /** Reject route to destination */
+  ICMP6_DUR_REJECT_ROUTE = 6
+};
+
+/** ICMP time exceeded codes */
+enum icmp6_te_code {
+  /** Hop limit exceeded in transit */
+  ICMP6_TE_HL = 0,
+  /** Fragment reassembly time exceeded */
+  ICMP6_TE_FRAG = 1
+};
+
+/** ICMP parameter code */
+enum icmp6_pp_code {
+  /** Erroneous header field encountered */
+  ICMP6_PP_FIELD = 0,
+  /** Unrecognized next header type encountered */
+  ICMP6_PP_HEADER = 1,
+  /** Unrecognized IPv6 option encountered */
+  ICMP6_PP_OPTION = 2
+};
+
 /** This is the standard ICMP6 header. */
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/bpstruct.h"
diff --git a/src/include/lwip/prot/ip4.h b/src/include/lwip/prot/ip4.h
index c3c3403..bd442c6 100644
--- a/src/include/lwip/prot/ip4.h
+++ b/src/include/lwip/prot/ip4.h
@@ -44,6 +44,22 @@
 extern "C" {
 #endif
 
+/** This is the packed version of ip4_addr_t,
+    used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip4_addr_packed {
+  PACK_STRUCT_FIELD(u32_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+typedef struct ip4_addr_packed ip4_addr_p_t;
+
 /* Size of the IPv4 header. Same as 'sizeof(struct ip_hdr)'. */
 #define IP_HLEN 20
 
diff --git a/src/include/lwip/prot/ip6.h b/src/include/lwip/prot/ip6.h
index 4e3ca37..6e1e263 100644
--- a/src/include/lwip/prot/ip6.h
+++ b/src/include/lwip/prot/ip6.h
@@ -43,6 +43,21 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+   
+/** This is the packed version of ip6_addr_t,
+    used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_addr_packed {
+  PACK_STRUCT_FIELD(u32_t addr[4]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+typedef struct ip6_addr_packed ip6_addr_p_t;
 
 #define IP6_HLEN 40
 
diff --git a/src/include/lwip/prot/mld6.h b/src/include/lwip/prot/mld6.h
index 2664829..be3a006 100644
--- a/src/include/lwip/prot/mld6.h
+++ b/src/include/lwip/prot/mld6.h
@@ -38,7 +38,7 @@
 #define LWIP_HDR_PROT_MLD6_H
 
 #include "lwip/arch.h"
-#include "lwip/ip6_addr.h"
+#include "lwip/prot/ip6.h"
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/include/lwip/prot/nd6.h b/src/include/lwip/prot/nd6.h
index eae3d28..2d4903d 100644
--- a/src/include/lwip/prot/nd6.h
+++ b/src/include/lwip/prot/nd6.h
@@ -39,6 +39,7 @@
 
 #include "lwip/arch.h"
 #include "lwip/ip6_addr.h"
+#include "lwip/prot/ip6.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -246,6 +247,29 @@
 #  include "arch/epstruct.h"
 #endif
 
+/** Recursive DNS Server Option. */
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+#define LWIP_RDNSS_OPTION_MAX_SERVERS LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+#else
+#define LWIP_RDNSS_OPTION_MAX_SERVERS 1
+#endif
+#define ND6_OPTION_TYPE_RDNSS (25)
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct rdnss_option {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t length);
+  PACK_STRUCT_FIELD(u16_t reserved);
+  PACK_STRUCT_FIELD(u32_t lifetime);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t rdnss_address[LWIP_RDNSS_OPTION_MAX_SERVERS]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/lwip/prot/tcp.h b/src/include/lwip/prot/tcp.h
index c2c03aa..67fe7b9 100644
--- a/src/include/lwip/prot/tcp.h
+++ b/src/include/lwip/prot/tcp.h
@@ -85,7 +85,7 @@
 
 #define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = lwip_htons(((len) << 12) | TCPH_FLAGS(phdr))
 #define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS(~TCP_FLAGS)) | lwip_htons(flags))
-#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = lwip_htons(((len) << 12) | (flags))
+#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = (u16_t)(lwip_htons((u16_t)((len) << 12) | (flags)))
 
 #define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | lwip_htons(flags))
 #define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags & ~lwip_htons(flags))
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index 9d76776..2522056 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -43,8 +43,6 @@
 
 #if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
 
-#include <stddef.h> /* for size_t */
-
 #include "lwip/ip_addr.h"
 #include "lwip/err.h"
 #include "lwip/inet.h"
diff --git a/src/include/lwip/sys.h b/src/include/lwip/sys.h
index bb06a40..d12bae0 100644
--- a/src/include/lwip/sys.h
+++ b/src/include/lwip/sys.h
@@ -34,44 +34,6 @@
  * Author: Adam Dunkels <adam@sics.se>
  */
 
-/**
- * @defgroup sys_layer System abstraction layer
- * @ingroup infrastructure
- * @verbinclude "sys_arch.txt"
- *
- * @defgroup sys_os OS abstraction layer
- * @ingroup sys_layer
- * No need to implement functions in this section in NO_SYS mode.
- *
- * @defgroup sys_sem Semaphores
- * @ingroup sys_os
- *
- * @defgroup sys_mutex Mutexes
- * @ingroup sys_os
- * Mutexes are recommended to correctly handle priority inversion,
- * especially if you use LWIP_CORE_LOCKING .
- *
- * @defgroup sys_mbox Mailboxes
- * @ingroup sys_os
- *
- * @defgroup sys_time Time
- * @ingroup sys_layer
- *
- * @defgroup sys_prot Critical sections
- * @ingroup sys_layer
- * Used to protect short regions of code against concurrent access.
- * - Your system is a bare-metal system (probably with an RTOS)
- *   and interrupts are under your control:
- *   Implement this as LockInterrupts() / UnlockInterrupts()
- * - Your system uses an RTOS with deferred interrupt handling from a
- *   worker thread: Implement as a global mutex or lock/unlock scheduler
- * - Your system uses a high-level OS with e.g. POSIX signals:
- *   Implement as a global mutex
- *
- * @defgroup sys_misc Misc
- * @ingroup sys_os
- */
-
 #ifndef LWIP_HDR_SYS_H
 #define LWIP_HDR_SYS_H
 
diff --git a/src/include/lwip/tcp.h b/src/include/lwip/tcp.h
index 35584b4..3fd4467 100644
--- a/src/include/lwip/tcp.h
+++ b/src/include/lwip/tcp.h
@@ -387,6 +387,7 @@
 err_t            tcp_connect (struct tcp_pcb *pcb, const ip_addr_t *ipaddr,
                               u16_t port, tcp_connected_fn connected);
 
+struct tcp_pcb * tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err);
 struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
 /** @ingroup tcp_raw */
 #define          tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
diff --git a/src/netif/lowpan6.c b/src/netif/lowpan6.c
index eca9a45..9a84cbc 100644
--- a/src/netif/lowpan6.c
+++ b/src/netif/lowpan6.c
@@ -616,7 +616,8 @@
 err_t
 lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
 {
-  s8_t i;
+  err_t result;
+  const u8_t *hwaddr;
   struct ieee_802154_addr src, dest;
 #if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
   ip6_addr_t ip6_src;
@@ -663,35 +664,23 @@
   }
 #endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
 
-
-  /* Get next hop record. */
-  i = nd6_get_next_hop_entry(ip6addr, netif);
-  if (i < 0) {
+  /* Ask ND6 what to do with the packet. */
+  result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+  if (result != ERR_OK) {
     MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
-    /* failed to get a next hop neighbor record. */
-    return ERR_MEM;
+    return result;
   }
 
-  /* Now that we have a destination record, send or queue the packet. */
-  if (neighbor_cache[i].state == ND6_STALE) {
-    /* Switch to delay state. */
-    neighbor_cache[i].state = ND6_DELAY;
-    neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME;
-  }
-  /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
-  if ((neighbor_cache[i].state == ND6_REACHABLE) ||
-      (neighbor_cache[i].state == ND6_DELAY) ||
-      (neighbor_cache[i].state == ND6_PROBE)) {
-
-    /* Send out. */
-    dest.addr_len = netif->hwaddr_len;
-    SMEMCPY(dest.addr, neighbor_cache[i].lladdr, netif->hwaddr_len);
-    MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
-    return lowpan6_frag(netif, q, &src, &dest);
+  /* If no hardware address is returned, nd6 has queued the packet for later. */
+  if (hwaddr == NULL) {
+    return ERR_OK;
   }
 
-  /* We should queue packet on this interface. */
-  return nd6_queue_packet(i, q);
+  /* Send out the packet using the returned hardware address. */
+  dest.addr_len = netif->hwaddr_len;
+  SMEMCPY(dest.addr, hwaddr, netif->hwaddr_len);
+  MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
+  return lowpan6_frag(netif, q, &src, &dest);
 }
 
 static struct pbuf *
diff --git a/src/netif/ppp/ppp.c b/src/netif/ppp/ppp.c
index 0e37eeb..d9b5077 100644
--- a/src/netif/ppp/ppp.c
+++ b/src/netif/ppp/ppp.c
@@ -1145,12 +1145,12 @@
   nsa = dns_getserver(0);
   ip_addr_set_ip4_u32(&nsb, ns1);
   if (ip_addr_cmp(nsa, &nsb)) {
-    dns_setserver(0, IP4_ADDR_ANY);
+    dns_setserver(0, IP_ADDR_ANY);
   }
   nsa = dns_getserver(1);
   ip_addr_set_ip4_u32(&nsb, ns2);
   if (ip_addr_cmp(nsa, &nsb)) {
-    dns_setserver(1, IP4_ADDR_ANY);
+    dns_setserver(1, IP_ADDR_ANY);
   }
   return 1;
 }
diff --git a/src/netif/ppp/pppapi.c b/src/netif/ppp/pppapi.c
index e2bedc7..947f7ba 100644
--- a/src/netif/ppp/pppapi.c
+++ b/src/netif/ppp/pppapi.c
@@ -222,6 +222,7 @@
     msg->msg.msg.l2tpcreate.secret_len,
 #else /* PPPOL2TP_AUTH_SUPPORT */
     NULL,
+    0,
 #endif /* PPPOL2TP_AUTH_SUPPORT */
     msg->msg.msg.l2tpcreate.link_status_cb, msg->msg.msg.l2tpcreate.ctx_cb);
   return ERR_OK;
@@ -239,6 +240,10 @@
   ppp_pcb* result;
   PPPAPI_VAR_DECLARE(msg);
   PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
+#if !PPPOL2TP_AUTH_SUPPORT
+  LWIP_UNUSED_ARG(secret);
+  LWIP_UNUSED_ARG(secret_len);
+#endif /* !PPPOL2TP_AUTH_SUPPORT */
 
   PPPAPI_VAR_REF(msg).msg.ppp = NULL;
   PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.pppif = pppif;
diff --git a/src/netif/ppp/pppol2tp.c b/src/netif/ppp/pppol2tp.c
index c215cff..90cf108 100644
--- a/src/netif/ppp/pppol2tp.c
+++ b/src/netif/ppp/pppol2tp.c
@@ -113,6 +113,10 @@
   ppp_pcb *ppp;
   pppol2tp_pcb *l2tp;
   struct udp_pcb *udp;
+#if !PPPOL2TP_AUTH_SUPPORT
+  LWIP_UNUSED_ARG(secret);
+  LWIP_UNUSED_ARG(secret_len);
+#endif /* !PPPOL2TP_AUTH_SUPPORT */
 
   if (ipaddr == NULL) {
     goto ipaddr_check_failed;
@@ -303,7 +307,7 @@
     udp_bind(l2tp->udp, IP6_ADDR_ANY, 0);
   } else
 #endif /* LWIP_IPV6 */
-  udp_bind(l2tp->udp, IP4_ADDR_ANY, 0);
+  udp_bind(l2tp->udp, IP_ADDR_ANY, 0);
 
 #if PPPOL2TP_AUTH_SUPPORT
   /* Generate random vector */
diff --git a/src/netif/ppp/pppos.c b/src/netif/ppp/pppos.c
index 5220ed3..5416429 100644
--- a/src/netif/ppp/pppos.c
+++ b/src/netif/ppp/pppos.c
@@ -35,8 +35,8 @@
 #if PPP_SUPPORT && PPPOS_SUPPORT /* don't build if not configured for use in lwipopts.h */
 
 #include <string.h>
-#include <stddef.h>
 
+#include "lwip/arch.h"
 #include "lwip/err.h"
 #include "lwip/pbuf.h"
 #include "lwip/sys.h"
@@ -471,18 +471,20 @@
   u8_t escaped;
   PPPOS_DECL_PROTECT(lev);
 
-  PPPOS_PROTECT(lev);
-  if (!pppos->open) {
-    PPPOS_UNPROTECT(lev);
-    return;
-  }
-  PPPOS_UNPROTECT(lev);
-
   PPPDEBUG(LOG_DEBUG, ("pppos_input[%d]: got %d bytes\n", ppp->netif->num, l));
   while (l-- > 0) {
     cur_char = *s++;
 
     PPPOS_PROTECT(lev);
+    /* ppp_input can disconnect the interface, we need to abort to prevent a memory
+     * leak if there are remaining bytes because pppos_connect and pppos_listen
+     * functions expect input buffer to be free. Furthermore there are no real
+     * reason to continue reading bytes if we are disconnected.
+     */
+    if (!pppos->open) {
+      PPPOS_UNPROTECT(lev);
+      return;
+    }
     escaped = ESCAPE_P(pppos->in_accm, cur_char);
     PPPOS_UNPROTECT(lev);
     /* Handle special characters. */
diff --git a/src/netif/ppp/utils.c b/src/netif/ppp/utils.c
index a5ae86f..008c633 100644
--- a/src/netif/ppp/utils.c
+++ b/src/netif/ppp/utils.c
@@ -247,11 +247,13 @@
 	    val = va_arg(args, unsigned int);
 	    base = 16;
 	    break;
+#if 0 /* unused (and wrong on LLP64 systems) */
 	case 'p':
 	    val = (unsigned long) va_arg(args, void *);
 	    base = 16;
 	    neg = 2;
 	    break;
+#endif /* unused (and wrong on LLP64 systems) */
 	case 's':
 	    str = va_arg(args, char *);
 	    break;
diff --git a/test/fuzz/Makefile b/test/fuzz/Makefile
new file mode 100644
index 0000000..67ffb19
--- /dev/null
+++ b/test/fuzz/Makefile
@@ -0,0 +1,53 @@
+#
+# Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
+# All rights reserved. 
+# 
+# Redistribution and use in source and binary forms, with or without modification, 
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+#    this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+#    derived from this software without specific prior written permission. 
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+# SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+# OF SUCH DAMAGE.
+#
+# This file is part of the lwIP TCP/IP stack.
+# 
+# Author: Adam Dunkels <adam@sics.se>
+#
+
+all compile: lwip_fuzz
+.PHONY: all clean
+
+CC=afl-gcc
+LDFLAGS=-lm
+CFLAGS=-O0
+
+CONTRIBDIR=../../../lwip-contrib
+include $(CONTRIBDIR)/ports/unix/Common.mk
+
+clean:
+	rm -f *.o $(LWIPLIBCOMMON) lwip_fuzz *.s .depend* *.core core
+
+depend dep: .depend
+
+include .depend
+
+.depend: fuzz.c $(LWIPFILES) $(APPFILES)
+	$(CCDEP) $(CFLAGS) -MM $^ > .depend || rm -f .depend
+
+lwip_fuzz: .depend $(LWIPLIBCOMMON) fuzz.o
+	$(CC) $(CFLAGS) -o lwip_fuzz fuzz.o $(LWIPLIBCOMMON) $(LDFLAGS)
diff --git a/test/fuzz/README b/test/fuzz/README
new file mode 100644
index 0000000..1d1e3d8
--- /dev/null
+++ b/test/fuzz/README
@@ -0,0 +1,34 @@
+
+Fuzzing the lwIP stack (afl-fuzz requires linux/unix or similar)
+
+This directory contains a small app that reads Ethernet frames from stdin and
+processes them. It is used together with the 'american fuzzy lop' tool (found
+at http://lcamtuf.coredump.cx/afl/) and the sample inputs to test how
+unexpected inputs are handled. The afl tool will read the known inputs, and
+try to modify them to exercise as many code paths as possible, by instrumenting
+the code and keeping track of which code is executed.
+
+Just running make will produce the test program.
+
+Then run afl with:
+
+afl-fuzz -i inputs/<INPUT> -o output ./lwip_fuzz
+
+and it should start working. It will probably complain about CPU scheduler,
+set AFL_SKIP_CPUFREQ=1 to ignore it.
+If it complains about invalid "/proc/sys/kernel/core_pattern" setting, try
+executing "sudo bash -c 'echo core > /proc/sys/kernel/core_pattern'".
+
+The input is split into different subdirectories since they test different
+parts of the code, and since you want to run one instance of afl-fuzz on each
+core.
+
+When afl finds a crash or a hang, the input that caused it will be placed in
+the output directory. If you have hexdump and text2pcap tools installed,
+running output_to_pcap.sh <outputdir> will create pcap files for each input
+file to simplify viewing in wireshark.
+
+The lwipopts.h file needs to have checksum checking off, otherwise almost every
+packet will be discarded because of that. The other options can be tuned to
+expose different parts of the code.
+
diff --git a/test/fuzz/config.h b/test/fuzz/config.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/fuzz/config.h
diff --git a/test/fuzz/fuzz.c b/test/fuzz/fuzz.c
new file mode 100644
index 0000000..a9157f1
--- /dev/null
+++ b/test/fuzz/fuzz.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Erik Ekman <erik@kryo.se>
+ *
+ */
+
+#include "lwip/init.h"
+#include "lwip/netif.h"
+#include "netif/etharp.h"
+#if LWIP_IPV6
+#include "lwip/ethip6.h"
+#include "lwip/nd6.h"
+#endif
+#include <string.h>
+#include <stdio.h>
+
+/* no-op send function */
+static err_t lwip_tx_func(struct netif *netif, struct pbuf *p)
+{
+  LWIP_UNUSED_ARG(netif);
+  LWIP_UNUSED_ARG(p);
+  return ERR_OK;
+}
+
+static err_t testif_init(struct netif *netif)
+{
+  netif->name[0] = 'f';
+  netif->name[1] = 'z';
+  netif->output = etharp_output;
+  netif->linkoutput = lwip_tx_func;
+  netif->mtu = 1500;
+  netif->hwaddr_len = 6;
+  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
+
+  netif->hwaddr[0] = 0x00;
+  netif->hwaddr[1] = 0x23;
+  netif->hwaddr[2] = 0xC1;
+  netif->hwaddr[3] = 0xDE;
+  netif->hwaddr[4] = 0xD0;
+  netif->hwaddr[5] = 0x0D;
+
+#if LWIP_IPV6
+  netif->output_ip6 = ethip6_output;
+  netif->ip6_autoconfig_enabled = 1;
+  netif_create_ip6_linklocal_address(netif, 1);
+  netif->flags |= NETIF_FLAG_MLD6;
+#endif
+
+  return ERR_OK;
+}
+
+static void input_pkt(struct netif *netif, const u8_t *data, size_t len)
+{
+  struct pbuf *p, *q;
+  err_t err;
+
+  LWIP_ASSERT("pkt too big", len <= 0xFFFF);
+  p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
+  LWIP_ASSERT("alloc failed", p);
+  for(q = p; q != NULL; q = q->next) {
+    MEMCPY(q->payload, data, q->len);
+    data += q->len;
+  }
+  err = netif->input(p, netif);
+  if (err != ERR_OK) {
+    pbuf_free(p);
+  }
+}
+
+int main(int argc, char** argv)
+{
+  struct netif net_test;
+  ip4_addr_t addr;
+  ip4_addr_t netmask;
+  ip4_addr_t gw;
+  u8_t pktbuf[2000];
+  size_t len;
+
+  lwip_init();
+
+  IP4_ADDR(&addr, 172, 30, 115, 84);
+  IP4_ADDR(&netmask, 255, 255, 255, 0);
+  IP4_ADDR(&gw, 172, 30, 115, 1);
+
+  netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+  netif_set_up(&net_test);
+
+#if LWIP_IPV6
+  nd6_tmr(); /* tick nd to join multicast groups */
+#endif
+
+  if(argc > 1) {
+    FILE* f;
+    const char* filename;
+    printf("reading input from file... ");
+    fflush(stdout);
+    filename = argv[1];
+    LWIP_ASSERT("invalid filename", filename != NULL);
+    f = fopen(filename, "rb");
+    LWIP_ASSERT("open failed", f != NULL);
+    len = fread(pktbuf, 1, sizeof(pktbuf), f);
+    fclose(f);
+    printf("testing file: \"%s\"...\r\n", filename);
+  } else {
+    len = fread(pktbuf, 1, sizeof(pktbuf), stdin);
+  }
+  input_pkt(&net_test, pktbuf, len);
+
+  return 0;
+}
diff --git a/test/fuzz/inputs/arp/arp_req.bin b/test/fuzz/inputs/arp/arp_req.bin
new file mode 100644
index 0000000..b317334
--- /dev/null
+++ b/test/fuzz/inputs/arp/arp_req.bin
Binary files differ
diff --git a/test/fuzz/inputs/icmp/icmp_ping.bin b/test/fuzz/inputs/icmp/icmp_ping.bin
new file mode 100644
index 0000000..87e1ea7
--- /dev/null
+++ b/test/fuzz/inputs/icmp/icmp_ping.bin
Binary files differ
diff --git a/test/fuzz/inputs/ipv6/neighbor_solicitation.bin b/test/fuzz/inputs/ipv6/neighbor_solicitation.bin
new file mode 100644
index 0000000..d2f921c
--- /dev/null
+++ b/test/fuzz/inputs/ipv6/neighbor_solicitation.bin
Binary files differ
diff --git a/test/fuzz/inputs/ipv6/router_adv.bin b/test/fuzz/inputs/ipv6/router_adv.bin
new file mode 100644
index 0000000..3aa9615
--- /dev/null
+++ b/test/fuzz/inputs/ipv6/router_adv.bin
Binary files differ
diff --git a/test/fuzz/inputs/tcp/tcp_syn.bin b/test/fuzz/inputs/tcp/tcp_syn.bin
new file mode 100644
index 0000000..d77f6d2
--- /dev/null
+++ b/test/fuzz/inputs/tcp/tcp_syn.bin
Binary files differ
diff --git a/test/fuzz/inputs/udp/udp_port_5000.bin b/test/fuzz/inputs/udp/udp_port_5000.bin
new file mode 100644
index 0000000..d77e267
--- /dev/null
+++ b/test/fuzz/inputs/udp/udp_port_5000.bin
Binary files differ
diff --git a/test/fuzz/lwipopts.h b/test/fuzz/lwipopts.h
new file mode 100644
index 0000000..4aff09b
--- /dev/null
+++ b/test/fuzz/lwipopts.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_LWIPOPTS_H__
+#define LWIP_HDR_LWIPOPTS_H__
+
+/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
+#define NO_SYS                          1
+#define LWIP_NETCONN                    0
+#define LWIP_SOCKET                     0
+#define SYS_LIGHTWEIGHT_PROT            0
+
+#define LWIP_IPV6                       1
+#define IPV6_FRAG_COPYHEADER            1
+#define LWIP_IPV6_DUP_DETECT_ATTEMPTS   0
+
+/* Enable DHCP to test it */
+#define LWIP_DHCP                       1
+
+/* Turn off checksum verification of fuzzed data */
+#define CHECKSUM_CHECK_IP               0
+#define CHECKSUM_CHECK_UDP              0
+#define CHECKSUM_CHECK_TCP              0
+#define CHECKSUM_CHECK_ICMP             0
+#define CHECKSUM_CHECK_ICMP6            0
+
+/* Minimal changes to opt.h required for tcp unit tests: */
+#define MEM_SIZE                        16000
+#define TCP_SND_QUEUELEN                40
+#define MEMP_NUM_TCP_SEG                TCP_SND_QUEUELEN
+#define TCP_SND_BUF                     (12 * TCP_MSS)
+#define TCP_WND                         (10 * TCP_MSS)
+#define LWIP_WND_SCALE                  1
+#define TCP_RCV_SCALE                   0
+#define PBUF_POOL_SIZE                  400 /* pbuf tests need ~200KByte */
+
+/* Minimal changes to opt.h required for etharp unit tests: */
+#define ETHARP_SUPPORT_STATIC_ENTRIES   1
+
+#endif /* LWIP_HDR_LWIPOPTS_H__ */
diff --git a/test/fuzz/output_to_pcap.sh b/test/fuzz/output_to_pcap.sh
new file mode 100644
index 0000000..c999ff0
--- /dev/null
+++ b/test/fuzz/output_to_pcap.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+if [ -z "$1" ]
+then
+	echo "This script will make pcap files from the afl-fuzz crash/hang files"
+	echo "It needs hexdump and text2pcap"
+	echo "Please give output directory as argument"
+	exit 2
+fi
+
+for i in `ls $1/crashes/id*`
+do
+	PCAPNAME=`echo $i | grep pcap`
+	if [ -z "$PCAPNAME" ]; then
+		hexdump -C $i > $1/$$.tmp
+		text2pcap $1/$$.tmp ${i}.pcap
+	fi
+done
+for i in `ls $1/hangs/id*`
+do
+	PCAPNAME=`echo $i | grep pcap`
+	if [ -z "$PCAPNAME" ]; then
+		hexdump -C $i > $1/$$.tmp
+		text2pcap $1/$$.tmp ${i}.pcap
+	fi
+done
+rm -f $1/$$.tmp
+
+echo
+echo "Created pcap files:"
+ls $1/*/*.pcap
diff --git a/test/unit/dhcp/test_dhcp.c b/test/unit/dhcp/test_dhcp.c
index 68f2881..3ff42cc 100644
--- a/test/unit/dhcp/test_dhcp.c
+++ b/test/unit/dhcp/test_dhcp.c
@@ -120,7 +120,8 @@
   TEST_LWIP_DHCP,
   TEST_LWIP_DHCP_NAK,
   TEST_LWIP_DHCP_RELAY,
-  TEST_LWIP_DHCP_NAK_NO_ENDMARKER
+  TEST_LWIP_DHCP_NAK_NO_ENDMARKER,
+  TEST_LWIP_DHCP_INVALID_OVERLOAD
 } tcase;
 
 static int debug = 0;
@@ -904,6 +905,111 @@
 }
 END_TEST
 
+START_TEST(test_dhcp_invalid_overload)
+{
+  u8_t dhcp_offer_invalid_overload[] = {
+      0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, /* To unit */
+      0x00, 0x0F, 0xEE, 0x30, 0xAB, 0x22, /* From Remote host */
+      0x08, 0x00, /* Protocol: IP */
+      0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, 0x36, 0xcc, 0xc3, 0xaa, 0xbd, 0xab, 0xc3, 0xaa, 0xbd, 0xc8, /* IP header */
+      0x00, 0x43, 0x00, 0x44, 0x01, 0x34, 0x00, 0x00, /* UDP header */
+
+      0x02, /* Type == Boot reply */
+      0x01, 0x06, /* Hw Ethernet, 6 bytes addrlen */
+      0x00, /* 0 hops */
+      0xAA, 0xAA, 0xAA, 0xAA, /* Transaction id, will be overwritten */
+      0x00, 0x00, /* 0 seconds elapsed */
+      0x00, 0x00, /* Flags (unicast) */
+      0x00, 0x00, 0x00, 0x00, /* Client ip */
+      0xc3, 0xaa, 0xbd, 0xc8, /* Your IP */
+      0xc3, 0xaa, 0xbd, 0xab, /* DHCP server ip */
+      0x00, 0x00, 0x00, 0x00, /* relay agent */
+      0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MAC addr + padding */
+
+      /* Empty server name */
+      0x34, 0x01, 0x02, 0xff, /* Overload: SNAME + END */
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      /* Empty boot file name */
+      0x34, 0x01, 0x01, 0xff, /* Overload FILE + END */
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x63, 0x82, 0x53, 0x63, /* Magic cookie */
+      0x35, 0x01, 0x02, /* Message type: Offer */
+      0x36, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Server identifier (IP) */
+      0x33, 0x04, 0x00, 0x00, 0x00, 0x78, /* Lease time 2 minutes */
+      0x03, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Router IP */
+      0x01, 0x04, 0xff, 0xff, 0xff, 0x00, /* Subnet mask */
+      0x34, 0x01, 0x03, /* Overload: FILE + SNAME */
+      0xff, /* End option */
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Padding */
+  };
+  ip4_addr_t addr;
+  ip4_addr_t netmask;
+  ip4_addr_t gw;
+  u32_t xid;
+  LWIP_UNUSED_ARG(_i);
+
+  tcase = TEST_LWIP_DHCP_INVALID_OVERLOAD;
+  setdebug(0);
+
+  IP4_ADDR(&addr, 0, 0, 0, 0);
+  IP4_ADDR(&netmask, 0, 0, 0, 0);
+  IP4_ADDR(&gw, 0, 0, 0, 0);
+
+  netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+  netif_set_up(&net_test);
+
+  dhcp_start(&net_test);
+
+  fail_unless(txpacket == 1); /* DHCP discover sent */
+  xid = htonl(netif_dhcp_data(&net_test)->xid);
+  memcpy(&dhcp_offer_invalid_overload[46], &xid, 4); /* insert correct transaction id */
+  dhcp_offer_invalid_overload[311] = 3;
+  send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer_invalid_overload));
+  /* IP addresses should be zero */
+  fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+  fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+  fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+  fail_unless(txpacket == 1); /* Nothing more sent */
+
+  dhcp_offer_invalid_overload[311] = 2;
+  send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer_invalid_overload));
+  /* IP addresses should be zero */
+  fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+  fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+  fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+  fail_unless(txpacket == 1); /* Nothing more sent */
+
+  dhcp_offer_invalid_overload[311] = 1;
+  send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer_invalid_overload));
+  /* IP addresses should be zero */
+  fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+  fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+  fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+  fail_unless(txpacket == 1); /* Nothing more sent */
+
+  dhcp_offer_invalid_overload[311] = 0;
+  send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer));
+
+  fail_unless(netif_dhcp_data(&net_test)->state == DHCP_STATE_REQUESTING);
+
+  fail_unless(txpacket == 2); /* No more sent */
+  xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */
+
+  netif_remove(&net_test);
+}
+END_TEST
 
 /** Create the suite including all tests for this module */
 Suite *
@@ -913,7 +1019,8 @@
     TESTFUNC(test_dhcp),
     TESTFUNC(test_dhcp_nak),
     TESTFUNC(test_dhcp_relayed),
-    TESTFUNC(test_dhcp_nak_no_endmarker)
+    TESTFUNC(test_dhcp_nak_no_endmarker),
+    TESTFUNC(test_dhcp_invalid_overload)
   };
   return create_suite("DHCP", tests, sizeof(tests)/sizeof(testfunc), dhcp_setup, dhcp_teardown);
 }
diff --git a/test/unit/mdns/test_mdns.c b/test/unit/mdns/test_mdns.c
index 223520c..ca9be64 100644
--- a/test/unit/mdns/test_mdns.c
+++ b/test/unit/mdns/test_mdns.c
@@ -42,6 +42,7 @@
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -60,6 +61,7 @@
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -78,6 +80,7 @@
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -89,18 +92,20 @@
 END_TEST
 
 START_TEST(readname_long_label)
-{  static const u8_t data[] = {
+{
+  static const u8_t data[] = {
       0x05, 'm', 'u', 'l', 't', 'i',
       0x52, 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
       'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
       'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
       'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
       'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
-      'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 0x00,
+      'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 0x00
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -151,11 +156,12 @@
       0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
       0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
       0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
-      0x00,
+      0x00
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -172,14 +178,15 @@
       /* Some padding needed, not supported to jump to bytes containing dns header */
       /*  0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       /* 10 */ 0x0f, 0x0e, 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab,
-      /* 20 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x0c,
+      /* 20 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x0c
   };
   static const u8_t fullname[] = {
-      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
+      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -200,14 +207,15 @@
       /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x0a, 0xf2,
       /* 0x10 */ 0x04, 'c', 'a', 's', 't', 0x00, 0xc0, 0x10,
-      /* 0x18 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x16,
+      /* 0x18 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x16
   };
   static const u8_t fullname[] = {
-      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00,
+      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -231,16 +239,17 @@
       /* 0x18 */ 0x03, 'd', 'n', 's', 0xc0, 0x10, 0xc0, 0x10,
       /* 0x20 */ 0x04, 'd', 'e', 'e', 'p', 0xc0, 0x18, 0x00,
       /* 0x28 */ 0x04, 'c', 'a', 's', 't', 0xc0, 0x20, 0xb0,
-      /* 0x30 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x28,
+      /* 0x30 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x28
   };
   static const u8_t fullname[] = {
       0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
       0x04, 'd', 'e', 'e', 'p', 0x03, 'd', 'n', 's',
-      0x04, 'n', 'a', 'm', 'e', 0x00,
+      0x04, 'n', 'a', 'm', 'e', 0x00
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -258,14 +267,15 @@
 {
   static const u8_t data[] = {
       /* 0x00 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10, 0x00, 0x01, 0x40,
-      /* 0x10 */ 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab,
+      /* 0x10 */ 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab
   };
   static const u8_t fullname[] = {
-      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
+      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -282,11 +292,12 @@
 START_TEST(readname_half_jump)
 {
   static const u8_t data[] = {
-      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0,
+      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -300,11 +311,12 @@
 START_TEST(readname_jump_toolong)
 {
   static const u8_t data[] = {
-      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc2, 0x10, 0x00, 0x01, 0x40,
+      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc2, 0x10, 0x00, 0x01, 0x40
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -319,11 +331,12 @@
 {
   static const u8_t data[] = {
       /*  0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10,
+      /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -338,11 +351,12 @@
 {
   static const u8_t data[] = {
       /*  0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x15,
+      /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x15
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -358,6 +372,7 @@
   static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00 };
   struct mdns_domain domain;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   memset(&domain, 0, sizeof(domain));
   res = mdns_domain_add_label(&domain, "multi", 5);
@@ -376,6 +391,7 @@
   static const char *toolong = "abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-";
   struct mdns_domain domain;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   memset(&domain, 0, sizeof(domain));
   res = mdns_domain_add_label(&domain, "multi", 5);
@@ -390,6 +406,7 @@
   static const char *label = "0123456789abcdef0123456789abcdef";
   struct mdns_domain domain;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   memset(&domain, 0, sizeof(domain));
   res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
@@ -437,10 +454,11 @@
 START_TEST(domain_eq_basic)
 {
   static const u8_t data[] = {
-      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00,
+      0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
   };
   struct mdns_domain domain1, domain2;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   memset(&domain1, 0, sizeof(domain1));
   res = mdns_domain_add_label(&domain1, "multi", 5);
@@ -467,6 +485,7 @@
 {
   struct mdns_domain domain1, domain2;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   memset(&domain1, 0, sizeof(domain1));
   res = mdns_domain_add_label(&domain1, "multi", 5);
@@ -492,6 +511,7 @@
 {
   struct mdns_domain domain1, domain2;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   memset(&domain1, 0, sizeof(domain1));
   res = mdns_domain_add_label(&domain1, "multi", 5);
@@ -519,6 +539,7 @@
   static const u8_t data2[] = { 0x7f, 0x8c, 0x01, 0xff, 0xcf };
   struct mdns_domain domain1, domain2;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   memset(&domain1, 0, sizeof(domain1));
   res = mdns_domain_add_label(&domain1, (const char*)data1, sizeof(data1));
@@ -548,6 +569,7 @@
 {
   struct mdns_domain domain1, domain2;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   memset(&domain1, 0, sizeof(domain1));
   memset(domain1.name, 0xAA, sizeof(MDNS_DOMAIN_MAXLEN));
@@ -578,6 +600,7 @@
   u16_t offset;
   u16_t length;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -612,6 +635,7 @@
   u16_t offset;
   u16_t length;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -641,13 +665,14 @@
     /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
     /* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
-    /* 0x20 */ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0xc0, 0x15,
+    /* 0x20 */ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0xc0, 0x15
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
   u16_t length;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -682,6 +707,7 @@
   u16_t offset;
   u16_t length;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -715,6 +741,7 @@
   u16_t offset;
   u16_t length;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -749,6 +776,7 @@
   u16_t offset;
   u16_t length;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -778,13 +806,14 @@
       /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
       /* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
-      /* 0x20 */ 0x07, 'b', 'a', 'n', 'a', 'n', 'a', 's', 0xc0, 0x15,
+      /* 0x20 */ 0x07, 'b', 'a', 'n', 'a', 'n', 'a', 's', 0xc0, 0x15
   };
   struct pbuf *p;
   struct mdns_domain domain;
   u16_t offset;
   u16_t length;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
@@ -824,6 +853,7 @@
   u16_t offset;
   u16_t length;
   err_t res;
+  LWIP_UNUSED_ARG(_i);
 
   p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
   p->payload = (void *)(size_t)data;
diff --git a/test/unit/tcp/tcp_helper.c b/test/unit/tcp/tcp_helper.c
index fc7e67f..aa7c84a 100644
--- a/test/unit/tcp/tcp_helper.c
+++ b/test/unit/tcp/tcp_helper.c
@@ -141,9 +141,18 @@
 tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip,
                    ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port)
 {
+  u32_t iss;
+
   /* @todo: are these all states? */
   /* @todo: remove from previous list */
   pcb->state = state;
+  
+  iss = tcp_next_iss(pcb);
+  pcb->snd_wl2 = iss;
+  pcb->snd_nxt = iss;
+  pcb->lastack = iss;
+  pcb->snd_lbb = iss;
+  
   if (state == ESTABLISHED) {
     TCP_REG(&tcp_active_pcbs, pcb);
     pcb->local_ip.addr = local_ip->addr;
diff --git a/test/unit/tcp/test_tcp.c b/test/unit/tcp/test_tcp.c
index bfcfa1d..1e5ef03 100644
--- a/test/unit/tcp/test_tcp.c
+++ b/test/unit/tcp/test_tcp.c
@@ -35,8 +35,8 @@
 {
   /* reset iss to default (6510) */
   tcp_ticks = 0;
-  tcp_ticks = 0 - (tcp_next_iss() - 6510);
-  tcp_next_iss();
+  tcp_ticks = 0 - (tcp_next_iss(NULL) - 6510);
+  tcp_next_iss(NULL);
   tcp_ticks = 0;
 
   test_tcp_timer = 0;
@@ -418,7 +418,6 @@
   tcp_ticks = SEQNO1 - ISS;
   pcb = test_tcp_new_counters_pcb(&counters);
   EXPECT_RET(pcb != NULL);
-  EXPECT(pcb->lastack == SEQNO1);
   tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
   pcb->mss = TCP_MSS;
   /* disable initial congestion window (we don't send a SYN here...) */
@@ -513,11 +512,10 @@
 
   /* create and initialize the pcb */
   tcp_ticks = 0;
-  tcp_ticks = 0 - tcp_next_iss();
-  tcp_ticks = SEQNO1 - tcp_next_iss();
+  tcp_ticks = 0 - tcp_next_iss(NULL);
+  tcp_ticks = SEQNO1 - tcp_next_iss(NULL);
   pcb = test_tcp_new_counters_pcb(&counters);
   EXPECT_RET(pcb != NULL);
-  EXPECT(pcb->lastack == SEQNO1);
   tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
   pcb->mss = TCP_MSS;
   /* disable initial congestion window (we don't send a SYN here...) */