dnsmasq/dnsmasq-2.81-tag-filtering-...

257 lines
11 KiB
Diff

From ac4d5ea9436d0196897b7a42fcc02fc4a8a13708 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
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=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
+.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][tag:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,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:<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