Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: DHCPv6 does not respect prefix assigned to interface (<=/64) #203

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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