[Dnsmasq-discuss] Wildcards in tags

Geoff Back geoff at demonlair.co.uk
Wed Aug 25 20:09:20 UTC 2021


On 25/08/2021 15:33, Simon Kelley wrote:
> On 25/08/2021 13:08, Geoff Back wrote:
>> Good morning,
>>
>> TL;DR: I need to wildcard-match interface tags and can't see how.
>>
>> I currently have dnsmasq working on a Linux VPN server to provide DHCP
>> options to VPN clients by using these config elements to bind only the
>> vpn interfaces:
>>
>> ====8<==============8<================
>> interface=ppp*
>> bind-dynamic
>> dhcp-range=10.99.0.254,static
>> dhcp-option=option:router
>> dhcp-option=6,10.99.0.254
>> dhcp-option=121,<routes>
>> dhcp-option=249,<routes>
>> ====8<==============8<================
>>
>> This works just fine.  However, I now need to also run regular DHCP on
>> other interfaces with different options.  Tags seem like the perfect
>> solution to this.
>>
>> As I understand it, the interface on which the request is received
>> becomes a tag, which would seem fine, but as far as I can tell from the
>> source of version 2.85, the tag:xxx in dhcp-option etc do not support
>> wildcards like the 'interface' directive, so I cannot use e.g.
>>
>> dhcp-option=tag:ppp*,option:router
>>
>> So I figure OK, set another tag based on the interface tag to represent
>> a "group of interfaces", and I end up with this:
>>
>> ====8<==============8<================
>>
>> # basic binding settings for all PPP interfaces plus two ethernets.
>> bind-dynamic
>> interface=ppp*
>> interface=lan2
>> interface=lan3
>>
>> # Set a tag for all requests on PPP interfaces.
>> # If I understand correctly requests on ppp2 (for example) set a tag ppp2,
>> # This should work according to docs but there could be dozens of ppp
>> # interfaces in use making this really long and adding a lot of checks
>> to every request.
>> tag-if=set:ppp,tag:ppp0
>> tag-if=set:ppp,tag:ppp1
>> tag-if=set:ppp,tag:ppp2
>> ... repeated as necessary ...
>>
>> # DHCP for all ppp interfaces
>> # match using tag set by tag-if above.
>> dhcp-range=tag:ppp,10.99.0.254,static
>> dhcp-option=tag:ppp,option:router
>> dhcp-option=tag:ppp,6,10.99.0.254
>> dhcp-option=tag:ppp,121,<routes>
>> dhcp-option=tag:ppp,249,<routes>
>>
>> # DHCP for lan2
>> # match to interface tag
>> dhcp-range=tag:lan2,10.0.0.1,10.0.0.99
>> dhcp-option=tag:lan2,option:router,10.0.0.254
>>
>> ====8<==============8<================
>>
>> Instead of having to give large numbers of "tag-if=set:ppp,tag:ppp9"
>> lines for all the possible ppp interfaces, I would like to do this:
>>
>> # set 'ppp' tag for all PPP interfaces
>> tag-if=set:ppp,tag:ppp*
>>
>> Of course, allowing wildcards on all tag matches would work too, but my
>> thinking is that restricting wildcards to 'tag-if' avoids the
>> performance impact of potentially doing wildcard checks on every
>> 'dhcp-option' etc. during DHCP packet processing and instead it gets
>> done once through 'tag-if'.
>>
>> Have I missed something and there is already a way to do this?
>> Or if not, is it something that could be added?
>>
>> If it's an acceptable add, I can knock up a patch to do it but I'll have
>> to learn enough of the dnsmasq code first.
>>
>> Regards,
>>
>> Geoff.
>>
>
> Your solution sounds like a good one to me. I'd certainly take a patch,
> especially if it updated the man page too.
>
> To get started, look at run_tag_if() and match_netid() in
> src/dhcp-common.c match_netid() does the matching and is called all over
> the place, so you either need to replace the call to match_netid() in
> run_tag_if() with code that implements the wildcard matching, or extend
> match_netid to do wildcard matching when the third argument is 2 or
> something similar.
>
>
> Cheers,
>
> Simon.
>
>
>
>
> _______________________________________________
> Dnsmasq-discuss mailing list
> Dnsmasq-discuss at lists.thekelleys.org.uk
> https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss

Hi Simon,

Attached is a patch against tag v2.86rc1 that implements wildcards in
--tag-if and updates the man page.

Cheers,

Geoff.

-- 
Geoff Back
What if we're all just characters in someone's nightmares?

-------------- next part --------------
--- build.orig/src/dhcp-common.c	2021-08-25 14:32:43.000000000 +0100
+++ build/src/dhcp-common.c	2021-08-25 20:46:30.960959376 +0100
@@ -79,13 +79,49 @@
   return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz;
 }
 
+/* like match_netid() except that the check can have a trailing * for wildcard */
+/* started as a direct copy of match_netid() */
+int match_netid_wild(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
+{
+  struct dhcp_netid *tmp1;
+  
+  if (!check && !tagnotneeded)
+    return 0;
+
+  for (; check; check = check->next)
+    {
+	  const int check_len = strlen(check->net);
+	  const int is_wc = (check->net[check_len - 1] == '*');
+
+      /* '#' for not is for backwards compat. */
+      if (check->net[0] != '!' && check->net[0] != '#')
+	{
+	  for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+	    if (is_wc ? strncmp(check->net, tmp1->net, check_len) :
+							(strcmp(check->net, tmp1->net) == 0))
+	      break;
+	  if (!tmp1)
+	    return 0;
+	}
+      else
+	for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+	  if (is_wc ? strncmp((check->net)+1, tmp1->net, check_len-2) :
+			  (strcmp((check->net)+1, tmp1->net) == 0))
+	    return 0;
+    }
+  return 1;
+}
+
 struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
 {
   struct tag_if *exprs;
   struct dhcp_netid_list *list;
 
+  /* this now uses match_netid_wild() above so that tag_if can
+   * be used to set a 'group of interfaces' tag.
+   */
   for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
-    if (match_netid(exprs->tag, tags, 1))
+    if (match_netid_wild(exprs->tag, tags, 1))
       for (list = exprs->set; list; list = list->next)
 	{
 	  list->list->next = tags;
--- build.orig/man/dnsmasq.8	2021-08-25 14:32:43.000000000 +0100
+++ build/man/dnsmasq.8	2021-08-25 20:53:35.190605791 +0100
@@ -1435,6 +1435,12 @@
 tag set by another
 .B --tag-if,
 the line which sets the tag must precede the one which tests it.
+
+As an extension, the tag:<tag> clauses support limited wildcard matching,
+similar to the matching in the \fB--interface\fP directive.  This allows, for
+example, using \fB--tag-if=set:ppp,tag:ppp*\fP to set the tag 'ppp' for all requests
+received on any matching interface (ppp0, ppp1, etc).  This can be used in conjunction
+with the tag:!<tag> format meaning that no tag matching the wildcard may be set.
 .TP
 .B \-J, --dhcp-ignore=tag:<tag>[,tag:<tag>]
 When all the given tags appear in the tag set ignore the host and do


More information about the Dnsmasq-discuss mailing list