|
@@ -248,12 +248,13 @@ void dhcpv6_ia_enum_addrs(struct interface *iface, struct dhcp_assignment *c,
|
|
|
if (!ADDR_ENTRY_VALID_IA_ADDR(iface, i, m, addrs))
|
|
|
continue;
|
|
|
|
|
|
- addr.s6_addr32[3] = htonl(c->assigned);
|
|
|
+ addr.s6_addr32[2] = htonl(c->assigned_host_id >> 32);
|
|
|
+ addr.s6_addr32[3] = htonl(c->assigned_host_id & UINT32_MAX);
|
|
|
} else {
|
|
|
if (!valid_prefix_length(c, addrs[i].prefix))
|
|
|
continue;
|
|
|
|
|
|
- addr.s6_addr32[1] |= htonl(c->assigned);
|
|
|
+ addr.s6_addr32[1] |= htonl(c->assigned_subnet_id);
|
|
|
addr.s6_addr32[2] = addr.s6_addr32[3] = 0;
|
|
|
}
|
|
|
|
|
@@ -362,15 +363,21 @@ void dhcpv6_ia_write_statefile(void)
|
|
|
|
|
|
odhcpd_hexlify(duidbuf, ctxt.c->clid_data, ctxt.c->clid_len);
|
|
|
|
|
|
- /* iface DUID iaid hostname lifetime assigned length [addrs...] */
|
|
|
- ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s %x %s%s %"PRId64" %x %u ",
|
|
|
+ /* iface DUID iaid hostname lifetime assigned_host_id length [addrs...] */
|
|
|
+ ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s %x %s%s %"PRId64" ",
|
|
|
ctxt.iface->ifname, duidbuf, ntohl(ctxt.c->iaid),
|
|
|
(ctxt.c->flags & OAF_BROKEN_HOSTNAME) ? "broken\\x20" : "",
|
|
|
(ctxt.c->hostname ? ctxt.c->hostname : "-"),
|
|
|
(ctxt.c->valid_until > now ?
|
|
|
(int64_t)(ctxt.c->valid_until - now + wall_time) :
|
|
|
- (INFINITE_VALID(ctxt.c->valid_until) ? -1 : 0)),
|
|
|
- ctxt.c->assigned, (unsigned)ctxt.c->length);
|
|
|
+ (INFINITE_VALID(ctxt.c->valid_until) ? -1 : 0)));
|
|
|
+
|
|
|
+ if (ctxt.c->flags & OAF_DHCPV6_NA)
|
|
|
+ ctxt.buf_idx += snprintf(ctxt.buf + ctxt.buf_idx, ctxt.buf_len - ctxt.buf_idx,
|
|
|
+ "%" PRIx64" %u ", ctxt.c->assigned_host_id, (unsigned)ctxt.c->length);
|
|
|
+ else
|
|
|
+ ctxt.buf_idx += snprintf(ctxt.buf + ctxt.buf_idx, ctxt.buf_len - ctxt.buf_idx,
|
|
|
+ "%" PRIx32" %u ", ctxt.c->assigned_subnet_id, (unsigned)ctxt.c->length);
|
|
|
|
|
|
if (INFINITE_VALID(ctxt.c->valid_until) || ctxt.c->valid_until > now)
|
|
|
dhcpv6_ia_enum_addrs(ctxt.iface, ctxt.c, now,
|
|
@@ -459,7 +466,7 @@ static void __apply_lease(struct dhcp_assignment *a,
|
|
|
continue;
|
|
|
|
|
|
prefix = addrs[i].addr.in6;
|
|
|
- prefix.s6_addr32[1] |= htonl(a->assigned);
|
|
|
+ prefix.s6_addr32[1] |= htonl(a->assigned_subnet_id);
|
|
|
prefix.s6_addr32[2] = prefix.s6_addr32[3] = 0;
|
|
|
netlink_setup_route(&prefix, (a->managed_size) ? addrs[i].prefix : a->length,
|
|
|
a->iface->ifindex, &a->peer.sin6_addr, 1024, add);
|
|
@@ -494,9 +501,9 @@ static void set_border_assignment_size(struct interface *iface, struct dhcp_assi
|
|
|
}
|
|
|
|
|
|
if (minprefix > 32 && minprefix <= 64)
|
|
|
- b->assigned = 1U << (64 - minprefix);
|
|
|
+ b->assigned_subnet_id = 1U << (64 - minprefix);
|
|
|
else
|
|
|
- b->assigned = 0;
|
|
|
+ b->assigned_subnet_id = 0;
|
|
|
}
|
|
|
|
|
|
/* More data was received from TCP connection */
|
|
@@ -627,12 +634,12 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign)
|
|
|
|
|
|
/* Try honoring the hint first */
|
|
|
uint32_t current = 1, asize = (1 << (64 - assign->length)) - 1;
|
|
|
- if (assign->assigned) {
|
|
|
+ if (assign->assigned_subnet_id) {
|
|
|
list_for_each_entry(c, &iface->ia_assignments, head) {
|
|
|
if (c->flags & OAF_DHCPV6_NA)
|
|
|
continue;
|
|
|
|
|
|
- if (assign->assigned >= current && assign->assigned + asize < c->assigned) {
|
|
|
+ if (assign->assigned_subnet_id >= current && assign->assigned_subnet_id + asize < c->assigned_subnet_id) {
|
|
|
list_add_tail(&assign->head, &c->head);
|
|
|
|
|
|
if (assign->flags & OAF_BOUND)
|
|
@@ -641,7 +648,7 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- current = (c->assigned + (1 << (64 - c->length)));
|
|
|
+ current = (c->assigned_subnet_id + (1 << (64 - c->length)));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -653,8 +660,8 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign)
|
|
|
|
|
|
current = (current + asize) & (~asize);
|
|
|
|
|
|
- if (current + asize < c->assigned) {
|
|
|
- assign->assigned = current;
|
|
|
+ if (current + asize < c->assigned_subnet_id) {
|
|
|
+ assign->assigned_subnet_id = current;
|
|
|
list_add_tail(&assign->head, &c->head);
|
|
|
|
|
|
if (assign->flags & OAF_BOUND)
|
|
@@ -663,24 +670,45 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- current = (c->assigned + (1 << (64 - c->length)));
|
|
|
+ current = (c->assigned_subnet_id + (1 << (64 - c->length)));
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+/* Check iid against reserved IPv6 interface identifiers.
|
|
|
+ Refer to:
|
|
|
+ http://www.iana.org/assignments/ipv6-interface-ids */
|
|
|
+static bool is_reserved_ipv6_iid(uint64_t iid)
|
|
|
+{
|
|
|
+ if (iid == 0x0000000000000000)
|
|
|
+ /* Subnet-Router Anycast [RFC4291] */
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if ((iid & 0xFFFFFFFFFF000000) == 0x02005EFFFE000000)
|
|
|
+ /* Reserved IPv6 Interface Identifiers corresponding
|
|
|
+ to the IANA Ethernet Block [RFC4291] */
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if ((iid & 0xFFFFFFFFFFFFFF80) == 0xFDFFFFFFFFFFFF80)
|
|
|
+ /* Reserved Subnet Anycast Addresses [RFC2526] */
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
static bool assign_na(struct interface *iface, struct dhcp_assignment *a)
|
|
|
{
|
|
|
struct dhcp_assignment *c;
|
|
|
uint32_t seed = 0;
|
|
|
|
|
|
/* Preconfigured assignment by static lease */
|
|
|
- if (a->assigned) {
|
|
|
+ if (a->assigned_host_id) {
|
|
|
list_for_each_entry(c, &iface->ia_assignments, head) {
|
|
|
- if (c->assigned > a->assigned || !(c->flags & OAF_DHCPV6_NA)) {
|
|
|
+ if (!(c->flags & OAF_DHCPV6_NA) || c->assigned_host_id > a->assigned_host_id ) {
|
|
|
list_add_tail(&a->head, &c->head);
|
|
|
return true;
|
|
|
- } else if (c->assigned == a->assigned)
|
|
|
+ } else if (c->assigned_host_id == a->assigned_host_id)
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
@@ -688,22 +716,46 @@ static bool assign_na(struct interface *iface, struct dhcp_assignment *a)
|
|
|
/* Seed RNG with checksum of DUID */
|
|
|
for (size_t i = 0; i < a->clid_len; ++i)
|
|
|
seed += a->clid_data[i];
|
|
|
- srand(seed);
|
|
|
+ srandom(seed);
|
|
|
|
|
|
/* Try to assign up to 100x */
|
|
|
for (size_t i = 0; i < 100; ++i) {
|
|
|
- uint32_t try;
|
|
|
- do try = ((uint32_t)rand()) % 0x0fff; while (try < 0x100);
|
|
|
+ uint64_t try;
|
|
|
+
|
|
|
+ if (iface->dhcpv6_hostid_len > 32) {
|
|
|
+ uint32_t mask_high;
|
|
|
+
|
|
|
+ if (iface->dhcpv6_hostid_len >= 64)
|
|
|
+ mask_high = UINT32_MAX;
|
|
|
+ else
|
|
|
+ mask_high = (1 << (iface->dhcpv6_hostid_len - 32)) - 1;
|
|
|
+
|
|
|
+ do {
|
|
|
+ try = (uint32_t)random();
|
|
|
+ try |= (uint64_t)((uint32_t)random() & mask_high) << 32;
|
|
|
+ } while (try < 0x100);
|
|
|
+ } else {
|
|
|
+ uint32_t mask_low;
|
|
|
+
|
|
|
+ if (iface->dhcpv6_hostid_len == 32)
|
|
|
+ mask_low = UINT32_MAX;
|
|
|
+ else
|
|
|
+ mask_low = (1 << iface->dhcpv6_hostid_len) - 1;
|
|
|
+ do try = ((uint32_t)random()) & mask_low; while (try < 0x100);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_reserved_ipv6_iid(try))
|
|
|
+ continue;
|
|
|
|
|
|
if (config_find_lease_by_hostid(try))
|
|
|
continue;
|
|
|
|
|
|
list_for_each_entry(c, &iface->ia_assignments, head) {
|
|
|
- if (c->assigned > try || !(c->flags & OAF_DHCPV6_NA)) {
|
|
|
- a->assigned = try;
|
|
|
+ if (!(c->flags & OAF_DHCPV6_NA) || c->assigned_host_id > try) {
|
|
|
+ a->assigned_host_id = try;
|
|
|
list_add_tail(&a->head, &c->head);
|
|
|
return true;
|
|
|
- } else if (c->assigned == try)
|
|
|
+ } else if (c->assigned_host_id == try)
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -735,7 +787,7 @@ static void handle_addrlist_change(struct netevent_handler_info *info)
|
|
|
c->managed_size)
|
|
|
continue;
|
|
|
|
|
|
- if (c->assigned >= border->assigned)
|
|
|
+ if (c->assigned_subnet_id >= border->assigned_subnet_id)
|
|
|
list_move(&c->head, &reassign);
|
|
|
else if (c->flags & OAF_BOUND)
|
|
|
apply_lease(c, true);
|
|
@@ -909,7 +961,7 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
|
|
|
.addr = addrs[i].addr.in6,
|
|
|
};
|
|
|
|
|
|
- o_ia_p.addr.s6_addr32[1] |= htonl(a->assigned);
|
|
|
+ o_ia_p.addr.s6_addr32[1] |= htonl(a->assigned_subnet_id);
|
|
|
o_ia_p.addr.s6_addr32[2] = o_ia_p.addr.s6_addr32[3] = 0;
|
|
|
|
|
|
if (!valid_prefix_length(a, addrs[i].prefix))
|
|
@@ -931,7 +983,8 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
|
|
|
.valid = htonl(prefix_valid)
|
|
|
};
|
|
|
|
|
|
- o_ia_a.addr.s6_addr32[3] = htonl(a->assigned);
|
|
|
+ o_ia_a.addr.s6_addr32[2] = htonl(a->assigned_host_id >> 32);
|
|
|
+ o_ia_a.addr.s6_addr32[3] = htonl(a->assigned_host_id & UINT32_MAX);
|
|
|
|
|
|
if (!ADDR_ENTRY_VALID_IA_ADDR(iface, i, m, addrs))
|
|
|
continue;
|
|
@@ -1002,14 +1055,15 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
|
|
|
|
|
|
addr = addrs[i].addr.in6;
|
|
|
if (ia->type == htons(DHCPV6_OPT_IA_PD)) {
|
|
|
- addr.s6_addr32[1] |= htonl(a->assigned);
|
|
|
+ addr.s6_addr32[1] |= htonl(a->assigned_subnet_id);
|
|
|
addr.s6_addr32[2] = addr.s6_addr32[3] = 0;
|
|
|
|
|
|
if (!memcmp(&ia_p->addr, &addr, sizeof(addr)) &&
|
|
|
ia_p->prefix == ((a->managed) ? addrs[i].prefix : a->length))
|
|
|
found = true;
|
|
|
} else {
|
|
|
- addr.s6_addr32[3] = htonl(a->assigned);
|
|
|
+ addr.s6_addr32[2] = htonl(a->assigned_host_id >> 32);
|
|
|
+ addr.s6_addr32[3] = htonl(a->assigned_host_id & UINT32_MAX);
|
|
|
|
|
|
if (!memcmp(&ia_a->addr, &addr, sizeof(addr)))
|
|
|
found = true;
|
|
@@ -1312,7 +1366,10 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
|
|
|
a->iaid = ia->iaid;
|
|
|
a->length = reqlen;
|
|
|
a->peer = *addr;
|
|
|
- a->assigned = is_na && l ? l->hostid : reqhint;
|
|
|
+ if (is_na)
|
|
|
+ a->assigned_host_id = l ? l->hostid : 0;
|
|
|
+ else
|
|
|
+ a->assigned_subnet_id = reqhint;
|
|
|
a->valid_until = now;
|
|
|
a->preferred_until = now;
|
|
|
a->dhcp_free_cb = dhcpv6_ia_free_assignment;
|
|
@@ -1441,7 +1498,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
|
|
|
} else if ((a->flags & OAF_DHCPV6_NA) && hdr->msg_type == DHCPV6_MSG_DECLINE) {
|
|
|
a->flags &= ~OAF_BOUND;
|
|
|
|
|
|
- if (!(a->flags & OAF_STATIC) || a->lease->hostid != a->assigned) {
|
|
|
+ if (!(a->flags & OAF_STATIC) || a->lease->hostid != a->assigned_host_id) {
|
|
|
memset(a->clid_data, 0, a->clid_len);
|
|
|
a->valid_until = now + 3600; /* Block address for 1h */
|
|
|
} else
|