[Dnsmasq-discuss] Wildcards in tags

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



On 25/08/2021 21:09, Geoff Back wrote:
> 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.
>
Hi,

Oops, there's a bug in the patch (which still lets it work in my test
case).  Updated patch attached.
Sorry about that.

Cheers,

Geoff.

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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/attachments/20210825/7ed55587/attachment-0001.htm>
-------------- 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-1) == 0) :
+							(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) == 0) :
+			  (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