diff --git a/CHANGELOG b/CHANGELOG index 319b230..5c3b13d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -76,7 +76,10 @@ version 2.81 address, only one gets the "static" address, but with this fix, enough addresses can be reserved for all the stages of the boot. Many thanks to Harald Jensås for his work on this idea and - earlier patches. + earlier patches. + + Add filtering by tag of --dhcp-host directives. Based on a patch + by Harald Jensås. version 2.80 diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 4c4bec6..6cbff1e 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -975,7 +975,7 @@ is also included, as described in RFC-3775 section 7.3. tells dnsmasq to advertise the prefix without the on-link (aka L) bit set. .TP -.B \-G, --dhcp-host=[][,id:|*][,set:][,][,][,][,ignore] +.B \-G, --dhcp-host=[][,id:|*][,set:][tag:][,][,][,][,ignore] Specify per host parameters for the DHCP server. This allows a machine with a particular hardware address to be always allocated the same hostname, IP address and lease time. A hostname specified like this @@ -1060,6 +1060,9 @@ ignore requests from unknown machines using .B --dhcp-ignore=tag:!known If the host matches only a \fB--dhcp-host\fP directive which cannot be used because it specifies an address on different subnet, the tag "known-othernet" is set. + +The tag: construct filters which dhcp-host directives are used. Tagged directives are used in preference to untagged ones. + Ethernet addresses (but not client-ids) may have wildcard bytes, so for example .B --dhcp-host=00:20:e0:3b:13:*,ignore diff --git a/src/dhcp-common.c b/src/dhcp-common.c index c434df0..a769fe9 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 f71c2ea..e06614c 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -767,6 +767,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 @@ -1555,7 +1556,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 0035d40..52cf30a 100644 --- a/src/lease.c +++ b/src/lease.c @@ -230,7 +230,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 1048522..07bb52a 100644 --- a/src/option.c +++ b/src/option.c @@ -963,8 +963,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)); @@ -1029,7 +1028,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); @@ -3217,6 +3217,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; @@ -3265,11 +3266,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 55fedcc..e27cd29 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -504,7 +504,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) @@ -514,7 +514,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; @@ -781,7 +781,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 b4b61b3..2e611b3 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -526,7 +526,8 @@ 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))) && have_config(config, CONFIG_NAME)) { state->hostname = config->hostname; @@ -546,7 +547,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; } @@ -572,7 +573,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;