mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
Support prefixed ranges of ipv6 addresses in dhcp-host.
When a request matching the clid or mac address is recieved the server will iterate over all candidate addresses until it find's one that is not already leased to a different clid/iaid and advertise this address. Using multiple reservations for a single host makes it possible to maintain a static leases only configuration which support network booting systems with UEFI firmware that request a new address (a new SOLICIT with a new IA_NA option using a new IAID) for different boot modes, for instance 'PXE over IPv6', and 'HTTP-Boot over IPv6'. Open Virtual Machine Firmware (OVMF) and most UEFI firmware build on the EDK2 code base exhibit this behaviour.
This commit is contained in:
@@ -69,6 +69,14 @@ version 2.81
|
||||
different interfaces on the same IPv6 net, and we're doing
|
||||
RA/DHCP service on only one of them. Thanks to NIIBE Yutaka
|
||||
for spotting this case and making the initial patch.
|
||||
|
||||
Support prefixed ranges of ipv6 addresses in dhcp-host.
|
||||
This eases problems chain-netbooting, where each link in the
|
||||
chain requests an address using a different UID. With a single
|
||||
address, only one gets the "static" address, but with this
|
||||
fix, enough addresses can be reserved for all the stages of the
|
||||
boot. Many thanks to Harald Jensås for his work on this idea and
|
||||
earlier patches.
|
||||
|
||||
|
||||
version 2.80
|
||||
|
||||
@@ -1013,7 +1013,13 @@ may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be
|
||||
IPv6 addresses may contain only the host-identifier part:
|
||||
.B --dhcp-host=laptop,[::56]
|
||||
in which case they act as wildcards in constructed dhcp ranges, with
|
||||
the appropriate network part inserted.
|
||||
the appropriate network part inserted. For IPv6, the address may include a prefix length:
|
||||
.B --dhcp-host=laptop,[1234:50/126]
|
||||
which (in this case) specifies four addresses, 1234::50 to 1234::53. This is useful
|
||||
when a host presents either a consistent name or hardware-ID, but varying DUIDs, since it allows
|
||||
dnsmasq to honour the static address allocation but assign a different adddress for each DUID. This
|
||||
typically occurs when chain netbooting, as each stage of the chain gets in turn allocates an address.
|
||||
|
||||
Note that in IPv6 DHCP, the hardware address may not be
|
||||
available, though it normally is for direct-connected clients, or
|
||||
clients using DHCP relays which support RFC 6939.
|
||||
|
||||
@@ -424,10 +424,11 @@ void dhcp_update_configs(struct dhcp_config *configs)
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (prot == AF_INET6 &&
|
||||
(!(conf_tmp = config_find_by_address6(configs, &crec->addr.addr6, 128, 0)) || conf_tmp == config))
|
||||
(!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr6)) || conf_tmp == config))
|
||||
{
|
||||
memcpy(&config->addr6, &crec->addr.addr6, IN6ADDRSZ);
|
||||
config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS;
|
||||
config->flags &= ~CONFIG_PREFIX;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
40
src/dhcp6.c
40
src/dhcp6.c
@@ -418,14 +418,14 @@ static int complete_context6(struct in6_addr *local, int prefix,
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
|
||||
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, struct in6_addr *addr)
|
||||
{
|
||||
struct dhcp_config *config;
|
||||
|
||||
for (config = configs; config; config = config->next)
|
||||
if ((config->flags & CONFIG_ADDR6) &&
|
||||
is_same_net6(&config->addr6, net, prefix) &&
|
||||
(prefix == 128 || addr6part(&config->addr6) == addr))
|
||||
(!net || is_same_net6(&config->addr6, net, prefix)) &&
|
||||
is_same_net6(&config->addr6, addr, (config->flags & CONFIG_PREFIX) ? config->prefix : 128))
|
||||
return config;
|
||||
|
||||
return NULL;
|
||||
@@ -494,16 +494,15 @@ struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned c
|
||||
for (d = context; d; d = d->current)
|
||||
if (addr == addr6part(&d->local6))
|
||||
break;
|
||||
|
||||
*ans = c->start6;
|
||||
setaddr6part (ans, addr);
|
||||
|
||||
if (!d &&
|
||||
!lease6_find_by_addr(&c->start6, c->prefix, addr) &&
|
||||
!config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
|
||||
{
|
||||
*ans = c->start6;
|
||||
setaddr6part (ans, addr);
|
||||
return c;
|
||||
}
|
||||
|
||||
!config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, ans))
|
||||
return c;
|
||||
|
||||
addr++;
|
||||
|
||||
if (addr == addr6part(&c->end6) + 1)
|
||||
@@ -557,27 +556,6 @@ struct dhcp_context *address6_valid(struct dhcp_context *context,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
|
||||
{
|
||||
if (!config || !(config->flags & CONFIG_ADDR6))
|
||||
return 0;
|
||||
|
||||
if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
|
||||
{
|
||||
*addr = context->start6;
|
||||
setaddr6part(addr, addr6part(&config->addr6));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_same_net6(&context->start6, &config->addr6, context->prefix))
|
||||
{
|
||||
*addr = config->addr6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void make_duid(time_t now)
|
||||
{
|
||||
(void)now;
|
||||
|
||||
@@ -767,6 +767,7 @@ struct dhcp_config {
|
||||
struct dhcp_netid_list *netid;
|
||||
#ifdef HAVE_DHCP6
|
||||
struct in6_addr addr6;
|
||||
int prefix;
|
||||
#endif
|
||||
struct in_addr addr;
|
||||
time_t decline_time;
|
||||
@@ -790,6 +791,7 @@ struct dhcp_config {
|
||||
#define CONFIG_ADDR6 4096
|
||||
#define CONFIG_WILDCARD 8192
|
||||
#define CONFIG_ADDR6_HOSTS 16384 /* address added by from /etc/hosts */
|
||||
#define CONFIG_PREFIX 32768 /* addr6 is a set, size given by prefix */
|
||||
|
||||
struct dhcp_opt {
|
||||
int opt, len, flags;
|
||||
@@ -1508,7 +1510,6 @@ void dhcp6_init(void);
|
||||
void dhcp6_packet(time_t now);
|
||||
struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr,
|
||||
unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans);
|
||||
int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
|
||||
struct dhcp_context *address6_available(struct dhcp_context *context,
|
||||
struct in6_addr *taddr,
|
||||
struct dhcp_netid *netids,
|
||||
@@ -1518,7 +1519,7 @@ struct dhcp_context *address6_valid(struct dhcp_context *context,
|
||||
struct dhcp_netid *netids,
|
||||
int plain_range);
|
||||
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net,
|
||||
int prefix, u64 addr);
|
||||
int prefix, struct in6_addr *addr);
|
||||
void make_duid(time_t now);
|
||||
void dhcp_construct_contexts(time_t now);
|
||||
void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
|
||||
|
||||
18
src/option.c
18
src/option.c
@@ -3264,8 +3264,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
|
||||
{
|
||||
char *pref;
|
||||
|
||||
arg[strlen(arg)-1] = 0;
|
||||
arg++;
|
||||
pref = split_chr(arg, '/');
|
||||
|
||||
if (!inet_pton(AF_INET6, arg, &new->addr6))
|
||||
{
|
||||
@@ -3273,6 +3276,21 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
ret_err(_("bad IPv6 address"));
|
||||
}
|
||||
|
||||
if (pref)
|
||||
{
|
||||
u64 addrpart = addr6part(&new->addr6);
|
||||
|
||||
if (!atoi_check(pref, &new->prefix) ||
|
||||
new->prefix > 128 ||
|
||||
(((1<<(128-new->prefix))-1) & addrpart) != 0)
|
||||
{
|
||||
dhcp_config_free(new);
|
||||
ret_err(_("bad IPv6 prefix"));
|
||||
}
|
||||
|
||||
new->flags |= CONFIG_PREFIX;
|
||||
}
|
||||
|
||||
for (i= 0; i < 8; i++)
|
||||
if (new->addr6.s6_addr[i] != 0)
|
||||
break;
|
||||
|
||||
@@ -49,6 +49,8 @@ static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz);
|
||||
static void mark_context_used(struct state *state, struct in6_addr *addr);
|
||||
static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr);
|
||||
static int check_address(struct state *state, struct in6_addr *addr);
|
||||
static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state);
|
||||
static int config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
|
||||
static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option,
|
||||
unsigned int *min_time, struct in6_addr *addr, time_t now);
|
||||
static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now);
|
||||
@@ -675,7 +677,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
/* If the client asks for an address on the same network as a configured address,
|
||||
offer the configured address instead, to make moving to newly-configured
|
||||
addresses automatic. */
|
||||
if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, &addr))
|
||||
if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr, state))
|
||||
{
|
||||
req_addr = addr;
|
||||
mark_config_used(c, &addr);
|
||||
@@ -699,8 +701,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
for (c = state->context; c; c = c->current)
|
||||
if (!(c->flags & CONTEXT_CONF_USED) &&
|
||||
match_netid(c->filter, solicit_tags, plain_range) &&
|
||||
config_valid(config, c, &addr) &&
|
||||
check_address(state, &addr))
|
||||
config_valid(config, c, &addr, state))
|
||||
{
|
||||
mark_config_used(state->context, &addr);
|
||||
if (have_config(config, CONFIG_TIME))
|
||||
@@ -838,14 +839,13 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
struct in6_addr req_addr;
|
||||
struct dhcp_context *dynamic, *c;
|
||||
unsigned int lease_time;
|
||||
struct in6_addr addr;
|
||||
int config_ok = 0;
|
||||
|
||||
/* align. */
|
||||
memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
|
||||
|
||||
if ((c = address6_valid(state->context, &req_addr, tagif, 1)))
|
||||
config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr);
|
||||
config_ok = config_implies(config, c, &req_addr);
|
||||
|
||||
if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c)
|
||||
{
|
||||
@@ -971,12 +971,11 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
|
||||
(this_context = address6_valid(state->context, &req_addr, tagif, 1)))
|
||||
{
|
||||
struct in6_addr addr;
|
||||
unsigned int lease_time;
|
||||
|
||||
get_context_tag(state, this_context);
|
||||
|
||||
if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr) && have_config(config, CONFIG_TIME))
|
||||
if (config_implies(config, this_context, &req_addr) && have_config(config, CONFIG_TIME))
|
||||
lease_time = config->lease_time;
|
||||
else
|
||||
lease_time = this_context->lease_time;
|
||||
@@ -1667,6 +1666,75 @@ static int check_address(struct state *state, struct in6_addr *addr)
|
||||
}
|
||||
|
||||
|
||||
/* return true of *addr could have been generated from config. */
|
||||
static int config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
|
||||
{
|
||||
int prefix;
|
||||
struct in6_addr wild_addr;
|
||||
|
||||
if (!config || !(config->flags & CONFIG_ADDR6))
|
||||
return 0;
|
||||
|
||||
prefix = (config->flags & CONFIG_PREFIX) ? config->prefix : 128;
|
||||
wild_addr = config->addr6;
|
||||
|
||||
if (!is_same_net6(&context->start6, addr, context->prefix))
|
||||
return 0;
|
||||
|
||||
if ((config->flags & CONFIG_WILDCARD))
|
||||
{
|
||||
if (context->prefix != 64)
|
||||
return 0;
|
||||
|
||||
wild_addr = context->start6;
|
||||
setaddr6part(&wild_addr, addr6part(&config->addr6));
|
||||
}
|
||||
|
||||
if (is_same_net6(&wild_addr, addr, prefix))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state)
|
||||
{
|
||||
u64 addrpart;
|
||||
|
||||
if (!config || !(config->flags & CONFIG_ADDR6))
|
||||
return 0;
|
||||
|
||||
addrpart = addr6part(&config->addr6);
|
||||
|
||||
if ((config->flags & CONFIG_WILDCARD))
|
||||
{
|
||||
if (context->prefix != 64)
|
||||
return 0;
|
||||
|
||||
*addr = context->start6;
|
||||
setaddr6part(addr, addrpart);
|
||||
}
|
||||
else if (is_same_net6(&context->start6, &config->addr6, context->prefix))
|
||||
*addr = config->addr6;
|
||||
else
|
||||
return 0;
|
||||
|
||||
while(1) {
|
||||
if (check_address(state, addr))
|
||||
return 1;
|
||||
|
||||
if (!(config->flags & CONFIG_PREFIX))
|
||||
return 0;
|
||||
|
||||
/* config may specify a set of addresses, return first one not in use
|
||||
by another client */
|
||||
|
||||
addrpart++;
|
||||
setaddr6part(addr, addrpart);
|
||||
if (!is_same_net6(addr, &config->addr6, config->prefix))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate valid and preferred times to send in leases/renewals.
|
||||
|
||||
Inputs are:
|
||||
|
||||
Reference in New Issue
Block a user