From 9b200103342c0909def9f8d9b97cfd889be6bfd8 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 3 Feb 2020 23:58:45 +0000 Subject: [PATCH] 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. (cherry picked from commit 79aba0f10ad0157fb4f48afbbcb03f094caff97a) Conflicts: CHANGELOG src/dhcp-common.c src/dnsmasq.h src/dhcp6.c Extend 79aba0f10ad0157fb4f48afbbcb03f094caff97a for multiple IPv6 addresses. (cherry picked from commit 137286e9baecf6a3ba97722ef1b49c851b531810) Conflicts: man/dnsmasq.8 src/dhcp-common.c src/dhcp6.c src/rfc3315.c src/option.c Fix bug with prefixed wildcard addresses in 137286e9baecf6a3ba97722ef1b49c851b531810 (cherry picked from commit f064188032a829efdcf3988b24ac795ff52785ec) Conflicts: src/rfc3315.c --- man/dnsmasq.8 | 13 +- src/dhcp-common.c | 56 +++++--- src/dhcp6.c | 51 +++---- src/dnsmasq.h | 17 +-- src/option.c | 402 ++++++++++++++++++++++++++++++------------------------ src/rfc3315.c | 83 ++++++++++- 6 files changed, 370 insertions(+), 252 deletions(-) diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index f52762f..2c9d9f6 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -985,13 +985,20 @@ allowed to specify the client ID as text, like this: .B --dhcp-host=id:clientidastext,..... 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: +.B --dhcp-host +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. +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 (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. + 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. diff --git a/src/dhcp-common.c b/src/dhcp-common.c index d9719d1..5d437dd 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -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.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; } @@ -418,10 +427,21 @@ void dhcp_update_configs(struct dhcp_config *configs) #ifdef HAVE_DHCP6 if (prot == AF_INET6 && - (!(conf_tmp = config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0)) || conf_tmp == config)) + (!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr.addr.addr6)) || conf_tmp == config)) { - memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ); - config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS; + /* 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.addr.addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ); + config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS; + } + continue; } #endif diff --git a/src/dhcp6.c b/src/dhcp6.c index 0853664..6f1f54e 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -384,21 +384,26 @@ 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)) - 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.addr.addr6, net, prefix) || ((addr_list->flags & ADDRLIST_WILDCARD) && prefix == 64)) && + is_same_net6(&addr_list->addr.addr.addr6, addr, (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128)) + return config; + } return NULL; } struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr, - int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans) + unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans) { /* Find a free address: exclude anything in use and anything allocated to a particular hwaddr/clientid/hostname in our configuration. @@ -453,16 +458,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) @@ -516,27 +520,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; diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 6b18bb7..9437226 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -343,9 +343,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 { struct all_addr addr; @@ -748,7 +750,7 @@ struct dhcp_config { char *hostname, *domain; struct dhcp_netid_list *netid; #ifdef HAVE_DHCP6 - struct in6_addr addr6; + struct addrlist *addr6; #endif struct in_addr addr; time_t decline_time; @@ -770,7 +772,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 */ struct dhcp_opt { int opt, len, flags; @@ -1463,8 +1465,7 @@ int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, 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, - 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); + unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans); struct dhcp_context *address6_available(struct dhcp_context *context, struct in6_addr *taddr, struct dhcp_netid *netids, @@ -1474,7 +1475,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, diff --git a/src/option.c b/src/option.c index b12183b..ea70ee3 100644 --- a/src/option.c +++ b/src/option.c @@ -1010,15 +1010,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); } } @@ -3143,8 +3158,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; @@ -3155,197 +3168,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")); - } + 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] == ':') + { + 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] == ']') - { - arg[strlen(arg)-1] = 0; - arg++; - - if (!inet_pton(AF_INET6, arg, &new->addr6)) - { - dhcp_config_free(new); - ret_err(_("bad IPv6 address")); - } - - for (i= 0; i < 8; i++) - if (new->addr6.s6_addr[i] != 0) - break; + 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")); + } - /* set WILDCARD if network part all zeros */ - if (i == 8) - new->flags |= CONFIG_WILDCARD; + new_addr = opt_malloc(sizeof(struct addrlist)); + new_addr->next = new->addr6; + new_addr->flags = 0; + new_addr->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; + } - new->flags |= CONFIG_ADDR6; - } + 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(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) + else { - 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) + 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) { - 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; + 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; + } - if (*cp) - { - if (lastp) - *lastp = last; - if (strcmp(a[j], "infinite") == 0) - { - 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; - } - } - daemon->dhcp_conf = new; break; } - + case LOPT_TAG_IF: /* --tag-if */ { struct tag_if *new = opt_malloc(sizeof(struct tag_if)); diff --git a/src/rfc3315.c b/src/rfc3315.c index ee1cf17..ee58b57 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -55,6 +55,8 @@ static struct prefix_class *prefix_class_from_context(struct dhcp_context *conte 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); @@ -717,7 +719,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); @@ -745,8 +747,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)) @@ -895,14 +896,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) { @@ -1032,12 +1032,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; @@ -1760,6 +1759,76 @@ 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; + struct addrlist *addr_list; + + if (!config || !(config->flags & CONFIG_ADDR6)) + return 0; + + for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) + { + prefix = (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128; + wild_addr = addr_list->addr.addr.addr6; + + if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64) + { + wild_addr = context->start6; + setaddr6part(&wild_addr, addr6part(&addr_list->addr.addr.addr6)); + } + else if (!is_same_net6(&context->start6, addr, context->prefix)) + continue; + + 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, i, addresses; + struct addrlist *addr_list; + + if (!config || !(config->flags & CONFIG_ADDR6)) + return 0; + + for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) + { + addrpart = addr6part(&addr_list->addr.addr.addr6); + addresses = 1; + + if (addr_list->flags & ADDRLIST_PREFIX) + addresses = 1<<(128-addr_list->prefixlen); + + if ((addr_list->flags & ADDRLIST_WILDCARD)) + { + if (context->prefix != 64) + continue; + + *addr = context->start6; + } + else if (is_same_net6(&context->start6, &addr_list->addr.addr.addr6, context->prefix)) + *addr = addr_list->addr.addr.addr6; + else + continue; + + for (i = 0 ; i < addresses; i++) + { + setaddr6part(addr, addrpart+i); + + if (check_address(state, addr)) + return 1; + } + } + + return 0; +} + /* Calculate valid and preferred times to send in leases/renewals. Inputs are: -- 1.8.3.1