Skip to content

Commit

Permalink
fix: DHCPv6 does not respect prefix assigned to interface (<=/64)
Browse files Browse the repository at this point in the history
fixes openwrt#199

Signed-off-by: Matthias Riegler <[email protected]>
  • Loading branch information
xvzf committed Sep 12, 2023
1 parent c9e619f commit 7e0f8d3
Showing 1 changed file with 36 additions and 14 deletions.
50 changes: 36 additions & 14 deletions src/dhcpv6-ia.c
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down

0 comments on commit 7e0f8d3

Please sign in to comment.