X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fodhcpd.git;a=blobdiff_plain;f=src%2Fdhcpv6-ia.c;h=181973d20328b017523e635c6bff957ef13bb582;hp=593b5bd5064ee704657ed80971b444b923878f52;hb=72082d2f6c2ee568470575c09ba4984f1a5c44f0;hpb=2b4200f2510874a3ee9c09f594d5ab6340ffb336 diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 593b5bd..181973d 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -50,10 +51,13 @@ int dhcpv6_ia_init(void) void free_dhcpv6_assignment(struct dhcpv6_assignment *c) { if (c->managed_sock.fd.registered) { - close(c->managed_sock.fd.fd); ustream_free(&c->managed_sock.stream); + close(c->managed_sock.fd.fd); } + if (c->head.next) + list_del(&c->head); + free(c->managed); free(c->hostname); free(c->classes); @@ -67,7 +71,6 @@ int setup_dhcpv6_ia_interface(struct interface *iface, bool enable) struct dhcpv6_assignment *c; while (!list_empty(&iface->ia_assignments)) { c = list_first_entry(&iface->ia_assignments, struct dhcpv6_assignment, head); - list_del(&c->head); free_dhcpv6_assignment(c); } } @@ -277,7 +280,7 @@ void dhcpv6_write_statefile(void) md5_hash(c->hostname, strlen(c->hostname), &md5); } - l += snprintf(leasebuf + l, sizeof(leasebuf) - l, "%s/%hhu ", ipbuf, + l += snprintf(leasebuf + l, sizeof(leasebuf) - l, "%s/%d ", ipbuf, (c->managed_size) ? addrs[i].prefix : c->length); } leasebuf[l - 1] = '\n'; @@ -401,8 +404,22 @@ static void managed_handle_pd_data(struct ustream *s, _unused int bytes_new) if (sscanf(x, "%u", &n->valid) < 1) continue; - n->preferred += now; - n->valid += now; + if (n->preferred > n->valid) + continue; + + if (UINT32_MAX - now < n->preferred) + n->preferred = UINT32_MAX; + else + n->preferred += now; + + if (UINT32_MAX - now < n->valid) + n->valid = UINT32_MAX; + else + n->valid += now; + + n->has_class = false; + n->class = 0; + n->dprefix = 0; ++c->managed_size; } @@ -410,10 +427,10 @@ static void managed_handle_pd_data(struct ustream *s, _unused int bytes_new) ustream_consume(s, end - data); } - if (first && c->managed_size == 0) { - list_del(&c->head); + if (first && c->managed_size == 0) free_dhcpv6_assignment(c); - } + else if (first) + c->valid_until = now + 150; } @@ -436,13 +453,26 @@ static bool assign_pd(struct interface *iface, struct dhcpv6_assignment *assign) if (iface->dhcpv6_pd_manager[0]) { int fd = usock(USOCK_UNIX | USOCK_TCP, iface->dhcpv6_pd_manager, NULL); if (fd >= 0) { + char iaidbuf[298]; + odhcpd_hexlify(iaidbuf, assign->clid_data, assign->clid_len); + assign->managed_sock.stream.notify_read = managed_handle_pd_data; assign->managed_sock.stream.notify_state = managed_handle_pd_done; ustream_fd_init(&assign->managed_sock, fd); - ustream_printf(&assign->managed_sock.stream, "::/%d,0,0\n\n", assign->length); + ustream_printf(&assign->managed_sock.stream, "%s,%x\n::/%d,0,0\n\n", + iaidbuf, assign->iaid, assign->length); ustream_write_pending(&assign->managed_sock.stream); assign->managed_size = -1; + assign->valid_until = odhcpd_time() + 15; list_add(&assign->head, &iface->ia_assignments); + + // Wait initial period of up to 250ms for immediate assignment + struct pollfd pfd = { .fd = fd, .events = POLLIN }; + poll(&pfd, 1, 250); + managed_handle_pd_data(&assign->managed_sock.stream, 0); + + if (fcntl(fd, F_GETFL) >= 0 && assign->managed_size > 0) + return true; } return false; @@ -688,11 +718,18 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status, datalen += sizeof(stat); } else { if (a) { - uint32_t pref = 3600; - uint32_t valid = 3600; + uint32_t leasetime = iface->dhcpv4_leasetime; + if (leasetime == 0) + leasetime = 3600; + else if (leasetime < 60) + leasetime = 60; + + uint32_t pref = leasetime; + uint32_t valid = leasetime; struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->ia_addr; size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->ia_addr_len; + for (size_t i = 0; i < addrlen; ++i) { bool match = true; if (addrs[i].has_class) { @@ -737,7 +774,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status, .len = htons(sizeof(p) - 4), .preferred = htonl(prefix_pref), .valid = htonl(prefix_valid), - .prefix = (a->managed_size) ? a->managed_size : a->length, + .prefix = (a->managed_size) ? addrs[i].prefix : a->length, .addr = addrs[i].addr }; p.addr.s6_addr32[1] |= htonl(a->assigned); @@ -751,7 +788,8 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status, } #endif - if (datalen + entrlen + 4 > buflen || a->assigned == 0) + if (datalen + entrlen + 4 > buflen || + (a->assigned == 0 && a->managed_size == 0)) continue; memcpy(buf + datalen, &p, sizeof(p)); @@ -886,7 +924,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status, } -size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, +ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, const struct sockaddr_in6 *addr, const void *data, const uint8_t *end) { time_t now = odhcpd_time(); @@ -901,6 +939,8 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, char hostname[256]; size_t hostname_len = 0; bool class_oro = false; + bool notonlink = false; + dhcpv6_for_each_option(start, end, otype, olen, odata) { if (otype == DHCPV6_OPT_CLIENTID) { clid_data = odata; @@ -935,12 +975,12 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, update(iface); bool update_state = false; - bool managed_pd_out = false; struct dhcpv6_assignment *first = NULL; dhcpv6_for_each_option(start, end, otype, olen, odata) { bool is_pd = (otype == DHCPV6_OPT_IA_PD); bool is_na = (otype == DHCPV6_OPT_IA_NA); + bool ia_addr_present = false; if (!is_pd && !is_na) continue; @@ -993,6 +1033,7 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, if (stype != DHCPV6_OPT_IA_ADDR || slen < sizeof(struct dhcpv6_ia_addr) - 4) continue; + ia_addr_present = true; #ifdef DHCPV6_OPT_PREFIX_CLASS uint8_t *xdata; uint16_t xtype, xlen; @@ -1040,9 +1081,7 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, // Generic message handling uint16_t status = DHCPV6_STATUS_OK; if (a && a->managed_size < 0) { - managed_pd_out = true; - status = DHCPV6_STATUS_NOTONLINK; - ia_response_len = append_reply(buf, buflen, status, ia, NULL, iface, true); + return -1; } else if (hdr->msg_type == DHCPV6_MSG_SOLICIT || hdr->msg_type == DHCPV6_MSG_REQUEST) { bool assigned = !!a; @@ -1070,12 +1109,12 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, if (is_pd) while (!(assigned = assign_pd(iface, a)) && - ++a->length <= 64 && !a->managed_size); + !a->managed_size && ++a->length <= 64); else assigned = assign_na(iface, a); if (a->managed_size && !assigned) - managed_pd_out = true; + return -1; } } @@ -1113,7 +1152,7 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, // Was only a solicitation: mark binding for removal if (assigned && hdr->msg_type == DHCPV6_MSG_SOLICIT) { - a->valid_until = now + 15; + a->valid_until = 0; } else if (assigned && hdr->msg_type == DHCPV6_MSG_REQUEST) { if (hostname_len > 0) { a->hostname = realloc(a->hostname, hostname_len + 1); @@ -1125,7 +1164,7 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, a->accept_reconf = accept_reconf; apply_lease(iface, a, true); update_state = true; - } else if (!assigned && a) { // Cleanup failed assignment + } else if (!assigned && a && a->managed_size == 0) { // Cleanup failed assignment free_dhcpv6_assignment(a); } } else if (hdr->msg_type == DHCPV6_MSG_RENEW || @@ -1149,10 +1188,11 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, a->valid_until = now + 3600; // Block address for 1h update_state = true; } - } else if (hdr->msg_type == DHCPV6_MSG_CONFIRM) { - // Always send NOTONLINK for CONFIRM so that clients restart connection + } else if (hdr->msg_type == DHCPV6_MSG_CONFIRM && ia_addr_present) { + // Send NOTONLINK for CONFIRM with addr present so that clients restart connection status = DHCPV6_STATUS_NOTONLINK; ia_response_len = append_reply(buf, buflen, status, ia, a, iface, true); + notonlink = true; } buf += ia_response_len; @@ -1160,21 +1200,14 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, response_len += ia_response_len; } - if (hdr->msg_type == DHCPV6_MSG_RELEASE && response_len + 6 < buflen) { - buf[0] = 0; - buf[1] = DHCPV6_OPT_STATUS; - buf[2] = 0; - buf[3] = 2; - buf[4] = 0; - buf[5] = DHCPV6_STATUS_OK; - response_len += 6; - } else if (managed_pd_out && response_len + 6 < buflen) { + if ((hdr->msg_type == DHCPV6_MSG_RELEASE || hdr->msg_type == DHCPV6_MSG_DECLINE || notonlink) && + response_len + 6 < buflen) { buf[0] = 0; buf[1] = DHCPV6_OPT_STATUS; buf[2] = 0; buf[3] = 2; buf[4] = 0; - buf[5] = DHCPV6_STATUS_NOADDRSAVAIL; + buf[5] = (notonlink) ? DHCPV6_STATUS_NOTONLINK : DHCPV6_STATUS_OK; response_len += 6; }