dnsmasq dhcp mac address hashing algorithm

So during this quarantine break I was setting up a new network layout for my parents place which includes 2 wireless OpenWRT routers connected together via an 802.11ac channel. Each wifi router broadcasts an 802.11n AP which has the same SSID & KEY but on different CHANNELS and BSSIDS. The traffic can be forwarded back and forth between the 2 separate APs via the single private AC connection (using the OWRT relayd package):

/usr/sbin/relayd -I wlan1 -I wlan0 -t -1 -p -1 -B

Each AP has dnsmasq running, however, they are each set to hand out a subset of unique addresses that are shared in a /20 block (so that whatever AP you are on, you can communicate to any other host on any of the APs as if you are on the same network). I noticed that when switching between the 2 APs, my machine was getting the same last octet in the IP range set! I thought this was pretty cool because even though the rest of the IP prefix was different, I could simply memorize the last digits if I wanted to connect to another machine on the network in the future. I then started looking into dnsmasq’s source code to see how it was doing this since the only info DHCP usually has is one’s MAC address:

https://github.com/dnsmasq/dnsmasq/blob/master/src/dhcp.c#L638

…note: they added a fix for the j variable recently: j= instead of j+=…
…note: note: there is a IP loop and epoch counter variable that gets increased in case of any hashing collisions or errors that occur!

So if you compile the code below and want to pre-calculate your likely IP address when asking dnsmasq for one, you can run the following (you need to know the IP range that was given in your configs):

$ a="11:22:33:44:55:66"
$ b=$(echo "$a" | sed -e 's/^/\\x/' -e 's/:/\\x/g')
$ c=$(printf "${b}")
$ ./dd "$c" 6 192.168.17.101 192.168.17.190

[192.168.17.106] [3842235635] [6]

---

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
	int i, l=atoi(argv[2]);
	unsigned int j, e=0;
	struct in_addr temp, addr;
	struct sockaddr_in beg, end;
	unsigned char *hwaddr = (unsigned char *)argv[1];

	for (j = 0, i = 0; i < l; i++) {
		j = hwaddr[i] + (j << 6) + (j << 16) - j;
	}

	int pass;
	for (pass = 0; pass <= 1; pass++) {
		inet_pton(AF_INET, argv[3], &(beg.sin_addr));
		inet_pton(AF_INET, argv[4], &(end.sin_addr));
		temp.s_addr = htonl(ntohl(beg.sin_addr.s_addr) + 
					 ((j + e) % (1 + ntohl(end.sin_addr.s_addr) - ntohl(beg.sin_addr.s_addr))));
		addr = temp;
		do {
			addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
			if (addr.s_addr == htonl(ntohl(end.sin_addr.s_addr) + 1))
				addr = beg.sin_addr;
		} while (addr.s_addr != temp.s_addr);
	}

	char str[64];
	bzero(str, 64 * sizeof(char));
	inet_ntop(AF_INET, &(addr.s_addr), str, INET_ADDRSTRLEN);

	printf("[%s] [%u] [%d]\n", str, j, l);
	return 0;
}

Leave a comment