[Dnsmasq-discuss] [PATCH] dhcp-host - tag filtering

Harald Jensås hjensas at redhat.com
Fri Jan 31 18:56:18 GMT 2020


>From a2a022cd3adc6c0ad6007c63bba3fd4a2ff28165 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Harald=20Jens=C3=A5s?= <hjensas at redhat.com>
Date: Fri, 31 Jan 2020 18:10:08 +0100
Subject: [PATCH] dhcp-host - tag filtering

Adds support for tag's on the dhcp-host config entries.

Find config will collect candidate configurations based
on clid and hwadd in a list. A candidate is then picked
by iterating the list, candidates with matching tag's
are preferred. If no entry with tag's is a match and
an entry without tags exist this will be used.

NOTE: For dhcp-host entries with a wildcard hardware
      address tags are ignored.
---
 src/dhcp-common.c | 68 ++++++++++++++++++++++++++++++++++++++++-------
 src/dnsmasq.h     |  9 ++++++-
 src/lease.c       |  2 +-
 src/option.c      |  9 ++++---
 src/rfc2131.c     |  6 ++---
 src/rfc3315.c     |  6 ++---
 6 files changed, 80 insertions(+), 20 deletions(-)

diff --git a/src/dhcp-common.c b/src/dhcp-common.c
index dc7f945..4d5e67d 100644
--- a/src/dhcp-common.c
+++ b/src/dhcp-common.c
@@ -295,15 +295,28 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config
   return 0;
 }
 
+static void dhcp_config_list_free(struct dhcp_config_list *config_list)
+{
+  while (config_list)
+    {
+      struct dhcp_config_list *tmplist = config_list;
+      config_list = config_list->next;
+      free(tmplist);
+    }
+}
+
 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 *tags)
 {
   int count, new;
-  struct dhcp_config *config, *candidate; 
+  struct dhcp_config *config, *candidate;
   struct hwaddr_config *conf_addr;
+  struct dhcp_config_list *config_candidates;
+  config_candidates = NULL;
 
   if (clid)
     for (config = configs; config; config = config->next)
@@ -312,7 +325,13 @@ 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))
-	    return config;
+	    {
+	      struct dhcp_config_list *newlist = safe_malloc(sizeof(struct dhcp_config));
+	      newlist->next = config_candidates;
+	      config_candidates = newlist;
+	      newlist->config = config;
+	      continue;
+	    }
 	  
 	  /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
 	     cope with that here. This is IPv4 only. context==NULL implies IPv4, 
@@ -320,24 +339,55 @@ struct dhcp_config *find_config(struct dhcp_config *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))
-	    return config;
+	    {
+	      struct dhcp_config_list *newlist = safe_malloc(sizeof(struct dhcp_config_list));
+	      newlist->next = config_candidates;
+	      config_candidates = newlist;
+	      newlist->config = config;
+	    }
 	}
-  
 
   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))
-	return config;
+        {
+          struct dhcp_config_list *newlist = safe_malloc(sizeof(struct dhcp_config_list));
+          newlist->next = config_candidates;
+          config_candidates = newlist;
+          newlist->config = 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))
-	return config;
+        {
+          struct dhcp_config_list *newlist = safe_malloc(sizeof(struct dhcp_config_list));
+          newlist->next = config_candidates;
+          config_candidates = newlist;
+          newlist->config = config;
+        }
+
+  if (config_candidates)
+    {
+      for (candidate = NULL, config_candidates = config_candidates;
+	   config_candidates; config_candidates = config_candidates->next)
+        if (!candidate && (config_candidates->config->filter == NULL))
+            candidate = config_candidates->config;
+	else if (config_candidates->config->filter != NULL)
+            if (match_netid(config_candidates->config->filter, tags, 0))
+	      {
+                candidate = config_candidates->config;
+	        break;
+	      }
+
+      dhcp_config_list_free(config_candidates);
+      if (candidate)
+	  return candidate;
+    }
 
-  
   if (!hwaddr)
     return NULL;
 
@@ -353,7 +403,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
 	      count = new;
 	      candidate = config;
 	  }
-  
+
   return candidate;
 }
 
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 7fb440c..072bbfd 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -759,12 +759,18 @@ struct hwaddr_config {
   struct hwaddr_config *next;
 };
 
+struct dhcp_config_list {
+  struct dhcp_config *config;
+  struct dhcp_config_list *next;
+};
+
 struct dhcp_config {
   unsigned int flags;
   int clid_len;          /* length of client identifier */
   unsigned char *clid;   /* clientid */
   char *hostname, *domain;
   struct dhcp_netid_list *netid;
+  struct dhcp_netid *filter;
 #ifdef HAVE_DHCP6
   struct in6_addr addr6;
 #endif
@@ -1555,7 +1561,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 *tags);
 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 f77545f..a7a175b 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1027,6 +1027,7 @@ static void dhcp_config_free(struct dhcp_config *config)
 	  free(tmp);
         }
       dhcp_netid_list_free(config->netid);
+      dhcp_netid_free(config->filter);
       if (config->flags & CONFIG_CLID)
         free(config->clid);
       free(config);
@@ -3204,6 +3205,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;
 
 	if ((a[0] = arg))
@@ -3257,9 +3259,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 *newlist = opt_malloc(sizeof(struct dhcp_netid));
+		  newlist->next = new->filter;
+		  new->filter = newlist;
+		  newlist->net = arg+4;
 		}
 #ifdef HAVE_DHCP6
 	      else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 55fedcc..26223fc 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, 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, 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, netid);
 	      if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
 		{
 		  config = new;
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 9471f5c..9fb1201 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -524,7 +524,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, state->tags)) &&
       have_config(config, CONFIG_NAME))
     {
       state->hostname = config->hostname;
@@ -544,7 +544,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, state->tags);
 	      if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
 		config = new;
 	    }
@@ -570,7 +570,7 @@ 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, state->tags))
     {
       known_id.net = "known-othernet";
       known_id.next = state->tags;
-- 
2.24.1






More information about the Dnsmasq-discuss mailing list