diff options
author | Mikael Magnusson <mikma@users.sourceforge.net> | 2021-07-14 22:46:55 +0200 |
---|---|---|
committer | Hans Dedecker <dedeckeh@gmail.com> | 2021-08-14 21:11:30 +0200 |
commit | 16667699228101fda936068e3d11c0cf19270e34 (patch) | |
tree | 48cf08135c5286bb45c8c5f3606b92208f811fdc /src/dhcpv6-ia.c | |
parent | bc9d317f2921ae6b529f2c9f8de79b75992e206f (diff) |
dhcpv6-ia: allow up to 64 bit wide hostid
Add dhcpv6_hostid_len config option which controls the number
of bits in the host identifier of dynamically assigned IPv6
addresses. The default is 12 bits which is also the minimum.
The maximum is the whole interface identifier, i.e. 64 bits.
Allow up to 64 bit wide hostid in static leases.
Fixes #84 and #27.
Signed-off-by: Mikael Magnusson <mikma@users.sourceforge.net>
Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
Diffstat (limited to 'src/dhcpv6-ia.c')
-rw-r--r-- | src/dhcpv6-ia.c | 119 |
1 files changed, 88 insertions, 31 deletions
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index c378c09..e8255b5 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -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 |