+ parsed_ia++;
+ }
+ }
+ return parsed_ia;
+}
+
+
+static int dhcpv6_calc_refresh_timers(void)
+{
+ struct odhcp6c_entry *e;
+ size_t ia_na_entries, ia_pd_entries, i;
+ int64_t l_t1 = UINT32_MAX, l_t2 = UINT32_MAX, l_t3 = 0;
+
+ e = odhcp6c_get_state(STATE_IA_NA, &ia_na_entries);
+ ia_na_entries /= sizeof(*e);
+ for (i = 0; i < ia_na_entries; i++) {
+ if (e[i].t1 < l_t1)
+ l_t1 = e[i].t1;
+
+ if (e[i].t2 < l_t2)
+ l_t2 = e[i].t2;
+
+ if (e[i].valid > l_t3)
+ l_t3 = e[i].valid;
+ }
+
+ e = odhcp6c_get_state(STATE_IA_PD, &ia_pd_entries);
+ ia_pd_entries /= sizeof(*e);
+ for (i = 0; i < ia_pd_entries; i++) {
+ if (e[i].t1 < l_t1)
+ l_t1 = e[i].t1;
+
+ if (e[i].t2 < l_t2)
+ l_t2 = e[i].t2;
+
+ if (e[i].valid > l_t3)
+ l_t3 = e[i].valid;
+ }
+
+ if (ia_pd_entries || ia_na_entries) {
+ t1 = l_t1;
+ t2 = l_t2;
+ t3 = l_t3;
+ } else {
+ t1 = 600;
+ }
+
+ return (int)(ia_pd_entries + ia_na_entries);
+}
+
+
+static void dhcpv6_log_status_code(const uint16_t code, const char *scope,
+ const void *status_msg, const int len)
+{
+ uint8_t buf[len + 3];
+
+ memset(buf, 0, sizeof(buf));
+ if (len) {
+ buf[0] = '(';
+ memcpy(&buf[1], status_msg, len);
+ buf[len + 1] = ')';
+ }
+
+ syslog(LOG_WARNING, "Server returned %s status %i %s",
+ scope, code, buf);
+}
+
+
+static void dhcpv6_handle_status_code(const enum dhcpv6_msg orig,
+ const uint16_t code, const void *status_msg, const int len,
+ int *ret)
+{
+ dhcpv6_log_status_code(code, "message", status_msg, len);
+
+ switch (code) {
+ case DHCPV6_UnspecFail:
+ // Generic failure
+ *ret = 0;
+ break;
+
+ case DHCPV6_UseMulticast:
+ // TODO handle multicast status code
+ break;
+
+ case DHCPV6_NoAddrsAvail:
+ case DHCPV6_NoPrefixAvail:
+ if (orig == DHCPV6_MSG_REQUEST)
+ *ret = 0; // Failure
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void dhcpv6_handle_ia_status_code(const enum dhcpv6_msg orig,
+ const struct dhcpv6_ia_hdr *ia_hdr, const uint16_t code,
+ const void *status_msg, const int len,
+ bool handled_status_codes[_DHCPV6_Status_Max], int *ret)
+{
+ dhcpv6_log_status_code(code, ia_hdr->type == DHCPV6_OPT_IA_NA ?
+ "IA_NA" : "IA_PD", status_msg, len);
+
+ switch (code) {
+ case DHCPV6_NoBinding:
+ switch (orig) {
+ case DHCPV6_MSG_RENEW:
+ case DHCPV6_MSG_REBIND:
+ if ((*ret > 0) && !handled_status_codes[code])
+ *ret = dhcpv6_request(DHCPV6_MSG_REQUEST);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case DHCPV6_NoAddrsAvail:
+ case DHCPV6_NoPrefixAvail:
+ switch (orig) {
+ case DHCPV6_MSG_REQUEST:
+ if (*ret != 0)
+ *ret = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case DHCPV6_NotOnLink:
+ // TODO handle not onlink in case of confirm
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void dhcpv6_add_server_cand(const struct dhcpv6_server_cand *cand)
+{
+ size_t cand_len, i;
+ struct dhcpv6_server_cand *c = odhcp6c_get_state(STATE_SERVER_CAND, &cand_len);
+
+ // Remove identical duid server candidate
+ for (i = 0; i < cand_len / sizeof(*c); ++i) {
+ if (cand->duid_len == c[i].duid_len &&
+ !memcmp(cand->duid, c[i].duid, cand->duid_len)) {
+ free(c[i].ia_na);
+ free(c[i].ia_pd);
+ odhcp6c_remove_state(STATE_SERVER_CAND, i * sizeof(*c), sizeof(*c));
+ break;