]> rtime.felk.cvut.cz Git - l4.git/blobdiff - l4/pkg/lwip/lib/contrib/src/core/ipv6/nd6.c
Update
[l4.git] / l4 / pkg / lwip / lib / contrib / src / core / ipv6 / nd6.c
index c7402c625579b0be0f884fcf4fa30f7c4829a983..e3f458d9162d3b1fdaddd28903925d1e516dfcc3 100644 (file)
@@ -55,6 +55,7 @@
 #include "lwip/netif.h"
 #include "lwip/icmp6.h"
 #include "lwip/mld6.h"
+#include "lwip/ip.h"
 #include "lwip/stats.h"
 
 #include <string.h>
@@ -77,30 +78,35 @@ static u8_t nd6_cached_destination_index;
 /* Multicast address holder. */
 static ip6_addr_t multicast_address;
 
+/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */
+static u8_t nd6_ra_buffer[sizeof(struct prefix_option)];
+
 /* Forward declarations. */
-static s8_t nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr);
+static s8_t nd6_find_neighbor_cache_entry(const ip6_addr_t * ip6addr);
 static s8_t nd6_new_neighbor_cache_entry(void);
 static void nd6_free_neighbor_cache_entry(s8_t i);
-static s8_t nd6_find_destination_cache_entry(ip6_addr_t * ip6addr);
+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(ip6_addr_t * ip6addr, struct netif * netif);
-static s8_t nd6_get_router(ip6_addr_t * router_addr, struct netif * netif);
-static s8_t nd6_new_router(ip6_addr_t * router_addr, struct netif * netif);
+static s8_t nd6_is_prefix_in_netif(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);
 
 #define ND6_SEND_FLAG_MULTICAST_DEST 0x01
 #define ND6_SEND_FLAG_ALLNODES_DEST 0x02
-static void nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags);
-static void nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags);
+static void nd6_send_ns(struct netif * netif, const ip6_addr_t * target_addr, u8_t flags);
+static void nd6_send_na(struct netif * netif, const ip6_addr_t * target_addr, u8_t flags);
 #if LWIP_IPV6_SEND_ROUTER_SOLICIT
-static void nd6_send_rs(struct netif * netif);
+static err_t nd6_send_rs(struct netif * netif);
 #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
 
 #if LWIP_ND6_QUEUEING
 static void nd6_free_q(struct nd6_q_entry *q);
-static void nd6_send_q(s8_t i);
+#else /* LWIP_ND6_QUEUEING */
+#define nd6_free_q(q) pbuf_free(q)
 #endif /* LWIP_ND6_QUEUEING */
+static void nd6_send_q(s8_t i);
 
 
 /**
@@ -124,8 +130,8 @@ nd6_input(struct pbuf *p, struct netif *inp)
     struct na_header * na_hdr;
     struct lladdr_option * lladdr_opt;
 
-    /* Check that na header and link-layer address option fit in packet. */
-    if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) {
+    /* Check that na header fits in packet. */
+    if (p->len < (sizeof(struct na_header))) {
       /* TODO debug message */
       pbuf_free(p);
       ND6_STATS_INC(nd6.lenerr);
@@ -134,7 +140,6 @@ nd6_input(struct pbuf *p, struct netif *inp)
     }
 
     na_hdr = (struct na_header *)p->payload;
-    lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
 
     /* Unsolicited NA?*/
     if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
@@ -142,13 +147,33 @@ nd6_input(struct pbuf *p, struct netif *inp)
        * 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;
+      }
+
       /* Override ip6_current_dest_addr() so that we have an aligned copy. */
       ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address));
 
 #if LWIP_IPV6_DUP_DETECT_ATTEMPTS
       /* If the target address matches this netif, it is a DAD response. */
       for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
-        if (ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) {
+        if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) {
           /* We are using a duplicate address. */
           netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
 
@@ -158,9 +183,6 @@ nd6_input(struct pbuf *p, struct netif *inp)
           mld6_leavegroup(netif_ip6_addr(inp, i), &multicast_address);
 #endif /* LWIP_IPV6_MLD */
 
-
-
-
 #if LWIP_IPV6_AUTOCONFIG
           /* Check to see if this address was autoconfigured. */
           if (!ip6_addr_islinklocal(ip6_current_dest_addr())) {
@@ -186,8 +208,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
           MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
         }
       }
-    }
-    else {
+    } else {
       /* This is a solicited NA.
        * neighbor address resolution response?
        * neighbor unreachability detection response? */
@@ -208,16 +229,33 @@ nd6_input(struct pbuf *p, struct netif *inp)
       neighbor_cache[i].counter.reachable_time = reachable_time;
       if ((na_hdr->flags & ND6_FLAG_OVERRIDE) ||
           (neighbor_cache[i].state == ND6_INCOMPLETE)) {
+        /* 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;
+        }
+
         MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
       }
       neighbor_cache[i].state = ND6_REACHABLE;
 
-#if LWIP_ND6_QUEUEING
       /* Send queued packets, if any. */
       if (neighbor_cache[i].q != NULL) {
         nd6_send_q(i);
       }
-#endif /* LWIP_ND6_QUEUEING */
     }
 
     break; /* ICMP6_TYPE_NA */
@@ -240,9 +278,13 @@ nd6_input(struct pbuf *p, struct netif *inp)
     ns_hdr = (struct ns_header *)p->payload;
 
     /* Check if there is a link-layer address provided. Only point to it if in this buffer. */
-    lladdr_opt = NULL;
-    if (p->len >= (sizeof(struct ns_header) + sizeof(struct lladdr_option))) {
+    if (p->len >= (sizeof(struct ns_header) + 2)) {
       lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+      if (p->len < (sizeof(struct ns_header) + (lladdr_opt->length << 3))) {
+        lladdr_opt = NULL;
+      }
+    } else {
+      lladdr_opt = NULL;
     }
 
     /* Check if the target address is configured on the receiving netif. */
@@ -267,7 +309,8 @@ nd6_input(struct pbuf *p, struct netif *inp)
     if (ip6_addr_isany(ip6_current_src_addr())) {
       /* Sender is validating this address. */
       for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
-        if (ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+        if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
           /* Send a NA back so that the sender does not use this address. */
           nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST);
           if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) {
@@ -276,8 +319,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
           }
         }
       }
-    }
-    else {
+    } else {
       /* Sender is trying to resolve our address. */
       /* Verify that they included their own link-layer address. */
       if (lladdr_opt == NULL) {
@@ -289,7 +331,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
       }
 
       i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
-      if ( i>= 0) {
+      if (i>= 0) {
         /* We already have a record for the solicitor. */
         if (neighbor_cache[i].state == ND6_INCOMPLETE) {
           neighbor_cache[i].netif = inp;
@@ -299,9 +341,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
           neighbor_cache[i].state = ND6_DELAY;
           neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME;
         }
-      }
-      else
-      {
+      } else {
         /* Add their IPv6 address and link-layer address to neighbor cache.
          * We will need it at least to send a unicast NA message, but most
          * likely we will also be communicating with this node soon. */
@@ -351,7 +391,9 @@ nd6_input(struct pbuf *p, struct netif *inp)
 
     /* If we are sending RS messages, stop. */
 #if LWIP_IPV6_SEND_ROUTER_SOLICIT
-    if (inp->rs_count > 0) {
+    /* ensure at least one solicitation is sent */
+    if ((inp->rs_count < LWIP_ND6_MAX_MULTICAST_SOLICIT) ||
+        (nd6_send_rs(inp) == ERR_OK)) {
       inp->rs_count = 0;
     }
 #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
@@ -371,15 +413,15 @@ nd6_input(struct pbuf *p, struct netif *inp)
     }
 
     /* Re-set invalidation timer. */
-    default_router_list[i].invalidation_timer = ra_hdr->router_lifetime;
+    default_router_list[i].invalidation_timer = htons(ra_hdr->router_lifetime);
 
     /* Re-set default timer values. */
 #if LWIP_ND6_ALLOW_RA_UPDATES
     if (ra_hdr->retrans_timer > 0) {
-      retrans_timer = ra_hdr->retrans_timer;
+      retrans_timer = htonl(ra_hdr->retrans_timer);
     }
     if (ra_hdr->reachable_time > 0) {
-      reachable_time = ra_hdr->reachable_time;
+      reachable_time = htonl(ra_hdr->reachable_time);
     }
 #endif /* LWIP_ND6_ALLOW_RA_UPDATES */
 
@@ -392,17 +434,15 @@ nd6_input(struct pbuf *p, struct netif *inp)
     /* Offset to options. */
     offset = sizeof(struct ra_header);
 
-    /* Allocate buffer to copy options (so we can traverse pbufs). */
-    buffer = (u8_t *)mem_malloc(sizeof(struct prefix_option)); /* Size of a prefix option, biggest option. */
-    if (buffer == NULL) {
-      pbuf_free(p);
-      ND6_STATS_INC(nd6.memerr);
-      return;
-    }
-
     /* Process each option. */
     while ((p->tot_len - offset) > 0) {
-      pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset);
+      if (p->len == p->tot_len) {
+        /* no need to copy from contiguous pbuf */
+        buffer = &((u8_t*)p->payload)[offset];
+      } else {
+        buffer = nd6_ra_buffer;
+        pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset);
+      }
       switch (buffer[0]) {
       case ND6_OPTION_TYPE_SOURCE_LLADDR:
       {
@@ -420,9 +460,9 @@ nd6_input(struct pbuf *p, struct netif *inp)
       {
         struct mtu_option * mtu_opt;
         mtu_opt = (struct mtu_option *)buffer;
-        if (mtu_opt->mtu >= 1280) {
+        if (htonl(mtu_opt->mtu) >= 1280) {
 #if LWIP_ND6_ALLOW_RA_UPDATES
-          inp->mtu = mtu_opt->mtu;
+          inp->mtu = (u16_t)htonl(mtu_opt->mtu);
 #endif /* LWIP_ND6_ALLOW_RA_UPDATES */
         }
         break;
@@ -434,24 +474,25 @@ nd6_input(struct pbuf *p, struct netif *inp)
 
         if (prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) {
           /* Add to on-link prefix list. */
+          s8_t prefix;
 
           /* Get a memory-aligned copy of the prefix. */
           ip6_addr_set(ip6_current_dest_addr(), &(prefix_opt->prefix));
 
           /* find cache entry for this prefix. */
-          i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp);
-          if (i < 0) {
+          prefix = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp);
+          if (prefix < 0) {
             /* Create a new cache entry. */
-            i = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp);
+            prefix = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp);
           }
-          if (i >= 0) {
-            prefix_list[i].invalidation_timer = prefix_opt->valid_lifetime;
+          if (prefix >= 0) {
+            prefix_list[prefix].invalidation_timer = htonl(prefix_opt->valid_lifetime);
 
 #if LWIP_IPV6_AUTOCONFIG
             if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) {
               /* Mark prefix as autonomous, so that address autoconfiguration can take place.
                * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/
-              prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS;
+              prefix_list[prefix].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS;
             }
 #endif /* LWIP_IPV6_AUTOCONFIG */
           }
@@ -460,14 +501,11 @@ nd6_input(struct pbuf *p, struct netif *inp)
         break;
       }
       case ND6_OPTION_TYPE_ROUTE_INFO:
-      {
+        /* TODO implement preferred routes.
         struct route_option * route_opt;
-        route_opt = (struct route_option *)buffer;
-
-        /* TODO implement preferred routes. */
+        route_opt = (struct route_option *)buffer;*/
 
         break;
-      }
       default:
         /* Unrecognized option, abort. */
         ND6_STATS_INC(nd6.proterr);
@@ -476,10 +514,6 @@ nd6_input(struct pbuf *p, struct netif *inp)
       offset += 8 * ((u16_t)buffer[1]);
     }
 
-
-    /* free options buffer. */
-    mem_free(buffer);
-
     break; /* ICMP6_TYPE_RA */
   }
   case ICMP6_TYPE_RD: /* Redirect */
@@ -498,9 +532,13 @@ nd6_input(struct pbuf *p, struct netif *inp)
 
     redir_hdr = (struct redirect_header *)p->payload;
 
-    lladdr_opt = NULL;
-    if (p->len >= (sizeof(struct redirect_header) + sizeof(struct lladdr_option))) {
+    if (p->len >= (sizeof(struct redirect_header) + 2)) {
       lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header));
+      if (p->len < (sizeof(struct redirect_header) + (lladdr_opt->length << 3))) {
+        lladdr_opt = NULL;
+      }
+    } else {
+      lladdr_opt = NULL;
     }
 
     /* Copy original destination address to current source address, to have an aligned copy. */
@@ -554,6 +592,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
   {
     struct icmp6_hdr *icmp6hdr; /* Packet too big message */
     struct ip6_hdr * ip6hdr; /* IPv6 header of the packet which caused the error */
+    u32_t pmtu;
 
     /* Check that ICMPv6 header + IPv6 header fit in payload */
     if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) {
@@ -579,7 +618,8 @@ nd6_input(struct pbuf *p, struct netif *inp)
     }
 
     /* Change the Path MTU. */
-    destination_cache[i].pmtu = icmp6hdr->data;
+    pmtu = htonl(icmp6hdr->data);
+    destination_cache[i].pmtu = (u16_t)LWIP_MIN(pmtu, 0xFFFF);
 
     break; /* ICMP6_TYPE_PTB */
   }
@@ -606,7 +646,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
 void
 nd6_tmr(void)
 {
-  s8_t i, j;
+  s8_t i;
   struct netif * netif;
 
   /* Process neighbor entries. */
@@ -616,26 +656,22 @@ nd6_tmr(void)
       if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) {
         /* Retries exceeded. */
         nd6_free_neighbor_cache_entry(i);
-      }
-      else {
+      } else {
         /* Send a NS for this entry. */
         neighbor_cache[i].counter.probes_sent++;
         nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), ND6_SEND_FLAG_MULTICAST_DEST);
       }
       break;
     case ND6_REACHABLE:
-#if LWIP_ND6_QUEUEING
       /* Send queued packets, if any are left. Should have been sent already. */
       if (neighbor_cache[i].q != NULL) {
         nd6_send_q(i);
       }
-#endif /* LWIP_ND6_QUEUEING */
       if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) {
         /* Change to stale state. */
         neighbor_cache[i].state = ND6_STALE;
         neighbor_cache[i].counter.stale_time = 0;
-      }
-      else {
+      } else {
         neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL;
       }
       break;
@@ -647,8 +683,7 @@ nd6_tmr(void)
         /* Change to PROBE state. */
         neighbor_cache[i].state = ND6_PROBE;
         neighbor_cache[i].counter.probes_sent = 0;
-      }
-      else {
+      } else {
         neighbor_cache[i].counter.delay_time -= ND6_TMR_INTERVAL;
       }
       break;
@@ -656,8 +691,7 @@ nd6_tmr(void)
       if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) {
         /* Retries exceeded. */
         nd6_free_neighbor_cache_entry(i);
-      }
-      else {
+      } else {
         /* Send a NS for this entry. */
         neighbor_cache[i].counter.probes_sent++;
         nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), 0);
@@ -683,7 +717,7 @@ nd6_tmr(void)
         default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
       }
       if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
-        /* Less than 1 second remainig. Clear this entry. */
+        /* Less than 1 second remaining. Clear this entry. */
         default_router_list[i].neighbor_entry->isrouter = 0;
         default_router_list[i].neighbor_entry = NULL;
         default_router_list[i].invalidation_timer = 0;
@@ -694,41 +728,64 @@ nd6_tmr(void)
 
   /* Process prefix entries. */
   for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
-    if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
-      prefix_list[i].invalidation_timer = 0;
-    }
-    if ((prefix_list[i].invalidation_timer > 0) &&
-        (prefix_list[i].netif != NULL)) {
-      prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+       if (prefix_list[i].netif != NULL) {
+      if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
+        /* Entry timed out, remove it */
+        prefix_list[i].invalidation_timer = 0;
 
 #if LWIP_IPV6_AUTOCONFIG
-      /* Initiate address autoconfiguration for this prefix, if conditions are met. */
-      if (prefix_list[i].netif->ip6_autoconfig_enabled &&
-          (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) &&
-          !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) {
-        /* Try to get an address on this netif that is invalid.
-         * Skip 0 index (link-local address) */
-        for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
-          if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDRESS_STATE_INVALID) {
-            /* Generate an address using this prefix and interface ID from link-local address. */
-            prefix_list[i].netif->ip6_addr[j].addr[0] = prefix_list[i].prefix.addr[0];
-            prefix_list[i].netif->ip6_addr[j].addr[1] = prefix_list[i].prefix.addr[1];
-            prefix_list[i].netif->ip6_addr[j].addr[2] = prefix_list[i].netif->ip6_addr[0].addr[2];
-            prefix_list[i].netif->ip6_addr[j].addr[3] = prefix_list[i].netif->ip6_addr[0].addr[3];
-
-            /* Mark it as tentative (DAD will be performed if configured). */
-            netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE);
-
-            /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */
-            prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED;
-
-            /* Exit loop. */
-            break;
+        /* If any addresses were configured with this prefix, remove them */
+        if (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)
+        {
+          s8_t j;
+
+          for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
+            if ((netif_ip6_addr_state(prefix_list[i].netif, j) != IP6_ADDR_INVALID) &&
+                ip6_addr_netcmp(&prefix_list[i].prefix, netif_ip6_addr(prefix_list[i].netif, j))) {
+              netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_INVALID);
+              prefix_list[i].flags = 0;
+
+              /* Exit loop. */
+              break;
+            }
           }
         }
-      }
 #endif /* LWIP_IPV6_AUTOCONFIG */
-    }
+
+        prefix_list[i].netif = NULL;
+        prefix_list[i].flags = 0;
+      } else {
+        prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+
+#if LWIP_IPV6_AUTOCONFIG
+        /* Initiate address autoconfiguration for this prefix, if conditions are met. */
+        if (prefix_list[i].netif->ip6_autoconfig_enabled &&
+            (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) &&
+            !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) {
+          s8_t j;
+          /* Try to get an address on this netif that is invalid.
+           * Skip 0 index (link-local address) */
+          for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
+            if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDR_INVALID) {
+              /* Generate an address using this prefix and interface ID from link-local address. */
+              IP_ADDR6(&prefix_list[i].netif->ip6_addr[j],
+                prefix_list[i].prefix.addr[0], prefix_list[i].prefix.addr[1],
+                netif_ip6_addr(prefix_list[i].netif, 0)->addr[2], netif_ip6_addr(prefix_list[i].netif, 0)->addr[3]);
+
+              /* Mark it as tentative (DAD will be performed if configured). */
+              netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE);
+
+              /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */
+              prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED;
+
+              /* Exit loop. */
+              break;
+            }
+          }
+        }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+      }
+       }
   }
 
 
@@ -740,8 +797,7 @@ nd6_tmr(void)
           /* No NA received in response. Mark address as valid. */
           netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED;
           /* TODO implement preferred and valid lifetimes. */
-        }
-        else if (netif->flags & NETIF_FLAG_UP) {
+        } else if (netif->flags & NETIF_FLAG_UP) {
 #if LWIP_IPV6_MLD
           if ((netif->ip6_addr_state[i] & 0x07) == 0) {
             /* Join solicited node multicast group. */
@@ -762,9 +818,11 @@ nd6_tmr(void)
 #if LWIP_IPV6_SEND_ROUTER_SOLICIT
   /* Send router solicitation messages, if necessary. */
   for (netif = netif_list; netif != NULL; netif = netif->next) {
-    if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP)) {
-      nd6_send_rs(netif);
-      netif->rs_count--;
+    if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP) &&
+        (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)))) {
+      if (nd6_send_rs(netif) == ERR_OK) {
+        netif->rs_count--;
+      }
     }
   }
 #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
@@ -779,23 +837,25 @@ nd6_tmr(void)
  * @param flags one of ND6_SEND_FLAG_*
  */
 static void
-nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
+nd6_send_ns(struct netif * netif, const ip6_addr_t * target_addr, u8_t flags)
 {
   struct ns_header * ns_hdr;
   struct lladdr_option * lladdr_opt;
   struct pbuf * p;
-  ip6_addr_t * src_addr;
+  const ip6_addr_t * src_addr;
+  u16_t lladdr_opt_len;
 
   if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) {
     /* Use link-local address as source address. */
     src_addr = netif_ip6_addr(netif, 0);
   } else {
-    src_addr = IP6_ADDR_ANY;
+    src_addr = IP6_ADDR_ANY6;
   }
 
   /* Allocate a packet. */
-  p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + sizeof(struct lladdr_option), PBUF_RAM);
-  if ((p == NULL) || (p->len < (sizeof(struct ns_header) + sizeof(struct lladdr_option)))) {
+  lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+  p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + (lladdr_opt_len << 3), PBUF_RAM);
+  if ((p == NULL) || (p->len < (sizeof(struct ns_header) + (lladdr_opt_len << 3)))) {
     /* We couldn't allocate a suitable pbuf for the ns. drop it. */
     if (p != NULL) {
       pbuf_free(p);
@@ -815,7 +875,7 @@ nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
   ip6_addr_set(&(ns_hdr->target_address), target_addr);
 
   lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
-  lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+  lladdr_opt->length = (u8_t)lladdr_opt_len;
   SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
 
   /* Generate the solicited node address for the target address. */
@@ -824,12 +884,16 @@ nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
     target_addr = &multicast_address;
   }
 
-  ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
-    target_addr);
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+      target_addr);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
 
   /* Send the packet out. */
   ND6_STATS_INC(nd6.xmit);
-  ip6_output_if(p, (src_addr == IP6_ADDR_ANY) ? NULL : src_addr, target_addr,
+  ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, target_addr,
       LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
   pbuf_free(p);
 }
@@ -842,13 +906,14 @@ nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
  * @param flags one of ND6_SEND_FLAG_*
  */
 static void
-nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
+nd6_send_na(struct netif * netif, const ip6_addr_t * target_addr, u8_t flags)
 {
   struct na_header * na_hdr;
   struct lladdr_option * lladdr_opt;
   struct pbuf * p;
-  ip6_addr_t * src_addr;
-  ip6_addr_t * dest_addr;
+  const ip6_addr_t * src_addr;
+  const ip6_addr_t * dest_addr;
+  u16_t lladdr_opt_len;
 
   /* Use link-local address as source address. */
   /* src_addr = &(netif->ip6_addr[0]); */
@@ -856,8 +921,9 @@ nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
   src_addr = target_addr;
 
   /* Allocate a packet. */
-  p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + sizeof(struct lladdr_option), PBUF_RAM);
-  if ((p == NULL) || (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option)))) {
+  lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+  p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + (lladdr_opt_len << 3), PBUF_RAM);
+  if ((p == NULL) || (p->len < (sizeof(struct na_header) + (lladdr_opt_len << 3)))) {
     /* We couldn't allocate a suitable pbuf for the ns. drop it. */
     if (p != NULL) {
       pbuf_free(p);
@@ -868,7 +934,7 @@ nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
 
   /* Set fields. */
   na_hdr = (struct na_header *)p->payload;
-  lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+  lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
 
   na_hdr->type = ICMP6_TYPE_NA;
   na_hdr->code = 0;
@@ -880,24 +946,26 @@ nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
   ip6_addr_set(&(na_hdr->target_address), target_addr);
 
   lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR;
-  lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+  lladdr_opt->length = (u8_t)lladdr_opt_len;
   SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
 
   /* Generate the solicited node address for the target address. */
   if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
     ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
     dest_addr = &multicast_address;
-  }
-  else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) {
+  } else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) {
     ip6_addr_set_allnodes_linklocal(&multicast_address);
     dest_addr = &multicast_address;
-  }
-  else {
+  } else {
     dest_addr = ip6_current_src_addr();
   }
 
-  na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
-    dest_addr);
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+      dest_addr);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
 
   /* Send the packet out. */
   ND6_STATS_INC(nd6.xmit);
@@ -912,39 +980,38 @@ nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
  *
  * @param netif the netif on which to send the message
  */
-static void
+static err_t
 nd6_send_rs(struct netif * netif)
 {
   struct rs_header * rs_hdr;
   struct lladdr_option * lladdr_opt;
   struct pbuf * p;
-  ip6_addr_t * src_addr;
-  u16_t packet_len;
+  const ip6_addr_t * src_addr;
+  err_t err;
+  u16_t lladdr_opt_len = 0;
 
   /* Link-local source address, or unspecified address? */
   if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
     src_addr = netif_ip6_addr(netif, 0);
-  }
-  else {
-    src_addr = IP6_ADDR_ANY;
+  } else {
+    src_addr = IP6_ADDR_ANY6;
   }
 
   /* Generate the all routers target address. */
   ip6_addr_set_allrouters_linklocal(&multicast_address);
 
   /* Allocate a packet. */
-  packet_len = sizeof(struct rs_header);
-  if (src_addr != IP6_ADDR_ANY) {
-    packet_len += sizeof(struct lladdr_option);
+  if (src_addr != IP6_ADDR_ANY6) {
+    lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
   }
-  p = pbuf_alloc(PBUF_IP, packet_len, PBUF_RAM);
-  if ((p == NULL) || (p->len < packet_len)) {
+  p = pbuf_alloc(PBUF_IP, sizeof(struct rs_header) + (lladdr_opt_len << 3), PBUF_RAM);
+  if ((p == NULL) || (p->len < (sizeof(struct rs_header) + (lladdr_opt_len << 3)))) {
     /* We couldn't allocate a suitable pbuf for the ns. drop it. */
     if (p != NULL) {
       pbuf_free(p);
     }
     ND6_STATS_INC(nd6.memerr);
-    return;
+    return ERR_BUF;
   }
 
   /* Set fields. */
@@ -955,22 +1022,29 @@ nd6_send_rs(struct netif * netif)
   rs_hdr->chksum = 0;
   rs_hdr->reserved = 0;
 
-  if (src_addr != IP6_ADDR_ANY) {
+  if (src_addr != IP6_ADDR_ANY6) {
     /* Include our hw address. */
     lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header));
     lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
-    lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+    lladdr_opt->length = (u8_t)lladdr_opt_len;
     SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
   }
 
-  rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
-    &multicast_address);
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+      &multicast_address);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
 
   /* Send the packet out. */
   ND6_STATS_INC(nd6.xmit);
-  ip6_output_if(p, src_addr, &multicast_address,
+
+  err = ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, &multicast_address,
       LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
   pbuf_free(p);
+
+  return err;
 }
 #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
 
@@ -982,7 +1056,7 @@ nd6_send_rs(struct netif * netif)
  * entry is found
  */
 static s8_t
-nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr)
+nd6_find_neighbor_cache_entry(const ip6_addr_t * ip6addr)
 {
   s8_t i;
   for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
@@ -1068,9 +1142,7 @@ nd6_new_neighbor_cache_entry(void)
   j = -1;
   for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
     if (
-#if LWIP_ND6_QUEUEING
         (neighbor_cache[i].q == NULL) &&
-#endif /* LWIP_ND6_QUEUEING */
         (neighbor_cache[i].state == ND6_INCOMPLETE) &&
         (!neighbor_cache[i].isrouter)) {
       if (neighbor_cache[i].counter.probes_sent >= time) {
@@ -1085,7 +1157,6 @@ nd6_new_neighbor_cache_entry(void)
   }
 
   /* Next, find oldest incomplete entry with queued packets. */
-#if LWIP_ND6_QUEUEING
   time = 0;
   j = -1;
   for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
@@ -1101,7 +1172,6 @@ nd6_new_neighbor_cache_entry(void)
     nd6_free_neighbor_cache_entry(j);
     return j;
   }
-#endif /* LWIP_ND6_QUEUEING */
 
   /* No more entries to try. */
   return -1;
@@ -1116,13 +1186,15 @@ nd6_new_neighbor_cache_entry(void)
 static void
 nd6_free_neighbor_cache_entry(s8_t i)
 {
-#if LWIP_ND6_QUEUEING
+  if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+    return;
+  }
+
   /* Free any queued packets. */
   if (neighbor_cache[i].q != NULL) {
     nd6_free_q(neighbor_cache[i].q);
     neighbor_cache[i].q = NULL;
   }
-#endif /* LWIP_ND6_QUEUEING */
 
   neighbor_cache[i].state = ND6_NO_ENTRY;
   neighbor_cache[i].isrouter = 0;
@@ -1139,7 +1211,7 @@ nd6_free_neighbor_cache_entry(s8_t i)
  * entry is found
  */
 static s8_t
-nd6_find_destination_cache_entry(ip6_addr_t * ip6addr)
+nd6_find_destination_cache_entry(const ip6_addr_t * ip6addr)
 {
   s8_t i;
   for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
@@ -1189,7 +1261,7 @@ nd6_new_destination_cache_entry(void)
  * @return 1 if the address is on-link, 0 otherwise
  */
 static s8_t
-nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif)
+nd6_is_prefix_in_netif(const ip6_addr_t * ip6addr, struct netif * netif)
 {
   s8_t i;
   for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
@@ -1218,7 +1290,7 @@ nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif)
  *         router is found
  */
 s8_t
-nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif)
+nd6_select_router(const ip6_addr_t * ip6addr, struct netif * netif)
 {
   s8_t i;
   /* last_router is used for round-robin router selection (as recommended
@@ -1277,7 +1349,7 @@ nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif)
  * @return the index of the router entry, or -1 if not found
  */
 static s8_t
-nd6_get_router(ip6_addr_t * router_addr, struct netif * netif)
+nd6_get_router(const ip6_addr_t * router_addr, struct netif * netif)
 {
   s8_t i;
 
@@ -1302,7 +1374,7 @@ nd6_get_router(ip6_addr_t * router_addr, struct netif * netif)
  * @return the index on the router table, or -1 if could not be created
  */
 static s8_t
-nd6_new_router(ip6_addr_t * router_addr, struct netif * netif)
+nd6_new_router(const ip6_addr_t * router_addr, struct netif * netif)
 {
   s8_t router_index;
   s8_t neighbor_index;
@@ -1386,7 +1458,9 @@ nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif)
       /* Found empty prefix entry. */
       prefix_list[i].netif = netif;
       ip6_addr_set(&(prefix_list[i].prefix), prefix);
+#if LWIP_IPV6_AUTOCONFIG
       prefix_list[i].flags = 0;
+#endif /* LWIP_IPV6_AUTOCONFIG */
       return i;
     }
   }
@@ -1408,7 +1482,7 @@ nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif)
  *         could be created
  */
 s8_t
-nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
+nd6_get_next_hop_entry(const ip6_addr_t * ip6addr, struct netif * netif)
 {
   s8_t i;
 
@@ -1433,8 +1507,7 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
     if (i >= 0) {
       /* found destination entry. make it our new cached index. */
       nd6_cached_destination_index = i;
-    }
-    else {
+    } else {
       /* Not found. Create a new destination entry. */
       i = nd6_new_destination_cache_entry();
       if (i >= 0) {
@@ -1446,16 +1519,15 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
       }
 
       /* Copy dest address to destination cache. */
-      ip6_addr_set(&(destination_cache[i].destination_addr), ip6addr);
+      ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr);
 
       /* Now find the next hop. is it a neighbor? */
       if (ip6_addr_islinklocal(ip6addr) ||
           nd6_is_prefix_in_netif(ip6addr, netif)) {
         /* Destination in local link. */
-        destination_cache[i].pmtu = netif->mtu;
-        ip6_addr_copy(destination_cache[i].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr);
-      }
-      else {
+        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);
+      } else {
         /* We need to select a router. */
         i = nd6_select_router(ip6addr, netif);
         if (i < 0) {
@@ -1487,8 +1559,7 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
     if (i >= 0) {
       /* Found a matching record, make it new cached entry. */
       nd6_cached_neighbor_index = i;
-    }
-    else {
+    } else {
       /* Neighbor not in cache. Make a new entry. */
       i = nd6_new_neighbor_cache_entry();
       if (i >= 0) {
@@ -1515,8 +1586,6 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
   return nd6_cached_neighbor_index;
 }
 
-#if LWIP_ND6_QUEUEING
-
 /**
  * Queue a packet for a neighbor.
  *
@@ -1530,30 +1599,42 @@ nd6_queue_packet(s8_t neighbor_index, struct pbuf * q)
   err_t result = ERR_MEM;
   struct pbuf *p;
   int copy_needed = 0;
+#if LWIP_ND6_QUEUEING
   struct nd6_q_entry *new_entry, *r;
+#endif /* LWIP_ND6_QUEUEING */
+
+  if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) {
+    return ERR_ARG;
+  }
 
   /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
    * to copy the whole queue into a new PBUF_RAM (see bug #11400)
    * PBUF_ROMs can be left as they are, since ROM must not get changed. */
   p = q;
   while (p) {
-    if(p->type != PBUF_ROM) {
+    if (p->type != PBUF_ROM) {
       copy_needed = 1;
       break;
     }
     p = p->next;
   }
-  if(copy_needed) {
+  if (copy_needed) {
     /* copy the whole packet into new pbufs */
     p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
-    if ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
+    while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
       /* Free oldest packet (as per RFC recommendation) */
+#if LWIP_ND6_QUEUEING
       r = neighbor_cache[neighbor_index].q;
       neighbor_cache[neighbor_index].q = r->next;
+      r->next = NULL;
       nd6_free_q(r);
+#else /* LWIP_ND6_QUEUEING */
+      pbuf_free(neighbor_cache[neighbor_index].q);
+      neighbor_cache[neighbor_index].q = NULL;
+#endif /* LWIP_ND6_QUEUEING */
       p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
     }
-    if(p != NULL) {
+    if (p != NULL) {
       if (pbuf_copy(p, q) != ERR_OK) {
         pbuf_free(p);
         p = NULL;
@@ -1567,19 +1648,21 @@ nd6_queue_packet(s8_t neighbor_index, struct pbuf * q)
   /* packet was copied/ref'd? */
   if (p != NULL) {
     /* queue packet ... */
+#if LWIP_ND6_QUEUEING
     /* allocate a new nd6 queue entry */
     new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
     if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
       /* Free oldest packet (as per RFC recommendation) */
       r = neighbor_cache[neighbor_index].q;
       neighbor_cache[neighbor_index].q = r->next;
+      r->next = NULL;
       nd6_free_q(r);
       new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
     }
     if (new_entry != NULL) {
       new_entry->next = NULL;
       new_entry->p = p;
-      if(neighbor_cache[neighbor_index].q != NULL) {
+      if (neighbor_cache[neighbor_index].q != NULL) {
         /* queue was already existent, append the new entry to the end */
         r = neighbor_cache[neighbor_index].q;
         while (r->next != NULL) {
@@ -1590,14 +1673,23 @@ nd6_queue_packet(s8_t neighbor_index, struct pbuf * q)
         /* queue did not exist, first item in queue */
         neighbor_cache[neighbor_index].q = new_entry;
       }
-      LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)q, (s16_t)neighbor_index));
+      LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
       result = ERR_OK;
     } else {
       /* the pool MEMP_ND6_QUEUE is empty */
       pbuf_free(p);
-      LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q));
+      LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p));
       /* { result == ERR_MEM } through initialization */
     }
+#else /* LWIP_ND6_QUEUEING */
+    /* Queue a single packet. If an older packet is already queued, free it as per RFC. */
+    if (neighbor_cache[neighbor_index].q != NULL) {
+      pbuf_free(neighbor_cache[neighbor_index].q);
+    }
+    neighbor_cache[neighbor_index].q = p;
+    LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
+    result = ERR_OK;
+#endif /* LWIP_ND6_QUEUEING */
   } else {
     LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q));
     /* { result == ERR_MEM } through initialization */
@@ -1606,6 +1698,7 @@ nd6_queue_packet(s8_t neighbor_index, struct pbuf * q)
   return result;
 }
 
+#if LWIP_ND6_QUEUEING
 /**
  * Free a complete queue of nd6 q entries
  *
@@ -1625,6 +1718,7 @@ nd6_free_q(struct nd6_q_entry *q)
     memp_free(MEMP_ND6_QUEUE, r);
   }
 }
+#endif /* LWIP_ND6_QUEUEING */
 
 /**
  * Send queued packets for a neighbor
@@ -1635,8 +1729,15 @@ static void
 nd6_send_q(s8_t i)
 {
   struct ip6_hdr *ip6hdr;
+#if LWIP_ND6_QUEUEING
   struct nd6_q_entry *q;
+#endif /* LWIP_ND6_QUEUEING */
+
+  if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+    return;
+  }
 
+#if LWIP_ND6_QUEUEING
   while (neighbor_cache[i].q != NULL) {
     /* remember first in queue */
     q = neighbor_cache[i].q;
@@ -1653,9 +1754,21 @@ nd6_send_q(s8_t i)
     /* now queue entry can be freed */
     memp_free(MEMP_ND6_QUEUE, q);
   }
+#else /* LWIP_ND6_QUEUEING */
+  if (neighbor_cache[i].q != NULL) {
+    /* Get ipv6 header. */
+    ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload);
+    /* Override ip6_current_dest_addr() so that we have an aligned copy. */
+    ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest));
+    /* send the queued IPv6 packet */
+    (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, ip6_current_dest_addr());
+    /* free the queued IP packet */
+    pbuf_free(neighbor_cache[i].q);
+    neighbor_cache[i].q = NULL;
+  }
+#endif /* LWIP_ND6_QUEUEING */
 }
 
-#endif /* LWIP_ND6_QUEUEING */
 
 /**
  * Get the Path MTU for a destination.
@@ -1665,7 +1778,7 @@ nd6_send_q(s8_t i)
  * @return the Path MTU, if known, or the netif default MTU
  */
 u16_t
-nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif)
+nd6_get_destination_mtu(const ip6_addr_t * ip6addr, struct netif * netif)
 {
   s8_t i;
 
@@ -1695,7 +1808,7 @@ nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif)
  *                by an upper layer protocol (TCP)
  */
 void
-nd6_reachability_hint(ip6_addr_t * ip6addr)
+nd6_reachability_hint(const ip6_addr_t * ip6addr)
 {
   s8_t i;
 
@@ -1703,8 +1816,7 @@ nd6_reachability_hint(ip6_addr_t * ip6addr)
   if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) {
     i = nd6_cached_destination_index;
     ND6_STATS_INC(nd6.cachehit);
-  }
-  else {
+  } else {
     i = nd6_find_destination_cache_entry(ip6addr);
   }
   if (i < 0) {
@@ -1715,14 +1827,18 @@ nd6_reachability_hint(ip6_addr_t * ip6addr)
   if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) {
     i = nd6_cached_neighbor_index;
     ND6_STATS_INC(nd6.cachehit);
-  }
-  else {
+  } else {
     i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr));
   }
   if (i < 0) {
     return;
   }
 
+  /* For safety: don't set as reachable if we don't have a LL address yet. Misuse protection. */
+  if (neighbor_cache[i].state == ND6_INCOMPLETE || neighbor_cache[i].state == ND6_NO_ENTRY) {
+    return;
+  }
+
   /* Set reachability state. */
   neighbor_cache[i].state = ND6_REACHABLE;
   neighbor_cache[i].counter.reachable_time = reachable_time;