static unsigned int dhcpv6_parse_ia(void *opt, void *end);
-static int dhcpv6_calc_refresh_timers(void);
+static unsigned int dhcpv6_calc_refresh_timers(void);
static void dhcpv6_handle_status_code(_unused const enum dhcpv6_msg orig,
const uint16_t code, const void *status_msg, const int len,
int *ret);
return ntohl(buf);
}
+static char *dhcpv6_msg_to_str(enum dhcpv6_msg msg)
+{
+ static char *dhcpv6_msg_str[] = {
+ "UNKNOWN",
+ "SOLICIT",
+ "ADVERTISE",
+ "REQUEST",
+ "RENEW",
+ "REBIND",
+ "REPLY",
+ "DECLINE",
+ "RECONFIGURE",
+ "INFORMATION REQUEST",
+ };
+
+ if (msg < _DHCPV6_MSG_MAX)
+ return dhcpv6_msg_str[msg];
+
+ return "Unknown";
+}
+
int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout)
{
client_options = options;
htons(DHCPV6_OPT_NTP_SERVER),
htons(DHCPV6_OPT_AFTR_NAME),
htons(DHCPV6_OPT_PD_EXCLUDE),
- htons(DHCPV6_OPT_SOL_MAX_RT),
- htons(DHCPV6_OPT_INF_MAX_RT),
#ifdef EXT_CER_ID
htons(DHCPV6_OPT_CER_ID),
#endif
size_t cnt = IOV_TOTAL;
if (type == DHCPV6_MSG_INFO_REQ)
- cnt = 8;
+ cnt = IOV_HDR_IA_NA;
// Disable IAs if not used
if (type != DHCPV6_MSG_SOLICIT && ia_na_len == 0)
if (sendmsg(sock, &msg, 0) < 0) {
char in6_str[INET6_ADDRSTRLEN];
- syslog(LOG_ERR, "Failed to send DHCPV6 message to %s (%s)",
+ syslog(LOG_ERR, "Failed to send %s message to %s (%s)",
+ dhcpv6_msg_to_str(type),
inet_ntop(AF_INET6, (const void *)&srv.sin6_addr,
in6_str, sizeof(in6_str)), strerror(errno));
}
.msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf.buf,
.msg_controllen = sizeof(cmsg_buf)};
struct in6_pktinfo *pktinfo = NULL;
+ const struct dhcpv6_header *hdr = (const struct dhcpv6_header *)buf;
// Check for pending signal
if (odhcp6c_signal_process())
round_start = odhcp6c_get_milli_time();
elapsed = round_start - start;
- syslog(LOG_NOTICE, "Got a valid reply after %"PRIu64"ms",
- elapsed);
+ syslog(LOG_NOTICE, "Got a valid %s after %"PRIu64"ms",
+ dhcpv6_msg_to_str(hdr->msg_type), elapsed);
if (retx->handler_reply)
len = retx->handler_reply(type, rc, opt, opt_end, &addr);
{
uint16_t otype, olen;
uint8_t *odata;
- int msg = -1;
+ enum dhcpv6_msg msg = DHCPV6_MSG_UNKNOWN;
dhcpv6_for_each_option(opt, end, otype, olen, odata) {
if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1) {
// Fall through
case DHCPV6_MSG_INFO_REQ:
msg = odata[0];
+ syslog(LOG_NOTICE, "Need to respond with %s in reply to %s",
+ dhcpv6_msg_to_str(msg), dhcpv6_msg_to_str(DHCPV6_MSG_RECONF));
break;
default:
}
}
- dhcpv6_handle_reply(orig, rc, NULL, NULL, NULL);
+ if (msg != DHCPV6_MSG_UNKNOWN)
+ dhcpv6_handle_reply(orig, rc, NULL, NULL, NULL);
- return msg;
+ return (msg == DHCPV6_MSG_UNKNOWN? -1: 1);
}
// Collect all advertised servers
dhcpv6_for_each_option(opt, end, otype, olen, odata) {
if (orig == DHCPV6_MSG_SOLICIT &&
- (otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA) &&
+ ((otype == DHCPV6_OPT_IA_PD && pd_mode != IA_MODE_NONE) ||
+ (otype == DHCPV6_OPT_IA_NA && na_mode != IA_MODE_NONE)) &&
olen > -4 + sizeof(struct dhcpv6_ia_hdr)) {
struct dhcpv6_ia_hdr *ia_hdr = (void*)(&odata[-4]);
dhcpv6_parse_ia(ia_hdr, odata + olen + sizeof(*ia_hdr));
uint16_t otype, olen;
uint32_t refresh = 86400;
int ret = 1;
+ unsigned int state_IAs;
unsigned int updated_IAs = 0;
bool handled_status_codes[_DHCPV6_Status_Max] = { false, };
}
}
+ // Bail out if fatal status code was received
+ if (ret <= 0)
+ return ret;
+
switch (orig) {
case DHCPV6_MSG_REQUEST:
case DHCPV6_MSG_REBIND:
case DHCPV6_MSG_RENEW:
- // Update refresh timers if no fatal status code was received
- if ((ret > 0) && (ret = dhcpv6_calc_refresh_timers())) {
- if (orig == DHCPV6_MSG_REQUEST) {
- // All server candidates can be cleared if not yet bound
- if (!odhcp6c_is_bound())
- dhcpv6_clear_all_server_cand();
-
- odhcp6c_clear_state(STATE_SERVER_ADDR);
- odhcp6c_add_state(STATE_SERVER_ADDR, &from->sin6_addr, 16);
- } else if (orig == DHCPV6_MSG_RENEW) {
- // Send further renews if T1 is not set and
- // no updated IAs
- if (!t1) {
- if (!updated_IAs)
- ret = -1;
- else if ((t2 - t1) > 1)
- // Grace period of 1 second
- t1 = 1;
- }
+ state_IAs = dhcpv6_calc_refresh_timers();
+ // In case there're no state IA entries
+ // keep sending request/renew/rebind messages
+ if (state_IAs == 0) {
+ ret = 0;
+ break;
+ }
- } else if (orig == DHCPV6_MSG_REBIND) {
- // Send further rebinds if T1 and T2 is not set and
- // no updated IAs
- if (!t1 && !t2) {
- if (!updated_IAs)
- ret = -1;
- else if ((t3 - t2) > 1)
- // Grace period of 1 second
- t2 = 1;
- }
+ if (orig == DHCPV6_MSG_REQUEST) {
+ // All server candidates can be cleared if not yet bound
+ if (!odhcp6c_is_bound())
+ dhcpv6_clear_all_server_cand();
- odhcp6c_clear_state(STATE_SERVER_ADDR);
- odhcp6c_add_state(STATE_SERVER_ADDR, &from->sin6_addr, 16);
+ odhcp6c_clear_state(STATE_SERVER_ADDR);
+ odhcp6c_add_state(STATE_SERVER_ADDR, &from->sin6_addr, 16);
+ } else if (orig == DHCPV6_MSG_RENEW) {
+ // Send further renews if T1 is not set and if
+ // there're IAs which were not in the Reply message
+ if (!t1 && state_IAs != updated_IAs) {
+ if (updated_IAs)
+ // Publish updates
+ script_call("updated", 0, false);
+
+ /*
+ * RFC8415 states following in §18.2.10.1 :
+ * Sends a Renew/Rebind if any of the IAs are not in the Reply
+ * message, but as this likely indicates that the server that
+ * responded does not support that IA type, sending immediately is
+ * unlikely to produce a different result. Therefore, the client
+ * MUST rate-limit its transmissions (see Section 14.1) and MAY just
+ * wait for the normal retransmission time (as if the Reply message
+ * had not been received). The client continues to use other
+ * bindings for which the server did return information
+ */
+ ret = -1;
+ }
+ } else if (orig == DHCPV6_MSG_REBIND) {
+ odhcp6c_clear_state(STATE_SERVER_ADDR);
+ odhcp6c_add_state(STATE_SERVER_ADDR, &from->sin6_addr, 16);
+
+ // Send further rebinds if T1 and T2 is not set and if
+ // there're IAs which were not in the Reply message
+ if (!t1 && !t2 && state_IAs != updated_IAs) {
+ if (updated_IAs)
+ // Publish updates
+ script_call("updated", 0, false);
+
+ /*
+ * RFC8415 states following in §18.2.10.1 :
+ * Sends a Renew/Rebind if any of the IAs are not in the Reply
+ * message, but as this likely indicates that the server that
+ * responded does not support that IA type, sending immediately is
+ * unlikely to produce a different result. Therefore, the client
+ * MUST rate-limit its transmissions (see Section 14.1) and MAY just
+ * wait for the normal retransmission time (as if the Reply message
+ * had not been received). The client continues to use other
+ * bindings for which the server did return information
+ */
+ ret = -1;
}
}
break;
case DHCPV6_MSG_INFO_REQ:
- if (ret > 0) {
- // All server candidates can be cleared if not yet bound
- if (!odhcp6c_is_bound())
- dhcpv6_clear_all_server_cand();
+ // All server candidates can be cleared if not yet bound
+ if (!odhcp6c_is_bound())
+ dhcpv6_clear_all_server_cand();
- t1 = refresh;
- }
+ t1 = refresh;
break;
default:
uint32_t t1, t2;
uint16_t otype, olen;
uint8_t *odata;
+ char buf[INET6_ADDRSTRLEN];
t1 = ntohl(ia_hdr->t1);
t2 = ntohl(ia_hdr->t2);
if (t1 > t2)
return 0;
+ syslog(LOG_INFO, "IAID %04x T1 %d T2 %d", htonl(ia_hdr->iaid), t1, t2);
+
// Update address IA
dhcpv6_for_each_option(&ia_hdr[1], end, otype, olen, odata) {
- struct odhcp6c_entry entry = {IN6ADDR_ANY_INIT, 0, 0, 0,
- IN6ADDR_ANY_INIT, 0, 0, 0, 0, 0};
+ struct odhcp6c_entry entry = {IN6ADDR_ANY_INIT, 0, 0,
+ IN6ADDR_ANY_INIT, 0, 0, 0, 0, 0, 0};
entry.iaid = ia_hdr->iaid;
if (ok) {
if (odhcp6c_update_entry(STATE_IA_PD, &entry, 0, 0))
updated_IAs++;
+
+ syslog(LOG_INFO, "%s/%d preferred %d valid %d",
+ inet_ntop(AF_INET6, &entry.target, buf, sizeof(buf)),
+ entry.length, entry.preferred , entry.valid);
}
entry.priority = 0;
if (odhcp6c_update_entry(STATE_IA_NA, &entry, 0, 0))
updated_IAs++;
+
+ syslog(LOG_INFO, "%s preferred %d valid %d",
+ inet_ntop(AF_INET6, &entry.target, buf, sizeof(buf)),
+ entry.preferred , entry.valid);
}
}
+
return updated_IAs;
}
-static int dhcpv6_calc_refresh_timers(void)
+static unsigned int dhcpv6_calc_refresh_timers(void)
{
struct odhcp6c_entry *e;
size_t ia_na_entries, ia_pd_entries, i;
t1 = l_t1;
t2 = l_t2;
t3 = l_t3;
+
+ syslog(LOG_INFO, "T1 %"PRId64"s, T2 %"PRId64"s, T3 %"PRId64"s", t1, t2, t3);
}
- return (int)(ia_pd_entries + ia_na_entries);
+ return (unsigned int)(ia_pd_entries + ia_na_entries);
}
static void dhcpv6_log_status_code(const uint16_t code, const char *scope,