static int dhcp6c_handle_reply()

in netutils/dhcp6c/dhcp6c.c [1169:1420]


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;
}