From 7e0f8d385b01ef7e1caabea2f6425571021924d4 Mon Sep 17 00:00:00 2001 From: Matthias Riegler Date: Mon, 12 Jun 2023 21:01:08 +0200 Subject: [PATCH] fix: DHCPv6 does not respect prefix assigned to interface (<=/64) fixes #199 Signed-off-by: Matthias Riegler --- src/dhcpv6-ia.c | 50 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 41c9f30..065c8e9 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -47,6 +47,8 @@ static void handle_addrlist_change(struct netevent_handler_info *info); static void start_reconf(struct dhcp_assignment *a); static void stop_reconf(struct dhcp_assignment *a); static void valid_until_cb(struct uloop_timeout *event); +static uint32_t mask_high_for_hostid(uint8_t dhcpv6_hostid_len); +static uint32_t mask_low_for_hostid(uint8_t dhcpv6_hostid_len); static struct netevent_handler dhcpv6_netevent_handler = { .cb = dhcpv6_netevent_cb, }; static struct uloop_timeout valid_until_timeout = {.cb = valid_until_cb}; @@ -249,8 +251,16 @@ 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[2] = htonl(c->assigned_host_id >> 32); - addr.s6_addr32[3] = htonl(c->assigned_host_id & UINT32_MAX); + uint32_t mask_low = mask_low_for_hostid(iface->dhcpv6_hostid_len); + uint32_t mask_high = mask_high_for_hostid(iface->dhcpv6_hostid_len); + addr.s6_addr32[2] = htonl(( + (c->assigned_host_id >> 32) & mask_high) + & (~mask_high & ntohl(addrs[i].addr.in6.s6_addr32[2]) + )); + addr.s6_addr32[3] = htonl(( + (c->assigned_host_id & UINT32_MAX) & mask_low) + & (~mask_low & ntohl(addrs[i].addr.in6.s6_addr32[3]) + )); } else { if (!valid_prefix_length(c, addrs[i].prefix)) continue; @@ -718,6 +728,23 @@ static bool is_reserved_ipv6_iid(uint64_t iid) return false; } +static uint32_t mask_low_for_hostid(uint8_t dhcpv6_hostid_len) { + if (dhcpv6_hostid_len >= 32) + return UINT32_MAX; + + return (1 << dhcpv6_hostid_len) - 1; +} + +static uint32_t mask_high_for_hostid(uint8_t dhcpv6_hostid_len) { + if (dhcpv6_hostid_len >= 64) + return UINT32_MAX; + + if (dhcpv6_hostid_len >= 32) + return (1 << (dhcpv6_hostid_len - 32)) - 1; + + return return 0; +} + static bool assign_na(struct interface *iface, struct dhcp_assignment *a) { struct dhcp_assignment *c; @@ -744,27 +771,22 @@ static bool assign_na(struct interface *iface, struct dhcp_assignment *a) 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; + uint32_t mask_high = mask_high_for_hostid(iface->dhcpv6_hostid_len); 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; + } else { + uint32_t mask_low = mask_low_for_hostid(iface->dhcpv6_hostid_len); do try = ((uint32_t)random()) & mask_low; while (try < 0x100); } + // If prefix is < 64 bits, we need to shift the IID to the left + if (iface->addr6_len < 64) + try <<= (64 - iface->addr6_len); + if (is_reserved_ipv6_iid(try)) continue;