You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/03/14 08:41:03 UTC

[GitHub] [incubator-nuttx-apps] coffee809721232 opened a new pull request #1067: Add dhcp6c module

coffee809721232 opened a new pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067


   ## Summary
   Add dhcp6c module for nuttx
   ## Impact
   Improve IPv6 function
   ## Testing
   dhcp6c test passed
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] coffee809721232 commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
coffee809721232 commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r829062191



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for ((_o) = (FAR uint8_t *)(start); (_o) + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = (_o)[0] << 8 | (_o)[1]) && ((odata) = (FAR void *)&(_o)[4]) &&\
+        ((olen) = (_o)[2] << 8 | (_o)[3]) + (odata) <= (FAR uint8_t *)(end); \
+        (_o) += 4 + ((_o)[2] << 8 | (_o)[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (c == NULL || c->preference < cand[i].preference)
+        {
+          c = &cand[i];
+        }
+    }
+
+  if (retry && pdhcp6c->ia_mode == IA_MODE_TRY)
+    {
+      /* We give it a second try without the IA_NA */
+
+      pdhcp6c->ia_mode = IA_MODE_NONE;
+      return dhcp6c_command(handle, DHCPV6_MSG_SOLICIT);
+    }
+
+  if (c != NULL)
+    {
+      uint16_t hdr[2] =
+      {
+        htons(DHCPV6_OPT_SERVERID), htons(c->duid_len)
+      };
+
+      dhcp6c_add_state(handle, STATE_SERVER_ID, hdr, sizeof(hdr));
+      dhcp6c_add_state(handle, STATE_SERVER_ID, c->duid, c->duid_len);
+      pdhcp6c->accept_reconfig = c->wants_reconfigure;
+    }
+
+  dhcp6c_clear_state(handle, STATE_SERVER_CAND);
+
+  if (c == NULL)
+    {
+      return -1;
+    }
+  else if (pdhcp6c->request_prefix || pdhcp6c->ia_mode != IA_MODE_NONE)
+    {
+      return DHCPV6_STATEFUL;
+    }
+  else
+    {
+      return DHCPV6_STATELESS;
+    }
+}
+
+static time_t dhcp6c_parse_ia(FAR void *handle, FAR void *opt, FAR void *end)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint32_t timeout = UINT32_MAX;
+  uint16_t otype;
+  uint16_t olen;
+  uint16_t stype;
+  uint16_t slen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  FAR uint8_t *sdata;
+
+  /* Update address IA */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_IA_PREFIX)
+        {
+          FAR struct dhcpv6_ia_prefix_s *prefix = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_prefix_s *local = NULL;
+          uint32_t valid;
+          uint32_t pref;
+          size_t pd_len;
+          FAR uint8_t *pd;
+          FAR uint8_t *ite1;
+
+          if (olen + 4u < sizeof(*prefix))
+            {
+              continue;
+            }
+
+          olen = sizeof(*prefix);
+          valid = ntohl(prefix->valid);
+          pref = ntohl(prefix->preferred);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          pd = dhcp6c_get_state(handle, STATE_IA_PD, &pd_len);
+          dhcpv6_for_each_option(ite1, pd, pd + pd_len,
+                                 stype, slen, sdata)
+            {
+              if (memcmp(sdata + 8, odata + 8,
+                         sizeof(local->addr) + 1) == 0)

Review comment:
       prefix address including length and value.
   length occupies one byte




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] xiaoxiang781216 commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r827995497



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < (ssize_t)sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)))
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(&rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4U == client_id_len) &&
+                        !memcmp((odata - 4), client_id, client_id_len);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4U == server_id_len) &&
+                        !memcmp((odata - 4), server_id, server_id_len);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint8_t buf[1536];
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      return -1;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * 1000 +
+              dhcp6c_rand_delay(handle, retx->init_timeo * 1000)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * 1000)
+        {
+          rto = retx->max_timeo * 1000 +
+                dhcp6c_rand_delay(handle, retx->max_timeo * 1000);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * 1000)
+        {
+          round_end = timeout * 1000 + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / 1000, (t % 1000) * 1000

Review comment:
       We can do it one by one. For general application, I don't see any reason why not make them could run on POSIX compliant OS(e.g. Linux) too.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] pkarashchenko commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r828277881



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for (_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\

Review comment:
       `_o` -> `(_o)`

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for (_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (!c || c->preference < cand[i].preference)
+        {
+          c = &cand[i];
+        }
+    }
+
+  if (retry && pdhcp6c->ia_mode == IA_MODE_TRY)
+    {
+      /* We give it a second try without the IA_NA */
+
+      pdhcp6c->ia_mode = IA_MODE_NONE;
+      return dhcp6c_command(handle, DHCPV6_MSG_SOLICIT);
+    }
+
+  if (c)
+    {
+      uint16_t hdr[2] =
+      {
+        htons(DHCPV6_OPT_SERVERID), htons(c->duid_len)
+      };
+
+      dhcp6c_add_state(handle, STATE_SERVER_ID, hdr, sizeof(hdr));
+      dhcp6c_add_state(handle, STATE_SERVER_ID, c->duid, c->duid_len);
+      pdhcp6c->accept_reconfig = c->wants_reconfigure;
+    }
+
+  dhcp6c_clear_state(handle, STATE_SERVER_CAND);
+
+  if (!c)
+    {
+      return -1;
+    }
+  else if (pdhcp6c->request_prefix || pdhcp6c->ia_mode != IA_MODE_NONE)
+    {
+      return DHCPV6_STATEFUL;
+    }
+  else
+    {
+      return DHCPV6_STATELESS;
+    }
+}
+
+static time_t dhcp6c_parse_ia(FAR void *handle, FAR void *opt, FAR void *end)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint32_t timeout = UINT32_MAX;
+  uint16_t otype;
+  uint16_t olen;
+  uint16_t stype;
+  uint16_t slen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  FAR uint8_t *sdata;
+
+  /* Update address IA */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_IA_PREFIX)
+        {
+          FAR struct dhcpv6_ia_prefix_s *prefix = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_prefix_s *local = NULL;
+          uint32_t valid;
+          uint32_t pref;
+          size_t pd_len;
+          FAR uint8_t *pd;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*prefix))
+            {
+              continue;
+            }
+
+          olen = sizeof(*prefix);
+          valid = ntohl(prefix->valid);
+          pref = ntohl(prefix->preferred);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          pd = dhcp6c_get_state(handle, STATE_IA_PD, &pd_len);
+          dhcpv6_for_each_option(ite1, pd, pd + pd_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata + 8, odata + 8,
+                          sizeof(local->addr) + 1))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = prefix->preferred;
+              local->valid = prefix->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_PD, prefix, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+      else if (otype == DHCPV6_OPT_IA_ADDR)
+        {
+          FAR struct dhcpv6_ia_addr_s *addr = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_addr_s *local = NULL;
+          uint32_t pref;
+          uint32_t valid;
+          size_t na_len;
+          FAR uint8_t *na;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*addr))
+            {
+              continue;
+            }
+
+          olen = sizeof(*addr);
+          pref = ntohl(addr->preferred);
+          valid = ntohl(addr->valid);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          na = dhcp6c_get_state(handle, STATE_IA_NA, &na_len);
+          dhcpv6_for_each_option(ite1, na, na + na_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata, odata, sizeof(local->addr)))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = addr->preferred;
+              local->valid = addr->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_NA, addr, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+    }
+
+  return timeout;
+}
+
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  bool have_update = false;
+  size_t ia_na_len;
+  size_t ia_pd_len;
+  size_t dns_len;
+  size_t search_len;
+  size_t sntp_ip_len;
+  size_t sntp_dns_len;
+  size_t sip_ip_len;
+  size_t sip_fqdn_len;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  FAR uint8_t *ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  FAR uint8_t *ia_end;
+  pdhcp6c->t1 = UINT32_MAX;
+  pdhcp6c->t2 = UINT32_MAX;
+  pdhcp6c->t3 = UINT32_MAX;
+
+  dhcp6c_get_state(handle, STATE_DNS, &dns_len);
+  dhcp6c_get_state(handle, STATE_SEARCH, &search_len);
+  dhcp6c_get_state(handle, STATE_SNTP_IP, &sntp_ip_len);
+  dhcp6c_get_state(handle, STATE_SNTP_FQDN, &sntp_dns_len);
+  dhcp6c_get_state(handle, STATE_SIP_IP, &sip_ip_len);
+  dhcp6c_get_state(handle, STATE_SIP_FQDN, &sip_fqdn_len);
+
+  /* Decrease valid and preferred lifetime of prefixes */
+
+  dhcpv6_for_each_option(ite0, ia_pd, ia_pd + ia_pd_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Decrease valid and preferred lifetime of addresses */
+
+  dhcpv6_for_each_option(ite0, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Parse and find all matching IAs */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)
+           && olen > sizeof(struct dhcpv6_ia_hdr_s))
+        {
+          FAR struct dhcpv6_ia_hdr_s *ia_hdr = (FAR void *)(odata - 4);
+          time_t l_t1 = ntohl(ia_hdr->t1);
+          time_t l_t2 = ntohl(ia_hdr->t2);
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *sdata;
+          time_t n;
+
+          /* Test ID and T1-T2 validity */
+
+          if (ia_hdr->iaid != 1 || l_t2 < l_t1)
+            {
+              continue;
+            }
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, &ia_hdr[1], odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (stype == DHCPV6_OPT_STATUS && slen >= 2 &&
+                  (sdata[0] || sdata[1]))
+                {
+                  continue;
+                }
+            }
+
+          /* Update times */
+
+          if (l_t1 > 0 && pdhcp6c->t1 > l_t1)
+            {
+              pdhcp6c->t1 = l_t1;
+            }
+
+          if (l_t2 > 0 && pdhcp6c->t2 > l_t2)
+            {
+              pdhcp6c->t2 = l_t2;
+            }
+
+          /* Always report update in case we have IA_PDs so that
+           * the state-script is called with updated times
+           */
+
+          if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+            {
+              have_update = true;
+            }
+
+          n = dhcp6c_parse_ia(handle, &ia_hdr[1], odata + olen);
+          if (n < pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = n;
+            }
+
+          if (n < pdhcp6c->t2)
+            {
+              pdhcp6c->t2 = n;
+            }
+
+          if (n < pdhcp6c->t3)
+            {
+              pdhcp6c->t3 = n;
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_SERVERS)
+        {
+          if (olen % 16 == 0)
+            {
+              dhcp6c_add_state(handle, STATE_DNS, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_DOMAIN)
+        {
+          dhcp6c_add_state(handle, STATE_SEARCH, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_NTP_SERVER)
+        {
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *sdata;
+          FAR uint8_t *ite1;
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, odata, odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (slen == 16 &&
+                  (stype == NTP_MC_ADDR || stype == NTP_SRV_ADDR))
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_IP, sdata, slen);
+                }
+              else if (slen > 0 && stype == NTP_SRV_FQDN)
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_FQDN, sdata, slen);
+                }
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_A)
+        {
+          if (olen == 16)
+            {
+              dhcp6c_add_state(handle, STATE_SIP_IP, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_D)
+        {
+          dhcp6c_add_state(handle, STATE_SIP_FQDN, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4)
+        {
+          uint32_t refresh = ntohl(*((FAR uint32_t *)odata));
+          if (refresh < (uint32_t)pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = refresh;
+            }
+        }
+      else if (otype != DHCPV6_OPT_CLIENTID && otype != DHCPV6_OPT_SERVERID)
+        {
+          dhcp6c_add_state(handle, STATE_CUSTOM_OPTS, (odata - 4), olen + 4);
+        }
+    }
+
+  if (opt)
+    {
+      size_t new_ia_pd_len;
+      size_t new_ia_na_len;
+      have_update |= dhcp6c_commit_state(handle, STATE_DNS, dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SEARCH, search_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_IP,
+                                         sntp_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_FQDN,
+                                         sntp_dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_IP, sip_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_FQDN,
+                                         sip_fqdn_len);
+      dhcp6c_get_state(handle, STATE_IA_PD, &new_ia_pd_len);
+      dhcp6c_get_state(handle, STATE_IA_NA, &new_ia_na_len);
+      have_update |= (new_ia_pd_len != ia_pd_len) ||
+                     (new_ia_na_len != ia_na_len);
+    }
+
+  /* Delete prefixes with 0 valid-time */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  ia_end = ia_pd + ia_pd_len;
+  dhcpv6_for_each_option(ite0, ia_pd, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_pd + dhcp6c_remove_state(handle, STATE_IA_PD,
+                   (FAR uint8_t *)p - ia_pd, olen + 4);
+          have_update = true;
+        }
+    }
+
+  /* Delete addresses with 0 valid-time */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  ia_end = ia_na + ia_na_len;
+  dhcpv6_for_each_option(ite0, ia_na, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_na + dhcp6c_remove_state(handle, STATE_IA_NA,
+                   (FAR uint8_t *)p - ia_na, olen + 4);
+          have_update = true;
+        }
+    }
+
+  return have_update;
+}
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *odata;
+  FAR uint8_t *ite;
+  uint8_t msg = DHCPV6_MSG_RENEW;
+
+  /* TODO: should verify the reconfigure message */
+
+  dhcpv6_for_each_option(ite, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1 &&
+          (odata[0] == DHCPV6_MSG_RENEW ||
+           odata[0] == DHCPV6_MSG_INFO_REQ))
+        {
+          msg = odata[0];
+        }
+    }
+
+  pdhcp6c->t1 -= elapsed;
+  pdhcp6c->t2 -= elapsed;
+  pdhcp6c->t3 -= elapsed;
+
+  if (pdhcp6c->t1 < 0)
+    {
+      pdhcp6c->t1 = 0;
+    }
+
+  if (pdhcp6c->t2 < 0)
+    {
+      pdhcp6c->t2 = 0;
+    }
+
+  if (pdhcp6c->t3 < 0)
+    {
+      pdhcp6c->t3 = 0;
+    }
+
+  dhcp6c_handle_reply(handle, DHCPV6_MSG_UNKNOWN, NULL, NULL, elapsed);
+
+  return msg;
+}
+
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed)
+{
+  dhcp6c_handle_advert(handle, orig, opt, end, elapsed);
+  if (dhcp6c_commit_advert(handle, elapsed) < 0)
+    {
+      return -1;
+    }
+
+  return dhcp6c_handle_reply(handle, orig, opt, end, elapsed);
+}
+
+static int dhcp6c_single_request(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  FAR const char *process = NULL;
+  enum dhcpv6_mode_e mode;
+  enum dhcpv6_msg_e type;
+  int ret = -1;
+
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_CAND);
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_CUSTOM_OPTS);
+  ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_SOLICIT);
+  if (ret < 0)
+    {
+      return -1;
+    }
+  else if (ret == DHCPV6_STATELESS)
+    {
+      mode = DHCPV6_STATELESS;
+      type = DHCPV6_MSG_INFO_REQ;
+      process = "informed";
+    }
+  else
+    {
+      mode = DHCPV6_STATEFUL;
+      type = DHCPV6_MSG_REQUEST;
+      process = "bound";
+    }
+
+  ret = dhcp6c_command(pdhcp6c, type);
+  if (ret >= 0)
+    {
+      ret = mode;
+      dhcp6c_switch_process(pdhcp6c, process);
+    }
+
+  return ret;
+}
+
+static int dhcp6c_lease(FAR void *args, uint8_t type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  enum dhcpv6_mode_e mode = (enum dhcpv6_mode_e)type;
+  size_t ia_pd_len;
+  size_t ia_na_len;
+  size_t ia_pd_new;
+  size_t ia_na_new;
+  size_t server_id_len;
+  int ret = -1;
+
+  if (mode == DHCPV6_STATELESS)
+    {
+      /* Stateless mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Wait for T1 to expire or until we get a reconfigure */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          /* Information-Request */
+
+          ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_INFO_REQ);
+          if (ret < 0)
+            {
+              nerr("DHCPV6_MSG_INFO_REQ error\n");
+              break;
+            }
+          else
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+        }
+    }
+  else
+    {
+      /* Stateful mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Renew Cycle
+           * Wait for T1 to expire or until we get a reconfigure
+           */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+          dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+
+          /* If we have any IAs, send renew, otherwise request */
+
+          if (ia_pd_len == 0 && ia_na_len == 0)
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REQUEST);
+          else
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_RENEW);
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          if (ret >= 0)
+            {
+              /* Publish updates */
+
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+          else
+            {
+              /* Remove binding */
+
+              dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+
+              /* If we have IAs, try rebind otherwise restart */
+
+              ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REBIND);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_new);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_new);
+
+              /* We lost all our IAs, restart */
+
+              if (ret < 0 || (ia_pd_new == 0 && ia_pd_len) ||
+                  (ia_na_new == 0 && ia_na_len))
+                {
+                  break;
+                }
+              else if (ret >= 0)
+                {
+                  dhcp6c_switch_process(pdhcp6c, "rebound");
+                }
+            }
+        }
+    }
+
+  dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+  dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+  dhcp6c_get_state(pdhcp6c, STATE_SERVER_ID, &server_id_len);
+
+  /* Add all prefixes to lost prefixes */
+
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_switch_process(pdhcp6c, "unbound");
+
+  /* Remove assigned addresses */
+
+  if (ia_na_len > 0)
+    {
+      dhcp6c_remove_addrs(pdhcp6c);
+    }
+
+  if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0))
+    {
+      dhcp6c_command(pdhcp6c, DHCPV6_MSG_RELEASE);
+    }
+
+  return ret;
+}
+
+static FAR void *dhcp6c_run(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  int ret;
+
+  while (!pdhcp6c->cancel)
+    {
+      ret = dhcp6c_single_request(pdhcp6c);
+      if (ret > 0)
+        {
+          dhcp6c_lease(pdhcp6c, ret);
+        }
+    }
+
+  return 0;
+}
+
+static FAR void *dhcp6c_precise_open(FAR const char *ifname,
+                                     enum dhcp6c_ia_mode_e ia_mode,
+                                     bool request_pd,
+                                     uint16_t opt[], int cnt)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c;
+  struct ifreq ifr;
+  size_t client_id_len;
+  int val = 1;
+  uint16_t oro[] =
+  {
+    htons(DHCPV6_OPT_DNS_SERVERS),
+    htons(DHCPV6_OPT_DNS_DOMAIN),
+    htons(DHCPV6_OPT_NTP_SERVER),
+    htons(DHCPV6_OPT_SIP_SERVER_A),
+    htons(DHCPV6_OPT_SIP_SERVER_D)
+  };
+
+  struct sockaddr_in6 client_addr =
+  {
+    .sin6_family = AF_INET6,
+    .sin6_port = htons(DHCPV6_CLIENT_PORT),
+    .sin6_flowinfo = 0
+  };
+
+  pdhcp6c = malloc(sizeof(struct dhcp6c_state_s));
+  if (!pdhcp6c)
+    {
+      return NULL;
+    }
+
+  memset(pdhcp6c, 0, sizeof(*pdhcp6c));
+  pdhcp6c->urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY);
+  if (pdhcp6c->urandom_fd < 0)
+    {
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->sockfd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+  if (pdhcp6c->sockfd < 0)
+    {
+      close(pdhcp6c->urandom_fd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  /* Detect interface */
+
+  strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+  if (ioctl(pdhcp6c->sockfd, SIOCGIFINDEX, &ifr))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->ifindex = ifr.ifr_ifindex;
+
+  /* Create client DUID */
+
+  dhcp6c_get_state(pdhcp6c, STATE_CLIENT_ID, &client_id_len);
+  if (client_id_len == 0)
+    {
+      uint8_t duid[14] =
+      {
+        0, DHCPV6_OPT_CLIENTID, 0, 10, 0,
+        DHCPV6_DUID_LLADDR, 0, 1
+      };
+
+      uint8_t zero[ETHER_ADDR_LEN];
+      struct ifreq ifs[100];
+      FAR struct ifreq *ifp;
+      FAR struct ifreq *ifend;
+      struct ifconf ifc;
+
+      ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+      memcpy(&duid[8], ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+      memset(zero, 0, sizeof(zero));
+      ifc.ifc_req = ifs;
+      ifc.ifc_len = sizeof(ifs);
+
+      if (!memcmp(&duid[8], zero, ETHER_ADDR_LEN) &&
+          ioctl(pdhcp6c->sockfd, SIOCGIFCONF, &ifc) >= 0)
+        {
+          /* If our interface doesn't have an address... */
+
+          ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+          for (ifp = ifc.ifc_req; ifp < ifend &&
+               !memcmp(&duid[8], zero, 6); ifp++)
+            {
+              memcpy(ifr.ifr_name, ifp->ifr_name,
+                     sizeof(ifr.ifr_name));
+              ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+              memcpy(&duid[8], ifr.ifr_hwaddr.sa_data,
+                     ETHER_ADDR_LEN);
+            }
+        }
+
+      dhcp6c_add_state(pdhcp6c, STATE_CLIENT_ID, duid, sizeof(duid));
+    }
+
+  /* Create ORO */
+
+  dhcp6c_add_state(pdhcp6c, STATE_ORO, oro, sizeof(oro));
+
+  /* Configure IPv6-options */
+
+  setsockopt(pdhcp6c->sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, UDP_BINDTODEVICE, ifname,
+             strlen(ifname));
+  if (bind(pdhcp6c->sockfd, (struct sockaddr *)&client_addr,
+           sizeof(client_addr)))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->thread = 0;
+  pdhcp6c->cancel = false;
+  pdhcp6c->t1 = 0;
+  pdhcp6c->t2 = 0;
+  pdhcp6c->t3 = 0;
+  pdhcp6c->request_prefix = request_pd;
+  switch (ia_mode)
+    {
+      case IA_MODE_NONE:
+      case IA_MODE_TRY:
+      case IA_MODE_FORCE:
+        break;
+      default:
+        ia_mode = IA_MODE_TRY;
+        break;
+    }
+
+  pdhcp6c->ia_mode = ia_mode;
+  pdhcp6c->accept_reconfig = false;
+  if (opt && cnt > 0)
+    {
+      uint16_t opttype;
+      for (int i = 0; i < cnt; i++)
+        {
+          opttype = htons(opt[i]);
+          dhcp6c_add_state(pdhcp6c, STATE_ORO, &opttype, 2);
+        }
+    }
+
+  return pdhcp6c;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR void *dhcp6c_open(FAR const char *interface)
+{
+  return dhcp6c_precise_open(interface, IA_MODE_TRY, true, NULL, 0);
+}
+
+int dhcp6c_request(FAR void *handle, FAR struct dhcp6c_state *presult)
+{
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  ret = dhcp6c_single_request(handle);
+  if (ret >= 0)
+    {
+      dhcp6c_get_result(handle, presult);
+      return OK;
+    }
+  else
+    {
+      return ERROR;
+    }
+}
+
+int dhcp6c_request_async(FAR void *handle, dhcp6c_callback_t callback)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  if (pdhcp6c->thread)
+    {
+      nerr("DHCP6C thread already running\n");
+      return ERROR;
+    }
+
+  pdhcp6c->callback = callback;
+  ret = pthread_create(&pdhcp6c->thread, NULL, dhcp6c_run, pdhcp6c);
+  if (ret != 0)
+    {
+      nerr("Failed to start the DHCP6C thread\n");
+      return ERROR;
+    }
+
+  return OK;
+}
+
+void dhcp6c_cancel(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  sighandler_t old;
+  int ret;
+
+  if (pdhcp6c)
+    {
+      pdhcp6c->cancel = true;
+      if (pdhcp6c->thread)
+        {
+          old = signal(SIGQUIT, SIG_IGN);
+
+          /* Signal the dhcp6c_run */
+
+          ret = pthread_kill(pdhcp6c->thread, SIGQUIT);
+          if (ret != 0)
+            {
+              nerr("pthread_kill DHCP6C thread\n");
+            }
+
+          /* Wait for the end of dhcp6c_run */
+
+          ret = pthread_join(pdhcp6c->thread, NULL);
+          if (ret != 0)
+            {
+              nerr("pthread_join DHCP6C thread\n");
+            }
+
+          pdhcp6c->thread = 0;
+          signal(SIGQUIT, old);
+        }
+    }
+}
+
+void dhcp6c_close(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  if (pdhcp6c)
+    {
+      dhcp6c_cancel(pdhcp6c);
+      if (pdhcp6c->urandom_fd)

Review comment:
       ```suggestion
         if (pdhcp6c->urandom_fd > 0)
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] pkarashchenko commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r828999819



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for ((_o) = (FAR uint8_t *)(start); (_o) + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = (_o)[0] << 8 | (_o)[1]) && ((odata) = (FAR void *)&(_o)[4]) &&\
+        ((olen) = (_o)[2] << 8 | (_o)[3]) + (odata) <= (FAR uint8_t *)(end); \
+        (_o) += 4 + ((_o)[2] << 8 | (_o)[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);

Review comment:
       Please add a check `if (buf == NULL)`

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for ((_o) = (FAR uint8_t *)(start); (_o) + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = (_o)[0] << 8 | (_o)[1]) && ((odata) = (FAR void *)&(_o)[4]) &&\
+        ((olen) = (_o)[2] << 8 | (_o)[3]) + (odata) <= (FAR uint8_t *)(end); \
+        (_o) += 4 + ((_o)[2] << 8 | (_o)[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (c == NULL || c->preference < cand[i].preference)
+        {
+          c = &cand[i];
+        }
+    }
+
+  if (retry && pdhcp6c->ia_mode == IA_MODE_TRY)
+    {
+      /* We give it a second try without the IA_NA */
+
+      pdhcp6c->ia_mode = IA_MODE_NONE;
+      return dhcp6c_command(handle, DHCPV6_MSG_SOLICIT);
+    }
+
+  if (c != NULL)
+    {
+      uint16_t hdr[2] =
+      {
+        htons(DHCPV6_OPT_SERVERID), htons(c->duid_len)
+      };
+
+      dhcp6c_add_state(handle, STATE_SERVER_ID, hdr, sizeof(hdr));
+      dhcp6c_add_state(handle, STATE_SERVER_ID, c->duid, c->duid_len);
+      pdhcp6c->accept_reconfig = c->wants_reconfigure;
+    }
+
+  dhcp6c_clear_state(handle, STATE_SERVER_CAND);
+
+  if (c == NULL)
+    {
+      return -1;
+    }
+  else if (pdhcp6c->request_prefix || pdhcp6c->ia_mode != IA_MODE_NONE)
+    {
+      return DHCPV6_STATEFUL;
+    }
+  else
+    {
+      return DHCPV6_STATELESS;
+    }
+}
+
+static time_t dhcp6c_parse_ia(FAR void *handle, FAR void *opt, FAR void *end)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint32_t timeout = UINT32_MAX;
+  uint16_t otype;
+  uint16_t olen;
+  uint16_t stype;
+  uint16_t slen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  FAR uint8_t *sdata;
+
+  /* Update address IA */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_IA_PREFIX)
+        {
+          FAR struct dhcpv6_ia_prefix_s *prefix = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_prefix_s *local = NULL;
+          uint32_t valid;
+          uint32_t pref;
+          size_t pd_len;
+          FAR uint8_t *pd;
+          FAR uint8_t *ite1;
+
+          if (olen + 4u < sizeof(*prefix))
+            {
+              continue;
+            }
+
+          olen = sizeof(*prefix);
+          valid = ntohl(prefix->valid);
+          pref = ntohl(prefix->preferred);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          pd = dhcp6c_get_state(handle, STATE_IA_PD, &pd_len);
+          dhcpv6_for_each_option(ite1, pd, pd + pd_len,
+                                 stype, slen, sdata)
+            {
+              if (memcmp(sdata + 8, odata + 8,
+                         sizeof(local->addr) + 1) == 0)

Review comment:
       Why we have `+1` here?

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for ((_o) = (FAR uint8_t *)(start); (_o) + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = (_o)[0] << 8 | (_o)[1]) && ((odata) = (FAR void *)&(_o)[4]) &&\
+        ((olen) = (_o)[2] << 8 | (_o)[3]) + (odata) <= (FAR uint8_t *)(end); \
+        (_o) += 4 + ((_o)[2] << 8 | (_o)[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (c == NULL || c->preference < cand[i].preference)
+        {
+          c = &cand[i];
+        }
+    }
+
+  if (retry && pdhcp6c->ia_mode == IA_MODE_TRY)
+    {
+      /* We give it a second try without the IA_NA */
+
+      pdhcp6c->ia_mode = IA_MODE_NONE;
+      return dhcp6c_command(handle, DHCPV6_MSG_SOLICIT);
+    }
+
+  if (c != NULL)
+    {
+      uint16_t hdr[2] =
+      {
+        htons(DHCPV6_OPT_SERVERID), htons(c->duid_len)
+      };
+
+      dhcp6c_add_state(handle, STATE_SERVER_ID, hdr, sizeof(hdr));
+      dhcp6c_add_state(handle, STATE_SERVER_ID, c->duid, c->duid_len);
+      pdhcp6c->accept_reconfig = c->wants_reconfigure;
+    }
+
+  dhcp6c_clear_state(handle, STATE_SERVER_CAND);
+
+  if (c == NULL)
+    {
+      return -1;
+    }
+  else if (pdhcp6c->request_prefix || pdhcp6c->ia_mode != IA_MODE_NONE)
+    {
+      return DHCPV6_STATEFUL;
+    }
+  else
+    {
+      return DHCPV6_STATELESS;
+    }
+}
+
+static time_t dhcp6c_parse_ia(FAR void *handle, FAR void *opt, FAR void *end)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint32_t timeout = UINT32_MAX;
+  uint16_t otype;
+  uint16_t olen;
+  uint16_t stype;
+  uint16_t slen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  FAR uint8_t *sdata;
+
+  /* Update address IA */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_IA_PREFIX)
+        {
+          FAR struct dhcpv6_ia_prefix_s *prefix = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_prefix_s *local = NULL;
+          uint32_t valid;
+          uint32_t pref;
+          size_t pd_len;
+          FAR uint8_t *pd;
+          FAR uint8_t *ite1;
+
+          if (olen + 4u < sizeof(*prefix))
+            {
+              continue;
+            }
+
+          olen = sizeof(*prefix);
+          valid = ntohl(prefix->valid);
+          pref = ntohl(prefix->preferred);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          pd = dhcp6c_get_state(handle, STATE_IA_PD, &pd_len);
+          dhcpv6_for_each_option(ite1, pd, pd + pd_len,
+                                 stype, slen, sdata)
+            {
+              if (memcmp(sdata + 8, odata + 8,
+                         sizeof(local->addr) + 1) == 0)
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local != NULL)
+            {
+              local->preferred = prefix->preferred;
+              local->valid = prefix->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_PD, prefix, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+      else if (otype == DHCPV6_OPT_IA_ADDR)
+        {
+          FAR struct dhcpv6_ia_addr_s *addr = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_addr_s *local = NULL;
+          uint32_t pref;
+          uint32_t valid;
+          size_t na_len;
+          FAR uint8_t *na;
+          FAR uint8_t *ite1;
+
+          if (olen + 4u < sizeof(*addr))
+            {
+              continue;
+            }
+
+          olen = sizeof(*addr);
+          pref = ntohl(addr->preferred);
+          valid = ntohl(addr->valid);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          na = dhcp6c_get_state(handle, STATE_IA_NA, &na_len);
+          dhcpv6_for_each_option(ite1, na, na + na_len,
+                                 stype, slen, sdata)
+            {
+              if (memcmp(sdata, odata, sizeof(local->addr)) == 0)
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local != NULL)
+            {
+              local->preferred = addr->preferred;
+              local->valid = addr->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_NA, addr, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+    }
+
+  return timeout;
+}
+
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  bool have_update = false;
+  size_t ia_na_len;
+  size_t ia_pd_len;
+  size_t dns_len;
+  size_t search_len;
+  size_t sntp_ip_len;
+  size_t sntp_dns_len;
+  size_t sip_ip_len;
+  size_t sip_fqdn_len;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  FAR uint8_t *ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  FAR uint8_t *ia_end;
+  pdhcp6c->t1 = UINT32_MAX;
+  pdhcp6c->t2 = UINT32_MAX;
+  pdhcp6c->t3 = UINT32_MAX;
+
+  dhcp6c_get_state(handle, STATE_DNS, &dns_len);
+  dhcp6c_get_state(handle, STATE_SEARCH, &search_len);
+  dhcp6c_get_state(handle, STATE_SNTP_IP, &sntp_ip_len);
+  dhcp6c_get_state(handle, STATE_SNTP_FQDN, &sntp_dns_len);
+  dhcp6c_get_state(handle, STATE_SIP_IP, &sip_ip_len);
+  dhcp6c_get_state(handle, STATE_SIP_FQDN, &sip_fqdn_len);
+
+  /* Decrease valid and preferred lifetime of prefixes */
+
+  dhcpv6_for_each_option(ite0, ia_pd, ia_pd + ia_pd_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Decrease valid and preferred lifetime of addresses */
+
+  dhcpv6_for_each_option(ite0, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Parse and find all matching IAs */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)
+           && olen > sizeof(struct dhcpv6_ia_hdr_s))
+        {
+          FAR struct dhcpv6_ia_hdr_s *ia_hdr = (FAR void *)(odata - 4);
+          time_t l_t1 = ntohl(ia_hdr->t1);
+          time_t l_t2 = ntohl(ia_hdr->t2);
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *sdata;
+          time_t n;
+
+          /* Test ID and T1-T2 validity */
+
+          if (ia_hdr->iaid != 1 || l_t2 < l_t1)
+            {
+              continue;
+            }
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, &ia_hdr[1], odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (stype == DHCPV6_OPT_STATUS && slen >= 2 &&
+                  (sdata[0] || sdata[1]))
+                {
+                  continue;
+                }
+            }
+
+          /* Update times */
+
+          if (l_t1 > 0 && pdhcp6c->t1 > l_t1)
+            {
+              pdhcp6c->t1 = l_t1;
+            }
+
+          if (l_t2 > 0 && pdhcp6c->t2 > l_t2)
+            {
+              pdhcp6c->t2 = l_t2;
+            }
+
+          /* Always report update in case we have IA_PDs so that
+           * the state-script is called with updated times
+           */
+
+          if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+            {
+              have_update = true;
+            }
+
+          n = dhcp6c_parse_ia(handle, &ia_hdr[1], odata + olen);
+          if (n < pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = n;
+            }
+
+          if (n < pdhcp6c->t2)
+            {
+              pdhcp6c->t2 = n;
+            }
+
+          if (n < pdhcp6c->t3)
+            {
+              pdhcp6c->t3 = n;
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_SERVERS)
+        {
+          if (olen % 16 == 0)
+            {
+              dhcp6c_add_state(handle, STATE_DNS, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_DOMAIN)
+        {
+          dhcp6c_add_state(handle, STATE_SEARCH, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_NTP_SERVER)
+        {
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *sdata;
+          FAR uint8_t *ite1;
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, odata, odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (slen == 16 &&
+                  (stype == NTP_MC_ADDR || stype == NTP_SRV_ADDR))
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_IP, sdata, slen);
+                }
+              else if (slen > 0 && stype == NTP_SRV_FQDN)
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_FQDN, sdata, slen);
+                }
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_A)
+        {
+          if (olen == 16)
+            {
+              dhcp6c_add_state(handle, STATE_SIP_IP, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_D)
+        {
+          dhcp6c_add_state(handle, STATE_SIP_FQDN, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4)
+        {
+          uint32_t refresh = ntohl(*((FAR uint32_t *)odata));
+          if (refresh < (uint32_t)pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = refresh;
+            }
+        }
+      else if (otype != DHCPV6_OPT_CLIENTID && otype != DHCPV6_OPT_SERVERID)
+        {
+          dhcp6c_add_state(handle, STATE_CUSTOM_OPTS, (odata - 4), olen + 4);
+        }
+    }
+
+  if (opt != NULL)
+    {
+      size_t new_ia_pd_len;
+      size_t new_ia_na_len;
+      have_update |= dhcp6c_commit_state(handle, STATE_DNS, dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SEARCH, search_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_IP,
+                                         sntp_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_FQDN,
+                                         sntp_dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_IP, sip_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_FQDN,
+                                         sip_fqdn_len);
+      dhcp6c_get_state(handle, STATE_IA_PD, &new_ia_pd_len);
+      dhcp6c_get_state(handle, STATE_IA_NA, &new_ia_na_len);
+      have_update |= (new_ia_pd_len != ia_pd_len) ||
+                     (new_ia_na_len != ia_na_len);
+    }
+
+  /* Delete prefixes with 0 valid-time */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  ia_end = ia_pd + ia_pd_len;
+  dhcpv6_for_each_option(ite0, ia_pd, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_pd + dhcp6c_remove_state(handle, STATE_IA_PD,
+                   (FAR uint8_t *)p - ia_pd, olen + 4);
+          have_update = true;
+        }
+    }
+
+  /* Delete addresses with 0 valid-time */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  ia_end = ia_na + ia_na_len;
+  dhcpv6_for_each_option(ite0, ia_na, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_na + dhcp6c_remove_state(handle, STATE_IA_NA,
+                   (FAR uint8_t *)p - ia_na, olen + 4);
+          have_update = true;
+        }
+    }
+
+  return have_update;
+}
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *odata;
+  FAR uint8_t *ite;
+  uint8_t msg = DHCPV6_MSG_RENEW;
+
+  /* TODO: should verify the reconfigure message */
+
+  dhcpv6_for_each_option(ite, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1 &&
+          (odata[0] == DHCPV6_MSG_RENEW ||
+           odata[0] == DHCPV6_MSG_INFO_REQ))
+        {
+          msg = odata[0];
+        }
+    }
+
+  pdhcp6c->t1 -= elapsed;
+  pdhcp6c->t2 -= elapsed;
+  pdhcp6c->t3 -= elapsed;
+
+  if (pdhcp6c->t1 < 0)
+    {
+      pdhcp6c->t1 = 0;
+    }
+
+  if (pdhcp6c->t2 < 0)
+    {
+      pdhcp6c->t2 = 0;
+    }
+
+  if (pdhcp6c->t3 < 0)
+    {
+      pdhcp6c->t3 = 0;
+    }
+
+  dhcp6c_handle_reply(handle, DHCPV6_MSG_UNKNOWN, NULL, NULL, elapsed);
+
+  return msg;
+}
+
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed)
+{
+  dhcp6c_handle_advert(handle, orig, opt, end, elapsed);
+  if (dhcp6c_commit_advert(handle, elapsed) < 0)
+    {
+      return -1;
+    }
+
+  return dhcp6c_handle_reply(handle, orig, opt, end, elapsed);
+}
+
+static int dhcp6c_single_request(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  FAR const char *process = NULL;
+  enum dhcpv6_mode_e mode;
+  enum dhcpv6_msg_e type;
+  int ret = -1;
+
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_CAND);
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_CUSTOM_OPTS);
+  ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_SOLICIT);
+  if (ret < 0)
+    {
+      return -1;
+    }
+  else if (ret == DHCPV6_STATELESS)
+    {
+      mode = DHCPV6_STATELESS;
+      type = DHCPV6_MSG_INFO_REQ;
+      process = "informed";
+    }
+  else
+    {
+      mode = DHCPV6_STATEFUL;
+      type = DHCPV6_MSG_REQUEST;
+      process = "bound";
+    }
+
+  ret = dhcp6c_command(pdhcp6c, type);
+  if (ret >= 0)
+    {
+      ret = mode;
+      dhcp6c_switch_process(pdhcp6c, process);
+    }
+
+  return ret;
+}
+
+static int dhcp6c_lease(FAR void *args, uint8_t type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  enum dhcpv6_mode_e mode = (enum dhcpv6_mode_e)type;
+  size_t ia_pd_len;
+  size_t ia_na_len;
+  size_t ia_pd_new;
+  size_t ia_na_new;
+  size_t server_id_len;
+  int ret = -1;
+
+  if (mode == DHCPV6_STATELESS)
+    {
+      /* Stateless mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Wait for T1 to expire or until we get a reconfigure */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          /* Information-Request */
+
+          ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_INFO_REQ);
+          if (ret < 0)
+            {
+              nerr("DHCPV6_MSG_INFO_REQ error\n");
+              break;
+            }
+          else
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+        }
+    }
+  else
+    {
+      /* Stateful mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Renew Cycle
+           * Wait for T1 to expire or until we get a reconfigure
+           */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+          dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+
+          /* If we have any IAs, send renew, otherwise request */
+
+          if (ia_pd_len == 0 && ia_na_len == 0)
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REQUEST);
+          else
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_RENEW);
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          if (ret >= 0)
+            {
+              /* Publish updates */
+
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+          else
+            {
+              /* Remove binding */
+
+              dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+
+              /* If we have IAs, try rebind otherwise restart */
+
+              ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REBIND);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_new);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_new);
+
+              /* We lost all our IAs, restart */
+
+              if (ret < 0 || (ia_pd_new == 0 && ia_pd_len) ||
+                  (ia_na_new == 0 && ia_na_len))
+                {
+                  break;
+                }
+              else if (ret >= 0)
+                {
+                  dhcp6c_switch_process(pdhcp6c, "rebound");
+                }
+            }
+        }
+    }
+
+  dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+  dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+  dhcp6c_get_state(pdhcp6c, STATE_SERVER_ID, &server_id_len);
+
+  /* Add all prefixes to lost prefixes */
+
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_switch_process(pdhcp6c, "unbound");
+
+  /* Remove assigned addresses */
+
+  if (ia_na_len > 0)
+    {
+      dhcp6c_remove_addrs(pdhcp6c);
+    }
+
+  if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0))
+    {
+      dhcp6c_command(pdhcp6c, DHCPV6_MSG_RELEASE);
+    }
+
+  return ret;
+}
+
+static FAR void *dhcp6c_run(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  int ret;
+
+  while (!pdhcp6c->cancel)
+    {
+      ret = dhcp6c_single_request(pdhcp6c);
+      if (ret > 0)
+        {
+          dhcp6c_lease(pdhcp6c, ret);
+        }
+    }
+
+  return 0;
+}
+
+static FAR void *dhcp6c_precise_open(FAR const char *ifname,
+                                     enum dhcp6c_ia_mode_e ia_mode,
+                                     bool request_pd,
+                                     uint16_t opt[], int cnt)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c;
+  struct ifreq ifr;
+  size_t client_id_len;
+  int val = 1;
+  uint16_t oro[] =
+  {
+    htons(DHCPV6_OPT_DNS_SERVERS),
+    htons(DHCPV6_OPT_DNS_DOMAIN),
+    htons(DHCPV6_OPT_NTP_SERVER),
+    htons(DHCPV6_OPT_SIP_SERVER_A),
+    htons(DHCPV6_OPT_SIP_SERVER_D)
+  };
+
+  struct sockaddr_in6 client_addr =
+  {
+    .sin6_family = AF_INET6,
+    .sin6_port = htons(DHCPV6_CLIENT_PORT),
+    .sin6_flowinfo = 0

Review comment:
       C89 incompatible




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] xiaoxiang781216 commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r827976743



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < (ssize_t)sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)))
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(&rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4U == client_id_len) &&
+                        !memcmp((odata - 4), client_id, client_id_len);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4U == server_id_len) &&
+                        !memcmp((odata - 4), server_id, server_id_len);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint8_t buf[1536];
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      return -1;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * 1000 +
+              dhcp6c_rand_delay(handle, retx->init_timeo * 1000)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * 1000)
+        {
+          rto = retx->max_timeo * 1000 +
+                dhcp6c_rand_delay(handle, retx->max_timeo * 1000);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * 1000)
+        {
+          round_end = timeout * 1000 + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / 1000, (t % 1000) * 1000

Review comment:
       @pkarashchenko since xSEC_PER_SEC is NuttX specific macro, it's better to avoid use them to improve dhcpv6 portability.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] coffee809721232 commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
coffee809721232 commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r829062191



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for ((_o) = (FAR uint8_t *)(start); (_o) + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = (_o)[0] << 8 | (_o)[1]) && ((odata) = (FAR void *)&(_o)[4]) &&\
+        ((olen) = (_o)[2] << 8 | (_o)[3]) + (odata) <= (FAR uint8_t *)(end); \
+        (_o) += 4 + ((_o)[2] << 8 | (_o)[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (c == NULL || c->preference < cand[i].preference)
+        {
+          c = &cand[i];
+        }
+    }
+
+  if (retry && pdhcp6c->ia_mode == IA_MODE_TRY)
+    {
+      /* We give it a second try without the IA_NA */
+
+      pdhcp6c->ia_mode = IA_MODE_NONE;
+      return dhcp6c_command(handle, DHCPV6_MSG_SOLICIT);
+    }
+
+  if (c != NULL)
+    {
+      uint16_t hdr[2] =
+      {
+        htons(DHCPV6_OPT_SERVERID), htons(c->duid_len)
+      };
+
+      dhcp6c_add_state(handle, STATE_SERVER_ID, hdr, sizeof(hdr));
+      dhcp6c_add_state(handle, STATE_SERVER_ID, c->duid, c->duid_len);
+      pdhcp6c->accept_reconfig = c->wants_reconfigure;
+    }
+
+  dhcp6c_clear_state(handle, STATE_SERVER_CAND);
+
+  if (c == NULL)
+    {
+      return -1;
+    }
+  else if (pdhcp6c->request_prefix || pdhcp6c->ia_mode != IA_MODE_NONE)
+    {
+      return DHCPV6_STATEFUL;
+    }
+  else
+    {
+      return DHCPV6_STATELESS;
+    }
+}
+
+static time_t dhcp6c_parse_ia(FAR void *handle, FAR void *opt, FAR void *end)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint32_t timeout = UINT32_MAX;
+  uint16_t otype;
+  uint16_t olen;
+  uint16_t stype;
+  uint16_t slen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  FAR uint8_t *sdata;
+
+  /* Update address IA */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_IA_PREFIX)
+        {
+          FAR struct dhcpv6_ia_prefix_s *prefix = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_prefix_s *local = NULL;
+          uint32_t valid;
+          uint32_t pref;
+          size_t pd_len;
+          FAR uint8_t *pd;
+          FAR uint8_t *ite1;
+
+          if (olen + 4u < sizeof(*prefix))
+            {
+              continue;
+            }
+
+          olen = sizeof(*prefix);
+          valid = ntohl(prefix->valid);
+          pref = ntohl(prefix->preferred);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          pd = dhcp6c_get_state(handle, STATE_IA_PD, &pd_len);
+          dhcpv6_for_each_option(ite1, pd, pd + pd_len,
+                                 stype, slen, sdata)
+            {
+              if (memcmp(sdata + 8, odata + 8,
+                         sizeof(local->addr) + 1) == 0)

Review comment:
       prefix address including length and value.
   length occupies one byte




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] xiaoxiang781216 commented on pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#issuecomment-1073029272


   @coffee809721232 could you report the stack size on some real device?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] pkarashchenko commented on pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#issuecomment-1070795976


   Do we have any profiling on a stack usage?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] pkarashchenko commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r828278753



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for (_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (!c || c->preference < cand[i].preference)

Review comment:
       ```suggestion
         if (c == NULL || c->preference < cand[i].preference)
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for (_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (!c || c->preference < cand[i].preference)
+        {
+          c = &cand[i];
+        }
+    }
+
+  if (retry && pdhcp6c->ia_mode == IA_MODE_TRY)
+    {
+      /* We give it a second try without the IA_NA */
+
+      pdhcp6c->ia_mode = IA_MODE_NONE;
+      return dhcp6c_command(handle, DHCPV6_MSG_SOLICIT);
+    }
+
+  if (c)
+    {
+      uint16_t hdr[2] =
+      {
+        htons(DHCPV6_OPT_SERVERID), htons(c->duid_len)
+      };
+
+      dhcp6c_add_state(handle, STATE_SERVER_ID, hdr, sizeof(hdr));
+      dhcp6c_add_state(handle, STATE_SERVER_ID, c->duid, c->duid_len);
+      pdhcp6c->accept_reconfig = c->wants_reconfigure;
+    }
+
+  dhcp6c_clear_state(handle, STATE_SERVER_CAND);
+
+  if (!c)
+    {
+      return -1;
+    }
+  else if (pdhcp6c->request_prefix || pdhcp6c->ia_mode != IA_MODE_NONE)
+    {
+      return DHCPV6_STATEFUL;
+    }
+  else
+    {
+      return DHCPV6_STATELESS;
+    }
+}
+
+static time_t dhcp6c_parse_ia(FAR void *handle, FAR void *opt, FAR void *end)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint32_t timeout = UINT32_MAX;
+  uint16_t otype;
+  uint16_t olen;
+  uint16_t stype;
+  uint16_t slen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  FAR uint8_t *sdata;
+
+  /* Update address IA */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_IA_PREFIX)
+        {
+          FAR struct dhcpv6_ia_prefix_s *prefix = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_prefix_s *local = NULL;
+          uint32_t valid;
+          uint32_t pref;
+          size_t pd_len;
+          FAR uint8_t *pd;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*prefix))
+            {
+              continue;
+            }
+
+          olen = sizeof(*prefix);
+          valid = ntohl(prefix->valid);
+          pref = ntohl(prefix->preferred);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          pd = dhcp6c_get_state(handle, STATE_IA_PD, &pd_len);
+          dhcpv6_for_each_option(ite1, pd, pd + pd_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata + 8, odata + 8,
+                          sizeof(local->addr) + 1))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = prefix->preferred;
+              local->valid = prefix->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_PD, prefix, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+      else if (otype == DHCPV6_OPT_IA_ADDR)
+        {
+          FAR struct dhcpv6_ia_addr_s *addr = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_addr_s *local = NULL;
+          uint32_t pref;
+          uint32_t valid;
+          size_t na_len;
+          FAR uint8_t *na;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*addr))
+            {
+              continue;
+            }
+
+          olen = sizeof(*addr);
+          pref = ntohl(addr->preferred);
+          valid = ntohl(addr->valid);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          na = dhcp6c_get_state(handle, STATE_IA_NA, &na_len);
+          dhcpv6_for_each_option(ite1, na, na + na_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata, odata, sizeof(local->addr)))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = addr->preferred;
+              local->valid = addr->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_NA, addr, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+    }
+
+  return timeout;
+}
+
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  bool have_update = false;
+  size_t ia_na_len;
+  size_t ia_pd_len;
+  size_t dns_len;
+  size_t search_len;
+  size_t sntp_ip_len;
+  size_t sntp_dns_len;
+  size_t sip_ip_len;
+  size_t sip_fqdn_len;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  FAR uint8_t *ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  FAR uint8_t *ia_end;
+  pdhcp6c->t1 = UINT32_MAX;
+  pdhcp6c->t2 = UINT32_MAX;
+  pdhcp6c->t3 = UINT32_MAX;
+
+  dhcp6c_get_state(handle, STATE_DNS, &dns_len);
+  dhcp6c_get_state(handle, STATE_SEARCH, &search_len);
+  dhcp6c_get_state(handle, STATE_SNTP_IP, &sntp_ip_len);
+  dhcp6c_get_state(handle, STATE_SNTP_FQDN, &sntp_dns_len);
+  dhcp6c_get_state(handle, STATE_SIP_IP, &sip_ip_len);
+  dhcp6c_get_state(handle, STATE_SIP_FQDN, &sip_fqdn_len);
+
+  /* Decrease valid and preferred lifetime of prefixes */
+
+  dhcpv6_for_each_option(ite0, ia_pd, ia_pd + ia_pd_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Decrease valid and preferred lifetime of addresses */
+
+  dhcpv6_for_each_option(ite0, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Parse and find all matching IAs */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)
+           && olen > sizeof(struct dhcpv6_ia_hdr_s))
+        {
+          FAR struct dhcpv6_ia_hdr_s *ia_hdr = (FAR void *)(odata - 4);
+          time_t l_t1 = ntohl(ia_hdr->t1);
+          time_t l_t2 = ntohl(ia_hdr->t2);
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *sdata;
+          time_t n;
+
+          /* Test ID and T1-T2 validity */
+
+          if (ia_hdr->iaid != 1 || l_t2 < l_t1)
+            {
+              continue;
+            }
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, &ia_hdr[1], odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (stype == DHCPV6_OPT_STATUS && slen >= 2 &&
+                  (sdata[0] || sdata[1]))
+                {
+                  continue;
+                }
+            }
+
+          /* Update times */
+
+          if (l_t1 > 0 && pdhcp6c->t1 > l_t1)
+            {
+              pdhcp6c->t1 = l_t1;
+            }
+
+          if (l_t2 > 0 && pdhcp6c->t2 > l_t2)
+            {
+              pdhcp6c->t2 = l_t2;
+            }
+
+          /* Always report update in case we have IA_PDs so that
+           * the state-script is called with updated times
+           */
+
+          if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+            {
+              have_update = true;
+            }
+
+          n = dhcp6c_parse_ia(handle, &ia_hdr[1], odata + olen);
+          if (n < pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = n;
+            }
+
+          if (n < pdhcp6c->t2)
+            {
+              pdhcp6c->t2 = n;
+            }
+
+          if (n < pdhcp6c->t3)
+            {
+              pdhcp6c->t3 = n;
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_SERVERS)
+        {
+          if (olen % 16 == 0)
+            {
+              dhcp6c_add_state(handle, STATE_DNS, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_DOMAIN)
+        {
+          dhcp6c_add_state(handle, STATE_SEARCH, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_NTP_SERVER)
+        {
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *sdata;
+          FAR uint8_t *ite1;
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, odata, odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (slen == 16 &&
+                  (stype == NTP_MC_ADDR || stype == NTP_SRV_ADDR))
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_IP, sdata, slen);
+                }
+              else if (slen > 0 && stype == NTP_SRV_FQDN)
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_FQDN, sdata, slen);
+                }
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_A)
+        {
+          if (olen == 16)
+            {
+              dhcp6c_add_state(handle, STATE_SIP_IP, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_D)
+        {
+          dhcp6c_add_state(handle, STATE_SIP_FQDN, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4)
+        {
+          uint32_t refresh = ntohl(*((FAR uint32_t *)odata));
+          if (refresh < (uint32_t)pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = refresh;
+            }
+        }
+      else if (otype != DHCPV6_OPT_CLIENTID && otype != DHCPV6_OPT_SERVERID)
+        {
+          dhcp6c_add_state(handle, STATE_CUSTOM_OPTS, (odata - 4), olen + 4);
+        }
+    }
+
+  if (opt)
+    {
+      size_t new_ia_pd_len;
+      size_t new_ia_na_len;
+      have_update |= dhcp6c_commit_state(handle, STATE_DNS, dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SEARCH, search_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_IP,
+                                         sntp_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_FQDN,
+                                         sntp_dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_IP, sip_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_FQDN,
+                                         sip_fqdn_len);
+      dhcp6c_get_state(handle, STATE_IA_PD, &new_ia_pd_len);
+      dhcp6c_get_state(handle, STATE_IA_NA, &new_ia_na_len);
+      have_update |= (new_ia_pd_len != ia_pd_len) ||
+                     (new_ia_na_len != ia_na_len);
+    }
+
+  /* Delete prefixes with 0 valid-time */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  ia_end = ia_pd + ia_pd_len;
+  dhcpv6_for_each_option(ite0, ia_pd, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_pd + dhcp6c_remove_state(handle, STATE_IA_PD,
+                   (FAR uint8_t *)p - ia_pd, olen + 4);
+          have_update = true;
+        }
+    }
+
+  /* Delete addresses with 0 valid-time */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  ia_end = ia_na + ia_na_len;
+  dhcpv6_for_each_option(ite0, ia_na, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_na + dhcp6c_remove_state(handle, STATE_IA_NA,
+                   (FAR uint8_t *)p - ia_na, olen + 4);
+          have_update = true;
+        }
+    }
+
+  return have_update;
+}
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *odata;
+  FAR uint8_t *ite;
+  uint8_t msg = DHCPV6_MSG_RENEW;
+
+  /* TODO: should verify the reconfigure message */
+
+  dhcpv6_for_each_option(ite, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1 &&
+          (odata[0] == DHCPV6_MSG_RENEW ||
+           odata[0] == DHCPV6_MSG_INFO_REQ))
+        {
+          msg = odata[0];
+        }
+    }
+
+  pdhcp6c->t1 -= elapsed;
+  pdhcp6c->t2 -= elapsed;
+  pdhcp6c->t3 -= elapsed;
+
+  if (pdhcp6c->t1 < 0)
+    {
+      pdhcp6c->t1 = 0;
+    }
+
+  if (pdhcp6c->t2 < 0)
+    {
+      pdhcp6c->t2 = 0;
+    }
+
+  if (pdhcp6c->t3 < 0)
+    {
+      pdhcp6c->t3 = 0;
+    }
+
+  dhcp6c_handle_reply(handle, DHCPV6_MSG_UNKNOWN, NULL, NULL, elapsed);
+
+  return msg;
+}
+
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed)
+{
+  dhcp6c_handle_advert(handle, orig, opt, end, elapsed);
+  if (dhcp6c_commit_advert(handle, elapsed) < 0)
+    {
+      return -1;
+    }
+
+  return dhcp6c_handle_reply(handle, orig, opt, end, elapsed);
+}
+
+static int dhcp6c_single_request(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  FAR const char *process = NULL;
+  enum dhcpv6_mode_e mode;
+  enum dhcpv6_msg_e type;
+  int ret = -1;
+
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_CAND);
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_CUSTOM_OPTS);
+  ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_SOLICIT);
+  if (ret < 0)
+    {
+      return -1;
+    }
+  else if (ret == DHCPV6_STATELESS)
+    {
+      mode = DHCPV6_STATELESS;
+      type = DHCPV6_MSG_INFO_REQ;
+      process = "informed";
+    }
+  else
+    {
+      mode = DHCPV6_STATEFUL;
+      type = DHCPV6_MSG_REQUEST;
+      process = "bound";
+    }
+
+  ret = dhcp6c_command(pdhcp6c, type);
+  if (ret >= 0)
+    {
+      ret = mode;
+      dhcp6c_switch_process(pdhcp6c, process);
+    }
+
+  return ret;
+}
+
+static int dhcp6c_lease(FAR void *args, uint8_t type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  enum dhcpv6_mode_e mode = (enum dhcpv6_mode_e)type;
+  size_t ia_pd_len;
+  size_t ia_na_len;
+  size_t ia_pd_new;
+  size_t ia_na_new;
+  size_t server_id_len;
+  int ret = -1;
+
+  if (mode == DHCPV6_STATELESS)
+    {
+      /* Stateless mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Wait for T1 to expire or until we get a reconfigure */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          /* Information-Request */
+
+          ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_INFO_REQ);
+          if (ret < 0)
+            {
+              nerr("DHCPV6_MSG_INFO_REQ error\n");
+              break;
+            }
+          else
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+        }
+    }
+  else
+    {
+      /* Stateful mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Renew Cycle
+           * Wait for T1 to expire or until we get a reconfigure
+           */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+          dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+
+          /* If we have any IAs, send renew, otherwise request */
+
+          if (ia_pd_len == 0 && ia_na_len == 0)
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REQUEST);
+          else
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_RENEW);
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          if (ret >= 0)
+            {
+              /* Publish updates */
+
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+          else
+            {
+              /* Remove binding */
+
+              dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+
+              /* If we have IAs, try rebind otherwise restart */
+
+              ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REBIND);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_new);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_new);
+
+              /* We lost all our IAs, restart */
+
+              if (ret < 0 || (ia_pd_new == 0 && ia_pd_len) ||
+                  (ia_na_new == 0 && ia_na_len))
+                {
+                  break;
+                }
+              else if (ret >= 0)
+                {
+                  dhcp6c_switch_process(pdhcp6c, "rebound");
+                }
+            }
+        }
+    }
+
+  dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+  dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+  dhcp6c_get_state(pdhcp6c, STATE_SERVER_ID, &server_id_len);
+
+  /* Add all prefixes to lost prefixes */
+
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_switch_process(pdhcp6c, "unbound");
+
+  /* Remove assigned addresses */
+
+  if (ia_na_len > 0)
+    {
+      dhcp6c_remove_addrs(pdhcp6c);
+    }
+
+  if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0))
+    {
+      dhcp6c_command(pdhcp6c, DHCPV6_MSG_RELEASE);
+    }
+
+  return ret;
+}
+
+static FAR void *dhcp6c_run(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  int ret;
+
+  while (!pdhcp6c->cancel)
+    {
+      ret = dhcp6c_single_request(pdhcp6c);
+      if (ret > 0)
+        {
+          dhcp6c_lease(pdhcp6c, ret);
+        }
+    }
+
+  return 0;
+}
+
+static FAR void *dhcp6c_precise_open(FAR const char *ifname,
+                                     enum dhcp6c_ia_mode_e ia_mode,
+                                     bool request_pd,
+                                     uint16_t opt[], int cnt)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c;
+  struct ifreq ifr;
+  size_t client_id_len;
+  int val = 1;
+  uint16_t oro[] =
+  {
+    htons(DHCPV6_OPT_DNS_SERVERS),
+    htons(DHCPV6_OPT_DNS_DOMAIN),
+    htons(DHCPV6_OPT_NTP_SERVER),
+    htons(DHCPV6_OPT_SIP_SERVER_A),
+    htons(DHCPV6_OPT_SIP_SERVER_D)
+  };
+
+  struct sockaddr_in6 client_addr =
+  {
+    .sin6_family = AF_INET6,
+    .sin6_port = htons(DHCPV6_CLIENT_PORT),
+    .sin6_flowinfo = 0
+  };
+
+  pdhcp6c = malloc(sizeof(struct dhcp6c_state_s));
+  if (!pdhcp6c)
+    {
+      return NULL;
+    }
+
+  memset(pdhcp6c, 0, sizeof(*pdhcp6c));
+  pdhcp6c->urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY);
+  if (pdhcp6c->urandom_fd < 0)
+    {
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->sockfd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+  if (pdhcp6c->sockfd < 0)
+    {
+      close(pdhcp6c->urandom_fd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  /* Detect interface */
+
+  strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+  if (ioctl(pdhcp6c->sockfd, SIOCGIFINDEX, &ifr))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->ifindex = ifr.ifr_ifindex;
+
+  /* Create client DUID */
+
+  dhcp6c_get_state(pdhcp6c, STATE_CLIENT_ID, &client_id_len);
+  if (client_id_len == 0)
+    {
+      uint8_t duid[14] =
+      {
+        0, DHCPV6_OPT_CLIENTID, 0, 10, 0,
+        DHCPV6_DUID_LLADDR, 0, 1
+      };
+
+      uint8_t zero[ETHER_ADDR_LEN];
+      struct ifreq ifs[100];
+      FAR struct ifreq *ifp;
+      FAR struct ifreq *ifend;
+      struct ifconf ifc;
+
+      ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+      memcpy(&duid[8], ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+      memset(zero, 0, sizeof(zero));
+      ifc.ifc_req = ifs;
+      ifc.ifc_len = sizeof(ifs);
+
+      if (!memcmp(&duid[8], zero, ETHER_ADDR_LEN) &&
+          ioctl(pdhcp6c->sockfd, SIOCGIFCONF, &ifc) >= 0)
+        {
+          /* If our interface doesn't have an address... */
+
+          ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+          for (ifp = ifc.ifc_req; ifp < ifend &&
+               !memcmp(&duid[8], zero, 6); ifp++)
+            {
+              memcpy(ifr.ifr_name, ifp->ifr_name,
+                     sizeof(ifr.ifr_name));
+              ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+              memcpy(&duid[8], ifr.ifr_hwaddr.sa_data,
+                     ETHER_ADDR_LEN);
+            }
+        }
+
+      dhcp6c_add_state(pdhcp6c, STATE_CLIENT_ID, duid, sizeof(duid));
+    }
+
+  /* Create ORO */
+
+  dhcp6c_add_state(pdhcp6c, STATE_ORO, oro, sizeof(oro));
+
+  /* Configure IPv6-options */
+
+  setsockopt(pdhcp6c->sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, UDP_BINDTODEVICE, ifname,
+             strlen(ifname));
+  if (bind(pdhcp6c->sockfd, (struct sockaddr *)&client_addr,
+           sizeof(client_addr)))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->thread = 0;
+  pdhcp6c->cancel = false;
+  pdhcp6c->t1 = 0;
+  pdhcp6c->t2 = 0;
+  pdhcp6c->t3 = 0;
+  pdhcp6c->request_prefix = request_pd;
+  switch (ia_mode)
+    {
+      case IA_MODE_NONE:
+      case IA_MODE_TRY:
+      case IA_MODE_FORCE:
+        break;
+      default:
+        ia_mode = IA_MODE_TRY;
+        break;
+    }
+
+  pdhcp6c->ia_mode = ia_mode;
+  pdhcp6c->accept_reconfig = false;
+  if (opt && cnt > 0)
+    {
+      uint16_t opttype;
+      for (int i = 0; i < cnt; i++)
+        {
+          opttype = htons(opt[i]);
+          dhcp6c_add_state(pdhcp6c, STATE_ORO, &opttype, 2);
+        }
+    }
+
+  return pdhcp6c;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR void *dhcp6c_open(FAR const char *interface)
+{
+  return dhcp6c_precise_open(interface, IA_MODE_TRY, true, NULL, 0);
+}
+
+int dhcp6c_request(FAR void *handle, FAR struct dhcp6c_state *presult)
+{
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  ret = dhcp6c_single_request(handle);
+  if (ret >= 0)
+    {
+      dhcp6c_get_result(handle, presult);
+      return OK;
+    }
+  else
+    {
+      return ERROR;
+    }
+}
+
+int dhcp6c_request_async(FAR void *handle, dhcp6c_callback_t callback)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  if (pdhcp6c->thread)
+    {
+      nerr("DHCP6C thread already running\n");
+      return ERROR;
+    }
+
+  pdhcp6c->callback = callback;
+  ret = pthread_create(&pdhcp6c->thread, NULL, dhcp6c_run, pdhcp6c);
+  if (ret != 0)
+    {
+      nerr("Failed to start the DHCP6C thread\n");
+      return ERROR;
+    }
+
+  return OK;
+}
+
+void dhcp6c_cancel(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  sighandler_t old;
+  int ret;
+
+  if (pdhcp6c)
+    {
+      pdhcp6c->cancel = true;
+      if (pdhcp6c->thread)
+        {
+          old = signal(SIGQUIT, SIG_IGN);
+
+          /* Signal the dhcp6c_run */
+
+          ret = pthread_kill(pdhcp6c->thread, SIGQUIT);
+          if (ret != 0)
+            {
+              nerr("pthread_kill DHCP6C thread\n");
+            }
+
+          /* Wait for the end of dhcp6c_run */
+
+          ret = pthread_join(pdhcp6c->thread, NULL);
+          if (ret != 0)
+            {
+              nerr("pthread_join DHCP6C thread\n");
+            }
+
+          pdhcp6c->thread = 0;
+          signal(SIGQUIT, old);
+        }
+    }
+}
+
+void dhcp6c_close(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  if (pdhcp6c)
+    {
+      dhcp6c_cancel(pdhcp6c);
+      if (pdhcp6c->urandom_fd)
+        {
+          close(pdhcp6c->urandom_fd);
+        }
+
+      if (pdhcp6c->sockfd)

Review comment:
       ```suggestion
         if (pdhcp6c->sockfd > 0)
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for (_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (!c || c->preference < cand[i].preference)
+        {
+          c = &cand[i];
+        }
+    }
+
+  if (retry && pdhcp6c->ia_mode == IA_MODE_TRY)
+    {
+      /* We give it a second try without the IA_NA */
+
+      pdhcp6c->ia_mode = IA_MODE_NONE;
+      return dhcp6c_command(handle, DHCPV6_MSG_SOLICIT);
+    }
+
+  if (c)
+    {
+      uint16_t hdr[2] =
+      {
+        htons(DHCPV6_OPT_SERVERID), htons(c->duid_len)
+      };
+
+      dhcp6c_add_state(handle, STATE_SERVER_ID, hdr, sizeof(hdr));
+      dhcp6c_add_state(handle, STATE_SERVER_ID, c->duid, c->duid_len);
+      pdhcp6c->accept_reconfig = c->wants_reconfigure;
+    }
+
+  dhcp6c_clear_state(handle, STATE_SERVER_CAND);
+
+  if (!c)
+    {
+      return -1;
+    }
+  else if (pdhcp6c->request_prefix || pdhcp6c->ia_mode != IA_MODE_NONE)
+    {
+      return DHCPV6_STATEFUL;
+    }
+  else
+    {
+      return DHCPV6_STATELESS;
+    }
+}
+
+static time_t dhcp6c_parse_ia(FAR void *handle, FAR void *opt, FAR void *end)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint32_t timeout = UINT32_MAX;
+  uint16_t otype;
+  uint16_t olen;
+  uint16_t stype;
+  uint16_t slen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  FAR uint8_t *sdata;
+
+  /* Update address IA */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_IA_PREFIX)
+        {
+          FAR struct dhcpv6_ia_prefix_s *prefix = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_prefix_s *local = NULL;
+          uint32_t valid;
+          uint32_t pref;
+          size_t pd_len;
+          FAR uint8_t *pd;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*prefix))
+            {
+              continue;
+            }
+
+          olen = sizeof(*prefix);
+          valid = ntohl(prefix->valid);
+          pref = ntohl(prefix->preferred);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          pd = dhcp6c_get_state(handle, STATE_IA_PD, &pd_len);
+          dhcpv6_for_each_option(ite1, pd, pd + pd_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata + 8, odata + 8,
+                          sizeof(local->addr) + 1))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = prefix->preferred;
+              local->valid = prefix->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_PD, prefix, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+      else if (otype == DHCPV6_OPT_IA_ADDR)
+        {
+          FAR struct dhcpv6_ia_addr_s *addr = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_addr_s *local = NULL;
+          uint32_t pref;
+          uint32_t valid;
+          size_t na_len;
+          FAR uint8_t *na;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*addr))
+            {
+              continue;
+            }
+
+          olen = sizeof(*addr);
+          pref = ntohl(addr->preferred);
+          valid = ntohl(addr->valid);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          na = dhcp6c_get_state(handle, STATE_IA_NA, &na_len);
+          dhcpv6_for_each_option(ite1, na, na + na_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata, odata, sizeof(local->addr)))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = addr->preferred;
+              local->valid = addr->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_NA, addr, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+    }
+
+  return timeout;
+}
+
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  bool have_update = false;
+  size_t ia_na_len;
+  size_t ia_pd_len;
+  size_t dns_len;
+  size_t search_len;
+  size_t sntp_ip_len;
+  size_t sntp_dns_len;
+  size_t sip_ip_len;
+  size_t sip_fqdn_len;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  FAR uint8_t *ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  FAR uint8_t *ia_end;
+  pdhcp6c->t1 = UINT32_MAX;
+  pdhcp6c->t2 = UINT32_MAX;
+  pdhcp6c->t3 = UINT32_MAX;
+
+  dhcp6c_get_state(handle, STATE_DNS, &dns_len);
+  dhcp6c_get_state(handle, STATE_SEARCH, &search_len);
+  dhcp6c_get_state(handle, STATE_SNTP_IP, &sntp_ip_len);
+  dhcp6c_get_state(handle, STATE_SNTP_FQDN, &sntp_dns_len);
+  dhcp6c_get_state(handle, STATE_SIP_IP, &sip_ip_len);
+  dhcp6c_get_state(handle, STATE_SIP_FQDN, &sip_fqdn_len);
+
+  /* Decrease valid and preferred lifetime of prefixes */
+
+  dhcpv6_for_each_option(ite0, ia_pd, ia_pd + ia_pd_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Decrease valid and preferred lifetime of addresses */
+
+  dhcpv6_for_each_option(ite0, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Parse and find all matching IAs */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)
+           && olen > sizeof(struct dhcpv6_ia_hdr_s))
+        {
+          FAR struct dhcpv6_ia_hdr_s *ia_hdr = (FAR void *)(odata - 4);
+          time_t l_t1 = ntohl(ia_hdr->t1);
+          time_t l_t2 = ntohl(ia_hdr->t2);
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *sdata;
+          time_t n;
+
+          /* Test ID and T1-T2 validity */
+
+          if (ia_hdr->iaid != 1 || l_t2 < l_t1)
+            {
+              continue;
+            }
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, &ia_hdr[1], odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (stype == DHCPV6_OPT_STATUS && slen >= 2 &&
+                  (sdata[0] || sdata[1]))
+                {
+                  continue;
+                }
+            }
+
+          /* Update times */
+
+          if (l_t1 > 0 && pdhcp6c->t1 > l_t1)
+            {
+              pdhcp6c->t1 = l_t1;
+            }
+
+          if (l_t2 > 0 && pdhcp6c->t2 > l_t2)
+            {
+              pdhcp6c->t2 = l_t2;
+            }
+
+          /* Always report update in case we have IA_PDs so that
+           * the state-script is called with updated times
+           */
+
+          if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+            {
+              have_update = true;
+            }
+
+          n = dhcp6c_parse_ia(handle, &ia_hdr[1], odata + olen);
+          if (n < pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = n;
+            }
+
+          if (n < pdhcp6c->t2)
+            {
+              pdhcp6c->t2 = n;
+            }
+
+          if (n < pdhcp6c->t3)
+            {
+              pdhcp6c->t3 = n;
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_SERVERS)
+        {
+          if (olen % 16 == 0)
+            {
+              dhcp6c_add_state(handle, STATE_DNS, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_DOMAIN)
+        {
+          dhcp6c_add_state(handle, STATE_SEARCH, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_NTP_SERVER)
+        {
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *sdata;
+          FAR uint8_t *ite1;
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, odata, odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (slen == 16 &&
+                  (stype == NTP_MC_ADDR || stype == NTP_SRV_ADDR))
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_IP, sdata, slen);
+                }
+              else if (slen > 0 && stype == NTP_SRV_FQDN)
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_FQDN, sdata, slen);
+                }
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_A)
+        {
+          if (olen == 16)
+            {
+              dhcp6c_add_state(handle, STATE_SIP_IP, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_D)
+        {
+          dhcp6c_add_state(handle, STATE_SIP_FQDN, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4)
+        {
+          uint32_t refresh = ntohl(*((FAR uint32_t *)odata));
+          if (refresh < (uint32_t)pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = refresh;
+            }
+        }
+      else if (otype != DHCPV6_OPT_CLIENTID && otype != DHCPV6_OPT_SERVERID)
+        {
+          dhcp6c_add_state(handle, STATE_CUSTOM_OPTS, (odata - 4), olen + 4);
+        }
+    }
+
+  if (opt)
+    {
+      size_t new_ia_pd_len;
+      size_t new_ia_na_len;
+      have_update |= dhcp6c_commit_state(handle, STATE_DNS, dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SEARCH, search_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_IP,
+                                         sntp_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_FQDN,
+                                         sntp_dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_IP, sip_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_FQDN,
+                                         sip_fqdn_len);
+      dhcp6c_get_state(handle, STATE_IA_PD, &new_ia_pd_len);
+      dhcp6c_get_state(handle, STATE_IA_NA, &new_ia_na_len);
+      have_update |= (new_ia_pd_len != ia_pd_len) ||
+                     (new_ia_na_len != ia_na_len);
+    }
+
+  /* Delete prefixes with 0 valid-time */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  ia_end = ia_pd + ia_pd_len;
+  dhcpv6_for_each_option(ite0, ia_pd, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_pd + dhcp6c_remove_state(handle, STATE_IA_PD,
+                   (FAR uint8_t *)p - ia_pd, olen + 4);
+          have_update = true;
+        }
+    }
+
+  /* Delete addresses with 0 valid-time */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  ia_end = ia_na + ia_na_len;
+  dhcpv6_for_each_option(ite0, ia_na, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_na + dhcp6c_remove_state(handle, STATE_IA_NA,
+                   (FAR uint8_t *)p - ia_na, olen + 4);
+          have_update = true;
+        }
+    }
+
+  return have_update;
+}
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *odata;
+  FAR uint8_t *ite;
+  uint8_t msg = DHCPV6_MSG_RENEW;
+
+  /* TODO: should verify the reconfigure message */
+
+  dhcpv6_for_each_option(ite, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1 &&
+          (odata[0] == DHCPV6_MSG_RENEW ||
+           odata[0] == DHCPV6_MSG_INFO_REQ))
+        {
+          msg = odata[0];
+        }
+    }
+
+  pdhcp6c->t1 -= elapsed;
+  pdhcp6c->t2 -= elapsed;
+  pdhcp6c->t3 -= elapsed;
+
+  if (pdhcp6c->t1 < 0)
+    {
+      pdhcp6c->t1 = 0;
+    }
+
+  if (pdhcp6c->t2 < 0)
+    {
+      pdhcp6c->t2 = 0;
+    }
+
+  if (pdhcp6c->t3 < 0)
+    {
+      pdhcp6c->t3 = 0;
+    }
+
+  dhcp6c_handle_reply(handle, DHCPV6_MSG_UNKNOWN, NULL, NULL, elapsed);
+
+  return msg;
+}
+
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed)
+{
+  dhcp6c_handle_advert(handle, orig, opt, end, elapsed);
+  if (dhcp6c_commit_advert(handle, elapsed) < 0)
+    {
+      return -1;
+    }
+
+  return dhcp6c_handle_reply(handle, orig, opt, end, elapsed);
+}
+
+static int dhcp6c_single_request(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  FAR const char *process = NULL;
+  enum dhcpv6_mode_e mode;
+  enum dhcpv6_msg_e type;
+  int ret = -1;
+
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_CAND);
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_CUSTOM_OPTS);
+  ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_SOLICIT);
+  if (ret < 0)
+    {
+      return -1;
+    }
+  else if (ret == DHCPV6_STATELESS)
+    {
+      mode = DHCPV6_STATELESS;
+      type = DHCPV6_MSG_INFO_REQ;
+      process = "informed";
+    }
+  else
+    {
+      mode = DHCPV6_STATEFUL;
+      type = DHCPV6_MSG_REQUEST;
+      process = "bound";
+    }
+
+  ret = dhcp6c_command(pdhcp6c, type);
+  if (ret >= 0)
+    {
+      ret = mode;
+      dhcp6c_switch_process(pdhcp6c, process);
+    }
+
+  return ret;
+}
+
+static int dhcp6c_lease(FAR void *args, uint8_t type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  enum dhcpv6_mode_e mode = (enum dhcpv6_mode_e)type;
+  size_t ia_pd_len;
+  size_t ia_na_len;
+  size_t ia_pd_new;
+  size_t ia_na_new;
+  size_t server_id_len;
+  int ret = -1;
+
+  if (mode == DHCPV6_STATELESS)
+    {
+      /* Stateless mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Wait for T1 to expire or until we get a reconfigure */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          /* Information-Request */
+
+          ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_INFO_REQ);
+          if (ret < 0)
+            {
+              nerr("DHCPV6_MSG_INFO_REQ error\n");
+              break;
+            }
+          else
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+        }
+    }
+  else
+    {
+      /* Stateful mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Renew Cycle
+           * Wait for T1 to expire or until we get a reconfigure
+           */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+          dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+
+          /* If we have any IAs, send renew, otherwise request */
+
+          if (ia_pd_len == 0 && ia_na_len == 0)
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REQUEST);
+          else
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_RENEW);
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          if (ret >= 0)
+            {
+              /* Publish updates */
+
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+          else
+            {
+              /* Remove binding */
+
+              dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+
+              /* If we have IAs, try rebind otherwise restart */
+
+              ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REBIND);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_new);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_new);
+
+              /* We lost all our IAs, restart */
+
+              if (ret < 0 || (ia_pd_new == 0 && ia_pd_len) ||
+                  (ia_na_new == 0 && ia_na_len))
+                {
+                  break;
+                }
+              else if (ret >= 0)
+                {
+                  dhcp6c_switch_process(pdhcp6c, "rebound");
+                }
+            }
+        }
+    }
+
+  dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+  dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+  dhcp6c_get_state(pdhcp6c, STATE_SERVER_ID, &server_id_len);
+
+  /* Add all prefixes to lost prefixes */
+
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_switch_process(pdhcp6c, "unbound");
+
+  /* Remove assigned addresses */
+
+  if (ia_na_len > 0)
+    {
+      dhcp6c_remove_addrs(pdhcp6c);
+    }
+
+  if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0))
+    {
+      dhcp6c_command(pdhcp6c, DHCPV6_MSG_RELEASE);
+    }
+
+  return ret;
+}
+
+static FAR void *dhcp6c_run(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  int ret;
+
+  while (!pdhcp6c->cancel)
+    {
+      ret = dhcp6c_single_request(pdhcp6c);
+      if (ret > 0)
+        {
+          dhcp6c_lease(pdhcp6c, ret);
+        }
+    }
+
+  return 0;
+}
+
+static FAR void *dhcp6c_precise_open(FAR const char *ifname,
+                                     enum dhcp6c_ia_mode_e ia_mode,
+                                     bool request_pd,
+                                     uint16_t opt[], int cnt)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c;
+  struct ifreq ifr;
+  size_t client_id_len;
+  int val = 1;
+  uint16_t oro[] =
+  {
+    htons(DHCPV6_OPT_DNS_SERVERS),
+    htons(DHCPV6_OPT_DNS_DOMAIN),
+    htons(DHCPV6_OPT_NTP_SERVER),
+    htons(DHCPV6_OPT_SIP_SERVER_A),
+    htons(DHCPV6_OPT_SIP_SERVER_D)
+  };
+
+  struct sockaddr_in6 client_addr =
+  {
+    .sin6_family = AF_INET6,
+    .sin6_port = htons(DHCPV6_CLIENT_PORT),
+    .sin6_flowinfo = 0
+  };
+
+  pdhcp6c = malloc(sizeof(struct dhcp6c_state_s));
+  if (!pdhcp6c)
+    {
+      return NULL;
+    }
+
+  memset(pdhcp6c, 0, sizeof(*pdhcp6c));
+  pdhcp6c->urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY);
+  if (pdhcp6c->urandom_fd < 0)
+    {
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->sockfd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+  if (pdhcp6c->sockfd < 0)
+    {
+      close(pdhcp6c->urandom_fd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  /* Detect interface */
+
+  strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+  if (ioctl(pdhcp6c->sockfd, SIOCGIFINDEX, &ifr))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->ifindex = ifr.ifr_ifindex;
+
+  /* Create client DUID */
+
+  dhcp6c_get_state(pdhcp6c, STATE_CLIENT_ID, &client_id_len);
+  if (client_id_len == 0)
+    {
+      uint8_t duid[14] =
+      {
+        0, DHCPV6_OPT_CLIENTID, 0, 10, 0,
+        DHCPV6_DUID_LLADDR, 0, 1
+      };
+
+      uint8_t zero[ETHER_ADDR_LEN];
+      struct ifreq ifs[100];
+      FAR struct ifreq *ifp;
+      FAR struct ifreq *ifend;
+      struct ifconf ifc;
+
+      ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+      memcpy(&duid[8], ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+      memset(zero, 0, sizeof(zero));
+      ifc.ifc_req = ifs;
+      ifc.ifc_len = sizeof(ifs);
+
+      if (!memcmp(&duid[8], zero, ETHER_ADDR_LEN) &&
+          ioctl(pdhcp6c->sockfd, SIOCGIFCONF, &ifc) >= 0)
+        {
+          /* If our interface doesn't have an address... */
+
+          ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+          for (ifp = ifc.ifc_req; ifp < ifend &&
+               !memcmp(&duid[8], zero, 6); ifp++)
+            {
+              memcpy(ifr.ifr_name, ifp->ifr_name,
+                     sizeof(ifr.ifr_name));
+              ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+              memcpy(&duid[8], ifr.ifr_hwaddr.sa_data,
+                     ETHER_ADDR_LEN);
+            }
+        }
+
+      dhcp6c_add_state(pdhcp6c, STATE_CLIENT_ID, duid, sizeof(duid));
+    }
+
+  /* Create ORO */
+
+  dhcp6c_add_state(pdhcp6c, STATE_ORO, oro, sizeof(oro));
+
+  /* Configure IPv6-options */
+
+  setsockopt(pdhcp6c->sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, UDP_BINDTODEVICE, ifname,
+             strlen(ifname));
+  if (bind(pdhcp6c->sockfd, (struct sockaddr *)&client_addr,
+           sizeof(client_addr)))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->thread = 0;
+  pdhcp6c->cancel = false;
+  pdhcp6c->t1 = 0;
+  pdhcp6c->t2 = 0;
+  pdhcp6c->t3 = 0;
+  pdhcp6c->request_prefix = request_pd;
+  switch (ia_mode)
+    {
+      case IA_MODE_NONE:
+      case IA_MODE_TRY:
+      case IA_MODE_FORCE:
+        break;
+      default:
+        ia_mode = IA_MODE_TRY;
+        break;
+    }
+
+  pdhcp6c->ia_mode = ia_mode;
+  pdhcp6c->accept_reconfig = false;
+  if (opt && cnt > 0)
+    {
+      uint16_t opttype;
+      for (int i = 0; i < cnt; i++)
+        {
+          opttype = htons(opt[i]);
+          dhcp6c_add_state(pdhcp6c, STATE_ORO, &opttype, 2);
+        }
+    }
+
+  return pdhcp6c;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR void *dhcp6c_open(FAR const char *interface)
+{
+  return dhcp6c_precise_open(interface, IA_MODE_TRY, true, NULL, 0);
+}
+
+int dhcp6c_request(FAR void *handle, FAR struct dhcp6c_state *presult)
+{
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  ret = dhcp6c_single_request(handle);
+  if (ret >= 0)
+    {
+      dhcp6c_get_result(handle, presult);
+      return OK;
+    }
+  else
+    {
+      return ERROR;
+    }
+}
+
+int dhcp6c_request_async(FAR void *handle, dhcp6c_callback_t callback)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  if (pdhcp6c->thread)
+    {
+      nerr("DHCP6C thread already running\n");
+      return ERROR;
+    }
+
+  pdhcp6c->callback = callback;
+  ret = pthread_create(&pdhcp6c->thread, NULL, dhcp6c_run, pdhcp6c);
+  if (ret != 0)
+    {
+      nerr("Failed to start the DHCP6C thread\n");
+      return ERROR;
+    }
+
+  return OK;
+}
+
+void dhcp6c_cancel(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  sighandler_t old;
+  int ret;
+
+  if (pdhcp6c)
+    {
+      pdhcp6c->cancel = true;
+      if (pdhcp6c->thread)
+        {
+          old = signal(SIGQUIT, SIG_IGN);
+
+          /* Signal the dhcp6c_run */
+
+          ret = pthread_kill(pdhcp6c->thread, SIGQUIT);
+          if (ret != 0)
+            {
+              nerr("pthread_kill DHCP6C thread\n");
+            }
+
+          /* Wait for the end of dhcp6c_run */
+
+          ret = pthread_join(pdhcp6c->thread, NULL);
+          if (ret != 0)
+            {
+              nerr("pthread_join DHCP6C thread\n");
+            }
+
+          pdhcp6c->thread = 0;
+          signal(SIGQUIT, old);
+        }
+    }
+}
+
+void dhcp6c_close(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  if (pdhcp6c)

Review comment:
       ```suggestion
     if (pdhcp6c != NULL)
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for (_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (!c || c->preference < cand[i].preference)
+        {
+          c = &cand[i];
+        }
+    }
+
+  if (retry && pdhcp6c->ia_mode == IA_MODE_TRY)
+    {
+      /* We give it a second try without the IA_NA */
+
+      pdhcp6c->ia_mode = IA_MODE_NONE;
+      return dhcp6c_command(handle, DHCPV6_MSG_SOLICIT);
+    }
+
+  if (c)
+    {
+      uint16_t hdr[2] =
+      {
+        htons(DHCPV6_OPT_SERVERID), htons(c->duid_len)
+      };
+
+      dhcp6c_add_state(handle, STATE_SERVER_ID, hdr, sizeof(hdr));
+      dhcp6c_add_state(handle, STATE_SERVER_ID, c->duid, c->duid_len);
+      pdhcp6c->accept_reconfig = c->wants_reconfigure;
+    }
+
+  dhcp6c_clear_state(handle, STATE_SERVER_CAND);
+
+  if (!c)
+    {
+      return -1;
+    }
+  else if (pdhcp6c->request_prefix || pdhcp6c->ia_mode != IA_MODE_NONE)
+    {
+      return DHCPV6_STATEFUL;
+    }
+  else
+    {
+      return DHCPV6_STATELESS;
+    }
+}
+
+static time_t dhcp6c_parse_ia(FAR void *handle, FAR void *opt, FAR void *end)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint32_t timeout = UINT32_MAX;
+  uint16_t otype;
+  uint16_t olen;
+  uint16_t stype;
+  uint16_t slen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  FAR uint8_t *sdata;
+
+  /* Update address IA */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_IA_PREFIX)
+        {
+          FAR struct dhcpv6_ia_prefix_s *prefix = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_prefix_s *local = NULL;
+          uint32_t valid;
+          uint32_t pref;
+          size_t pd_len;
+          FAR uint8_t *pd;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*prefix))
+            {
+              continue;
+            }
+
+          olen = sizeof(*prefix);
+          valid = ntohl(prefix->valid);
+          pref = ntohl(prefix->preferred);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          pd = dhcp6c_get_state(handle, STATE_IA_PD, &pd_len);
+          dhcpv6_for_each_option(ite1, pd, pd + pd_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata + 8, odata + 8,
+                          sizeof(local->addr) + 1))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = prefix->preferred;
+              local->valid = prefix->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_PD, prefix, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+      else if (otype == DHCPV6_OPT_IA_ADDR)
+        {
+          FAR struct dhcpv6_ia_addr_s *addr = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_addr_s *local = NULL;
+          uint32_t pref;
+          uint32_t valid;
+          size_t na_len;
+          FAR uint8_t *na;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*addr))
+            {
+              continue;
+            }
+
+          olen = sizeof(*addr);
+          pref = ntohl(addr->preferred);
+          valid = ntohl(addr->valid);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          na = dhcp6c_get_state(handle, STATE_IA_NA, &na_len);
+          dhcpv6_for_each_option(ite1, na, na + na_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata, odata, sizeof(local->addr)))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = addr->preferred;
+              local->valid = addr->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_NA, addr, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+    }
+
+  return timeout;
+}
+
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  bool have_update = false;
+  size_t ia_na_len;
+  size_t ia_pd_len;
+  size_t dns_len;
+  size_t search_len;
+  size_t sntp_ip_len;
+  size_t sntp_dns_len;
+  size_t sip_ip_len;
+  size_t sip_fqdn_len;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  FAR uint8_t *ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  FAR uint8_t *ia_end;
+  pdhcp6c->t1 = UINT32_MAX;
+  pdhcp6c->t2 = UINT32_MAX;
+  pdhcp6c->t3 = UINT32_MAX;
+
+  dhcp6c_get_state(handle, STATE_DNS, &dns_len);
+  dhcp6c_get_state(handle, STATE_SEARCH, &search_len);
+  dhcp6c_get_state(handle, STATE_SNTP_IP, &sntp_ip_len);
+  dhcp6c_get_state(handle, STATE_SNTP_FQDN, &sntp_dns_len);
+  dhcp6c_get_state(handle, STATE_SIP_IP, &sip_ip_len);
+  dhcp6c_get_state(handle, STATE_SIP_FQDN, &sip_fqdn_len);
+
+  /* Decrease valid and preferred lifetime of prefixes */
+
+  dhcpv6_for_each_option(ite0, ia_pd, ia_pd + ia_pd_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Decrease valid and preferred lifetime of addresses */
+
+  dhcpv6_for_each_option(ite0, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Parse and find all matching IAs */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)
+           && olen > sizeof(struct dhcpv6_ia_hdr_s))
+        {
+          FAR struct dhcpv6_ia_hdr_s *ia_hdr = (FAR void *)(odata - 4);
+          time_t l_t1 = ntohl(ia_hdr->t1);
+          time_t l_t2 = ntohl(ia_hdr->t2);
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *sdata;
+          time_t n;
+
+          /* Test ID and T1-T2 validity */
+
+          if (ia_hdr->iaid != 1 || l_t2 < l_t1)
+            {
+              continue;
+            }
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, &ia_hdr[1], odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (stype == DHCPV6_OPT_STATUS && slen >= 2 &&
+                  (sdata[0] || sdata[1]))
+                {
+                  continue;
+                }
+            }
+
+          /* Update times */
+
+          if (l_t1 > 0 && pdhcp6c->t1 > l_t1)
+            {
+              pdhcp6c->t1 = l_t1;
+            }
+
+          if (l_t2 > 0 && pdhcp6c->t2 > l_t2)
+            {
+              pdhcp6c->t2 = l_t2;
+            }
+
+          /* Always report update in case we have IA_PDs so that
+           * the state-script is called with updated times
+           */
+
+          if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+            {
+              have_update = true;
+            }
+
+          n = dhcp6c_parse_ia(handle, &ia_hdr[1], odata + olen);
+          if (n < pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = n;
+            }
+
+          if (n < pdhcp6c->t2)
+            {
+              pdhcp6c->t2 = n;
+            }
+
+          if (n < pdhcp6c->t3)
+            {
+              pdhcp6c->t3 = n;
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_SERVERS)
+        {
+          if (olen % 16 == 0)
+            {
+              dhcp6c_add_state(handle, STATE_DNS, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_DOMAIN)
+        {
+          dhcp6c_add_state(handle, STATE_SEARCH, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_NTP_SERVER)
+        {
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *sdata;
+          FAR uint8_t *ite1;
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, odata, odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (slen == 16 &&
+                  (stype == NTP_MC_ADDR || stype == NTP_SRV_ADDR))
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_IP, sdata, slen);
+                }
+              else if (slen > 0 && stype == NTP_SRV_FQDN)
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_FQDN, sdata, slen);
+                }
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_A)
+        {
+          if (olen == 16)
+            {
+              dhcp6c_add_state(handle, STATE_SIP_IP, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_D)
+        {
+          dhcp6c_add_state(handle, STATE_SIP_FQDN, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4)
+        {
+          uint32_t refresh = ntohl(*((FAR uint32_t *)odata));
+          if (refresh < (uint32_t)pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = refresh;
+            }
+        }
+      else if (otype != DHCPV6_OPT_CLIENTID && otype != DHCPV6_OPT_SERVERID)
+        {
+          dhcp6c_add_state(handle, STATE_CUSTOM_OPTS, (odata - 4), olen + 4);
+        }
+    }
+
+  if (opt)
+    {
+      size_t new_ia_pd_len;
+      size_t new_ia_na_len;
+      have_update |= dhcp6c_commit_state(handle, STATE_DNS, dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SEARCH, search_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_IP,
+                                         sntp_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_FQDN,
+                                         sntp_dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_IP, sip_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_FQDN,
+                                         sip_fqdn_len);
+      dhcp6c_get_state(handle, STATE_IA_PD, &new_ia_pd_len);
+      dhcp6c_get_state(handle, STATE_IA_NA, &new_ia_na_len);
+      have_update |= (new_ia_pd_len != ia_pd_len) ||
+                     (new_ia_na_len != ia_na_len);
+    }
+
+  /* Delete prefixes with 0 valid-time */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  ia_end = ia_pd + ia_pd_len;
+  dhcpv6_for_each_option(ite0, ia_pd, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_pd + dhcp6c_remove_state(handle, STATE_IA_PD,
+                   (FAR uint8_t *)p - ia_pd, olen + 4);
+          have_update = true;
+        }
+    }
+
+  /* Delete addresses with 0 valid-time */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  ia_end = ia_na + ia_na_len;
+  dhcpv6_for_each_option(ite0, ia_na, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_na + dhcp6c_remove_state(handle, STATE_IA_NA,
+                   (FAR uint8_t *)p - ia_na, olen + 4);
+          have_update = true;
+        }
+    }
+
+  return have_update;
+}
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *odata;
+  FAR uint8_t *ite;
+  uint8_t msg = DHCPV6_MSG_RENEW;
+
+  /* TODO: should verify the reconfigure message */
+
+  dhcpv6_for_each_option(ite, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1 &&
+          (odata[0] == DHCPV6_MSG_RENEW ||
+           odata[0] == DHCPV6_MSG_INFO_REQ))
+        {
+          msg = odata[0];
+        }
+    }
+
+  pdhcp6c->t1 -= elapsed;
+  pdhcp6c->t2 -= elapsed;
+  pdhcp6c->t3 -= elapsed;
+
+  if (pdhcp6c->t1 < 0)
+    {
+      pdhcp6c->t1 = 0;
+    }
+
+  if (pdhcp6c->t2 < 0)
+    {
+      pdhcp6c->t2 = 0;
+    }
+
+  if (pdhcp6c->t3 < 0)
+    {
+      pdhcp6c->t3 = 0;
+    }
+
+  dhcp6c_handle_reply(handle, DHCPV6_MSG_UNKNOWN, NULL, NULL, elapsed);
+
+  return msg;
+}
+
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed)
+{
+  dhcp6c_handle_advert(handle, orig, opt, end, elapsed);
+  if (dhcp6c_commit_advert(handle, elapsed) < 0)
+    {
+      return -1;
+    }
+
+  return dhcp6c_handle_reply(handle, orig, opt, end, elapsed);
+}
+
+static int dhcp6c_single_request(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  FAR const char *process = NULL;
+  enum dhcpv6_mode_e mode;
+  enum dhcpv6_msg_e type;
+  int ret = -1;
+
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_CAND);
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_CUSTOM_OPTS);
+  ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_SOLICIT);
+  if (ret < 0)
+    {
+      return -1;
+    }
+  else if (ret == DHCPV6_STATELESS)
+    {
+      mode = DHCPV6_STATELESS;
+      type = DHCPV6_MSG_INFO_REQ;
+      process = "informed";
+    }
+  else
+    {
+      mode = DHCPV6_STATEFUL;
+      type = DHCPV6_MSG_REQUEST;
+      process = "bound";
+    }
+
+  ret = dhcp6c_command(pdhcp6c, type);
+  if (ret >= 0)
+    {
+      ret = mode;
+      dhcp6c_switch_process(pdhcp6c, process);
+    }
+
+  return ret;
+}
+
+static int dhcp6c_lease(FAR void *args, uint8_t type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  enum dhcpv6_mode_e mode = (enum dhcpv6_mode_e)type;
+  size_t ia_pd_len;
+  size_t ia_na_len;
+  size_t ia_pd_new;
+  size_t ia_na_new;
+  size_t server_id_len;
+  int ret = -1;
+
+  if (mode == DHCPV6_STATELESS)
+    {
+      /* Stateless mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Wait for T1 to expire or until we get a reconfigure */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          /* Information-Request */
+
+          ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_INFO_REQ);
+          if (ret < 0)
+            {
+              nerr("DHCPV6_MSG_INFO_REQ error\n");
+              break;
+            }
+          else
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+        }
+    }
+  else
+    {
+      /* Stateful mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Renew Cycle
+           * Wait for T1 to expire or until we get a reconfigure
+           */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+          dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+
+          /* If we have any IAs, send renew, otherwise request */
+
+          if (ia_pd_len == 0 && ia_na_len == 0)
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REQUEST);
+          else
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_RENEW);
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          if (ret >= 0)
+            {
+              /* Publish updates */
+
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+          else
+            {
+              /* Remove binding */
+
+              dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+
+              /* If we have IAs, try rebind otherwise restart */
+
+              ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REBIND);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_new);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_new);
+
+              /* We lost all our IAs, restart */
+
+              if (ret < 0 || (ia_pd_new == 0 && ia_pd_len) ||
+                  (ia_na_new == 0 && ia_na_len))
+                {
+                  break;
+                }
+              else if (ret >= 0)
+                {
+                  dhcp6c_switch_process(pdhcp6c, "rebound");
+                }
+            }
+        }
+    }
+
+  dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+  dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+  dhcp6c_get_state(pdhcp6c, STATE_SERVER_ID, &server_id_len);
+
+  /* Add all prefixes to lost prefixes */
+
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_switch_process(pdhcp6c, "unbound");
+
+  /* Remove assigned addresses */
+
+  if (ia_na_len > 0)
+    {
+      dhcp6c_remove_addrs(pdhcp6c);
+    }
+
+  if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0))
+    {
+      dhcp6c_command(pdhcp6c, DHCPV6_MSG_RELEASE);
+    }
+
+  return ret;
+}
+
+static FAR void *dhcp6c_run(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  int ret;
+
+  while (!pdhcp6c->cancel)
+    {
+      ret = dhcp6c_single_request(pdhcp6c);
+      if (ret > 0)
+        {
+          dhcp6c_lease(pdhcp6c, ret);
+        }
+    }
+
+  return 0;
+}
+
+static FAR void *dhcp6c_precise_open(FAR const char *ifname,
+                                     enum dhcp6c_ia_mode_e ia_mode,
+                                     bool request_pd,
+                                     uint16_t opt[], int cnt)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c;
+  struct ifreq ifr;
+  size_t client_id_len;
+  int val = 1;
+  uint16_t oro[] =
+  {
+    htons(DHCPV6_OPT_DNS_SERVERS),
+    htons(DHCPV6_OPT_DNS_DOMAIN),
+    htons(DHCPV6_OPT_NTP_SERVER),
+    htons(DHCPV6_OPT_SIP_SERVER_A),
+    htons(DHCPV6_OPT_SIP_SERVER_D)
+  };
+
+  struct sockaddr_in6 client_addr =
+  {
+    .sin6_family = AF_INET6,
+    .sin6_port = htons(DHCPV6_CLIENT_PORT),
+    .sin6_flowinfo = 0
+  };
+
+  pdhcp6c = malloc(sizeof(struct dhcp6c_state_s));
+  if (!pdhcp6c)
+    {
+      return NULL;
+    }
+
+  memset(pdhcp6c, 0, sizeof(*pdhcp6c));
+  pdhcp6c->urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY);
+  if (pdhcp6c->urandom_fd < 0)
+    {
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->sockfd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+  if (pdhcp6c->sockfd < 0)
+    {
+      close(pdhcp6c->urandom_fd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  /* Detect interface */
+
+  strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+  if (ioctl(pdhcp6c->sockfd, SIOCGIFINDEX, &ifr))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->ifindex = ifr.ifr_ifindex;
+
+  /* Create client DUID */
+
+  dhcp6c_get_state(pdhcp6c, STATE_CLIENT_ID, &client_id_len);
+  if (client_id_len == 0)
+    {
+      uint8_t duid[14] =
+      {
+        0, DHCPV6_OPT_CLIENTID, 0, 10, 0,
+        DHCPV6_DUID_LLADDR, 0, 1
+      };
+
+      uint8_t zero[ETHER_ADDR_LEN];
+      struct ifreq ifs[100];
+      FAR struct ifreq *ifp;
+      FAR struct ifreq *ifend;
+      struct ifconf ifc;
+
+      ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+      memcpy(&duid[8], ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+      memset(zero, 0, sizeof(zero));
+      ifc.ifc_req = ifs;
+      ifc.ifc_len = sizeof(ifs);
+
+      if (!memcmp(&duid[8], zero, ETHER_ADDR_LEN) &&
+          ioctl(pdhcp6c->sockfd, SIOCGIFCONF, &ifc) >= 0)
+        {
+          /* If our interface doesn't have an address... */
+
+          ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+          for (ifp = ifc.ifc_req; ifp < ifend &&
+               !memcmp(&duid[8], zero, 6); ifp++)
+            {
+              memcpy(ifr.ifr_name, ifp->ifr_name,
+                     sizeof(ifr.ifr_name));
+              ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+              memcpy(&duid[8], ifr.ifr_hwaddr.sa_data,
+                     ETHER_ADDR_LEN);
+            }
+        }
+
+      dhcp6c_add_state(pdhcp6c, STATE_CLIENT_ID, duid, sizeof(duid));
+    }
+
+  /* Create ORO */
+
+  dhcp6c_add_state(pdhcp6c, STATE_ORO, oro, sizeof(oro));
+
+  /* Configure IPv6-options */
+
+  setsockopt(pdhcp6c->sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, UDP_BINDTODEVICE, ifname,
+             strlen(ifname));
+  if (bind(pdhcp6c->sockfd, (struct sockaddr *)&client_addr,
+           sizeof(client_addr)))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->thread = 0;
+  pdhcp6c->cancel = false;
+  pdhcp6c->t1 = 0;
+  pdhcp6c->t2 = 0;
+  pdhcp6c->t3 = 0;
+  pdhcp6c->request_prefix = request_pd;
+  switch (ia_mode)
+    {
+      case IA_MODE_NONE:
+      case IA_MODE_TRY:
+      case IA_MODE_FORCE:
+        break;
+      default:
+        ia_mode = IA_MODE_TRY;
+        break;
+    }
+
+  pdhcp6c->ia_mode = ia_mode;
+  pdhcp6c->accept_reconfig = false;
+  if (opt && cnt > 0)
+    {
+      uint16_t opttype;
+      for (int i = 0; i < cnt; i++)
+        {
+          opttype = htons(opt[i]);
+          dhcp6c_add_state(pdhcp6c, STATE_ORO, &opttype, 2);
+        }
+    }
+
+  return pdhcp6c;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR void *dhcp6c_open(FAR const char *interface)
+{
+  return dhcp6c_precise_open(interface, IA_MODE_TRY, true, NULL, 0);
+}
+
+int dhcp6c_request(FAR void *handle, FAR struct dhcp6c_state *presult)
+{
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  ret = dhcp6c_single_request(handle);
+  if (ret >= 0)
+    {
+      dhcp6c_get_result(handle, presult);
+      return OK;
+    }
+  else
+    {
+      return ERROR;
+    }
+}
+
+int dhcp6c_request_async(FAR void *handle, dhcp6c_callback_t callback)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  if (pdhcp6c->thread)
+    {
+      nerr("DHCP6C thread already running\n");
+      return ERROR;
+    }
+
+  pdhcp6c->callback = callback;
+  ret = pthread_create(&pdhcp6c->thread, NULL, dhcp6c_run, pdhcp6c);
+  if (ret != 0)
+    {
+      nerr("Failed to start the DHCP6C thread\n");
+      return ERROR;
+    }
+
+  return OK;
+}
+
+void dhcp6c_cancel(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  sighandler_t old;
+  int ret;
+
+  if (pdhcp6c)
+    {
+      pdhcp6c->cancel = true;
+      if (pdhcp6c->thread)
+        {
+          old = signal(SIGQUIT, SIG_IGN);
+
+          /* Signal the dhcp6c_run */
+
+          ret = pthread_kill(pdhcp6c->thread, SIGQUIT);
+          if (ret != 0)
+            {
+              nerr("pthread_kill DHCP6C thread\n");
+            }
+
+          /* Wait for the end of dhcp6c_run */
+
+          ret = pthread_join(pdhcp6c->thread, NULL);
+          if (ret != 0)
+            {
+              nerr("pthread_join DHCP6C thread\n");
+            }
+
+          pdhcp6c->thread = 0;
+          signal(SIGQUIT, old);
+        }
+    }
+}
+
+void dhcp6c_close(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  if (pdhcp6c)
+    {
+      dhcp6c_cancel(pdhcp6c);
+      if (pdhcp6c->urandom_fd)
+        {
+          close(pdhcp6c->urandom_fd);
+        }
+
+      if (pdhcp6c->sockfd)
+        {
+          close(pdhcp6c->sockfd);
+        }
+
+      for (int i = 0; i < STATE_MAX; i++)
+        {
+          if (pdhcp6c->state_data[i])

Review comment:
       ```suggestion
             if (pdhcp6c->state_data[i] != NULL)
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] pkarashchenko commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r828292077



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <nuttx/clock.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(_o, start, end, otype, olen, odata)\
+    for (_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  {false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL},
+  {true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert},
+  {0},
+  {true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL},
+  {0},
+  {false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL},
+  {false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL},
+  {0},
+  {false, 1, 600, "RELEASE", NULL, NULL},
+  {false, 1, 3, "DECLINE", NULL, NULL},
+  {0},
+  {true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL},
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * MSEC_PER_SEC + t.tv_nsec / USEC_PER_SEC;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n != NULL || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n != NULL)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = (new_len != old_len) ||
+             (memcmp(old_data, new_data, new_len) != 0);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (handle == NULL || presult == NULL)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(ite, s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback != NULL)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *ite;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ite, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *ite;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(ite, &rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4u == client_id_len) &&
+                        (memcmp((odata - 4), client_id, client_id_len) == 0);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4u == server_id_len) &&
+                        (memcmp((odata - 4), server_id, server_id_len) == 0);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  const int buf_length = 1536;
+  FAR uint8_t *buf = (FAR uint8_t *)malloc(buf_length);
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      len = -1;
+      goto end;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * MSEC_PER_SEC +
+              dhcp6c_rand_delay(handle, retx->init_timeo * MSEC_PER_SEC)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * MSEC_PER_SEC)
+        {
+          rto = retx->max_timeo * MSEC_PER_SEC +
+                dhcp6c_rand_delay(handle, retx->max_timeo * MSEC_PER_SEC);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * MSEC_PER_SEC)
+        {
+          round_end = timeout * MSEC_PER_SEC + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * MSEC_PER_SEC
+          };
+
+          /* check for dhcp6c_close */
+
+          if (pdhcp6c->cancel)
+            {
+              len = -1;
+              goto end;
+            }
+
+          setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                     &retime, sizeof(retime));
+
+          /* Receive cycle */
+
+          len = recv(pdhcp6c->sockfd, buf, buf_length, 0);
+          if (type != DHCPV6_MSG_UNKNOWN)
+            {
+              ninfo("%s[type:%d] recv len[%d]\n", __func__, type, len);
+            }
+
+          if (!dhcp6c_response_is_valid(handle, buf, len, trid, type))
+            {
+              len = -1;
+            }
+
+          if (len > 0)
+            {
+              FAR uint8_t *opt = &buf[4];
+              FAR uint8_t *opt_end = opt + len - 4;
+
+              round_start = dhcp6c_get_milli_time();
+              elapsed = round_start - start;
+              ninfo("Got a valid reply after %ums\n", (unsigned)elapsed);
+
+              if (retx->handler_reply != NULL)
+                {
+                  len = retx->handler_reply(handle, type, opt,
+                                            opt_end, elapsed / MSEC_PER_SEC);
+                }
+            }
+        }
+
+      if (retx->handler_finish != NULL)
+        {
+          len = retx->handler_finish(handle, elapsed / MSEC_PER_SEC);
+        }
+    }
+  while (len < 0 && elapsed / MSEC_PER_SEC < timeout);
+
+end:
+  free(buf);
+  return len;
+}
+
+static int dhcp6c_poll_reconfigure(FAR void *handle)
+{
+  int ret = dhcp6c_command(handle, DHCPV6_MSG_UNKNOWN);
+  if (ret != -1)
+    {
+      ret = dhcp6c_command(handle, ret);
+    }
+
+  return ret;
+}
+
+/* Collect all advertised servers */
+
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  struct dhcpv6_server_cand_s cand;
+
+  memset(&cand, 0, sizeof(cand));
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_SERVERID && olen <= 130)
+        {
+          memcpy(cand.duid, odata, olen);
+          cand.duid_len = olen;
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOADDRSAVAIL)
+        {
+          if (pdhcp6c->ia_mode == IA_MODE_FORCE)
+            {
+              return -1;
+            }
+          else
+            {
+              cand.has_noaddravail = true;
+              cand.preference -= 1000;
+            }
+        }
+      else if (otype == DHCPV6_OPT_STATUS && olen >= 2 && !odata[0]
+               && odata[1] == DHCPV6_NOPREFIXAVAIL)
+        {
+          cand.preference -= 2000;
+        }
+      else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
+               cand.preference >= 0)
+        {
+          cand.preference = odata[1];
+        }
+      else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+        {
+          cand.wants_reconfigure = true;
+        }
+      else if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+        {
+          FAR struct dhcpv6_ia_hdr_s *h = (FAR void *)odata;
+          FAR uint8_t *oend = odata + olen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *d;
+
+          dhcpv6_for_each_option(ite1, &h[1], oend, otype, olen, d)
+            {
+              if (otype == DHCPV6_OPT_IA_PREFIX)
+                {
+                  cand.preference += 2000;
+                }
+              else if (otype == DHCPV6_OPT_STATUS &&
+                       olen >= 2 && d[0] == 0 &&
+                       d[1] == DHCPV6_NOPREFIXAVAIL)
+                {
+                  cand.preference -= 2000;
+                }
+            }
+        }
+    }
+
+  if (cand.duid_len > 0)
+    {
+      dhcp6c_add_state(handle, STATE_SERVER_CAND, &cand, sizeof(cand));
+    }
+
+  return 0;
+}
+
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t cand_len;
+  FAR struct dhcpv6_server_cand_s *c = NULL;
+  FAR struct dhcpv6_server_cand_s *cand = dhcp6c_get_state(handle,
+                                                           STATE_SERVER_CAND,
+                                                           &cand_len);
+  bool retry = false;
+
+  for (size_t i = 0; i < cand_len / sizeof(*c); ++i)
+    {
+      if (cand[i].has_noaddravail)
+        {
+          retry = true;
+        }
+
+      if (!c || c->preference < cand[i].preference)
+        {
+          c = &cand[i];
+        }
+    }
+
+  if (retry && pdhcp6c->ia_mode == IA_MODE_TRY)
+    {
+      /* We give it a second try without the IA_NA */
+
+      pdhcp6c->ia_mode = IA_MODE_NONE;
+      return dhcp6c_command(handle, DHCPV6_MSG_SOLICIT);
+    }
+
+  if (c)
+    {
+      uint16_t hdr[2] =
+      {
+        htons(DHCPV6_OPT_SERVERID), htons(c->duid_len)
+      };
+
+      dhcp6c_add_state(handle, STATE_SERVER_ID, hdr, sizeof(hdr));
+      dhcp6c_add_state(handle, STATE_SERVER_ID, c->duid, c->duid_len);
+      pdhcp6c->accept_reconfig = c->wants_reconfigure;
+    }
+
+  dhcp6c_clear_state(handle, STATE_SERVER_CAND);
+
+  if (!c)
+    {
+      return -1;
+    }
+  else if (pdhcp6c->request_prefix || pdhcp6c->ia_mode != IA_MODE_NONE)
+    {
+      return DHCPV6_STATEFUL;
+    }
+  else
+    {
+      return DHCPV6_STATELESS;
+    }
+}
+
+static time_t dhcp6c_parse_ia(FAR void *handle, FAR void *opt, FAR void *end)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint32_t timeout = UINT32_MAX;
+  uint16_t otype;
+  uint16_t olen;
+  uint16_t stype;
+  uint16_t slen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  FAR uint8_t *sdata;
+
+  /* Update address IA */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_IA_PREFIX)
+        {
+          FAR struct dhcpv6_ia_prefix_s *prefix = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_prefix_s *local = NULL;
+          uint32_t valid;
+          uint32_t pref;
+          size_t pd_len;
+          FAR uint8_t *pd;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*prefix))
+            {
+              continue;
+            }
+
+          olen = sizeof(*prefix);
+          valid = ntohl(prefix->valid);
+          pref = ntohl(prefix->preferred);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          pd = dhcp6c_get_state(handle, STATE_IA_PD, &pd_len);
+          dhcpv6_for_each_option(ite1, pd, pd + pd_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata + 8, odata + 8,
+                          sizeof(local->addr) + 1))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = prefix->preferred;
+              local->valid = prefix->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_PD, prefix, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+      else if (otype == DHCPV6_OPT_IA_ADDR)
+        {
+          FAR struct dhcpv6_ia_addr_s *addr = (FAR void *)(odata - 4);
+          FAR struct dhcpv6_ia_addr_s *local = NULL;
+          uint32_t pref;
+          uint32_t valid;
+          size_t na_len;
+          FAR uint8_t *na;
+          FAR uint8_t *ite1;
+
+          if (olen + 4U < sizeof(*addr))
+            {
+              continue;
+            }
+
+          olen = sizeof(*addr);
+          pref = ntohl(addr->preferred);
+          valid = ntohl(addr->valid);
+
+          if (pref > valid)
+            {
+              continue;
+            }
+
+          /* Search matching IA */
+
+          na = dhcp6c_get_state(handle, STATE_IA_NA, &na_len);
+          dhcpv6_for_each_option(ite1, na, na + na_len,
+                                 stype, slen, sdata)
+            {
+              if (!memcmp(sdata, odata, sizeof(local->addr)))
+                {
+                  local = (FAR void *)(sdata - 4);
+                }
+            }
+
+          if (local)
+            {
+              local->preferred = addr->preferred;
+              local->valid = addr->valid;
+            }
+          else
+            {
+              dhcp6c_add_state(handle, STATE_IA_NA, addr, olen);
+            }
+
+          if (timeout > valid)
+            {
+              timeout = valid;
+            }
+        }
+    }
+
+  return timeout;
+}
+
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *ite0;
+  FAR uint8_t *odata;
+  bool have_update = false;
+  size_t ia_na_len;
+  size_t ia_pd_len;
+  size_t dns_len;
+  size_t search_len;
+  size_t sntp_ip_len;
+  size_t sntp_dns_len;
+  size_t sip_ip_len;
+  size_t sip_fqdn_len;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  FAR uint8_t *ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  FAR uint8_t *ia_end;
+  pdhcp6c->t1 = UINT32_MAX;
+  pdhcp6c->t2 = UINT32_MAX;
+  pdhcp6c->t3 = UINT32_MAX;
+
+  dhcp6c_get_state(handle, STATE_DNS, &dns_len);
+  dhcp6c_get_state(handle, STATE_SEARCH, &search_len);
+  dhcp6c_get_state(handle, STATE_SNTP_IP, &sntp_ip_len);
+  dhcp6c_get_state(handle, STATE_SNTP_FQDN, &sntp_dns_len);
+  dhcp6c_get_state(handle, STATE_SIP_IP, &sip_ip_len);
+  dhcp6c_get_state(handle, STATE_SIP_FQDN, &sip_fqdn_len);
+
+  /* Decrease valid and preferred lifetime of prefixes */
+
+  dhcpv6_for_each_option(ite0, ia_pd, ia_pd + ia_pd_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Decrease valid and preferred lifetime of addresses */
+
+  dhcpv6_for_each_option(ite0, ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      uint32_t valid = ntohl(p->valid);
+      uint32_t pref = ntohl(p->preferred);
+
+      if (valid != UINT32_MAX)
+        {
+          p->valid = (valid < elapsed) ? 0 : htonl(valid - elapsed);
+        }
+
+      if (pref != UINT32_MAX)
+        {
+          p->preferred = (pref < elapsed) ? 0 : htonl(pref - elapsed);
+        }
+    }
+
+  /* Parse and find all matching IAs */
+
+  dhcpv6_for_each_option(ite0, opt, end, otype, olen, odata)
+    {
+      if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)
+           && olen > sizeof(struct dhcpv6_ia_hdr_s))
+        {
+          FAR struct dhcpv6_ia_hdr_s *ia_hdr = (FAR void *)(odata - 4);
+          time_t l_t1 = ntohl(ia_hdr->t1);
+          time_t l_t2 = ntohl(ia_hdr->t2);
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *ite1;
+          FAR uint8_t *sdata;
+          time_t n;
+
+          /* Test ID and T1-T2 validity */
+
+          if (ia_hdr->iaid != 1 || l_t2 < l_t1)
+            {
+              continue;
+            }
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, &ia_hdr[1], odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (stype == DHCPV6_OPT_STATUS && slen >= 2 &&
+                  (sdata[0] || sdata[1]))
+                {
+                  continue;
+                }
+            }
+
+          /* Update times */
+
+          if (l_t1 > 0 && pdhcp6c->t1 > l_t1)
+            {
+              pdhcp6c->t1 = l_t1;
+            }
+
+          if (l_t2 > 0 && pdhcp6c->t2 > l_t2)
+            {
+              pdhcp6c->t2 = l_t2;
+            }
+
+          /* Always report update in case we have IA_PDs so that
+           * the state-script is called with updated times
+           */
+
+          if (otype == DHCPV6_OPT_IA_PD && pdhcp6c->request_prefix)
+            {
+              have_update = true;
+            }
+
+          n = dhcp6c_parse_ia(handle, &ia_hdr[1], odata + olen);
+          if (n < pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = n;
+            }
+
+          if (n < pdhcp6c->t2)
+            {
+              pdhcp6c->t2 = n;
+            }
+
+          if (n < pdhcp6c->t3)
+            {
+              pdhcp6c->t3 = n;
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_SERVERS)
+        {
+          if (olen % 16 == 0)
+            {
+              dhcp6c_add_state(handle, STATE_DNS, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_DNS_DOMAIN)
+        {
+          dhcp6c_add_state(handle, STATE_SEARCH, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_NTP_SERVER)
+        {
+          uint16_t stype;
+          uint16_t slen;
+          FAR uint8_t *sdata;
+          FAR uint8_t *ite1;
+
+          /* Test status and bail if error */
+
+          dhcpv6_for_each_option(ite1, odata, odata + olen,
+                                 stype, slen, sdata)
+            {
+              if (slen == 16 &&
+                  (stype == NTP_MC_ADDR || stype == NTP_SRV_ADDR))
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_IP, sdata, slen);
+                }
+              else if (slen > 0 && stype == NTP_SRV_FQDN)
+                {
+                  dhcp6c_add_state(handle, STATE_SNTP_FQDN, sdata, slen);
+                }
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_A)
+        {
+          if (olen == 16)
+            {
+              dhcp6c_add_state(handle, STATE_SIP_IP, odata, olen);
+            }
+        }
+      else if (otype == DHCPV6_OPT_SIP_SERVER_D)
+        {
+          dhcp6c_add_state(handle, STATE_SIP_FQDN, odata, olen);
+        }
+      else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4)
+        {
+          uint32_t refresh = ntohl(*((FAR uint32_t *)odata));
+          if (refresh < (uint32_t)pdhcp6c->t1)
+            {
+              pdhcp6c->t1 = refresh;
+            }
+        }
+      else if (otype != DHCPV6_OPT_CLIENTID && otype != DHCPV6_OPT_SERVERID)
+        {
+          dhcp6c_add_state(handle, STATE_CUSTOM_OPTS, (odata - 4), olen + 4);
+        }
+    }
+
+  if (opt)
+    {
+      size_t new_ia_pd_len;
+      size_t new_ia_na_len;
+      have_update |= dhcp6c_commit_state(handle, STATE_DNS, dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SEARCH, search_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_IP,
+                                         sntp_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SNTP_FQDN,
+                                         sntp_dns_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_IP, sip_ip_len);
+      have_update |= dhcp6c_commit_state(handle, STATE_SIP_FQDN,
+                                         sip_fqdn_len);
+      dhcp6c_get_state(handle, STATE_IA_PD, &new_ia_pd_len);
+      dhcp6c_get_state(handle, STATE_IA_NA, &new_ia_na_len);
+      have_update |= (new_ia_pd_len != ia_pd_len) ||
+                     (new_ia_na_len != ia_na_len);
+    }
+
+  /* Delete prefixes with 0 valid-time */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  ia_end = ia_pd + ia_pd_len;
+  dhcpv6_for_each_option(ite0, ia_pd, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_prefix_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_pd + dhcp6c_remove_state(handle, STATE_IA_PD,
+                   (FAR uint8_t *)p - ia_pd, olen + 4);
+          have_update = true;
+        }
+    }
+
+  /* Delete addresses with 0 valid-time */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  ia_end = ia_na + ia_na_len;
+  dhcpv6_for_each_option(ite0, ia_na, ia_end, otype, olen, odata)
+    {
+      FAR struct dhcpv6_ia_addr_s *p = (FAR void *)(odata - 4);
+      while (!p->valid)
+        {
+          ia_end = ia_na + dhcp6c_remove_state(handle, STATE_IA_NA,
+                   (FAR uint8_t *)p - ia_na, olen + 4);
+          have_update = true;
+        }
+    }
+
+  return have_update;
+}
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint16_t otype;
+  uint16_t olen;
+  FAR uint8_t *odata;
+  FAR uint8_t *ite;
+  uint8_t msg = DHCPV6_MSG_RENEW;
+
+  /* TODO: should verify the reconfigure message */
+
+  dhcpv6_for_each_option(ite, opt, end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1 &&
+          (odata[0] == DHCPV6_MSG_RENEW ||
+           odata[0] == DHCPV6_MSG_INFO_REQ))
+        {
+          msg = odata[0];
+        }
+    }
+
+  pdhcp6c->t1 -= elapsed;
+  pdhcp6c->t2 -= elapsed;
+  pdhcp6c->t3 -= elapsed;
+
+  if (pdhcp6c->t1 < 0)
+    {
+      pdhcp6c->t1 = 0;
+    }
+
+  if (pdhcp6c->t2 < 0)
+    {
+      pdhcp6c->t2 = 0;
+    }
+
+  if (pdhcp6c->t3 < 0)
+    {
+      pdhcp6c->t3 = 0;
+    }
+
+  dhcp6c_handle_reply(handle, DHCPV6_MSG_UNKNOWN, NULL, NULL, elapsed);
+
+  return msg;
+}
+
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed)
+{
+  dhcp6c_handle_advert(handle, orig, opt, end, elapsed);
+  if (dhcp6c_commit_advert(handle, elapsed) < 0)
+    {
+      return -1;
+    }
+
+  return dhcp6c_handle_reply(handle, orig, opt, end, elapsed);
+}
+
+static int dhcp6c_single_request(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  FAR const char *process = NULL;
+  enum dhcpv6_mode_e mode;
+  enum dhcpv6_msg_e type;
+  int ret = -1;
+
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+  dhcp6c_clear_state(pdhcp6c, STATE_SERVER_CAND);
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SNTP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_IP);
+  dhcp6c_clear_state(pdhcp6c, STATE_SIP_FQDN);
+  dhcp6c_clear_state(pdhcp6c, STATE_CUSTOM_OPTS);
+  ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_SOLICIT);
+  if (ret < 0)
+    {
+      return -1;
+    }
+  else if (ret == DHCPV6_STATELESS)
+    {
+      mode = DHCPV6_STATELESS;
+      type = DHCPV6_MSG_INFO_REQ;
+      process = "informed";
+    }
+  else
+    {
+      mode = DHCPV6_STATEFUL;
+      type = DHCPV6_MSG_REQUEST;
+      process = "bound";
+    }
+
+  ret = dhcp6c_command(pdhcp6c, type);
+  if (ret >= 0)
+    {
+      ret = mode;
+      dhcp6c_switch_process(pdhcp6c, process);
+    }
+
+  return ret;
+}
+
+static int dhcp6c_lease(FAR void *args, uint8_t type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  enum dhcpv6_mode_e mode = (enum dhcpv6_mode_e)type;
+  size_t ia_pd_len;
+  size_t ia_na_len;
+  size_t ia_pd_new;
+  size_t ia_na_new;
+  size_t server_id_len;
+  int ret = -1;
+
+  if (mode == DHCPV6_STATELESS)
+    {
+      /* Stateless mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Wait for T1 to expire or until we get a reconfigure */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          /* Information-Request */
+
+          ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_INFO_REQ);
+          if (ret < 0)
+            {
+              nerr("DHCPV6_MSG_INFO_REQ error\n");
+              break;
+            }
+          else
+            {
+              dhcp6c_switch_process(pdhcp6c, "informed");
+            }
+        }
+    }
+  else
+    {
+      /* Stateful mode */
+
+      while (!pdhcp6c->cancel)
+        {
+          /* Renew Cycle
+           * Wait for T1 to expire or until we get a reconfigure
+           */
+
+          ret = dhcp6c_poll_reconfigure(pdhcp6c);
+          if (ret >= 0)
+            {
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+          dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+
+          /* If we have any IAs, send renew, otherwise request */
+
+          if (ia_pd_len == 0 && ia_na_len == 0)
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REQUEST);
+          else
+            ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_RENEW);
+
+          if (pdhcp6c->cancel)
+            {
+              break;
+            }
+
+          if (ret >= 0)
+            {
+              /* Publish updates */
+
+              dhcp6c_switch_process(pdhcp6c, "updated");
+            }
+          else
+            {
+              /* Remove binding */
+
+              dhcp6c_clear_state(pdhcp6c, STATE_SERVER_ID);
+
+              /* If we have IAs, try rebind otherwise restart */
+
+              ret = dhcp6c_command(pdhcp6c, DHCPV6_MSG_REBIND);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_new);
+              dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_new);
+
+              /* We lost all our IAs, restart */
+
+              if (ret < 0 || (ia_pd_new == 0 && ia_pd_len) ||
+                  (ia_na_new == 0 && ia_na_len))
+                {
+                  break;
+                }
+              else if (ret >= 0)
+                {
+                  dhcp6c_switch_process(pdhcp6c, "rebound");
+                }
+            }
+        }
+    }
+
+  dhcp6c_get_state(pdhcp6c, STATE_IA_PD, &ia_pd_len);
+  dhcp6c_get_state(pdhcp6c, STATE_IA_NA, &ia_na_len);
+  dhcp6c_get_state(pdhcp6c, STATE_SERVER_ID, &server_id_len);
+
+  /* Add all prefixes to lost prefixes */
+
+  dhcp6c_clear_state(pdhcp6c, STATE_IA_PD);
+  dhcp6c_switch_process(pdhcp6c, "unbound");
+
+  /* Remove assigned addresses */
+
+  if (ia_na_len > 0)
+    {
+      dhcp6c_remove_addrs(pdhcp6c);
+    }
+
+  if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0))
+    {
+      dhcp6c_command(pdhcp6c, DHCPV6_MSG_RELEASE);
+    }
+
+  return ret;
+}
+
+static FAR void *dhcp6c_run(FAR void *args)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)args;
+  int ret;
+
+  while (!pdhcp6c->cancel)
+    {
+      ret = dhcp6c_single_request(pdhcp6c);
+      if (ret > 0)
+        {
+          dhcp6c_lease(pdhcp6c, ret);
+        }
+    }
+
+  return 0;
+}
+
+static FAR void *dhcp6c_precise_open(FAR const char *ifname,
+                                     enum dhcp6c_ia_mode_e ia_mode,
+                                     bool request_pd,
+                                     uint16_t opt[], int cnt)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c;
+  struct ifreq ifr;
+  size_t client_id_len;
+  int val = 1;
+  uint16_t oro[] =
+  {
+    htons(DHCPV6_OPT_DNS_SERVERS),
+    htons(DHCPV6_OPT_DNS_DOMAIN),
+    htons(DHCPV6_OPT_NTP_SERVER),
+    htons(DHCPV6_OPT_SIP_SERVER_A),
+    htons(DHCPV6_OPT_SIP_SERVER_D)
+  };
+
+  struct sockaddr_in6 client_addr =
+  {
+    .sin6_family = AF_INET6,
+    .sin6_port = htons(DHCPV6_CLIENT_PORT),
+    .sin6_flowinfo = 0
+  };
+
+  pdhcp6c = malloc(sizeof(struct dhcp6c_state_s));
+  if (!pdhcp6c)
+    {
+      return NULL;
+    }
+
+  memset(pdhcp6c, 0, sizeof(*pdhcp6c));
+  pdhcp6c->urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY);
+  if (pdhcp6c->urandom_fd < 0)
+    {
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->sockfd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+  if (pdhcp6c->sockfd < 0)
+    {
+      close(pdhcp6c->urandom_fd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  /* Detect interface */
+
+  strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+  if (ioctl(pdhcp6c->sockfd, SIOCGIFINDEX, &ifr))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->ifindex = ifr.ifr_ifindex;
+
+  /* Create client DUID */
+
+  dhcp6c_get_state(pdhcp6c, STATE_CLIENT_ID, &client_id_len);
+  if (client_id_len == 0)
+    {
+      uint8_t duid[14] =
+      {
+        0, DHCPV6_OPT_CLIENTID, 0, 10, 0,
+        DHCPV6_DUID_LLADDR, 0, 1
+      };
+
+      uint8_t zero[ETHER_ADDR_LEN];
+      struct ifreq ifs[100];
+      FAR struct ifreq *ifp;
+      FAR struct ifreq *ifend;
+      struct ifconf ifc;
+
+      ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+      memcpy(&duid[8], ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+      memset(zero, 0, sizeof(zero));
+      ifc.ifc_req = ifs;
+      ifc.ifc_len = sizeof(ifs);
+
+      if (!memcmp(&duid[8], zero, ETHER_ADDR_LEN) &&
+          ioctl(pdhcp6c->sockfd, SIOCGIFCONF, &ifc) >= 0)
+        {
+          /* If our interface doesn't have an address... */
+
+          ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+          for (ifp = ifc.ifc_req; ifp < ifend &&
+               !memcmp(&duid[8], zero, 6); ifp++)
+            {
+              memcpy(ifr.ifr_name, ifp->ifr_name,
+                     sizeof(ifr.ifr_name));
+              ioctl(pdhcp6c->sockfd, SIOCGIFHWADDR, &ifr);
+              memcpy(&duid[8], ifr.ifr_hwaddr.sa_data,
+                     ETHER_ADDR_LEN);
+            }
+        }
+
+      dhcp6c_add_state(pdhcp6c, STATE_CLIENT_ID, duid, sizeof(duid));
+    }
+
+  /* Create ORO */
+
+  dhcp6c_add_state(pdhcp6c, STATE_ORO, oro, sizeof(oro));
+
+  /* Configure IPv6-options */
+
+  setsockopt(pdhcp6c->sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+  setsockopt(pdhcp6c->sockfd, SOL_SOCKET, UDP_BINDTODEVICE, ifname,
+             strlen(ifname));
+  if (bind(pdhcp6c->sockfd, (struct sockaddr *)&client_addr,
+           sizeof(client_addr)))
+    {
+      close(pdhcp6c->urandom_fd);
+      close(pdhcp6c->sockfd);
+      free(pdhcp6c);
+      return NULL;
+    }
+
+  pdhcp6c->thread = 0;
+  pdhcp6c->cancel = false;
+  pdhcp6c->t1 = 0;
+  pdhcp6c->t2 = 0;
+  pdhcp6c->t3 = 0;
+  pdhcp6c->request_prefix = request_pd;
+  switch (ia_mode)
+    {
+      case IA_MODE_NONE:
+      case IA_MODE_TRY:
+      case IA_MODE_FORCE:
+        break;
+      default:
+        ia_mode = IA_MODE_TRY;
+        break;
+    }
+
+  pdhcp6c->ia_mode = ia_mode;
+  pdhcp6c->accept_reconfig = false;
+  if (opt && cnt > 0)
+    {
+      uint16_t opttype;
+      for (int i = 0; i < cnt; i++)
+        {
+          opttype = htons(opt[i]);
+          dhcp6c_add_state(pdhcp6c, STATE_ORO, &opttype, 2);
+        }
+    }
+
+  return pdhcp6c;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR void *dhcp6c_open(FAR const char *interface)
+{
+  return dhcp6c_precise_open(interface, IA_MODE_TRY, true, NULL, 0);
+}
+
+int dhcp6c_request(FAR void *handle, FAR struct dhcp6c_state *presult)
+{
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  ret = dhcp6c_single_request(handle);
+  if (ret >= 0)
+    {
+      dhcp6c_get_result(handle, presult);
+      return OK;
+    }
+  else
+    {
+      return ERROR;
+    }
+}
+
+int dhcp6c_request_async(FAR void *handle, dhcp6c_callback_t callback)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int ret;
+
+  if (!handle)
+    {
+      return ERROR;
+    }
+
+  if (pdhcp6c->thread)
+    {
+      nerr("DHCP6C thread already running\n");
+      return ERROR;
+    }
+
+  pdhcp6c->callback = callback;
+  ret = pthread_create(&pdhcp6c->thread, NULL, dhcp6c_run, pdhcp6c);
+  if (ret != 0)
+    {
+      nerr("Failed to start the DHCP6C thread\n");
+      return ERROR;
+    }
+
+  return OK;
+}
+
+void dhcp6c_cancel(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  sighandler_t old;
+  int ret;
+
+  if (pdhcp6c)

Review comment:
       ```suggestion
     if (pdhcp6c != NULL)
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] pkarashchenko commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r825709017



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\

Review comment:
       Better to pass iterator as a parameter, because current version is C89 incompliant

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)&odata[-4];
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)&odata[-4];
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)&odata[-4];
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < (ssize_t)sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)))
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(&rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4U == client_id_len) &&
+                        !memcmp(&odata[-4], client_id, client_id_len);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4U == server_id_len) &&
+                        !memcmp(&odata[-4], server_id, server_id_len);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint8_t buf[1536];

Review comment:
       I suggest to move it to heap and then we can remove `_packed_struct` from related types.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] pkarashchenko commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r827981718



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < (ssize_t)sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)))
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(&rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4U == client_id_len) &&
+                        !memcmp((odata - 4), client_id, client_id_len);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4U == server_id_len) &&
+                        !memcmp((odata - 4), server_id, server_id_len);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint8_t buf[1536];
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      return -1;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * 1000 +
+              dhcp6c_rand_delay(handle, retx->init_timeo * 1000)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * 1000)
+        {
+          rto = retx->max_timeo * 1000 +
+                dhcp6c_rand_delay(handle, retx->max_timeo * 1000);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * 1000)
+        {
+          round_end = timeout * 1000 + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / 1000, (t % 1000) * 1000

Review comment:
       Many apps from already use it. Including `ping`. do you recommend to clean-up the usage of those macros in a separate PR?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] xiaoxiang781216 merged pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067


   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx-apps] pkarashchenko commented on a change in pull request #1067: Add dhcp6c module

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #1067:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1067#discussion_r827739031



##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)

Review comment:
       ```suggestion
     if (n != NULL)
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)

Review comment:
       ```suggestion
     if (n != NULL || pdhcp6c->state_len[state] + len == 0)
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < (ssize_t)sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)))
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(&rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4U == client_id_len) &&
+                        !memcmp((odata - 4), client_id, client_id_len);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4U == server_id_len) &&
+                        !memcmp((odata - 4), server_id, server_id_len);

Review comment:
       ```suggestion
             serverid_ok = (olen + 4u == server_id_len) &&
                           memcmp((odata - 4), server_id, server_id_len) == 0;
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < (ssize_t)sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)))

Review comment:
       ```suggestion
     if (len < sizeof(*rep) ||
         memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)) != 0)
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);

Review comment:
       ```suggestion
     bool upd = new_len != old_len || memcmp(old_data, new_data, new_len) != 0;
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },

Review comment:
       this assignment is C89 incompatible

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)

Review comment:
       ```suggestion
     if (pdhcp6c->callback != NULL)
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < (ssize_t)sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)))
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(&rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4U == client_id_len) &&
+                        !memcmp((odata - 4), client_id, client_id_len);

Review comment:
       ```suggestion
             clientid_ok = (olen + 4u == client_id_len) &&
                           memcmp((odata - 4), client_id, client_id_len) == 0;
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)

Review comment:
       ```suggestion
     if (handle == NULL || presult == NULL)
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < (ssize_t)sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)))
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(&rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4U == client_id_len) &&
+                        !memcmp((odata - 4), client_id, client_id_len);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4U == server_id_len) &&
+                        !memcmp((odata - 4), server_id, server_id_len);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint8_t buf[1536];
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3? 3 :

Review comment:
       ```suggestion
     uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3 ? 3 :
   ```

##########
File path: netutils/dhcp6c/dhcp6c.c
##########
@@ -0,0 +1,1951 @@
+/****************************************************************************
+ * apps/netutils/dhcp6c/dhcp6c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <resolv.h>
+#include <string.h>
+#include <unistd.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "netutils/dhcp6c.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DHCPV6_ALL_RELAYS {{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02}}}
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_DUID_LLADDR 3
+#define DHCPV6_REQ_DELAY 1
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
+    for (FAR uint8_t *_o = (FAR uint8_t *)(start); _o + 4 <= (FAR uint8_t *)(end) &&\
+        ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (FAR void *)&_o[4]) &&\
+        ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (FAR uint8_t *)(end); \
+        _o += 4 + (_o[2] << 8 | _o[3]))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum dhcpv6_opt_e
+{
+  DHCPV6_OPT_CLIENTID = 1,
+  DHCPV6_OPT_SERVERID = 2,
+  DHCPV6_OPT_IA_NA = 3,
+  DHCPV6_OPT_IA_ADDR = 5,
+  DHCPV6_OPT_ORO = 6,
+  DHCPV6_OPT_PREF = 7,
+  DHCPV6_OPT_ELAPSED = 8,
+  DHCPV6_OPT_RELAY_MSG = 9,
+  DHCPV6_OPT_AUTH = 11,
+  DHCPV6_OPT_STATUS = 13,
+  DHCPV6_OPT_RAPID_COMMIT = 14,
+  DHCPV6_OPT_RECONF_MESSAGE = 19,
+  DHCPV6_OPT_RECONF_ACCEPT = 20,
+  DHCPV6_OPT_DNS_SERVERS = 23,
+  DHCPV6_OPT_DNS_DOMAIN = 24,
+  DHCPV6_OPT_IA_PD = 25,
+  DHCPV6_OPT_IA_PREFIX = 26,
+  DHCPV6_OPT_INFO_REFRESH = 32,
+  DHCPV6_OPT_FQDN = 39,
+  DHCPV6_OPT_NTP_SERVER = 56,
+  DHCPV6_OPT_SIP_SERVER_D = 21,
+  DHCPV6_OPT_SIP_SERVER_A = 22,
+};
+
+enum dhcpv6_opt_npt_e
+{
+  NTP_SRV_ADDR = 1,
+  NTP_MC_ADDR = 2,
+  NTP_SRV_FQDN = 3
+};
+
+enum dhcpv6_msg_e
+{
+  DHCPV6_MSG_UNKNOWN = 0,
+  DHCPV6_MSG_SOLICIT = 1,
+  DHCPV6_MSG_ADVERT = 2,
+  DHCPV6_MSG_REQUEST = 3,
+  DHCPV6_MSG_RENEW = 5,
+  DHCPV6_MSG_REBIND = 6,
+  DHCPV6_MSG_REPLY = 7,
+  DHCPV6_MSG_RELEASE = 8,
+  DHCPV6_MSG_DECLINE = 9,
+  DHCPV6_MSG_RECONF = 10,
+  DHCPV6_MSG_INFO_REQ = 11,
+  DHCPV6_MSG_MAX
+};
+
+enum dhcpv6_status_e
+{
+  DHCPV6_NOADDRSAVAIL = 2,
+  DHCPV6_NOPREFIXAVAIL = 6
+};
+
+enum dhcpv6_mode_e
+{
+  DHCPV6_UNKNOWN,
+  DHCPV6_STATELESS,
+  DHCPV6_STATEFUL
+};
+
+enum dhcpv6_state_e
+{
+  STATE_CLIENT_ID,
+  STATE_SERVER_ID,
+  STATE_SERVER_CAND,
+  STATE_ORO,
+  STATE_DNS,
+  STATE_SEARCH,
+  STATE_IA_NA,
+  STATE_IA_PD,
+  STATE_CUSTOM_OPTS,
+  STATE_SNTP_IP,
+  STATE_SNTP_FQDN,
+  STATE_SIP_IP,
+  STATE_SIP_FQDN,
+  STATE_MAX
+};
+
+enum dhcp6c_ia_mode_e
+{
+  IA_MODE_NONE,
+  IA_MODE_TRY,
+  IA_MODE_FORCE,
+};
+
+/* DHCPV6 Protocol Headers */
+
+begin_packed_struct struct dhcpv6_header_s
+{
+  uint8_t msg_type;
+  uint8_t tr_id[3];
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_hdr_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t iaid;
+  uint32_t t1;
+  uint32_t t2;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_addr_s
+{
+  uint16_t type;
+  uint16_t len;
+  struct in6_addr addr;
+  uint32_t preferred;
+  uint32_t valid;
+} end_packed_struct;
+
+begin_packed_struct struct dhcpv6_ia_prefix_s
+{
+  uint16_t type;
+  uint16_t len;
+  uint32_t preferred;
+  uint32_t valid;
+  uint8_t prefix;
+  struct in6_addr addr;
+} end_packed_struct;
+
+struct dhcpv6_server_cand_s
+{
+  bool has_noaddravail;
+  bool wants_reconfigure;
+  int16_t preference;
+  uint8_t duid_len;
+  uint8_t duid[130];
+};
+
+struct dhcp6c_retx_s
+{
+  bool delay;
+  uint8_t init_timeo;
+  uint16_t max_timeo;
+  char name[8];
+  int(*handler_reply)(FAR void *handle, enum dhcpv6_msg_e orig,
+                      FAR const void *opt, FAR const void *end,
+                      uint32_t elapsed);
+  int(*handler_finish)(FAR void *handle, uint32_t elapsed);
+};
+
+struct dhcp6c_state_s
+{
+  pthread_t thread;
+  bool cancel;
+  dhcp6c_callback_t callback;
+  int sockfd;
+  int urandom_fd;
+  int ifindex;
+  time_t t1;
+  time_t t2;
+  time_t t3;
+  bool request_prefix;
+  enum dhcp6c_ia_mode_e ia_mode;
+  bool accept_reconfig;
+  FAR uint8_t *state_data[STATE_MAX];
+  size_t state_len[STATE_MAX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int dhcp6c_handle_reconfigure(FAR void *handle,
+                                     enum dhcpv6_msg_e orig,
+                                     FAR const void *opt,
+                                     FAR const void *end,
+                                     uint32_t elapsed);
+static int dhcp6c_handle_advert(FAR void *handle, enum dhcpv6_msg_e orig,
+                                FAR const void *opt, FAR const void *end,
+                                uint32_t elapsed);
+static int dhcp6c_commit_advert(FAR void *handle, uint32_t elapsed);
+static int dhcp6c_handle_reply(FAR void *handle, enum dhcpv6_msg_e orig,
+                               FAR const void *opt, FAR const void *end,
+                               uint32_t elapsed);
+static int dhcp6c_handle_rebind_reply(FAR void *handle,
+                                      enum dhcpv6_msg_e orig,
+                                      FAR const void *opt,
+                                      FAR const void *end,
+                                      uint32_t elapsed);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* RFC 3315 - 5.5 Timeout and Delay values */
+
+static const struct dhcp6c_retx_s g_dhcp6c_retx[DHCPV6_MSG_MAX] =
+{
+  [DHCPV6_MSG_UNKNOWN] =
+  {
+    false, 1, 120, "<POLL>", dhcp6c_handle_reconfigure, NULL
+  },
+  [DHCPV6_MSG_SOLICIT] =
+  {
+    true, 1, 120, "SOLICIT", dhcp6c_handle_advert, dhcp6c_commit_advert
+  },
+  [DHCPV6_MSG_REQUEST] =
+  {
+    true, 1, 30, "REQUEST", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_RENEW] =
+  {
+    false, 10, 600, "RENEW", dhcp6c_handle_reply, NULL
+  },
+  [DHCPV6_MSG_REBIND] =
+  {
+    false, 10, 600, "REBIND", dhcp6c_handle_rebind_reply, NULL
+  },
+  [DHCPV6_MSG_RELEASE] =
+  {
+    false, 1, 600, "RELEASE", NULL, NULL
+  },
+  [DHCPV6_MSG_DECLINE] =
+  {
+    false, 1, 3, "DECLINE", NULL, NULL
+  },
+  [DHCPV6_MSG_INFO_REQ] =
+  {
+    true, 1, 120, "INFOREQ", dhcp6c_handle_reply, NULL
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint64_t dhcp6c_get_milli_time(void)
+{
+  struct timespec t;
+
+  clock_gettime(CLOCK_MONOTONIC, &t);
+
+  return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+static FAR uint8_t *dhcp6c_resize_state(FAR void *handle,
+                                        enum dhcpv6_state_e state,
+                                        ssize_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *n;
+
+  if (len == 0)
+    {
+      return pdhcp6c->state_data[state] + pdhcp6c->state_len[state];
+    }
+
+  n = realloc(pdhcp6c->state_data[state], pdhcp6c->state_len[state] + len);
+  if (n || pdhcp6c->state_len[state] + len == 0)
+    {
+      pdhcp6c->state_data[state] = n;
+      n += pdhcp6c->state_len[state];
+      pdhcp6c->state_len[state] += len;
+    }
+
+  return n;
+}
+
+static void dhcp6c_clear_state(FAR void *handle, enum dhcpv6_state_e state)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  pdhcp6c->state_len[state] = 0;
+}
+
+static void dhcp6c_add_state(FAR void *handle, enum dhcpv6_state_e state,
+                             FAR const void *data, size_t len)
+{
+  FAR uint8_t *n = dhcp6c_resize_state(handle, state, len);
+
+  if (n)
+    {
+      memcpy(n, data, len);
+    }
+}
+
+static size_t dhcp6c_remove_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  size_t offset, size_t len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR uint8_t *data = pdhcp6c->state_data[state];
+  ssize_t len_after = pdhcp6c->state_len[state] - (offset + len);
+
+  if (len_after < 0)
+    {
+      return pdhcp6c->state_len[state];
+    }
+
+  memmove(data + offset, data + offset + len, len_after);
+
+  return pdhcp6c->state_len[state] -= len;
+}
+
+static bool dhcp6c_commit_state(FAR void *handle, enum dhcpv6_state_e state,
+                                size_t old_len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t new_len = pdhcp6c->state_len[state] - old_len;
+  FAR uint8_t *old_data = pdhcp6c->state_data[state];
+  FAR uint8_t *new_data = old_data + old_len;
+  bool upd = new_len != old_len || memcmp(old_data, new_data, new_len);
+
+  memmove(old_data, new_data, new_len);
+  dhcp6c_resize_state(handle, state, -old_len);
+
+  return upd;
+}
+
+static FAR void *dhcp6c_get_state(FAR void *handle,
+                                  enum dhcpv6_state_e state,
+                                  FAR size_t *len)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+
+  *len = pdhcp6c->state_len[state];
+  return pdhcp6c->state_data[state];
+}
+
+static void dhcp6c_get_result(FAR void *handle,
+                              FAR struct dhcp6c_state *presult)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t s_len;
+  FAR uint8_t *s_data;
+  uint16_t olen;
+  uint16_t otype;
+  FAR uint8_t *odata;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  FAR struct dhcpv6_ia_prefix_s *pd;
+  FAR struct in6_addr *dns;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  if (!handle || !presult)
+    {
+      return;
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_NA, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      memcpy(&presult->addr, &addr->addr, sizeof(presult->addr));
+      inet_ntop(AF_INET6, &presult->addr, addr_str, sizeof(addr_str));
+      ninfo("IA_NA %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  s_data = dhcp6c_get_state(handle, STATE_IA_PD, &s_len);
+  dhcpv6_for_each_option(s_data, s_data + s_len, otype, olen, odata)
+    {
+      pd = (FAR void *)(odata - 4);
+      memcpy(&presult->pd, &pd->addr, sizeof(presult->pd));
+      presult->pl = pd->prefix;
+      netlib_prefix2ipv6netmask(presult->pl, &presult->netmask);
+      inet_ntop(AF_INET6, &presult->pd, addr_str, sizeof(addr_str));
+      ninfo("IA_PD %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+      inet_ntop(AF_INET6, &presult->netmask, addr_str, sizeof(addr_str));
+      ninfo("netmask %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+    }
+
+  dns = dhcp6c_get_state(handle, STATE_DNS, &s_len);
+  memcpy(&presult->dns, dns, sizeof(presult->dns));
+  inet_ntop(AF_INET6, &presult->dns, addr_str, sizeof(addr_str));
+  ninfo("DNS server %s for iface %i\n", addr_str, pdhcp6c->ifindex);
+
+  presult->t1 = pdhcp6c->t1;
+  presult->t2 = pdhcp6c->t2;
+  ninfo("T1:%d T2:%d for iface %i\n", presult->t1, presult->t2,
+        pdhcp6c->ifindex);
+}
+
+static void dhcp6c_switch_process(FAR void *handle, FAR const char *name)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  struct dhcp6c_state result;
+
+  ninfo("Process switch to %s\n", name);
+
+  /* Delete lost prefixes and user opts */
+
+  dhcp6c_clear_state(handle, STATE_CUSTOM_OPTS);
+
+  if (pdhcp6c->callback)
+    {
+      memset(&result, 0, sizeof(result));
+      dhcp6c_get_result(pdhcp6c, &result);
+      pdhcp6c->callback(&result);
+    }
+}
+
+static void dhcp6c_remove_addrs(FAR void *handle)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  size_t ia_na_len;
+  FAR uint8_t *odata;
+  FAR uint8_t *ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  uint16_t otype;
+  uint16_t olen;
+  FAR struct dhcpv6_ia_addr_s *addr;
+  char addr_str[INET6_ADDRSTRLEN];
+
+  dhcpv6_for_each_option(ia_na, ia_na + ia_na_len, otype, olen, odata)
+    {
+      addr = (FAR void *)(odata - 4);
+      inet_ntop(AF_INET6, &addr->addr, addr_str, sizeof(addr_str));
+      ninfo("removing address %s/128 for iface %i\n",
+            addr_str, pdhcp6c->ifindex);
+    }
+}
+
+static void dhcp6c_set_iov(FAR struct iovec *piov,
+                           FAR void *base, size_t len)
+{
+  piov->iov_base = base;
+  piov->iov_len = len;
+}
+
+static void dhcp6c_send(FAR void *handle, enum dhcpv6_msg_e type,
+                        uint8_t trid[3], uint32_t ecs)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  char fqdn_buf[256];
+  struct
+  {
+    uint16_t type;
+    uint16_t len;
+    uint8_t flags;
+    uint8_t data[256];
+  } fqdn;
+
+  size_t fqdn_len;
+  size_t cl_id_len;
+  FAR void *cl_id;
+  size_t srv_id_len;
+  FAR void *srv_id;
+  size_t ia_pd_len;
+  FAR void *ia_pd;
+  struct dhcpv6_ia_hdr_s hdr_ia_pd;
+  struct dhcpv6_ia_prefix_s pref;
+  size_t ia_na_len;
+  FAR void *ia_na;
+  struct dhcpv6_ia_hdr_s hdr_ia_na;
+  struct
+  {
+    uint16_t type;
+    uint16_t length;
+  } reconf_accept;
+
+  uint16_t oro_refresh;
+  size_t oro_len;
+  FAR void *oro;
+  struct
+  {
+    uint8_t type;
+    uint8_t trid[3];
+    uint16_t elapsed_type;
+    uint16_t elapsed_len;
+    uint16_t elapsed_value;
+    uint16_t oro_type;
+    uint16_t oro_len;
+  } hdr;
+
+  struct iovec iov[11];
+  size_t cnt;
+  struct sockaddr_in6 dest =
+  {
+    AF_INET6, htons(DHCPV6_SERVER_PORT),
+    0, DHCPV6_ALL_RELAYS, pdhcp6c->ifindex
+  };
+
+  struct msghdr msg;
+
+  /* Build FQDN */
+
+  gethostname(fqdn_buf, sizeof(fqdn_buf));
+  fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data, sizeof(fqdn.data), NULL, NULL);
+  fqdn.type = htons(DHCPV6_OPT_FQDN);
+  fqdn.len = htons(fqdn_len - 4);
+  fqdn.flags = 0;
+
+  /* Build Client ID */
+
+  cl_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &cl_id_len);
+
+  /* Build Server ID */
+
+  srv_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &srv_id_len);
+
+  /* Build IA_PDs */
+
+  ia_pd = dhcp6c_get_state(handle, STATE_IA_PD, &ia_pd_len);
+  hdr_ia_pd.type = htons(DHCPV6_OPT_IA_PD);
+  hdr_ia_pd.len = htons(sizeof(hdr_ia_pd) - 4 + ia_pd_len);
+  hdr_ia_pd.iaid = 1;
+  hdr_ia_pd.t1 = 0;
+  hdr_ia_pd.t2 = 0;
+  pref.type = htons(DHCPV6_OPT_IA_PREFIX);
+  pref.len = htons(25);
+  pref.prefix = pdhcp6c->request_prefix;
+
+  if (ia_pd_len == 0 && pdhcp6c->request_prefix &&
+      (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST))
+    {
+      ia_pd = &pref;
+      ia_pd_len = sizeof(pref);
+    }
+
+  /* Build IA_NAs */
+
+  ia_na = dhcp6c_get_state(handle, STATE_IA_NA, &ia_na_len);
+  hdr_ia_na.type = htons(DHCPV6_OPT_IA_NA);
+  hdr_ia_na.len = htons(sizeof(hdr_ia_na) - 4 + ia_na_len);
+  hdr_ia_na.iaid = 1;
+  hdr_ia_na.t1 = 0;
+  hdr_ia_na.t2 = 0;
+
+  /* Reconfigure Accept */
+
+  reconf_accept.type = htons(DHCPV6_OPT_RECONF_ACCEPT);
+  reconf_accept.length = 0;
+
+  /* Request Information Refresh */
+
+  oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
+
+  /* Prepare Header */
+
+  oro = dhcp6c_get_state(handle, STATE_ORO, &oro_len);
+  hdr.type = type;
+  hdr.trid[0] = trid[0];
+  hdr.trid[1] = trid[1];
+  hdr.trid[2] = trid[2];
+  hdr.elapsed_type = htons(DHCPV6_OPT_ELAPSED);
+  hdr.elapsed_len =  htons(2);
+  hdr.elapsed_value = htons((ecs > 0xffff) ? 0xffff : ecs);
+  hdr.oro_type = htons(DHCPV6_OPT_ORO);
+  hdr.oro_len = htons(oro_len);
+
+  /* Prepare iov */
+
+  dhcp6c_set_iov(&iov[0], &hdr, sizeof(hdr));
+  dhcp6c_set_iov(&iov[1], oro, oro_len);
+  dhcp6c_set_iov(&iov[2], &oro_refresh, 0);
+  dhcp6c_set_iov(&iov[3], cl_id, cl_id_len);
+  dhcp6c_set_iov(&iov[4], srv_id, srv_id_len);
+  dhcp6c_set_iov(&iov[5], &reconf_accept, 0);
+  dhcp6c_set_iov(&iov[6], &fqdn, fqdn_len);
+  dhcp6c_set_iov(&iov[7], &hdr_ia_na, sizeof(hdr_ia_na));
+  dhcp6c_set_iov(&iov[8], ia_na, ia_na_len);
+  dhcp6c_set_iov(&iov[9], &hdr_ia_pd, sizeof(hdr_ia_pd));
+  dhcp6c_set_iov(&iov[10], ia_pd, ia_pd_len);
+
+  cnt = ARRAY_SIZE(iov);
+  if (type == DHCPV6_MSG_INFO_REQ)
+    {
+      cnt = 5;
+      iov[2].iov_len = sizeof(oro_refresh);
+      hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
+    }
+  else if (!pdhcp6c->request_prefix)
+    {
+      cnt = 9;
+    }
+
+  /* Disable IAs if not used */
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      iov[5].iov_len = sizeof(reconf_accept);
+    }
+  else if (type != DHCPV6_MSG_REQUEST)
+    {
+      if (ia_na_len == 0)
+        {
+          iov[7].iov_len = 0;
+        }
+
+      if (ia_pd_len == 0)
+        {
+          iov[9].iov_len = 0;
+        }
+    }
+
+  if (pdhcp6c->ia_mode == IA_MODE_NONE)
+    {
+      iov[7].iov_len = 0;
+    }
+
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = cnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  sendmsg(pdhcp6c->sockfd, &msg, 0);
+}
+
+static int64_t dhcp6c_rand_delay(FAR void *handle, int64_t time)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  int random;
+
+  read(pdhcp6c->urandom_fd, &random, sizeof(random));
+  return (time * (random % 1000)) / 10000;
+}
+
+static bool dhcp6c_response_is_valid(FAR void *handle, FAR const void *buf,
+                                     ssize_t len,
+                                     const uint8_t transaction[3],
+                                     enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  FAR const struct dhcpv6_header_s *rep = buf;
+  FAR uint8_t *end;
+  FAR uint8_t *odata;
+  uint16_t otype;
+  uint16_t olen;
+  bool clientid_ok = false;
+  bool serverid_ok = false;
+  size_t client_id_len;
+  size_t server_id_len;
+  FAR void *client_id;
+  FAR void *server_id;
+
+  if (len < (ssize_t)sizeof(*rep) ||
+      memcmp(rep->tr_id, transaction, sizeof(rep->tr_id)))
+    {
+      return false;
+    }
+
+  if (type == DHCPV6_MSG_SOLICIT)
+    {
+      if (rep->msg_type != DHCPV6_MSG_ADVERT &&
+          rep->msg_type != DHCPV6_MSG_REPLY)
+        {
+          return false;
+        }
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      if (!pdhcp6c->accept_reconfig ||
+          rep->msg_type != DHCPV6_MSG_RECONF)
+        {
+          return false;
+        }
+    }
+  else if (rep->msg_type != DHCPV6_MSG_REPLY)
+    {
+      return false;
+    }
+
+  end = ((FAR uint8_t *)buf) + len;
+  client_id = dhcp6c_get_state(handle, STATE_CLIENT_ID, &client_id_len);
+  server_id = dhcp6c_get_state(handle, STATE_SERVER_ID, &server_id_len);
+
+  dhcpv6_for_each_option(&rep[1], end, otype, olen, odata)
+    {
+      if (otype == DHCPV6_OPT_CLIENTID)
+        {
+          clientid_ok = (olen + 4U == client_id_len) &&
+                        !memcmp((odata - 4), client_id, client_id_len);
+        }
+      else if (otype == DHCPV6_OPT_SERVERID)
+        {
+          serverid_ok = (olen + 4U == server_id_len) &&
+                        !memcmp((odata - 4), server_id, server_id_len);
+        }
+    }
+
+  return clientid_ok && (serverid_ok || server_id_len == 0);
+}
+
+static int dhcp6c_command(FAR void *handle, enum dhcpv6_msg_e type)
+{
+  FAR struct dhcp6c_state_s *pdhcp6c = (FAR struct dhcp6c_state_s *)handle;
+  uint8_t buf[1536];
+  uint32_t timeout = CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT < 3? 3 :
+                     CONFIG_NETUTILS_DHCP6C_REQUEST_TIMEOUT;
+  FAR const struct dhcp6c_retx_s *retx = &g_dhcp6c_retx[type];
+  uint64_t start;
+  uint64_t round_start;
+  uint64_t round_end;
+  uint64_t elapsed;
+  uint8_t trid[3];
+  ssize_t len = -1;
+  int64_t rto = 0;
+
+  if (retx->delay)
+    {
+      struct timespec ts;
+      ts.tv_sec = 0;
+      ts.tv_nsec = dhcp6c_rand_delay(handle, 10 * DHCPV6_REQ_DELAY);
+      nanosleep(&ts, NULL);
+    }
+
+  if (type == DHCPV6_MSG_RELEASE || type == DHCPV6_MSG_DECLINE)
+    {
+      timeout = 3;
+    }
+  else if (type == DHCPV6_MSG_UNKNOWN)
+    {
+      timeout = pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_RENEW)
+    {
+      timeout = pdhcp6c->t2 - pdhcp6c->t1;
+    }
+  else if (type == DHCPV6_MSG_REBIND)
+    {
+      timeout = pdhcp6c->t3 - pdhcp6c->t2;
+    }
+
+  if (timeout == 0)
+    {
+      return -1;
+    }
+
+  ninfo("Sending %s (timeout %u s)\n", retx->name, timeout);
+  start = dhcp6c_get_milli_time();
+  round_start = start;
+
+  /* Generate transaction ID */
+
+  read(pdhcp6c->urandom_fd, trid, sizeof(trid));
+
+  do
+    {
+      rto = (rto == 0) ? (retx->init_timeo * 1000 +
+              dhcp6c_rand_delay(handle, retx->init_timeo * 1000)) :
+              (2 * rto + dhcp6c_rand_delay(handle, rto));
+
+      if (rto >= retx->max_timeo * 1000)
+        {
+          rto = retx->max_timeo * 1000 +
+                dhcp6c_rand_delay(handle, retx->max_timeo * 1000);
+        }
+
+      /* Calculate end for this round and elapsed time */
+
+      round_end = round_start + rto;
+      elapsed = round_start - start;
+
+      /* Don't wait too long */
+
+      if (round_end - start > timeout * 1000)
+        {
+          round_end = timeout * 1000 + start;
+        }
+
+      /* Built and send package */
+
+      if (type != DHCPV6_MSG_UNKNOWN)
+        {
+          dhcp6c_send(handle, type, trid, elapsed / 10);
+        }
+
+      /* Receive rounds */
+
+      for (; len < 0 && round_start < round_end;
+              round_start = dhcp6c_get_milli_time())
+        {
+          /* Set timeout for receiving */
+
+          uint64_t t = round_end - round_start;
+          struct timeval retime =
+          {
+            t / 1000, (t % 1000) * 1000

Review comment:
       ```suggestion
               t / MSEC_PER_SEC, (t % MSEC_PER_SEC) * USEC_PER_MSEC
   ```
   here and in other places




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org