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/17 12:27:26 UTC

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

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