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