diff --git a/SOURCES/dnsmasq-2.76-rh1728698-1.patch b/SOURCES/dnsmasq-2.76-rh1728698-1.patch new file mode 100644 index 0000000..2546301 --- /dev/null +++ b/SOURCES/dnsmasq-2.76-rh1728698-1.patch @@ -0,0 +1,52 @@ +From cae343c1f3bea9d1ca2e71d3709d3f02b799f94d Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Thu, 4 Jul 2019 20:28:08 +0200 +Subject: [PATCH 1/5] Log listening on new interfaces + +Log in debug mode listening on interfaces. They can be dynamically +found, include interface number, since it is checked on TCP connections. +Print also addresses found on them. +--- + src/network.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/network.c b/src/network.c +index d75f560..fd90288 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -662,6 +662,13 @@ int enumerate_interfaces(int reset) + else + { + *up = l->next; ++ if (l->iface->done) ++ { ++ iface = l->iface; ++ (void)prettyprint_addr(&iface->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s"), ++ iface->name, iface->index, daemon->addrbuff); ++ } + + /* In case it ever returns */ + l->iface->done = 0; +@@ -978,6 +985,9 @@ void create_bound_listeners(int dienow) + new->next = daemon->listeners; + daemon->listeners = new; + iface->done = 1; ++ (void)prettyprint_addr(&iface->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s"), ++ iface->name, iface->index, daemon->addrbuff); + } + + /* Check for --listen-address options that haven't been used because there's +@@ -997,6 +1007,8 @@ void create_bound_listeners(int dienow) + { + new->next = daemon->listeners; + daemon->listeners = new; ++ (void)prettyprint_addr(&if_tmp->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("listening on %s"), daemon->addrbuff); + } + } + +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.76-rh1728698-3.patch b/SOURCES/dnsmasq-2.76-rh1728698-3.patch new file mode 100644 index 0000000..cdb690d --- /dev/null +++ b/SOURCES/dnsmasq-2.76-rh1728698-3.patch @@ -0,0 +1,74 @@ +From 527029312cbe37c0285240943ad02352d64d403d Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Tue, 9 Jul 2019 14:05:59 +0200 +Subject: [PATCH 3/5] Cleanup interfaces no longer available + +Clean addresses and interfaces not found after enumerate. Free unused +records to speed up checking active interfaces and reduce used memory. +--- + src/network.c | 32 ++++++++++++++++++++++++++++++-- + 1 file changed, 30 insertions(+), 2 deletions(-) + +diff --git a/src/network.c b/src/network.c +index f247811..d6d4b01 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -553,7 +553,30 @@ static int iface_allowed_v4(struct in_addr local, int if_index, char *label, + + return iface_allowed((struct iface_param *)vparam, if_index, label, &addr, netmask, prefix, 0); + } +- ++ ++/* ++ * Clean old interfaces no longer found. ++ */ ++static void clean_interfaces() ++{ ++ struct irec *iface; ++ struct irec **up = &daemon->interfaces; ++ ++ for (iface = *up; iface; iface = *up) ++ { ++ if (!iface->found && !iface->done) ++ { ++ *up = iface->next; ++ free(iface->name); ++ free(iface); ++ } ++ else ++ { ++ up = &iface->next; ++ } ++ } ++} ++ + int enumerate_interfaces(int reset) + { + static struct addrlist *spare = NULL; +@@ -653,6 +676,7 @@ int enumerate_interfaces(int reset) + in OPT_CLEVERBIND mode, that at listener will just disappear after + a call to enumerate_interfaces, this is checked OK on all calls. */ + struct listener *l, *tmp, **up; ++ int freed = 0; + + for (up = &daemon->listeners, l = daemon->listeners; l; l = tmp) + { +@@ -682,10 +706,14 @@ int enumerate_interfaces(int reset) + close(l->tftpfd); + + free(l); ++ freed = 1; + } + } ++ ++ if (freed) ++ clean_interfaces(); + } +- ++ + errno = errsave; + spare = param.spare; + +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.79-rh1700916.patch b/SOURCES/dnsmasq-2.79-rh1700916.patch new file mode 100644 index 0000000..0511e24 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1700916.patch @@ -0,0 +1,95 @@ +From 10642f9fb350e118d88e995b8dfa2badc7be1c30 Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Wed, 11 Dec 2019 13:41:57 +0100 +Subject: [PATCH] Restore ability to answer non-recursive requests + +Instead, check only local configured entries are answered without +rdbit set. All cached replies are still denied, but locally configured +names are available with both recursion and without it. +--- + src/rfc1035.c | 27 ++++++++++++++------------- + 1 file changed, 14 insertions(+), 13 deletions(-) + +diff --git a/src/rfc1035.c b/src/rfc1035.c +index 6b3bb27..6a7c154 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -1262,7 +1262,11 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now) + else + return daemon->max_ttl; + } +- ++ ++static int cache_validated(const struct crec *crecp) ++{ ++ return (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)); ++} + + /* return zero if we can't answer from cache, or packet size if we can */ + size_t answer_request(struct dns_header *header, char *limit, size_t qlen, +@@ -1281,6 +1285,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; + struct mx_srv_record *rec; + size_t len; ++ int rd_bit; + // Make sure we do not underflow here too. + if (qlen > (limit - ((char *)header))) return 0; + +@@ -1290,10 +1295,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + OPCODE(header) != QUERY ) + return 0; + +- /* always servfail queries with RD unset, to avoid cache snooping. */ +- if (!(header->hb3 & HB3_RD)) +- return setup_reply(header, qlen, NULL, F_SERVFAIL, 0); +- ++ rd_bit = (header->hb3 & HB3_RD); ++ + /* Don't return AD set if checking disabled. */ + if (header->hb4 & HB4_CD) + sec_data = 0; +@@ -1458,9 +1461,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + /* Don't use cache when DNSSEC data required, unless we know that + the zone is unsigned, which implies that we're doing + validation. */ +- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || +- !do_bit || +- (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) ++ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || ++ (rd_bit && (!do_bit || cache_validated(crecp)) )) + { + do + { +@@ -1657,8 +1659,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + + /* If the client asked for DNSSEC don't use cached data. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || +- !do_bit || +- (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) ++ (rd_bit && (!do_bit || cache_validated(crecp)) )) + do + { + /* don't answer wildcard queries with data not from /etc/hosts +@@ -1741,8 +1742,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + if (qtype == T_CNAME || qtype == T_ANY) + { + if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME | (dryrun ? F_NO_RR : 0))) && +- (qtype == T_CNAME || (crecp->flags & F_CONFIG)) && +- ((crecp->flags & F_CONFIG) || !do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))) ++ ((qtype == T_CNAME && rd_bit) || (crecp->flags & F_CONFIG)) && ++ ((crecp->flags & F_CONFIG) || (!do_bit || cache_validated(crecp)))) + { + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; +@@ -1780,7 +1781,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + } + } + +- if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && ++ if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && + cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR)) + { + ans = 1; +-- +2.21.0 + diff --git a/SOURCES/dnsmasq-2.79-rh1728698-2.patch b/SOURCES/dnsmasq-2.79-rh1728698-2.patch new file mode 100644 index 0000000..9c335e5 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1728698-2.patch @@ -0,0 +1,48 @@ +From 7e3250d52921b5f75bdbe0b794514bb78a209969 Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Wed, 3 Jul 2019 17:02:16 +0200 +Subject: [PATCH 2/5] Compare address and interface index for allowed interface + +If interface is recreated with the same address but different index, it +would not change any other parameter. + +Test also address family on incoming TCP queries. +--- + src/dnsmasq.c | 3 ++- + src/network.c | 3 ++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index f3d2671..7812be8 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -1667,7 +1667,8 @@ static void check_dns_listeners(time_t now) + #endif + + for (iface = daemon->interfaces; iface; iface = iface->next) +- if (iface->index == if_index) ++ if (iface->index == if_index && ++ iface->addr.sa.sa_family == tcp_addr.sa.sa_family) + break; + + if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name)) +diff --git a/src/network.c b/src/network.c +index fd90288..f247811 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -404,10 +404,11 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, + /* check whether the interface IP has been added already + we call this routine multiple times. */ + for (iface = daemon->interfaces; iface; iface = iface->next) +- if (sockaddr_isequal(&iface->addr, addr)) ++ if (sockaddr_isequal(&iface->addr, addr) && iface->index == if_index) + { + iface->dad = !!(iface_flags & IFACE_TENTATIVE); + iface->found = 1; /* for garbage collection */ ++ iface->netmask = netmask; + return 1; + } + +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.79-rh1728698-4.patch b/SOURCES/dnsmasq-2.79-rh1728698-4.patch new file mode 100644 index 0000000..86d7e02 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1728698-4.patch @@ -0,0 +1,188 @@ +From 11ab42e63f9089c4c14a391f30175d4c2d071e99 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Mon, 15 Jul 2019 17:13:12 +0200 +Subject: [PATCH 4/5] Handle listening on duplicate addresses + +Save listening address into listener. Use it to find existing listeners +before creating new one. If it exist, increase just used counter. +Release only listeners not already used. + +Duplicates family in listener. +--- + src/dnsmasq.h | 3 +- + src/network.c | 115 ++++++++++++++++++++++++++++++++++++-------------- + 2 files changed, 85 insertions(+), 33 deletions(-) + +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 89d138a..3b3f6ef 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -552,7 +552,8 @@ struct irec { + }; + + struct listener { +- int fd, tcpfd, tftpfd, family; ++ int fd, tcpfd, tftpfd, family, used; ++ union mysockaddr addr; + struct irec *iface; /* only sometimes valid for non-wildcard */ + struct listener *next; + }; +diff --git a/src/network.c b/src/network.c +index d6d4b01..4bbd810 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -577,6 +577,56 @@ static void clean_interfaces() + } + } + ++/** Release listener if no other interface needs it. ++ * ++ * @return 1 if released, 0 if still required ++ */ ++static int release_listener(struct listener *l) ++{ ++ if (l->used > 1) ++ { ++ struct irec *iface; ++ for (iface = daemon->interfaces; iface; iface = iface->next) ++ if (iface->done && sockaddr_isequal(&l->addr, &iface->addr)) ++ { ++ if (iface->found) ++ { ++ /* update listener to point to active interface instead */ ++ if (!l->iface->found) ++ l->iface = iface; ++ } ++ else ++ { ++ l->used--; ++ iface->done = 0; ++ } ++ } ++ ++ /* Someone is still using this listener, skip its deletion */ ++ if (l->used > 0) ++ return 0; ++ } ++ ++ if (l->iface->done) ++ { ++ (void)prettyprint_addr(&l->iface->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s"), ++ l->iface->name, l->iface->index, daemon->addrbuff); ++ /* In case it ever returns */ ++ l->iface->done = 0; ++ } ++ ++ if (l->fd != -1) ++ close(l->fd); ++ if (l->tcpfd != -1) ++ close(l->tcpfd); ++ if (l->tftpfd != -1) ++ close(l->tftpfd); ++ ++ free(l); ++ return 1; ++} ++ + int enumerate_interfaces(int reset) + { + static struct addrlist *spare = NULL; +@@ -684,29 +734,10 @@ int enumerate_interfaces(int reset) + + if (!l->iface || l->iface->found) + up = &l->next; +- else ++ else if (release_listener(l)) + { +- *up = l->next; +- if (l->iface->done) +- { +- iface = l->iface; +- (void)prettyprint_addr(&iface->addr, daemon->addrbuff); +- my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s"), +- iface->name, iface->index, daemon->addrbuff); +- } +- +- /* In case it ever returns */ +- l->iface->done = 0; +- +- if (l->fd != -1) +- close(l->fd); +- if (l->tcpfd != -1) +- close(l->tcpfd); +- if (l->tftpfd != -1) +- close(l->tftpfd); +- +- free(l); +- freed = 1; ++ *up = tmp; ++ freed = 1; + } + } + +@@ -959,7 +990,9 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, in + l->family = addr->sa.sa_family; + l->fd = fd; + l->tcpfd = tcpfd; +- l->tftpfd = tftpfd; ++ l->tftpfd = tftpfd; ++ l->addr = *addr; ++ l->used = 1; + l->iface = NULL; + } + +@@ -1000,23 +1033,41 @@ void create_wildcard_listeners(void) + daemon->listeners = l; + } + ++static struct listener *find_listener(union mysockaddr *addr) ++{ ++ struct listener *l; ++ for (l = daemon->listeners; l; l = l->next) ++ if (sockaddr_isequal(&l->addr, addr)) ++ return l; ++ return NULL; ++} ++ + void create_bound_listeners(int dienow) + { + struct listener *new; + struct irec *iface; + struct iname *if_tmp; ++ struct listener *existing; + + for (iface = daemon->interfaces; iface; iface = iface->next) +- if (!iface->done && !iface->dad && iface->found && +- (new = create_listeners(&iface->addr, iface->tftp_ok, dienow))) ++ if (!iface->done && !iface->dad && iface->found) + { +- new->iface = iface; +- new->next = daemon->listeners; +- daemon->listeners = new; +- iface->done = 1; +- (void)prettyprint_addr(&iface->addr, daemon->addrbuff); +- my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s"), +- iface->name, iface->index, daemon->addrbuff); ++ existing = find_listener(&iface->addr); ++ if (existing) ++ { ++ iface->done = 1; ++ existing->used++; /* increase usage counter */ ++ } ++ else if ((new = create_listeners(&iface->addr, iface->tftp_ok, dienow))) ++ { ++ new->iface = iface; ++ new->next = daemon->listeners; ++ daemon->listeners = new; ++ iface->done = 1; ++ (void)prettyprint_addr(&iface->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s"), ++ iface->name, iface->index, daemon->addrbuff); ++ } + } + + /* Check for --listen-address options that haven't been used because there's +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.79-rh1746411.patch b/SOURCES/dnsmasq-2.79-rh1746411.patch new file mode 100644 index 0000000..74922c5 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1746411.patch @@ -0,0 +1,22 @@ +From: Simon Kelley +Date: Wed, 14 Aug 2019 20:52:50 +0000 (+0100) +Subject: Fix breakage of dhcp_lease_time utility. +X-Git-Url: http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commitdiff_plain;h=225accd235a09413ca253e710d7d691a3475c523 + +Fix breakage of dhcp_lease_time utility. +--- + +diff --git a/contrib/lease-tools/dhcp_lease_time.c b/contrib/lease-tools/dhcp_lease_time.c +index 697d627..91edbfa 100644 +--- a/contrib/lease-tools/dhcp_lease_time.c ++++ b/contrib/lease-tools/dhcp_lease_time.c +@@ -83,7 +83,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt + if (p >= end - 2) + return NULL; /* malformed packet */ + opt_len = option_len(p); +- if (end - p >= (2 + opt_len)) ++ if (end - p < (2 + opt_len)) + return NULL; /* malformed packet */ + if (*p == opt && opt_len >= minsize) + return p; + diff --git a/SOURCES/dnsmasq-2.80-rh1795370.patch b/SOURCES/dnsmasq-2.80-rh1795370.patch new file mode 100644 index 0000000..220a6af --- /dev/null +++ b/SOURCES/dnsmasq-2.80-rh1795370.patch @@ -0,0 +1,47 @@ +From 69bc94779c2f035a9fffdb5327a54c3aeca73ed5 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Wed, 14 Aug 2019 20:44:50 +0100 +Subject: [PATCH] Fix memory leak in helper.c + +Thanks to Xu Mingjie for spotting this. +--- + src/helper.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/src/helper.c b/src/helper.c +index 33ba120..c392eec 100644 +--- a/src/helper.c ++++ b/src/helper.c +@@ -80,7 +80,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) + pid_t pid; + int i, pipefd[2]; + struct sigaction sigact; +- ++ unsigned char *alloc_buff = NULL; ++ + /* create the pipe through which the main program sends us commands, + then fork our process. */ + if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1) +@@ -186,11 +187,16 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) + struct script_data data; + char *p, *action_str, *hostname = NULL, *domain = NULL; + unsigned char *buf = (unsigned char *)daemon->namebuff; +- unsigned char *end, *extradata, *alloc_buff = NULL; ++ unsigned char *end, *extradata; + int is6, err = 0; + int pipeout[2]; + +- free(alloc_buff); ++ /* Free rarely-allocated memory from previous iteration. */ ++ if (alloc_buff) ++ { ++ free(alloc_buff); ++ alloc_buff = NULL; ++ } + + /* we read zero bytes when pipe closed: this is our signal to exit */ + if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1)) +-- +1.7.10.4 + + diff --git a/SOURCES/dnsmasq-2.80-unaligned-addresses-in-DHCPv6-packet.patch b/SOURCES/dnsmasq-2.80-unaligned-addresses-in-DHCPv6-packet.patch new file mode 100644 index 0000000..bc55656 --- /dev/null +++ b/SOURCES/dnsmasq-2.80-unaligned-addresses-in-DHCPv6-packet.patch @@ -0,0 +1,317 @@ +From 653481c6ebf46dcadb5a017085325d956dd04a28 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Tue, 21 Aug 2018 22:06:36 +0100 +Subject: [PATCH] Properly deal with unaligned addresses in DHCPv6 packets. + +Thanks to Vladislav Grishenko for spotting this. + +(cherry picked from commit 97f876b64c22b2b18412e2e3d8506ee33e42db7c) + +Conflicts: + src/rfc3315.c +--- + src/rfc1035.c | 2 +- + src/rfc3315.c | 101 ++++++++++++++++++++++++++++++++++------------------------ + 2 files changed, 61 insertions(+), 42 deletions(-) + +diff --git a/src/rfc1035.c b/src/rfc1035.c +index 6b3bb27..ee5f7a0 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -1376,7 +1376,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->local_ttl, NULL, + t->class, C_IN, "t", t->len, t->txt)) +- anscount ++; ++ anscount++; + } + } + +diff --git a/src/rfc3315.c b/src/rfc3315.c +index 21fcd9b..ee1cf17 100644 +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -639,9 +639,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + int plain_range = 1; + u32 lease_time; + struct dhcp_lease *ltmp; +- struct in6_addr *req_addr; +- struct in6_addr addr; +- ++ struct in6_addr req_addr, addr; ++ + if (!check_ia(state, opt, &ia_end, &ia_option)) + continue; + +@@ -709,9 +708,10 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + + for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { +- req_addr = opt6_ptr(ia_option, 0); ++ /* worry about alignment here. */ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); + +- if ((c = address6_valid(state->context, req_addr, solicit_tags, plain_range))) ++ if ((c = address6_valid(state->context, &req_addr, solicit_tags, plain_range))) + { + lease_time = c->lease_time; + /* If the client asks for an address on the same network as a configured address, +@@ -719,14 +719,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + addresses automatic. */ + if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, &addr)) + { +- req_addr = &addr; ++ req_addr = addr; + mark_config_used(c, &addr); + if (have_config(config, CONFIG_TIME)) + lease_time = config->lease_time; + } +- else if (!(c = address6_available(state->context, req_addr, solicit_tags, plain_range))) ++ else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) + continue; /* not an address we're allowed */ +- else if (!check_address(state, req_addr)) ++ else if (!check_address(state, &req_addr)) + continue; /* address leased elsewhere */ + + /* add address to output packet */ +@@ -734,8 +734,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA) + state->send_prefix_class = prefix_class_from_context(c); + #endif +- add_address(state, c, lease_time, ia_option, &min_time, req_addr, now); +- mark_context_used(state, req_addr); ++ add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now); ++ mark_context_used(state, &req_addr); + get_context_tag(state, c); + address_assigned = 1; + } +@@ -768,15 +768,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + ltmp = NULL; + while ((ltmp = lease6_find_by_client(ltmp, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->clid, state->clid_len, state->iaid))) + { +- req_addr = <mp->addr6; +- if ((c = address6_available(state->context, req_addr, solicit_tags, plain_range))) ++ req_addr = ltmp->addr6; ++ if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) + { + #ifdef OPTION6_PREFIX_CLASS + if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA) + state->send_prefix_class = prefix_class_from_context(c); + #endif +- add_address(state, c, c->lease_time, NULL, &min_time, req_addr, now); +- mark_context_used(state, req_addr); ++ add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now); ++ mark_context_used(state, &req_addr); + get_context_tag(state, c); + address_assigned = 1; + } +@@ -892,16 +892,19 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + + for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { +- struct in6_addr *req_addr = opt6_ptr(ia_option, 0); ++ 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); ++ if ((c = address6_valid(state->context, &req_addr, tagif, 1))) ++ config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr); + +- if ((dynamic = address6_available(state->context, req_addr, tagif, 1)) || c) ++ if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c) + { + if (!dynamic && !config_ok) + { +@@ -911,7 +914,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + put_opt6_string(_("address unavailable")); + end_opt6(o1); + } +- else if (!check_address(state, req_addr)) ++ else if (!check_address(state, &req_addr)) + { + /* Address leased to another DUID/IAID */ + o1 = new_opt6(OPTION6_STATUS_CODE); +@@ -933,7 +936,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA) + state->send_prefix_class = prefix_class_from_context(c); + #endif +- add_address(state, dynamic, lease_time, ia_option, &min_time, req_addr, now); ++ add_address(state, dynamic, lease_time, ia_option, &min_time, &req_addr, now); + get_context_tag(state, dynamic); + address_assigned = 1; + } +@@ -996,15 +999,17 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { + struct dhcp_lease *lease = NULL; +- struct in6_addr *req_addr = opt6_ptr(ia_option, 0); ++ struct in6_addr req_addr; + unsigned int preferred_time = opt6_uint(ia_option, 16, 4); + unsigned int valid_time = opt6_uint(ia_option, 20, 4); + char *message = NULL; + struct dhcp_context *this_context; ++ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); + + if (!(lease = lease6_find(state->clid, state->clid_len, + state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, +- state->iaid, req_addr))) ++ state->iaid, &req_addr))) + { + /* If the server cannot find a client entry for the IA the server + returns the IA containing no addresses with a Status Code option set +@@ -1012,7 +1017,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + save_counter(iacntr); + t1cntr = 0; + +- log6_packet(state, "DHCPREPLY", req_addr, _("lease not found")); ++ log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found")); + + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOBINDING); +@@ -1024,15 +1029,15 @@ 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))) ++ 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_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr) && have_config(config, CONFIG_TIME)) + lease_time = config->lease_time; + else + lease_time = this_context->lease_time; +@@ -1045,7 +1050,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0); + if (state->ia_type == OPTION6_IA_NA && state->hostname) + { +- char *addr_domain = get_domain6(req_addr); ++ char *addr_domain = get_domain6(&req_addr); + if (!state->send_domain) + state->send_domain = addr_domain; + lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain); +@@ -1063,12 +1068,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + } + + if (message && (message != state->hostname)) +- log6_packet(state, "DHCPREPLY", req_addr, message); ++ log6_packet(state, "DHCPREPLY", &req_addr, message); + else +- log6_quiet(state, "DHCPREPLY", req_addr, message); ++ log6_quiet(state, "DHCPREPLY", &req_addr, message); + + o1 = new_opt6(OPTION6_IAADDR); +- put_opt6(req_addr, sizeof(*req_addr)); ++ put_opt6(&req_addr, sizeof(req_addr)); + put_opt6_long(preferred_time); + put_opt6_long(valid_time); + end_opt6(o1); +@@ -1100,19 +1105,23 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + ia_option; + ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { +- struct in6_addr *req_addr = opt6_ptr(ia_option, 0); ++ struct in6_addr req_addr; ++ ++ /* alignment */ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); + +- if (!address6_valid(state->context, req_addr, tagif, 1)) ++ if (!address6_valid(state->context, &req_addr, tagif, 1)) + { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOTONLINK); + put_opt6_string(_("confirm failed")); + end_opt6(o1); ++ log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed")); + return 1; + } + + good_addr = 1; +- log6_quiet(state, "DHCPREPLY", req_addr, state->hostname); ++ log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); + } + } + +@@ -1171,9 +1180,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { + struct dhcp_lease *lease; +- ++ struct in6_addr addr; ++ ++ /* align */ ++ memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); + if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, +- state->iaid, opt6_ptr(ia_option, 0)))) ++ state->iaid, &addr))) + lease_prune(lease, now); + else + { +@@ -1233,12 +1245,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { + struct dhcp_lease *lease; +- struct in6_addr *addrp = opt6_ptr(ia_option, 0); ++ struct in6_addr addr; + +- if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, addrp)) ++ /* align */ ++ memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, &addr)) + { + prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF); +- inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN); ++ inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), + daemon->addrbuff, daemon->dhcp_buff3); + config->flags |= CONFIG_DECLINED; +@@ -1250,7 +1265,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + context_tmp->addr_epoch++; + + if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, +- state->iaid, opt6_ptr(ia_option, 0)))) ++ state->iaid, &addr))) + lease_prune(lease, now); + else + { +@@ -1267,7 +1282,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + } + + o1 = new_opt6(OPTION6_IAADDR); +- put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ put_opt6(&addr, IN6ADDRSZ); + put_opt6_long(0); + put_opt6_long(0); + end_opt6(o1); +@@ -1935,7 +1950,11 @@ static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_op + } + else if (type == OPTION6_IAADDR) + { +- inet_ntop(AF_INET6, opt6_ptr(opt, 0), daemon->addrbuff, ADDRSTRLEN); ++ struct in6_addr addr; ++ ++ /* align */ ++ memcpy(&addr, opt6_ptr(opt, 0), IN6ADDRSZ); ++ inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN); + sprintf(daemon->namebuff, "%s PL=%u VL=%u", + daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4)); + optname = "iaaddr"; +-- +1.8.3.1 + diff --git a/SOURCES/dnsmasq-2.81-correct-range-check-of-dhcp-host-prefix.patch b/SOURCES/dnsmasq-2.81-correct-range-check-of-dhcp-host-prefix.patch new file mode 100644 index 0000000..fad386f --- /dev/null +++ b/SOURCES/dnsmasq-2.81-correct-range-check-of-dhcp-host-prefix.patch @@ -0,0 +1,44 @@ +From 6307208c806f9b968eca178931b3d77c4ed83c54 Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Fri, 6 Mar 2020 15:37:23 +0100 +Subject: [PATCH] Correct range check of dhcp-host prefix + +It incorrectly works with 32 bit integer only when counting number of +addresses in range. It works correctly only between prefixlen 96 and +128. Use 64bit shift to work with well with numbers higher than 64. + +Fixes commit 79aba0f10ad0157fb4f48afbbcb03f094caff97a error. +--- + src/option.c | 2 +- + src/rfc3315.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/option.c b/src/option.c +index 88cd2ab..79122df 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -3247,7 +3247,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + + if (!atoi_check(pref, &new_addr->prefixlen) || + new_addr->prefixlen > 128 || +- (((1<<(128-new_addr->prefixlen))-1) & addrpart) != 0) ++ ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0) + { + dhcp_config_free(new); + ret_err(_("bad IPv6 prefix")); +diff --git a/src/rfc3315.c b/src/rfc3315.c +index a0067e9..f59aedc 100644 +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -1798,7 +1798,7 @@ static int config_valid(struct dhcp_config *config, struct dhcp_context *context + addresses = 1; + + if (addr_list->flags & ADDRLIST_PREFIX) +- addresses = 1<<(128-addr_list->prefixlen); ++ addresses = (u64)1<<(128-addr_list->prefixlen); + + if ((addr_list->flags & ADDRLIST_WILDCARD)) + { +-- +2.21.1 + diff --git a/SOURCES/dnsmasq-2.81-prefix-ranges-or-list-of-ipv6-addresses.patch b/SOURCES/dnsmasq-2.81-prefix-ranges-or-list-of-ipv6-addresses.patch new file mode 100644 index 0000000..cf4833f --- /dev/null +++ b/SOURCES/dnsmasq-2.81-prefix-ranges-or-list-of-ipv6-addresses.patch @@ -0,0 +1,895 @@ +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 + diff --git a/SOURCES/dnsmasq-2.81-tag-filtering-of-dhcp-host-directives.patch b/SOURCES/dnsmasq-2.81-tag-filtering-of-dhcp-host-directives.patch new file mode 100644 index 0000000..65592a0 --- /dev/null +++ b/SOURCES/dnsmasq-2.81-tag-filtering-of-dhcp-host-directives.patch @@ -0,0 +1,322 @@ +From dd04a0d90d2fca66b5f91952ae7286c5de1714f1 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 | 14 ++++++-------- + src/rfc2131.c | 6 +++--- + src/rfc3315.c | 49 ++++++++++++++++++++++--------------------------- + 7 files changed, 71 insertions(+), 51 deletions(-) + +diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 +index 2c9d9f6..a59b06f 100644 +--- a/man/dnsmasq.8 ++++ b/man/dnsmasq.8 +@@ -953,7 +953,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 +@@ -1038,6 +1038,9 @@ ignore requests from unknown machines using + .B --dhcp-ignore=tag:!known + If the host matches only a dhcp-host 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 5d437dd..71e9e5b 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 9437226..055a0d1 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -749,6 +749,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 +@@ -1514,7 +1515,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 5c33df7..00c82f6 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 ea70ee3..88cd2ab 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -953,8 +953,7 @@ static char *set_prefix(char *arg) + return arg; + } + +-static struct dhcp_netid * +-dhcp_netid_create(const char *net, struct dhcp_netid *next) ++static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next) + { + struct dhcp_netid *tt; + tt = opt_malloc(sizeof (struct dhcp_netid)); +@@ -1019,7 +1018,8 @@ static void dhcp_config_free(struct dhcp_config *config) + } + + dhcp_netid_list_free(config->netid); +- ++ dhcp_netid_free(config->filter); ++ + if (config->flags & CONFIG_CLID) + free(config->clid); + +@@ -3167,6 +3167,7 @@ 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->filter = NULL; + new->clid = NULL; + new->addr6 = NULL; + +@@ -3215,11 +3216,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + 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")); +- } ++ new->filter = dhcp_netid_create(arg+4, new->filter); ++ + #ifdef HAVE_DHCP6 + else if (arg[0] == '[' && arg[strlen(arg)-1] == ']') + { +diff --git a/src/rfc2131.c b/src/rfc2131.c +index 997575a..a741f9f 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; +@@ -725,7 +725,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 ee58b57..a0067e9 100644 +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -486,35 +486,29 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + } + } + +- if (state->clid) ++ 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, run_tag_if(state->tags))) && ++ have_config(config, CONFIG_NAME)) + { +- config = find_config(daemon->dhcp_conf, state->context, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL); +- +- if (have_config(config, CONFIG_NAME)) +- { +- state->hostname = config->hostname; +- state->domain = config->domain; +- state->hostname_auth = 1; +- } +- else if (state->client_hostname) +- { +- state->domain = strip_hostname(state->client_hostname); ++ state->hostname = config->hostname; ++ state->domain = config->domain; ++ state->hostname_auth = 1; ++ } ++ else if (state->client_hostname) ++ { ++ state->domain = strip_hostname(state->client_hostname); + +- if (strlen(state->client_hostname) != 0) +- { +- state->hostname = state->client_hostname; +- if (!config) +- { +- /* 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); +- if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) +- config = new; +- } +- } ++ if (strlen(state->client_hostname) != 0) ++ { ++ /* 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, run_tag_if(state->tags)); ++ if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) ++ config = new; + } +- } ++ } + + if (config) + { +@@ -535,7 +529,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; +-- +1.8.3.1 + diff --git a/SPECS/dnsmasq.spec b/SPECS/dnsmasq.spec index 1bd3e4d..a2dc4b1 100644 --- a/SPECS/dnsmasq.spec +++ b/SPECS/dnsmasq.spec @@ -13,7 +13,7 @@ Name: dnsmasq Version: 2.79 -Release: 6%{?extraversion:.%{extraversion}}%{?dist} +Release: 11%{?extraversion:.%{extraversion}}%{?dist} Summary: A lightweight DHCP/caching DNS server License: GPLv2 or GPLv3 @@ -33,6 +33,23 @@ Patch6: dnsmasq-2.79-rh1602477-2.patch Patch7: dnsmasq-2.76-rh1752569.patch # Report failure when no release would be sent Patch8: dnsmasq-2.79-rh1749092-fail.patch +Patch9: dnsmasq-2.76-rh1728698-1.patch +Patch10: dnsmasq-2.79-rh1728698-2.patch +Patch11: dnsmasq-2.76-rh1728698-3.patch +Patch12: dnsmasq-2.79-rh1728698-4.patch +Patch13: dnsmasq-2.79-rh1746411.patch +Patch14: dnsmasq-2.79-rh1700916.patch +Patch15: dnsmasq-2.80-rh1795370.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1779187 +# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=97f876b64c22b2b18412e2e3d8506ee33e42db7c +Patch16: dnsmasq-2.80-unaligned-addresses-in-DHCPv6-packet.patch +# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=f064188032a829efdcf3988b24ac795ff52785ec +# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=137286e9baecf6a3ba97722ef1b49c851b531810 +# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=79aba0f10ad0157fb4f48afbbcb03f094caff97a +Patch17: dnsmasq-2.81-prefix-ranges-or-list-of-ipv6-addresses.patch +# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=52ec7836139e7a11374971905e5ac0d2d02e32c0 +Patch18: dnsmasq-2.81-tag-filtering-of-dhcp-host-directives.patch +Patch19: dnsmasq-2.81-correct-range-check-of-dhcp-host-prefix.patch # This is workaround to nettle bug #1549190 # https://bugzilla.redhat.com/show_bug.cgi?id=1549190 @@ -74,6 +91,17 @@ server's leases. %patch6 -p1 -b .rh1602477-2 %patch7 -p1 -b .rh1752569 %patch8 -p1 -b .rh1752569 +%patch9 -p1 -b .rh1728698-1 +%patch10 -p1 -b .rh1728698-2 +%patch11 -p1 -b .rh1728698-3 +%patch12 -p1 -b .rh1728698-4 +%patch13 -p1 -b .rh1746411 +%patch14 -p1 -b .rh1700916 +%patch15 -p1 -b .rh1795370 +%patch16 -p1 -b .rh1779187-1 +%patch17 -p1 -b .rh1779187-2 +%patch18 -p1 -b .rh1779187-3 +%patch19 -p1 -b .rh1779187-4 # use /var/lib/dnsmasq instead of /var/lib/misc for file in dnsmasq.conf.example man/dnsmasq.8 man/es/dnsmasq.8 src/config.h; do @@ -174,6 +202,21 @@ install -Dpm 644 %{SOURCE2} %{buildroot}%{_sysusersdir}/dnsmasq.conf %{_mandir}/man1/dhcp_* %changelog +* Mon Mar 02 2020 Petr Menšík - 2.79-11 +- Support multiple static leases for single mac on IPv6 (#1779187) + +* Mon Feb 17 2020 Tomas Korbar - 2.79-10 +- Fix memory leak in helper.c (#1795370) + +* Tue Dec 10 2019 Tomas Korbar - 2.79-9 +- Fix replies to non-recursive queries (#1700916) + +* Mon Dec 09 2019 Tomas Korbar - 2.79-8 +- Fix dhcp_lease_time (#1746411) + +* Mon Dec 09 2019 Tomas Korbar - 2.79-7 +- Fix TCP queries after interface recreation (#1728698) + * Mon Sep 30 2019 Petr Menšík - 2.79-6 - Send dhcp_release even for addresses not on local network (#1749092)