X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=src%2Fdhcpv6.c;h=51b99924991e1ea46f06142a1f9e20bf3c65ff9e;hb=eac1961983b8e7a74c494feedcb878170b135072;hp=857cbe1ce1b52c408d4ce22befc829c00e3b7b09;hpb=b079733bee3a616c765383be35656693627716be;p=project%2Fodhcp6c.git diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 857cbe1..51b9992 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -61,7 +61,7 @@ static bool dhcpv6_response_is_valid(const void *buf, ssize_t len, 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); @@ -122,6 +122,77 @@ static uint32_t ntohl_unaligned(const uint8_t *data) return ntohl(buf); } +static char *dhcpv6_msg_to_str(enum dhcpv6_msg msg) +{ + switch (msg) { + case DHCPV6_MSG_SOLICIT: + return "SOLICIT"; + + case DHCPV6_MSG_ADVERT: + return "ADVERTISE"; + + case DHCPV6_MSG_REQUEST: + return "REQUEST"; + + case DHCPV6_MSG_RENEW: + return "RENEW"; + + case DHCPV6_MSG_REBIND: + return "REBIND"; + + case DHCPV6_MSG_REPLY: + return "REPLY"; + + case DHCPV6_MSG_RELEASE: + return "RELEASE"; + + case DHCPV6_MSG_DECLINE: + return "DECLINE"; + + case DHCPV6_MSG_RECONF: + return "RECONFIGURE"; + + case DHCPV6_MSG_INFO_REQ: + return "INFORMATION REQUEST"; + + default: + break; + } + + return "UNKNOWN"; +} + +static char *dhcpv6_status_code_to_str(uint16_t code) +{ + switch (code) { + case DHCPV6_Success: + return "Success"; + + case DHCPV6_UnspecFail: + return "Unspecified Failure"; + + case DHCPV6_NoAddrsAvail: + return "No Address Available"; + + case DHCPV6_NoBinding: + return "No Binding"; + + case DHCPV6_NotOnLink: + return "Not On Link"; + + case DHCPV6_UseMulticast: + return "Use Multicast"; + + case DHCPV6_NoPrefixAvail: + return "No Prefix Available"; + + default: + break; + } + + return "Unknown"; +} + int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout) { client_options = options; @@ -186,8 +257,6 @@ int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout) 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 @@ -538,7 +607,8 @@ static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs) 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)); } @@ -640,6 +710,7 @@ int dhcpv6_request(enum dhcpv6_msg type) .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()) @@ -683,8 +754,8 @@ int dhcpv6_request(enum dhcpv6_msg type) 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); @@ -809,8 +880,21 @@ int dhcpv6_poll_reconfigure(void) { int ret = dhcpv6_request(DHCPV6_MSG_UNKNOWN); - if (ret != -1) + switch (ret) { + /* + * Only RENEW/REBIND/INFORMATION REQUEST + * message transmission can be requested + * by a RECONFIGURE + */ + case DHCPV6_MSG_RENEW: + case DHCPV6_MSG_REBIND: + case DHCPV6_MSG_INFO_REQ: ret = dhcpv6_request(ret); + break; + + default: + break; + } return ret; } @@ -820,7 +904,7 @@ static int dhcpv6_handle_reconfigure(enum dhcpv6_msg orig, const int rc, { 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) { @@ -835,6 +919,8 @@ static int dhcpv6_handle_reconfigure(enum dhcpv6_msg orig, const int rc, // 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: @@ -843,9 +929,10 @@ static int dhcpv6_handle_reconfigure(enum dhcpv6_msg orig, const int rc, } } - 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: (int)msg); } // Collect all advertised servers @@ -862,7 +949,8 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc, 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)); @@ -971,6 +1059,7 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc, 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, }; @@ -1156,55 +1245,83 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc, } } + // 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: @@ -1221,6 +1338,7 @@ static unsigned int dhcpv6_parse_ia(void *opt, void *end) 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); @@ -1228,10 +1346,12 @@ static unsigned int dhcpv6_parse_ia(void *opt, void *end) if (t1 > t2) return 0; + syslog(LOG_INFO, "%s %04x T1 %d T2 %d", ntohs(ia_hdr->type) == DHCPV6_OPT_IA_PD ? "IA_PD" : "IA_NA", ntohl(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; @@ -1295,6 +1415,10 @@ static unsigned int dhcpv6_parse_ia(void *opt, void *end) 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; @@ -1320,12 +1444,17 @@ static unsigned int dhcpv6_parse_ia(void *opt, void *end) 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; @@ -1363,9 +1492,11 @@ static int dhcpv6_calc_refresh_timers(void) 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, @@ -1387,8 +1518,8 @@ static void dhcpv6_log_status_code(const uint16_t code, const char *scope, *dst = 0; - syslog(LOG_WARNING, "Server returned %s status %i %s", - scope, code, buf); + syslog(LOG_WARNING, "Server returned %s status '%s %s'", + scope, dhcpv6_status_code_to_str(code), buf); } static void dhcpv6_handle_status_code(const enum dhcpv6_msg orig,