Extend 79aba0f10a for multiple IPv6 addresses.

This commit is contained in:
Simon Kelley
2020-02-06 22:09:30 +00:00
parent 79aba0f10a
commit 137286e9ba
6 changed files with 328 additions and 280 deletions

View File

@@ -1008,14 +1008,15 @@ allowed to specify the client ID as text, like this:
A single
.B --dhcp-host
may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be bracketed by square brackets thus:
may contain an IPv4 address or one or more IPv6 addresses, or both. IPv6 addresses must be bracketed by square brackets thus:
.B --dhcp-host=laptop,[1234::56]
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. For IPv6, the address may include a prefix length:
the appropriate network part inserted. For IPv6, an 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
which (in this case) specifies four addresses, 1234::50 to 1234::53. This (an the ability
to specify multiple addresses) 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.

View File

@@ -271,26 +271,35 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config
{
if (!context) /* called via find_config() from lease_update_from_configs() */
return 1;
if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
return 1;
#ifdef HAVE_DHCP6
if ((context->flags & CONTEXT_V6) && (config->flags & CONFIG_WILDCARD))
return 1;
#endif
if (context->flags & CONTEXT_V6)
{
struct addrlist *addr_list;
for (; context; context = context->current)
#ifdef HAVE_DHCP6
if (context->flags & CONTEXT_V6)
{
if ((config->flags & CONFIG_ADDR6) && is_same_net6(&config->addr6, &context->start6, context->prefix))
return 1;
}
else
if (!(config->flags & CONFIG_ADDR6))
return 1;
for (; context; context = context->current)
for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
{
if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
return 1;
if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
return 1;
}
}
else
#endif
if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
{
if (!(config->flags & CONFIG_ADDR))
return 1;
for (; context; context = context->current)
if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
return 1;
}
return 0;
}
@@ -426,9 +435,19 @@ void dhcp_update_configs(struct dhcp_config *configs)
if (prot == AF_INET6 &&
(!(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;
/* host must have exactly one address if comming from /etc/hosts. */
if (!config->addr6 && (config->addr6 = whine_malloc(sizeof(struct addrlist))))
{
config->addr6->next = NULL;
config->addr6->flags = 0;
}
if (config->addr6 && !config->addr6->next && !(config->addr6->flags & (ADDRLIST_WILDCARD|ADDRLIST_PREFIX)))
{
memcpy(&config->addr6->addr.addr6, &crec->addr.addr6, IN6ADDRSZ);
config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS;
}
continue;
}
#endif

View File

@@ -423,10 +423,15 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct
struct dhcp_config *config;
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_ADDR6) &&
(!net || is_same_net6(&config->addr6, net, prefix)) &&
is_same_net6(&config->addr6, addr, (config->flags & CONFIG_PREFIX) ? config->prefix : 128))
return config;
if (config->flags & CONFIG_ADDR6)
{
struct addrlist *addr_list;
for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
if ((!net || is_same_net6(&addr_list->addr.addr6, net, prefix) || ((addr_list->flags & ADDRLIST_WILDCARD) && prefix == 64)) &&
is_same_net6(&addr_list->addr.addr6, addr, (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128))
return config;
}
return NULL;
}

View File

@@ -374,9 +374,11 @@ struct ds_config {
struct ds_config *next;
};
#define ADDRLIST_LITERAL 1
#define ADDRLIST_IPV6 2
#define ADDRLIST_REVONLY 4
#define ADDRLIST_LITERAL 1
#define ADDRLIST_IPV6 2
#define ADDRLIST_REVONLY 4
#define ADDRLIST_PREFIX 8
#define ADDRLIST_WILDCARD 16
struct addrlist {
union all_addr addr;
@@ -766,8 +768,7 @@ struct dhcp_config {
char *hostname, *domain;
struct dhcp_netid_list *netid;
#ifdef HAVE_DHCP6
struct in6_addr addr6;
int prefix;
struct addrlist *addr6;
#endif
struct in_addr addr;
time_t decline_time;
@@ -789,9 +790,7 @@ struct dhcp_config {
#define CONFIG_DECLINED 1024 /* address declined by client */
#define CONFIG_BANK 2048 /* from dhcp hosts file */
#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;

View File

@@ -1020,15 +1020,30 @@ static void dhcp_config_free(struct dhcp_config *config)
if (config)
{
struct hwaddr_config *hwaddr = config->hwaddr;
while (hwaddr)
{
struct hwaddr_config *tmp = hwaddr;
hwaddr = hwaddr->next;
free(tmp);
}
dhcp_netid_list_free(config->netid);
if (config->flags & CONFIG_CLID)
free(config->clid);
if (config->flags & CONFIG_ADDR6)
{
struct addrlist *addr, *tmp;
for (addr = config->addr6; addr; addr = tmp)
{
tmp = addr->next;
free(addr);
}
}
free(config);
}
}
@@ -3193,8 +3208,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_BANK:
case 'G': /* --dhcp-host */
{
int j, k = 0;
char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
struct dhcp_config *new;
struct in_addr in;
@@ -3205,215 +3218,222 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->hwaddr = NULL;
new->netid = NULL;
new->clid = NULL;
new->addr6 = NULL;
if ((a[0] = arg))
for (k = 1; k < 7; k++)
if (!(a[k] = split(a[k-1])))
break;
for (j = 0; j < k; j++)
if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
{
char *arg = a[j];
if ((arg[0] == 'i' || arg[0] == 'I') &&
(arg[1] == 'd' || arg[1] == 'D') &&
arg[2] == ':')
{
if (arg[3] == '*')
new->flags |= CONFIG_NOCLID;
else
{
int len;
arg += 3; /* dump id: */
if (strchr(arg, ':'))
len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
else
{
unhide_metas(arg);
len = (int) strlen(arg);
}
if (len == -1)
{
dhcp_config_free(new);
ret_err(_("bad hex constant"));
}
else if ((new->clid = opt_malloc(len)))
{
new->flags |= CONFIG_CLID;
new->clid_len = len;
memcpy(new->clid, arg, len);
}
}
}
/* dhcp-host has strange backwards-compat needs. */
else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
{
struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
newlist->next = new->netid;
new->netid = newlist;
newlist->list = dhcp_netid_create(arg+4, NULL);
}
else if (strstr(arg, "tag:") == arg)
{
dhcp_config_free(new);
ret_err(_("cannot match tags in --dhcp-host"));
}
#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))
{
dhcp_config_free(new);
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;
/* set WILDCARD if network part all zeros */
if (i == 8)
new->flags |= CONFIG_WILDCARD;
new->flags |= CONFIG_ADDR6;
}
#endif
else
{
struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
&newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
{
free(newhw);
dhcp_config_free(new);
ret_err(_("bad hex constant"));
}
else
{
newhw->next = new->hwaddr;
new->hwaddr = newhw;
}
}
}
else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
{
struct dhcp_config *configs;
new->addr = in;
new->flags |= CONFIG_ADDR;
/* If the same IP appears in more than one host config, then DISCOVER
for one of the hosts will get the address, but REQUEST will be NAKed,
since the address is reserved by the other one -> protocol loop. */
for (configs = daemon->dhcp_conf; configs; configs = configs->next)
if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
while (arg)
{
comma = split(arg);
if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
{
if ((arg[0] == 'i' || arg[0] == 'I') &&
(arg[1] == 'd' || arg[1] == 'D') &&
arg[2] == ':')
{
sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
return 0;
}
}
else
{
char *cp, *lastp = NULL, last = 0;
int fac = 1, isdig = 0;
if (strlen(a[j]) > 1)
{
lastp = a[j] + strlen(a[j]) - 1;
last = *lastp;
switch (last)
{
case 'w':
case 'W':
fac *= 7;
/* fall through */
case 'd':
case 'D':
fac *= 24;
/* fall through */
case 'h':
case 'H':
fac *= 60;
/* fall through */
case 'm':
case 'M':
fac *= 60;
/* fall through */
case 's':
case 'S':
*lastp = 0;
}
}
for (cp = a[j]; *cp; cp++)
if (isdigit((unsigned char)*cp))
isdig = 1;
else if (*cp != ' ')
break;
if (arg[3] == '*')
new->flags |= CONFIG_NOCLID;
else
{
int len;
arg += 3; /* dump id: */
if (strchr(arg, ':'))
len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
else
{
unhide_metas(arg);
len = (int) strlen(arg);
}
if (len == -1)
{
dhcp_config_free(new);
ret_err(_("bad hex constant"));
}
else if ((new->clid = opt_malloc(len)))
{
new->flags |= CONFIG_CLID;
new->clid_len = len;
memcpy(new->clid, arg, len);
}
}
}
/* dhcp-host has strange backwards-compat needs. */
else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
{
struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
newlist->next = new->netid;
new->netid = newlist;
newlist->list = dhcp_netid_create(arg+4, NULL);
}
else if (strstr(arg, "tag:") == arg)
{
dhcp_config_free(new);
ret_err(_("cannot match tags in --dhcp-host"));
}
#ifdef HAVE_DHCP6
else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
{
char *pref;
struct in6_addr in6;
struct addrlist *new_addr;
arg[strlen(arg)-1] = 0;
arg++;
pref = split_chr(arg, '/');
if (!inet_pton(AF_INET6, arg, &in6))
{
dhcp_config_free(new);
ret_err(_("bad IPv6 address"));
}
if (*cp)
{
if (lastp)
*lastp = last;
if (strcmp(a[j], "infinite") == 0)
new_addr = opt_malloc(sizeof(struct addrlist));
new_addr->next = new->addr6;
new_addr->flags = 0;
new_addr->addr.addr6 = in6;
new->addr6 = new_addr;
if (pref)
{
u64 addrpart = addr6part(&in6);
if (!atoi_check(pref, &new_addr->prefixlen) ||
new_addr->prefixlen > 128 ||
(((1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
{
dhcp_config_free(new);
ret_err(_("bad IPv6 prefix"));
}
new_addr->flags |= ADDRLIST_PREFIX;
}
for (i= 0; i < 8; i++)
if (in6.s6_addr[i] != 0)
break;
/* set WILDCARD if network part all zeros */
if (i == 8)
new_addr->flags |= ADDRLIST_WILDCARD;
new->flags |= CONFIG_ADDR6;
}
#endif
else
{
struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
&newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
{
free(newhw);
dhcp_config_free(new);
ret_err(_("bad hex constant"));
}
else
{
newhw->next = new->hwaddr;
new->hwaddr = newhw;
}
}
}
else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
{
struct dhcp_config *configs;
new->addr = in;
new->flags |= CONFIG_ADDR;
/* If the same IP appears in more than one host config, then DISCOVER
for one of the hosts will get the address, but REQUEST will be NAKed,
since the address is reserved by the other one -> protocol loop. */
for (configs = daemon->dhcp_conf; configs; configs = configs->next)
if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
{
new->lease_time = 0xffffffff;
new->flags |= CONFIG_TIME;
}
else if (strcmp(a[j], "ignore") == 0)
new->flags |= CONFIG_DISABLE;
else
{
if (!(new->hostname = canonicalise_opt(a[j])) ||
!legal_hostname(new->hostname))
{
dhcp_config_free(new);
ret_err(_("bad DHCP host name"));
}
new->flags |= CONFIG_NAME;
new->domain = strip_hostname(new->hostname);
}
}
else if (isdig)
{
new->lease_time = atoi(a[j]) * fac;
/* Leases of a minute or less confuse
some clients, notably Apple's */
if (new->lease_time < 120)
new->lease_time = 120;
new->flags |= CONFIG_TIME;
}
}
sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
return 0;
}
}
else
{
char *cp, *lastp = NULL, last = 0;
int fac = 1, isdig = 0;
if (strlen(arg) > 1)
{
lastp = arg + strlen(arg) - 1;
last = *lastp;
switch (last)
{
case 'w':
case 'W':
fac *= 7;
/* fall through */
case 'd':
case 'D':
fac *= 24;
/* fall through */
case 'h':
case 'H':
fac *= 60;
/* fall through */
case 'm':
case 'M':
fac *= 60;
/* fall through */
case 's':
case 'S':
*lastp = 0;
}
}
for (cp = arg; *cp; cp++)
if (isdigit((unsigned char)*cp))
isdig = 1;
else if (*cp != ' ')
break;
if (*cp)
{
if (lastp)
*lastp = last;
if (strcmp(arg, "infinite") == 0)
{
new->lease_time = 0xffffffff;
new->flags |= CONFIG_TIME;
}
else if (strcmp(arg, "ignore") == 0)
new->flags |= CONFIG_DISABLE;
else
{
if (!(new->hostname = canonicalise_opt(arg)) ||
!legal_hostname(new->hostname))
{
dhcp_config_free(new);
ret_err(_("bad DHCP host name"));
}
new->flags |= CONFIG_NAME;
new->domain = strip_hostname(new->hostname);
}
}
else if (isdig)
{
new->lease_time = atoi(arg) * fac;
/* Leases of a minute or less confuse
some clients, notably Apple's */
if (new->lease_time < 120)
new->lease_time = 120;
new->flags |= CONFIG_TIME;
}
}
arg = comma;
}
daemon->dhcp_conf = new;
break;
}
case LOPT_TAG_IF: /* --tag-if */
{
struct tag_if *new = opt_malloc(sizeof(struct tag_if));

View File

@@ -1671,68 +1671,72 @@ static int config_implies(struct dhcp_config *config, struct dhcp_context *conte
{
int prefix;
struct in6_addr wild_addr;
struct addrlist *addr_list;
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))
for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
{
if (context->prefix != 64)
return 0;
prefix = (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128;
wild_addr = addr_list->addr.addr6;
wild_addr = context->start6;
setaddr6part(&wild_addr, addr6part(&config->addr6));
if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
{
wild_addr = context->start6;
setaddr6part(&wild_addr, addr6part(&addr_list->addr.addr6));
}
else if (!is_same_net6(&context->start6, addr, context->prefix))
continue;
if (is_same_net6(&wild_addr, addr, prefix))
return 1;
}
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;
struct addrlist *addr_list;
if (!config || !(config->flags & CONFIG_ADDR6))
return 0;
addrpart = addr6part(&config->addr6);
if ((config->flags & CONFIG_WILDCARD))
for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
{
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;
addrpart = addr6part(&addr_list->addr.addr6);
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;
}
if ((addr_list->flags & ADDRLIST_WILDCARD))
{
if (context->prefix != 64)
continue;
*addr = context->start6;
setaddr6part(addr, addrpart);
}
else if (is_same_net6(&context->start6, &addr_list->addr.addr6, context->prefix))
*addr = addr_list->addr.addr6;
else
continue;
while(1)
{
if (check_address(state, addr))
return 1;
if (!(addr_list->flags & ADDRLIST_PREFIX))
break;
addrpart++;
setaddr6part(addr, addrpart);
if (!is_same_net6(addr, &addr_list->addr.addr6, addr_list->prefixlen))
break;
}
}
return 0;
}
/* Calculate valid and preferred times to send in leases/renewals.