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