diff --git a/dnsmasq-2.81-Extend-79aba0f10ad0157fb4f48afbbcb03f094caff97a.patch b/dnsmasq-2.81-Extend-79aba0f10ad0157fb4f48afbbcb03f094caff97a.patch new file mode 100644 index 0000000..228f94e --- /dev/null +++ b/dnsmasq-2.81-Extend-79aba0f10ad0157fb4f48afbbcb03f094caff97a.patch @@ -0,0 +1,760 @@ +From c491296241396d0144750f178ffd5c0e0b089a80 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Thu, 6 Feb 2020 22:09:30 +0000 +Subject: [PATCH] Extend 79aba0f10ad0157fb4f48afbbcb03f094caff97a for multiple + IPv6 addresses. + +(cherry picked from commit 137286e9baecf6a3ba97722ef1b49c851b531810) +--- + man/dnsmasq.8 | 7 +- + src/dhcp-common.c | 55 ++++-- + src/dhcp6.c | 13 +- + src/dnsmasq.h | 13 +- + src/option.c | 416 +++++++++++++++++++++++++--------------------- + src/rfc3315.c | 88 +++++----- + 6 files changed, 330 insertions(+), 262 deletions(-) + +diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 +index 95cd3ca..d1caeed 100644 +--- a/man/dnsmasq.8 ++++ b/man/dnsmasq.8 +@@ -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. +diff --git a/src/dhcp-common.c b/src/dhcp-common.c +index 99d34c8..2933343 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.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; + } +@@ -420,9 +429,19 @@ void dhcp_update_configs(struct dhcp_config *configs) + if (prot == AF_INET6 && + (!(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; +- 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 +diff --git a/src/dhcp6.c b/src/dhcp6.c +index 11a9d83..4e28e61 100644 +--- a/src/dhcp6.c ++++ b/src/dhcp6.c +@@ -389,10 +389,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; + } +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 86b8168..08484ba 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -350,9 +350,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; +@@ -774,8 +776,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; +@@ -797,8 +798,6 @@ 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_PREFIX 32768 /* addr6 is a set, size given by prefix */ + + struct dhcp_opt { + int opt, len, flags; +diff --git a/src/option.c b/src/option.c +index 389eb02..2bbb11b 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -988,6 +988,19 @@ static void dhcp_config_free(struct dhcp_config *config) + free(list); + } + ++#ifdef HAVE_DHCP6 ++ if (config->flags & CONFIG_ADDR6) ++ { ++ struct addrlist *addr, *tmp; ++ ++ for (addr = config->addr6; addr; addr = tmp) ++ { ++ tmp = addr->next; ++ free(addr); ++ } ++ } ++#endif ++ + if (config->flags & CONFIG_NAME) + free(config->hostname); + +@@ -995,6 +1008,11 @@ static void dhcp_config_free(struct dhcp_config *config) + } + + ++ ++ ++ ++ ++ + /* This is too insanely large to keep in-line in the switch */ + static int parse_dhcp_opt(char *errstr, char *arg, int flags) + { +@@ -3053,8 +3071,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; + +@@ -3064,203 +3080,227 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0; + new->hwaddr = NULL; + new->netid = 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) +- 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 *newtag = opt_malloc(sizeof(struct dhcp_netid)); +- struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); +- newtag->net = opt_malloc(strlen(arg + 4) + 1); +- newlist->next = new->netid; +- new->netid = newlist; +- newlist->list = newtag; +- strcpy(newtag->net, arg+4); +- unhide_metas(newtag->net); +- } +- else if (strstr(arg, "tag:") == arg) +- 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 *newtag = opt_malloc(sizeof(struct dhcp_netid)); ++ struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); ++ newtag->net = opt_malloc(strlen(arg + 4) + 1); ++ newlist->next = new->netid; ++ new->netid = newlist; ++ newlist->list = newtag; ++ strcpy(newtag->net, arg+4); ++ unhide_metas(newtag->net); ++ } ++ 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)) +- 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; ++ 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.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) +- 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)) +- 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 f4f032e..9dc33f9 100644 +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -1793,68 +1793,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; ++ ++ 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; + +- wild_addr = context->start6; +- setaddr6part(&wild_addr, addr6part(&config->addr6)); ++ 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; ++ addrpart = addr6part(&addr_list->addr.addr6); ++ ++ if ((addr_list->flags & ADDRLIST_WILDCARD)) ++ { ++ if (context->prefix != 64) ++ continue; + +- *addr = context->start6; +- setaddr6part(addr, addrpart); ++ *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; ++ } + } +- 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; +- } ++ return 0; + } + + /* Calculate valid and preferred times to send in leases/renewals. +-- +2.21.1 + diff --git a/dnsmasq-2.81-adjust-changes-to-version-2.80.patch b/dnsmasq-2.81-adjust-changes-to-version-2.80.patch new file mode 100644 index 0000000..2e84d25 --- /dev/null +++ b/dnsmasq-2.81-adjust-changes-to-version-2.80.patch @@ -0,0 +1,116 @@ +From 0ced3a9527a2163bdb8f7da30a71f2f327c2e0fb Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Wed, 4 Mar 2020 18:57:04 +0100 +Subject: [PATCH] Adjust changes to version 2.80 + +Modify previous changes to current version. +--- + src/dhcp-common.c | 6 +++--- + src/dhcp6.c | 4 ++-- + src/option.c | 2 +- + src/rfc3315.c | 12 ++++++------ + 4 files changed, 12 insertions(+), 12 deletions(-) + +diff --git a/src/dhcp-common.c b/src/dhcp-common.c +index 2933343..ffa927d 100644 +--- a/src/dhcp-common.c ++++ b/src/dhcp-common.c +@@ -286,7 +286,7 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config + if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64) + return 1; + +- if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix)) ++ if (is_same_net6(&addr_list->addr.addr.addr6, &context->start6, context->prefix)) + return 1; + } + } +@@ -438,8 +438,8 @@ void dhcp_update_configs(struct dhcp_config *configs) + + 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; ++ memcpy(&config->addr6->addr.addr.addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ); ++ config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS; + } + + continue; +diff --git a/src/dhcp6.c b/src/dhcp6.c +index 4e28e61..1dedd2f 100644 +--- a/src/dhcp6.c ++++ b/src/dhcp6.c +@@ -394,8 +394,8 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct + 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)) ++ 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; + } + +diff --git a/src/option.c b/src/option.c +index 2bbb11b..61cfb8c 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -3156,7 +3156,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + new_addr = opt_malloc(sizeof(struct addrlist)); + new_addr->next = new->addr6; + new_addr->flags = 0; +- new_addr->addr.addr6 = in6; ++ new_addr->addr.addr.addr6 = in6; + new->addr6 = new_addr; + + if (pref) +diff --git a/src/rfc3315.c b/src/rfc3315.c +index 9dc33f9..a7bf929 100644 +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -1801,12 +1801,12 @@ static int config_implies(struct dhcp_config *config, struct dhcp_context *conte + 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.addr6; ++ 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.addr6)); ++ setaddr6part(&wild_addr, addr6part(&addr_list->addr.addr.addr6)); + } + else if (!is_same_net6(&context->start6, addr, context->prefix)) + continue; +@@ -1828,7 +1828,7 @@ static int config_valid(struct dhcp_config *config, struct dhcp_context *context + + for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) + { +- addrpart = addr6part(&addr_list->addr.addr6); ++ addrpart = addr6part(&addr_list->addr.addr.addr6); + + if ((addr_list->flags & ADDRLIST_WILDCARD)) + { +@@ -1838,8 +1838,8 @@ static int config_valid(struct dhcp_config *config, struct dhcp_context *context + *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 if (is_same_net6(&context->start6, &addr_list->addr.addr.addr6, context->prefix)) ++ *addr = addr_list->addr.addr.addr6; + else + continue; + +@@ -1853,7 +1853,7 @@ static int config_valid(struct dhcp_config *config, struct dhcp_context *context + + addrpart++; + setaddr6part(addr, addrpart); +- if (!is_same_net6(addr, &addr_list->addr.addr6, addr_list->prefixlen)) ++ if (!is_same_net6(addr, &addr_list->addr.addr.addr6, addr_list->prefixlen)) + break; + } + } +-- +2.21.1 + diff --git a/dnsmasq-2.81-prefix-ranges-or-list-of-ipv6-addresses.patch b/dnsmasq-2.81-prefix-ranges-or-list-of-ipv6-addresses.patch new file mode 100644 index 0000000..2898ede --- /dev/null +++ b/dnsmasq-2.81-prefix-ranges-or-list-of-ipv6-addresses.patch @@ -0,0 +1,396 @@ +From e81e994303a89998c5796a5951192e3a0c0395bc 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) +--- + man/dnsmasq.8 | 8 ++++- + src/dhcp-common.c | 3 +- + src/dhcp6.c | 40 ++++++----------------- + src/dnsmasq.h | 5 +-- + src/option.c | 48 +++++++++++++++++++++++++++ + src/rfc3315.c | 82 +++++++++++++++++++++++++++++++++++++++++++---- + 6 files changed, 144 insertions(+), 42 deletions(-) + +diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 +index f01a5ba..95cd3ca 100644 +--- a/man/dnsmasq.8 ++++ b/man/dnsmasq.8 +@@ -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. +diff --git a/src/dhcp-common.c b/src/dhcp-common.c +index 78c1d9b..99d34c8 100644 +--- a/src/dhcp-common.c ++++ b/src/dhcp-common.c +@@ -418,10 +418,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.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; ++ config->flags &= ~CONFIG_PREFIX; + continue; + } + #endif +diff --git a/src/dhcp6.c b/src/dhcp6.c +index b7cce45..11a9d83 100644 +--- a/src/dhcp6.c ++++ b/src/dhcp6.c +@@ -384,14 +384,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; +@@ -453,16 +453,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 +515,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 8d84714..86b8168 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -775,6 +775,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; +@@ -797,6 +798,7 @@ struct dhcp_config { + #define CONFIG_BANK 2048 /* from dhcp hosts file */ + #define CONFIG_ADDR6 4096 + #define CONFIG_WILDCARD 8192 ++#define CONFIG_PREFIX 32768 /* addr6 is a set, size given by prefix */ + + struct dhcp_opt { + int opt, len, flags; +@@ -1514,7 +1516,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, + 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, +@@ -1524,7 +1525,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 f03a6b3..389eb02 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -965,6 +965,35 @@ static char *set_prefix(char *arg) + + return arg; + } ++ ++/* Legacy workaround, backported from 2.81 */ ++static void dhcp_config_free(struct dhcp_config *config) ++{ ++ struct hwaddr_config *mac, *tmp; ++ struct dhcp_netid_list *list, *tmplist; ++ ++ for (mac = config->hwaddr; mac; mac = tmp) ++ { ++ tmp = mac->next; ++ free(mac); ++ } ++ ++ if (config->flags & CONFIG_CLID) ++ free(config->clid); ++ ++ for (list = config->netid; list; list = tmplist) ++ { ++ free(list->list); ++ tmplist = list->next; ++ free(list); ++ } ++ ++ if (config->flags & CONFIG_NAME) ++ free(config->hostname); ++ ++ free(config); ++} ++ + + /* This is too insanely large to keep in-line in the switch */ + static int parse_dhcp_opt(char *errstr, char *arg, int flags) +@@ -1512,6 +1541,7 @@ void reset_option_bool(unsigned int opt) + daemon->options2 &= ~(1u << (opt - 32)); + } + ++ + static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only) + { + int i; +@@ -3090,12 +3120,30 @@ 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)) + 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; +diff --git a/src/rfc3315.c b/src/rfc3315.c +index a20776d..f4f032e 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); +@@ -746,7 +748,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); +@@ -774,8 +776,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)) +@@ -924,14 +925,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) + { +@@ -1061,12 +1061,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; +@@ -1789,6 +1788,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: +-- +2.21.1 + diff --git a/dnsmasq-2.81-tag-filtering-of-dhcp-host-directives.patch b/dnsmasq-2.81-tag-filtering-of-dhcp-host-directives.patch new file mode 100644 index 0000000..3a82fe6 --- /dev/null +++ b/dnsmasq-2.81-tag-filtering-of-dhcp-host-directives.patch @@ -0,0 +1,256 @@ +From ac4d5ea9436d0196897b7a42fcc02fc4a8a13708 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Fri, 7 Feb 2020 21:05:54 +0000 +Subject: [PATCH] Add tag filtering of dhcp-host directives. + +(cherry picked from commit 52ec7836139e7a11374971905e5ac0d2d02e32c0) + +Conflicts: + CHANGELOG + src/rfc3315.c +--- + man/dnsmasq.8 | 5 ++++- + src/dhcp-common.c | 42 ++++++++++++++++++++++++++++++++---------- + src/dnsmasq.h | 4 +++- + src/lease.c | 2 +- + src/option.c | 7 ++++--- + src/rfc2131.c | 6 +++--- + src/rfc3315.c | 7 ++++--- + 7 files changed, 51 insertions(+), 22 deletions(-) + +diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 +index d1caeed..2c97738 100644 +--- a/man/dnsmasq.8 ++++ b/man/dnsmasq.8 +@@ -975,7 +975,7 @@ is also included, as described in RFC-3775 section 7.3. + tells dnsmasq to advertise the prefix without the on-link (aka L) bit set. + + .TP +-.B \-G, --dhcp-host=[][,id:|*][,set:][,][,][,][,ignore] ++.B \-G, --dhcp-host=[][,id:|*][,set:][tag:][,][,][,][,ignore] + Specify per host parameters for the DHCP server. This allows a machine + with a particular hardware address to be always allocated the same + hostname, IP address and lease time. A hostname specified like this +@@ -1060,6 +1060,9 @@ ignore requests from unknown machines using + .B --dhcp-ignore=tag:!known + If the host matches only a \fB--dhcp-host\fP directive which cannot + be used because it specifies an address on different subnet, the tag "known-othernet" is set. ++ ++The tag: construct filters which dhcp-host directives are used. Tagged directives are used in preference to untagged ones. ++ + Ethernet addresses (but not client-ids) may have + wildcard bytes, so for example + .B --dhcp-host=00:20:e0:3b:13:*,ignore +diff --git a/src/dhcp-common.c b/src/dhcp-common.c +index ffa927d..3ee82a5 100644 +--- a/src/dhcp-common.c ++++ b/src/dhcp-common.c +@@ -304,11 +304,12 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config + return 0; + } + +-struct dhcp_config *find_config(struct dhcp_config *configs, +- struct dhcp_context *context, +- unsigned char *clid, int clid_len, +- unsigned char *hwaddr, int hw_len, +- int hw_type, char *hostname) ++static struct dhcp_config *find_config_match(struct dhcp_config *configs, ++ struct dhcp_context *context, ++ unsigned char *clid, int clid_len, ++ unsigned char *hwaddr, int hw_len, ++ int hw_type, char *hostname, ++ struct dhcp_netid *tags, int tag_not_needed) + { + int count, new; + struct dhcp_config *config, *candidate; +@@ -320,7 +321,9 @@ struct dhcp_config *find_config(struct dhcp_config *configs, + { + if (config->clid_len == clid_len && + memcmp(config->clid, clid, clid_len) == 0 && +- is_config_in_context(context, config)) ++ is_config_in_context(context, config) && ++ match_netid(config->filter, tags, tag_not_needed)) ++ + return config; + + /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and +@@ -328,7 +331,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs, + see lease_update_from_configs() */ + if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1 && + memcmp(config->clid, clid+1, clid_len-1) == 0 && +- is_config_in_context(context, config)) ++ is_config_in_context(context, config) && ++ match_netid(config->filter, tags, tag_not_needed)) + return config; + } + +@@ -336,14 +340,16 @@ struct dhcp_config *find_config(struct dhcp_config *configs, + if (hwaddr) + for (config = configs; config; config = config->next) + if (config_has_mac(config, hwaddr, hw_len, hw_type) && +- is_config_in_context(context, config)) ++ is_config_in_context(context, config) && ++ match_netid(config->filter, tags, tag_not_needed)) + return config; + + if (hostname && context) + for (config = configs; config; config = config->next) + if ((config->flags & CONFIG_NAME) && + hostname_isequal(config->hostname, hostname) && +- is_config_in_context(context, config)) ++ is_config_in_context(context, config) && ++ match_netid(config->filter, tags, tag_not_needed)) + return config; + + +@@ -352,7 +358,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs, + + /* use match with fewest wildcard octets */ + for (candidate = NULL, count = 0, config = configs; config; config = config->next) +- if (is_config_in_context(context, config)) ++ if (is_config_in_context(context, config) && ++ match_netid(config->filter, tags, tag_not_needed)) + for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next) + if (conf_addr->wildcard_mask != 0 && + conf_addr->hwaddr_len == hw_len && +@@ -366,6 +373,21 @@ struct dhcp_config *find_config(struct dhcp_config *configs, + return candidate; + } + ++/* Find tagged configs first. */ ++struct dhcp_config *find_config(struct dhcp_config *configs, ++ struct dhcp_context *context, ++ unsigned char *clid, int clid_len, ++ unsigned char *hwaddr, int hw_len, ++ int hw_type, char *hostname, struct dhcp_netid *tags) ++{ ++ struct dhcp_config *ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 0); ++ ++ if (!ret) ++ ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 1); ++ ++ return ret; ++} ++ + void dhcp_update_configs(struct dhcp_config *configs) + { + /* Some people like to keep all static IP addresses in /etc/hosts. +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 08484ba..1904e43 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -775,6 +775,7 @@ struct dhcp_config { + unsigned char *clid; /* clientid */ + char *hostname, *domain; + struct dhcp_netid_list *netid; ++ struct dhcp_netid *filter; + #ifdef HAVE_DHCP6 + struct addrlist *addr6; + #endif +@@ -1563,7 +1564,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs, + struct dhcp_context *context, + unsigned char *clid, int clid_len, + unsigned char *hwaddr, int hw_len, +- int hw_type, char *hostname); ++ int hw_type, char *hostname, ++ struct dhcp_netid *filter); + int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type); + #ifdef HAVE_LINUX_NETWORK + char *whichdevice(void); +diff --git a/src/lease.c b/src/lease.c +index 6012183..2332de8 100644 +--- a/src/lease.c ++++ b/src/lease.c +@@ -222,7 +222,7 @@ void lease_update_from_configs(void) + if (lease->flags & (LEASE_TA | LEASE_NA)) + continue; + else if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, +- lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && ++ lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL, NULL)) && + (config->flags & CONFIG_NAME) && + (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr)) + lease_set_hostname(lease, config->hostname, 1, get_domain(lease->addr), NULL); +diff --git a/src/option.c b/src/option.c +index 61cfb8c..d61c5f7 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -3132,9 +3132,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + else if (strstr(arg, "tag:") == arg) + { +- +- dhcp_config_free(new); +- ret_err(_("cannot match tags in --dhcp-host")); ++ struct dhcp_netid *newtag = opt_malloc(sizeof (struct dhcp_netid)); ++ newtag->net = opt_string_alloc(arg+4); ++ newtag->next = new->filter; ++ new->filter = newtag; + } + #ifdef HAVE_DHCP6 + else if (arg[0] == '[' && arg[strlen(arg)-1] == ']') +diff --git a/src/rfc2131.c b/src/rfc2131.c +index 56dc3d1..924a960 100644 +--- a/src/rfc2131.c ++++ b/src/rfc2131.c +@@ -479,7 +479,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, + mess->op = BOOTREPLY; + + config = find_config(daemon->dhcp_conf, context, clid, clid_len, +- mess->chaddr, mess->hlen, mess->htype, NULL); ++ mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid)); + + /* set "known" tag for known hosts */ + if (config) +@@ -489,7 +489,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, + netid = &known_id; + } + else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len, +- mess->chaddr, mess->hlen, mess->htype, NULL)) ++ mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid))) + { + known_id.net = "known-othernet"; + known_id.next = netid; +@@ -731,7 +731,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, + to avoid impersonation by name. */ + struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, + mess->chaddr, mess->hlen, +- mess->htype, hostname); ++ mess->htype, hostname, run_tag_if(netid)); + if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) + { + config = new; +diff --git a/src/rfc3315.c b/src/rfc3315.c +index a7bf929..e92ce59 100644 +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -488,7 +488,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + + if (state->clid) + { +- config = find_config(daemon->dhcp_conf, state->context, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL); ++ config = find_config(daemon->dhcp_conf, state->context, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL, run_tag_if(state->tags)); + + if (have_config(config, CONFIG_NAME)) + { +@@ -513,7 +513,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + /* Search again now we have a hostname. + Only accept configs without CLID here, (it won't match) + to avoid impersonation by name. */ +- struct dhcp_config *new = find_config(daemon->dhcp_conf, state->context, NULL, 0, NULL, 0, 0, state->hostname); ++ struct dhcp_config *new = find_config(daemon->dhcp_conf, state->context, NULL, 0, NULL, 0, 0, state->hostname, run_tag_if(state->tags)); + if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) + config = new; + } +@@ -564,7 +564,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + ignore = 1; + } + else if (state->clid && +- find_config(daemon->dhcp_conf, NULL, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL)) ++ find_config(daemon->dhcp_conf, NULL, state->clid, state->clid_len, ++ state->mac, state->mac_len, state->mac_type, NULL, run_tag_if(state->tags))) + { + known_id.net = "known-othernet"; + known_id.next = state->tags; +-- +2.21.1 + diff --git a/dnsmasq.spec b/dnsmasq.spec index 2e40484..5c49119 100644 --- a/dnsmasq.spec +++ b/dnsmasq.spec @@ -13,7 +13,7 @@ Name: dnsmasq Version: 2.80 -Release: 11%{?extraversion:.%{extraversion}}%{?dist} +Release: 12%{?extraversion:.%{extraversion}}%{?dist} Summary: A lightweight DHCP/caching DNS server License: GPLv2 or GPLv3 @@ -35,6 +35,14 @@ Patch8: dnsmasq-2.80-nettle.patch Patch9: dnsmasq-2.80-SIOCGSTAMP.patch # https://bugzilla.redhat.com/show_bug.cgi?id=1739797 Patch10: dnsmasq-2.80-rh1739797.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1810172 +# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=79aba0f10ad0157fb4f48afbbcb03f094caff97a +Patch11: dnsmasq-2.81-prefix-ranges-or-list-of-ipv6-addresses.patch +# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=137286e9baecf6a3ba97722ef1b49c851b531810 +Patch12: dnsmasq-2.81-Extend-79aba0f10ad0157fb4f48afbbcb03f094caff97a.patch +Patch13: dnsmasq-2.81-adjust-changes-to-version-2.80.patch +# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=52ec7836139e7a11374971905e5ac0d2d02e32c0 +Patch14: dnsmasq-2.81-tag-filtering-of-dhcp-host-directives.patch # This is workaround to nettle bug #1549190 # https://bugzilla.redhat.com/show_bug.cgi?id=1549190 @@ -167,6 +175,9 @@ install -Dpm 644 %{SOURCE2} %{buildroot}%{_sysusersdir}/%{name}.conf %{_mandir}/man1/dhcp_* %changelog +* Wed Mar 04 2020 Petr Menšík - 2.80-12 +- Support multiple static leases for single mac on IPv6 (#1810172) + * Tue Jan 28 2020 Fedora Release Engineering - 2.80-11 - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild