X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fodhcpd.git;a=blobdiff_plain;f=src%2Fdhcpv6-ia.c;h=d103ecb422adfc3e398d9f36d703666fb1f39d7d;hp=c655d93c1911235c187615523997ea59a3bcb141;hb=0a9d2fcf8e955b7e7a3247ef354a2d4e12a17f62;hpb=d3cc614ef3120d504f2029a8e9494dccafdc14dd diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index c655d93..d103ecb 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -258,6 +258,8 @@ void dhcpv6_write_statefile(void) for (size_t i = 0; i < addrlen; ++i) { if (addrs[i].prefix > 96) continue; + if (c->valid_until <= now) + continue; addr = addrs[i].addr; if (c->length == 128) @@ -267,7 +269,7 @@ void dhcpv6_write_statefile(void) inet_ntop(AF_INET6, &addr, ipbuf, sizeof(ipbuf) - 1); - if (c->length == 128 && c->hostname && i == 0) { + if (c->length == 128 && c->hostname) { fputs(ipbuf, fp); char b[256]; @@ -593,7 +595,7 @@ static void update(struct interface *iface) int minprefix = -1; for (int i = 0; i < len; ++i) { - if (addr[i].prefix > minprefix) + if (addr[i].preferred > 0 && addr[i].prefix > minprefix) minprefix = addr[i].prefix; addr[i].addr.s6_addr32[3] = 0; @@ -620,7 +622,7 @@ static void update(struct interface *iface) if (change) { struct dhcpv6_assignment *c; list_for_each_entry(c, &iface->ia_assignments, head) - if (c != border) + if (c != border && !iface->managed) apply_lease(iface, c, false); } @@ -718,8 +720,14 @@ 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; @@ -802,6 +810,10 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status, n.addr.s6_addr32[3] = htonl(a->assigned); size_t entrlen = sizeof(n) - 4; + if (!a->accept_reconf && iface->managed < RELAYD_MANAGED_NO_AFLAG && + addrs[i].prefix == 64) + n.preferred = htonl(1); + #ifdef DHCPV6_OPT_PREFIX_CLASS if (iface->ia_addr[i].has_class) { entrlen += sizeof(pclass); @@ -865,7 +877,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status, addr.s6_addr32[1] |= htonl(a->assigned); if (!memcmp(&p->addr, &addr, sizeof(addr)) && - p->prefix == a->length) + p->prefix == ((a->managed) ? addrs[i].prefix : a->length)) found = true; } else { addr.s6_addr32[3] = htonl(a->assigned); @@ -918,6 +930,87 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status, } +static void dhcpv6_log(uint8_t msgtype, struct interface *iface, time_t now, + const char *duidbuf, bool is_pd, struct dhcpv6_assignment *a, int code) +{ + const char *type = "UNKNOWN"; + const char *status = "UNKNOWN"; + + if (msgtype == DHCPV6_MSG_RENEW) + return; + + switch (msgtype) { + case DHCPV6_MSG_SOLICIT: + type = "SOLICIT"; + break; + case DHCPV6_MSG_REQUEST: + type = "REQUEST"; + break; + case DHCPV6_MSG_CONFIRM: + type = "CONFIRM"; + break; + case DHCPV6_MSG_RENEW: + type = "RENEW"; + break; + case DHCPV6_MSG_REBIND: + type = "REBIND"; + break; + case DHCPV6_MSG_RELEASE: + type = "RELEASE"; + break; + case DHCPV6_MSG_DECLINE: + type = "DECLINE"; + break; + } + + switch (code) { + case DHCPV6_STATUS_OK: + status = "ok"; + break; + case DHCPV6_STATUS_NOADDRSAVAIL: + status = "no addresses available"; + break; + case DHCPV6_STATUS_NOBINDING: + status = "no binding"; + break; + case DHCPV6_STATUS_NOTONLINK: + status = "not on-link"; + break; + case DHCPV6_STATUS_NOPREFIXAVAIL: + status = "no prefix available"; + break; + } + + char leasebuf[256] = ""; + + if (a) { + 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; + size_t lbsize = 0; + char addrbuf[INET6_ADDRSTRLEN]; + + for (size_t i = 0; i < addrlen; ++i) { + if (addrs[i].prefix > 96 || addrs[i].preferred <= (uint32_t)now) + continue; + + struct in6_addr addr = addrs[i].addr; + int prefix = a->managed ? addrs[i].prefix : a->length; + if (prefix == 128) + addr.s6_addr32[3] = htonl(a->assigned); + else + addr.s6_addr32[1] |= htonl(a->assigned); + + inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf)); + lbsize += snprintf(leasebuf + lbsize, sizeof(leasebuf) - lbsize, "%s/%d ", addrbuf, prefix); + } + } + + syslog(LOG_WARNING, "DHCPV6 %s %s from %s on %s: %s %s", type, (is_pd) ? "IA_PD" : "IA_NA", + duidbuf, iface->ifname, status, leasebuf); +} + + + 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) { @@ -933,6 +1026,9 @@ ssize_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; + char duidbuf[261]; + dhcpv6_for_each_option(start, end, otype, olen, odata) { if (otype == DHCPV6_OPT_CLIENTID) { clid_data = odata; @@ -942,6 +1038,9 @@ ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, memcpy(mac, &odata[8], sizeof(mac)); else if (olen == 10 && odata[0] == 0 && odata[1] == 3) memcpy(mac, &odata[4], sizeof(mac)); + + if (olen <= 130) + odhcpd_hexlify(duidbuf, odata, olen); } else if (otype == DHCPV6_OPT_FQDN && olen >= 2 && olen <= 255) { uint8_t fqdn_buf[256]; memcpy(fqdn_buf, odata, olen); @@ -966,7 +1065,6 @@ ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, goto out; update(iface); - bool update_state = false; struct dhcpv6_assignment *first = NULL; dhcpv6_for_each_option(start, end, otype, olen, odata) { @@ -1155,7 +1253,6 @@ ssize_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 && a->managed_size == 0) { // Cleanup failed assignment free_dhcpv6_assignment(a); } @@ -1174,36 +1271,35 @@ ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, } else if (hdr->msg_type == DHCPV6_MSG_RELEASE) { a->valid_until = 0; apply_lease(iface, a, false); - update_state = true; } else if (hdr->msg_type == DHCPV6_MSG_DECLINE && a->length == 128) { a->clid_len = 0; a->valid_until = now + 3600; // Block address for 1h - update_state = true; } } 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; buflen -= ia_response_len; response_len += ia_response_len; + dhcpv6_log(hdr->msg_type, iface, now, duidbuf, is_pd, a, status); } - if ((hdr->msg_type == DHCPV6_MSG_RELEASE || hdr->msg_type == DHCPV6_MSG_DECLINE) && + 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_OK; + buf[5] = (notonlink) ? DHCPV6_STATUS_NOTONLINK : DHCPV6_STATUS_OK; response_len += 6; } - if (update_state) - dhcpv6_write_statefile(); + dhcpv6_write_statefile(); out: return response_len;