Re: [Dnsmasq-discuss] [PATCH] Reimplement rev-server to support arbitrary IPv4 prefix lengths
Hey (again) Simon and the other list members, I received some private user's requests (mostly Pi-hole users, apparently) which do seem to ultimately need arbitrary prefix-lengths also for IPv6 addresses. As the majority of code now already existed for IPv4, I changed my mind and added arbitrary prefix-lengths now as well for IPv6 (patch 4). I was going back and forth about the choice of a more simple or a more compact algorithm. As this is code is absolutely not performance-critical, I chose the former as this eases accessibility of the code and, thereby, possible maintenance in the future. The result is a new set of three patches meant to come on top of my patches from yesterday (this also makes patch 3 mandatory). I also found it useful to make the prefix-length an optional field while I was at it (patch 5). > ./dnsmasq --rev-server=fe80::3aea:a711:fea1:2101,10.0.0.1 > dnsmasq[9870]: rev-server fe80::3aea:a711:fea1:2101[/128]: > address-to-name queries for fe80::3aea:a711:fea1:2101 are sent to 10.0.0.1 The last patch improves the output of the explanations provided for rev-server in case no server is specified (patch 6). Best regards, Dominik On 14.04.20 20:26, Dominik wrote: > Hey Simon, > > I recently noticed that the "--rev-server" option is limited to 8, 16, > 24, and 32 bit prefixes for IPv4 addresses. Since I needed support for > 22 bit subnets in the network I'm currently setting up, I decided to > extend dnsmasq to support arbitrary IPv4 prefix-lengths. This mail has > three patches attached that contain my changes. I hope this is helpful > and can be merged into upstream dnsmasq. > > Patch 1 implements support for arbitrary IPv4 prefixes by largely > rewriting option.c:add_rev4() and option.c:add_rev6() > > Patch 2 adds error checking for IPv6 prefix-lengths as the current > algorithm only makes sense in 4 bit steps. As 4 bit steps in 128 bit > wide addresses seems sufficient to me, I didn't change the nice and > short algorithm used here. > > Patch 3 adds logging for the inner details of what rev-server is doing. > It is an entirely optional patch. However, I would still recommend > adding it as it facilitates understanding of what the rev-server option > actually does (and if it does what the user is thinking it should be doing). > > Exemplary log outputs: > >> ./dnsmasq --rev-server=10.0.3.10/22,10.0.0.1 >> dnsmasq[29637]: rev-server 10.0.3.10/22: address-to-name queries for >> 10.0.0.0 to 10.0.3.255 are sent to 10.0.0.1 > >> ./dnsmasq --rev-server=10.0.3.10/17,10.0.0.1#5353 >> dnsmasq[29662]: rev-server 10.0.3.10/17: address-to-name queries for >> 10.0.0.0 to 10.0.127.255 are sent to 10.0.0.1#5353 > >> ./dnsmasq --rev-server=fe80::3aea:a711:fea1:2101/64,10.0.0.1 >> dnsmasq[29667]: rev-server fe80::3aea:a711:fea1:2101/64: address-to-name >> queries for fe80::3a00:0:0:0 to fe80::3aff::: are sent to >> 10.0.0.1 > > > (the displayed IPv6 addresses try to be as short as possible) > > Best regards, > Dominik > From f02939cdbbf33f2591e6667b5766596279a45395 Mon Sep 17 00:00:00 2001 From: Dominik Derigs Date: Wed, 15 Apr 2020 20:01:54 +0200 Subject: [PATCH 4/6] Add support for arbitrary IPv6 prefix-lengths in --rev-server. --- man/dnsmasq.8 | 2 +- src/option.c | 99 +++ 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index c836ae5..4a64df0 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -500,7 +500,7 @@ while .B --rev-server=192.168.3.10/22,192.168.0.1 is equivalent to .B --server=/0.168.192.in-addr.arpa/192.168.0.1 --server=/1.168.192.in-addr.arpa/192.168.0.1 --server=/2.168.192.in-addr.arpa/192.168.0.1 --server=/3.168.192.in-addr.arpa/192.168.0.1 -While any valid prefix-length may be used with IPv4 addresses (/1 - /32), dnsmasq only accepts IPv6 prefix-lengths in steps of 4 (/4, /8, /12, ..., /128). +Allowed prefix lengths are 1-32 (IPv4) and 1-128 (IPv6). .TP .B \-A, --address=/[/...]/[] Specify an IP address to return for any host in the given domains. diff --git a/src/option.c b/src/option.c index 1cb3caf..9105b4c 100644 --- a/src/option.c +++ b/src/option.c @@ -983,65 +983,88 @@ static int add_rev4(struct in_addr addr, int msize, char *comma, const int serv_ static int add_rev6(struct in6_addr *addr, int msize, char *comma, const int serv_flags, int explain, char *errstr) { int i; - char *p, *domain; + int idx = 31; + char *p, *top_domain; + unsigned char a_min8[32], a_max8[32]; + struct in6_addr bitmask, a_min, a_max; - if(msize < 1 || msize > 128 || msize%4) + if(msize < 1 || msize > 128) ret_err("bad prefix"); - p = domain = opt_malloc(73); /* strlen("32*ip6.arpa")+1 */ + memset(, 0, sizeof(bitmask)); + for(i = 0; i < (int)sizeof(bitmask.s6_addr); i++) +{ + /* Construct binary mask from specified prefix-length */ + if(msize >= 8*(i+1)) + bitmask.s6_addr[i] = 0xFF; + else if(msize > 8*i) + bitmask.s6_addr[i] = 0xFF <<
[Dnsmasq-discuss] [PATCH] Reimplement rev-server to support arbitrary IPv4 prefix lengths
Hey Simon, I recently noticed that the "--rev-server" option is limited to 8, 16, 24, and 32 bit prefixes for IPv4 addresses. Since I needed support for 22 bit subnets in the network I'm currently setting up, I decided to extend dnsmasq to support arbitrary IPv4 prefix-lengths. This mail has three patches attached that contain my changes. I hope this is helpful and can be merged into upstream dnsmasq. Patch 1 implements support for arbitrary IPv4 prefixes by largely rewriting option.c:add_rev4() and option.c:add_rev6() Patch 2 adds error checking for IPv6 prefix-lengths as the current algorithm only makes sense in 4 bit steps. As 4 bit steps in 128 bit wide addresses seems sufficient to me, I didn't change the nice and short algorithm used here. Patch 3 adds logging for the inner details of what rev-server is doing. It is an entirely optional patch. However, I would still recommend adding it as it facilitates understanding of what the rev-server option actually does (and if it does what the user is thinking it should be doing). Exemplary log outputs: ./dnsmasq --rev-server=10.0.3.10/22,10.0.0.1 >>> dnsmasq[29637]: rev-server 10.0.3.10/22: address-to-name queries for 10.0.0.0 to 10.0.3.255 are sent to 10.0.0.1 ./dnsmasq --rev-server=10.0.3.10/17,10.0.0.1#5353 >>> dnsmasq[29662]: rev-server 10.0.3.10/17: address-to-name queries for 10.0.0.0 to 10.0.127.255 are sent to 10.0.0.1#5353 ./dnsmasq --rev-server=fe80::3aea:a711:fea1:2101/64,10.0.0.1 >>> dnsmasq[29667]: rev-server fe80::3aea:a711:fea1:2101/64: address-to-name queries for fe80::3a00:0:0:0 to fe80::3aff::: are sent to 10.0.0.1 (the displayed IPv6 addresses try to be as short as possible) Best regards, Dominik From d038771987084c09df60af9c9c117e3fe14cdaa8 Mon Sep 17 00:00:00 2001 From: Dominik Derigs Date: Tue, 14 Apr 2020 19:40:21 +0200 Subject: [PATCH 3/3] Add explanatory log message to rev-server to facilitate understanding of the --rev-server option. --- src/option.c | 75 +++- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/src/option.c b/src/option.c index 809739e..1cb3caf 100644 --- a/src/option.c +++ b/src/option.c @@ -914,7 +914,7 @@ static int add_rev_serv(char* domain, char *comma, const int serv_flags, char *e return 1; } -static int add_rev4(struct in_addr addr, int msize, char *comma, const int serv_flags, char *errstr) +static int add_rev4(struct in_addr addr, int msize, char *comma, const int serv_flags, int explain, char *errstr) { int i; unsigned char a[3]; @@ -930,6 +930,25 @@ static int add_rev4(struct in_addr addr, int msize, char *comma, const int serv_ a_min.s_addr = addr.s_addr & bitmask.s_addr; a_max.s_addr = a_min.s_addr + ~bitmask.s_addr; + /* Print derived subnet address range to ease configuration (if requested) */ + if(explain) +{ + char original_address[INET_ADDRSTRLEN], min_address[INET_ADDRSTRLEN], max_address[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, , original_address, sizeof(original_address)); + inet_ntop(AF_INET, _min, min_address, sizeof(min_address)); + if(a_min.s_addr != a_max.s_addr) + { + inet_ntop(AF_INET, _max, max_address, sizeof(max_address)); + my_syslog(LOG_INFO, "rev-server %s/%i: address-to-name queries for %s to %s are sent to %s", + original_address, msize, min_address, max_address, comma); + } + else + { + my_syslog(LOG_INFO, "rev-server %s/%i: address-to-name queries for %s are sent to %s", + original_address, msize, min_address, comma); + } +} + /* Extract IP address octets in host-format for in-addr.arpa string-assembly */ for(i = 0; i < 3; i++) a[i] = (ntohl(a_min.s_addr) >> 8*(3-i)) & 0xFF; @@ -961,7 +980,7 @@ static int add_rev4(struct in_addr addr, int msize, char *comma, const int serv_ return 1; } -static int add_rev6(struct in6_addr *addr, int msize, char *comma, const int serv_flags, char *errstr) +static int add_rev6(struct in6_addr *addr, int msize, char *comma, const int serv_flags, int explain, char *errstr) { int i; char *p, *domain; @@ -970,7 +989,49 @@ static int add_rev6(struct in6_addr *addr, int msize, char *comma, const int ser ret_err("bad prefix"); p = domain = opt_malloc(73); /* strlen("32*ip6.arpa")+1 */ - + + /* Print derived subnet address range to ease configuration (if requested) */ + if(explain) +{ + struct in6_addr bitmask, a_min, a_max; + memset(, 0, sizeof(bitmask)); + for(i = 0; i < 16; i++) + { + if(msize >= 8*i) + bitmask.s6_addr[i] = 0xFF; + else if(msize > 8*(i) && msize < 8*(i+1)) + bitmask.s6_addr[i] = ~(((uint16_t)1 << (8 - (msize%8))) - 1) & 0xFF; + else + break; + } + + /* Apply bitmask to address to compute subnet address range */ + int addresses_differ = 0; + for(i = 0; i < 16; i++) + { + a_min.s6_addr[i] = (*addr).s6_addr[i] & bitmask.s6_addr[i]; + a_max.s6_addr[i] = a_min.s6_addr[i] + ~bitmask.s6_addr[i];