nd6: add support for RDNSS option (as per RFC 6106)
diff --git a/CHANGELOG b/CHANGELOG
index 42e8189..653a887 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -6,6 +6,9 @@
  
   ++ New features:
 
+  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()
 
diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c
index 4dbaa7c..689b56e 100644
--- a/src/core/ipv6/nd6.c
+++ b/src/core/ipv6/nd6.c
@@ -60,6 +60,7 @@
 #include "lwip/mld6.h"
 #include "lwip/ip.h"
 #include "lwip/stats.h"
+#include "lwip/dns.h"
 
 #include <string.h>
 
@@ -394,6 +395,10 @@
     struct ra_header *ra_hdr;
     u8_t *buffer; /* Used to copy options. */
     u16_t offset;
+#ifdef 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)) {
@@ -538,6 +543,34 @@
         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++) {
+          /* Get a memory-aligned copy of the prefix. */
+          ip6_addr_set(ip6_current_dest_addr(), &(rdnss_opt->rdnss_address[n]));
+
+          if (htonl(rdnss_opt->lifetime) > 0) {
+            /* TODO implement Lifetime > 0 */
+            dns_setserver(rdnss_server_idx++, ip_current_dest_addr());
+          } 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, ip_current_dest_addr())) {
+                dns_setserver(s, NULL);
+              }
+            }
+          }
+        }
+        break;
+      }
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
       default:
         /* Unrecognized option, abort. */
         ND6_STATS_INC(nd6.proterr);
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 2326b30..d350f3c 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -2379,6 +2379,14 @@
 #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
+/**
  * @}
  */
 
diff --git a/src/include/lwip/prot/nd6.h b/src/include/lwip/prot/nd6.h
index 7f46c53..2d4903d 100644
--- a/src/include/lwip/prot/nd6.h
+++ b/src/include/lwip/prot/nd6.h
@@ -247,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