From 1f122521bba0058fe69d25f5832cad5f052d5a82 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 27 Jul 2026 21:02:53 +0100 Subject: [PATCH] Make configuration for DHCPv4 options and DHCPv6 options consistent. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Historically, DHCPv4 client options are configured as dhcp-option=option:ntp-server,.... dhcp-option=42,.... DHCPv6 long ago added dhcp-option=option6:sntp-server,.... dhcp-option=option6:31,.... This patch adds equivalents of this for DHCPv4 dhcp-option=option4:ntp-server,.... dhcp-option=option4:42,..... and for good measure dhcp-option=option:42,..... and clarifies the man page. Thanks to Martin-Éric Racine for pointing this out. --- man/dnsmasq.8 | 12 +++++++++-- src/option.c | 57 ++++++++++++++++++++------------------------------- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index fcb079d..cb86c28 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -1329,7 +1329,7 @@ have exactly the same effect as options containing the same information. /etc/ethers is re-read when dnsmasq receives SIGHUP. IPv6 addresses are NOT read from /etc/ethers. .TP -.B \-O, --dhcp-option=[tag:,[tag:,]][encap:,][vi-encap:,][vendor:[],][|option:|option6:|option6:],[[,]] +.B \-O, --dhcp-option=[tag:,[tag:,]][encap:,][vi-encap:,][vendor:[],][|option:|option:|option4:|option4:|option6:|option6:],[[,]] Send various extra options to DHCP clients. By default, dnsmasq sends some standard options to DHCP clients, the netmask and broadcast address are set to the same as the host running dnsmasq, and @@ -1348,6 +1348,13 @@ For example, to set the default route option to 192.168.4.4, use and to set the time-server address to 192.168.0.4, use \fB--dhcp-option=42,192.168.0.4\fP or \fB--dhcp-option=option:ntp-server,192.168.0.4\fP. + +The above examples are backwards compatible, for consistency with IPV6, the time-server example +may also be given as +\fB--dhcp-option=option:42,192.168.0.4\fP or +\fB--dhcp-option=option4:42,192.168.0.4\fP or +\fB--dhcp-option=option4:ntp-server,192.168.0.4\fP + The special address 0.0.0.0 means "the address of the system running dnsmasq". An option without data is valid, and includes just the option without data. @@ -1372,7 +1379,8 @@ described in RFC 3442. IPv6 options are specified using the \fBoption6:\fP keyword, followed by the option number or option name. The IPv6 option name space is disjoint from the IPv4 option name space. IPv6 addresses -in options must be bracketed with square brackets, eg. \fB --dhcp-option=option6:ntp-server,[1234::56]\fP. +in options must be bracketed with square brackets, eg. \fB --dhcp-option=option6:sntp-server,[1234::56]\fP or + --dhcp-option=option6:31,[1234::56]. For IPv6, [::] means "the global address of the machine running dnsmasq", whilst [fd00::] is replaced with the ULA, if it exists, and [fe80::] with the link-local address. The vendorclass option diff --git a/src/option.c b/src/option.c index 2341dfb..2846ea5 100644 --- a/src/option.c +++ b/src/option.c @@ -1471,58 +1471,45 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) while (arg) { + char *start = arg; comma = split(arg); - - for (cp = arg; *cp; cp++) + + if (strstr(arg, "option:") == arg) + start = arg+7; +#ifdef HAVE_DHCP6 + else if (strstr(arg, "option6:") == arg) + { + start = arg+8; + is6 = 1; + } +#endif + else if (strstr(arg, "option4:") == arg) + start = arg+8; + + for (cp = start; *cp; cp++) if (*cp < '0' || *cp > '9') break; if (!*cp) { - new->opt = atoi(arg); + new->opt = atoi(start); opt_len = 0; option_ok = 1; break; } - - if (strstr(arg, "option:") == arg) + + /* option*:| must follow tag and vendor string. */ + if (start != arg) { - if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1) + if ((new->opt = lookup_dhcp_opt(is6 ? AF_INET6: AF_INET, start)) != -1) { - opt_len = lookup_dhcp_len(AF_INET, new->opt); + opt_len = lookup_dhcp_len(is6 ? AF_INET6: AF_INET, new->opt); /* option: must follow tag and vendor string. */ if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH) option_ok = 1; } break; } -#ifdef HAVE_DHCP6 - else if (strstr(arg, "option6:") == arg) - { - for (cp = arg+8; *cp; cp++) - if (*cp < '0' || *cp > '9') - break; - - if (!*cp) - { - new->opt = atoi(arg+8); - opt_len = 0; - option_ok = 1; - } - else - { - if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1) - { - opt_len = lookup_dhcp_len(AF_INET6, new->opt); - if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH) - option_ok = 1; - } - } - /* option6:| must follow tag and vendor string. */ - is6 = 1; - break; - } -#endif else if (strstr(arg, "vendor:") == arg) { new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7); @@ -2010,7 +1997,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) if (flags == DHOPT_PXE_OPT && (new->flags & DHOPT_VENDOR)) goto_err(_("No vendor-encap options allowed in dhcp-option-pxe")); - + if (flags == DHOPT_MATCH) { if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||