802.11n -> 802.11ac AP-Client Kick-Off Script (Py)

So I have been running multiple APs with the same SSID on separate channels and frequencies and noticed that the clients are really good at switching from a weak 802.11ac signal strength to the stronger but lower speed 802.11n AP station. This is good, however, they don’t seem to be as aggressive in switching back to 802.11ac once they get closer again (unless they turn off or shutdown or restart their network stack since the 802.11n just gets stronger the closer you get). I found an OpenWRT compatible shell script which kicks clients off a given radio depending on their signal strength to the router. I adjusted it to disconnect a client if they start to get too close to the N router as they are likely going to get a good signal strength from the AC AP instead. You can set the AP deauth time (ex 19 seconds), the time between kicking the same client on/off again (ex 31 mins), and it checks for the signal-to-noise ratio to get above a certain amount (ex 45 SNR) before activating on a client!

python apc.py n 20 40
import os,sys,time

radios = []
mode = sys.argv[1]
secs = (int(sys.argv[2]) * 1000)
macs = {}
band = (int(sys.argv[3]) * 60)

if (mode == "ac"):
	radios = ["wlan0"]

if (mode == "n"):
	radios = ["wlan1"]

while True:
	sec = int(time.time())

	for intf in radios:
		os.system("iwinfo '%s' assoclist | grep 'SNR' | tr '(/)' ' ' | tr -s ' ' > /tmp/apc.log" % (intf))
		f = open("/tmp/apc.log", "r")
		lines = f.readlines()
		f.close()

		for line in lines:
			info = line.strip().split(" ")
			(mac, sig, snr) = (info[0], int(info[1]), abs(int(info[6])))
			print("> %d %d [%s][%s]" % (snr, sig, intf, mac))

			if (secs >= 1000):
				delc = 0 ; kick = "true"
				opts = ("'addr':'%s', 'reason':5, 'deauth':%s, 'ban_time':%s" % (mac, kick, secs))

				if ((mode == "ac") and (sig <= -83)):
					delc = 1

				#if ((mode == "n") and (sig >= -51)):
				#	delc = 1

				if ((mode == "n") and (snr >= 45)):
					delc = 1

				if (delc == 1):
					if (not mac in macs.keys()):
						macs[mac] = 0
					if (sec >= (macs[mac] + band)):
						macs[mac] = sec
						print("* %d %d [%s][%s][%d][%d]" % (macs[mac], sec, mac, intf, sig, snr))
						os.system('ubus call "hostapd.%s" del_client "{%s}"' % (intf, opts))

	for mac in macs.keys():
		if (sec >= (macs[mac] + band)):
			print("x", mac, macs[mac])
			del macs[mac]

	print("")
	time.sleep(9)

802.11n -> 802.11ac AP-Client Kick-Off Script (Py)

Creating a simple relayd replacement in C (compiled for an ARMv7 WRT router)

I’ve been running into different issues with relayd that I tried to manually fix in the framework, for example: not locking host route entries onto a specific interface, better detection for when a host changes interfaces, better arp table & ip routing management, etc.. However, I’ve still been experiencing some flakiness in it’s operation so I made a simple alternative that I can run which duplicates its most essential features needed for layer 2 bridging:

  • Layer 2 server socket to listen for ARP request packets
  • Layer 2 client sockets to send ARP packets with the routers MAC address inserted into the ARP replies for each host and for each bridged interface
  • Layer 3 UDP socket-based pinging for unknown host detection & discovery (auto IP-to-MAC ARP table entry resolution for each bridged interface)
  • Layer 3 IP routing-table host-entry updates related to which bridged interface the client is currently on

The code is just under 300 lines but it seems to be working so far and will keep testing it out to see if it works better than what I was experiencing with relayd!

https://github.com/stoops/arprb/blob/master/arprb.c?ts=4

--
arp req [42] [1544][256] [192.168.17.175][192.168.16.1]
arp reply [wlan0] [00:be:ee:ca:fe:ff][192.168.17.175] <-> [192.168.16.1]
--
arp req [42] [1544][256] [192.168.16.1][192.168.17.175]
arp reply [br-wan] [00:be:ee:ca:fe:00][192.168.16.1] <-> [192.168.17.175]
--
void send_arps(struct intf *relay, char *who_adr, uchar *dst_mac, char *dst_adr) {
	int sock = relay->sock_arp;
	unsigned char *src_mac = relay->mac;
	struct sockaddr ssa = relay->ssa;

	unsigned int psiz = sizeof(struct arp_pkt);
	struct arp_pkt pkt;

	printf("arp reply !! [%s][%s] -> [%s][%s]\n", relay->smac, who_adr, relay->ifn, dst_adr);

	pkt.frame_type = htons(ETH_P_ARP);
	pkt.hw_type    = htons(ETH_HW_TYPE);
	pkt.proto_type = htons(ETH_P_IP);
	pkt.hw_size    = HW_ADDR_LEN;
	pkt.proto_size = IP_ADDR_LEN;
	pkt.op_code    = htons(ARP_OP_REPLY);

	bcopy(src_mac, pkt.sorc_hw, HW_ADDR_LEN); /* src mac */
	bcopy(dst_mac, pkt.dest_hw, HW_ADDR_LEN); /* dst mac */

	bcopy(src_mac, pkt.sndr_hw, HW_ADDR_LEN); /* who mac */
	ipstr(who_adr, pkt.sndr_ip);              /* who ip  */

	bcopy(dst_mac, pkt.rcpt_hw, HW_ADDR_LEN); /* dst mac */
	ipstr(dst_adr, pkt.rcpt_ip);              /* dst ip  */

	sendto(sock, &pkt, psiz, 0, &ssa, sizeof(ssa));
}

Creating a simple relayd replacement in C (compiled for an ARMv7 WRT router)

Following along with the OpenWRT subreddit users

There was a post on a subreddit that I follow (OpenWRT) that was asking for peoples router/network setups and how they are using OpenWRT to accomplish that so I decided to try and make a network diagram of the setup I worked on here at my parents home while I’ve been working remotely (needing stable Internet/WiFi!). There are 2 levels that are covered and the Internet comes into the lower level which then gets sent up via a dedicated 802.11ac radio (separate backchannel). That is then rebroadcasted to the rest of the home with another dedicated, separate 802.11ac AP via the middle relay bridge in between. The relay bridge runs the customized app below that I compiled and it all shares one flat network throughout the home (one /20)! There is also 802.11n sent out for better reach and distance just in case as well 🙂

Edit: You can also add guest networks on each of the radios but with different SSIDs and wlan interfaces which will let them act like wifi-vlans that you can section off with dnsmasq & iptables…

Edit-edit (Updated diagram/layout):

I made this network graph with this site: https://www.lucidchart.com/documents#/dashboard

Historical documentation purposes,
Previous setup with semi-ok results:

Following along with the OpenWRT subreddit users

Turning a small stream cipher (ARC4) into a hash function (ARCH)!

$ python hash.py b b
('fd5f1b4640e0f5cdda0368b67601b9077cbe4a4931a94b52fdf46bf1be0c2bf5', '3a4627878dca5c4daeb243a661b56ebbaa03c268f659b6001cd84911051c103b', 'bb', 2)

$ python hash.py b c
('1181834e637bf3519010de1902889807341cd57a1d2e68a281524d91f2f86cf9', '7deadb485c3d329026d04cefadb4b43a9af19536aaf450052a2edce404f6065f', 'bc', 2)

$ python hash.py c b
('2f4802585ed79a61d25c92a3d7af8fa2a16e2e269e9c6fbd21891e6a7bae15ec', '6a25a3b18a93d966d7c878f9263f053d653618782a9f7918db05b06e0fa27787', 'cb', 2)

$ python hash.py c c
('6683682f501fdaa1c4a95e1ec6b7fb5ff49206af34e1729a17ded27fda355ea4', '7f12dc4dd32e3c9b31d03382b9c9af1efc0ea3ba620ba8e27fa9b9d8ebc1a88b', 'cc', 2)
import os,sys

def swap(s, a, b):
	t = s[a] ; s[a] = s[b] ; s[b] = t
	return s

def init(mesg, leng):
	j = 0 ; skey = []
	for i in range(0, 256):
		skey.append(i)
	for i in range(0, 256+leng):
		k = (i % 256) ; m = (i % leng)
		l = (((i + 1 + ord(mesg[m])) * leng) % 256)
		j = ((j + skey[k] + skey[l]) % 256)
		swap(skey, k, j)
	return skey

def arch(skey):
	i = 0 ; j = 0
	l = 0 ; rnds = 4 ; outp = ""
	for z in range(0, 256*rnds):
		i = ((i + 1) % 256)
		j = ((j + skey[i]) % 256)
		swap(skey, i, j)
	for z in range(0, 32):
		i = ((i + 1) % 256)
		j = ((j + skey[i]) % 256)
		swap(skey, i, j)
		k = ((skey[i] + skey[j] + skey[l]) % 256)
		outp += chr(skey[k])
		l = (l ^ ord(outp[z]))
	return outp

def hmac(mesg, mlen, skey, klen):
	inner_pad = 0x36 ; outer_pad = 0x5C
	block_size = 64 ; ikey = "" ; okey = ""
	tkey = skey ; tlen = klen
	if (klen > block_size):
		zkey = init(skey, klen)
		tkey = arch(zkey)
		tlen = len(tkey)
	for x in range(0, block_size):
		c = 0
		if (x < tlen):
			c = ord(tkey[x])
		ikey += chr(inner_pad ^ c)
		okey += chr(outer_pad ^ c)
	zkey = init(ikey+mesg, block_size+mlen)
	ihsh = arch(zkey)
	ilen = len(ihsh)
	zkey = init(okey+ihsh, block_size+ilen)
	ohsh = arch(zkey)
	return ohsh

def stoh(inpt):
	o = ""
	for c in inpt:
		s = hex(ord(c))[2:]
		if len(s) != 2:
			s = "0" + s
		o += s
	return o

m = sys.argv[1] ; l = len(m)
k = sys.argv[2] ; n = len(k)
z = init(m+k, l+n)
print(stoh(hmac(m, l, k, n)), stoh(arch(z)), m+k, l+n)

Turning a small stream cipher (ARC4) into a hash function (ARCH)!

A simpler improvement to relayd (checking the arp table before expiring)

So instead of running a process heavy static arp broadcaster client/server service, I realized that I could just make relayd a bit smarter in order to properly detect disconnected hosts faster (simply by double checking the arp table). I was also able to compile this software in an Ubuntu VM running in a VirtualBox on OSX for mips & armv7 (archer c7 tplink & wrt1900acs wifi extended setup):

https://github.com/stoops/relayd/compare/master…stoops:moddel

./relayd -I wlan0 -I wlan1 -t 2 -p -1 -B -D -P

With a couple of arp table clean up helper scripts the setup is much simpler with this and less involved in managing the arp table entries!

mode:  ap  |  ssid:  Peppi Place      bmac: 00:be:ee:ca:fe:01  key: psk2+ccmp  |  freq: 2.4GHz  chan:   1  mode:  HT40  |  timeouts: 300 300
mode:  ap  |  ssid:  Peppi Place      bmac: 00:be:ee:ca:fe:11  key: psk2+ccmp  |  freq: 2.4GHz  chan:  11  mode:  HT40  |  timeouts: 300 300
mode:  ap  |  ssid:  Peppi Place      bmac: 00:be:ee:ca:fe:61  key: psk2+ccmp  |  freq: 5.0GHz  chan: 161  mode: VHT80  |  timeouts: 300 300
mode:  ap  |  ssid:  Peppi Place AC   bmac: 00:be:ee:ca:fe:63  key: psk2+ccmp  |  freq: 5.0GHz  chan: 161  mode: VHT80  |  timeouts: 300 300

A simpler improvement to relayd (checking the arp table before expiring)

GIT Rebase with HEAD Conflicts (Auto Accept & Merge the Changes)

I have a bad habit of using git amends (vs commit & squash)
git commit -a --amend --no-edit
but this can cause some future issues when force pulling in the new changes (for example to another branch possibly). I learnt a new way when rebasing to auto accept all of the head changes listed as a conflict:

git checkout --conflict=merge . ; git checkout --ours . ; git add .

GIT Rebase with HEAD Conflicts (Auto Accept & Merge the Changes)

Turning the sdbm hash method into an hmac version

Some small hash loop tweaks and I increased the number of hash rounds from 3 to 4 also:

import os,sys

def tt(ll):
	return (ll & 0xffffffff)

def sdbm(inpt, leng):
	hshs = 0
	for x in range(0, leng):
		hshs = tt(ord(inpt[x]) + tt(hshs << 6) + tt(hshs << 16) - hshs)
	return hshs

def sdbm_hash(inpt, leng):
	mixs = [1, 6, 16, 13, 33, 27, 67, 55, 123]
	hshs = [0, 0, 0, 0, 0, 0, 0, 0, 0]
	more = 0
	mlen = len(hshs) ; rnds = (4 * mlen)
	for x in range(0, mlen+leng):
		hshs[0] = tt(mixs[x%mlen] + ord(inpt[x%leng]) + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0])
		more = (more ^ (hshs[0] >> 16))
		i = ((x % (mlen - 1)) + 1)
		less = ((hshs[i] & 0xffff0000) ^ ((hshs[i] & 0xffff) << 16))
		hshs[i] = (less ^ more)
	for z in range(0, rnds):
		hshs[0] = tt(z + more + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0])
		more = (more ^ (hshs[mlen-1] >> 16))
		for y in range(mlen-1, 0, -1):
			hshs[y] = tt((hshs[y] << 16) | (hshs[y-1] >> 16))
			hshs[y-1] = (hshs[y-1] & 0xffff)
	o = ""
	for h in hshs[1:]:
		for x in range(3, -1, -1):
			o += chr((h >> (x * 8)) & 0xff)
	return o

def sdbm_hmac(mesg, mlen, skey, klen):
	inner_pad = 0x36 ; outer_pad = 0x5C
	block_size = 64 ; ikey = "" ; okey = ""
	tkey = skey ; tlen = klen
	if (klen > block_size):
		tkey = sdbm_hash(skey, klen)
		tlen = len(tkey)
	for x in range(0, block_size):
		c = 0
		if (x < tlen):
			c = ord(tkey[x])
		ikey += chr(inner_pad ^ c)
		okey += chr(outer_pad ^ c)
	ihsh = sdbm_hash(ikey+mesg, block_size+mlen)
	ilen = len(ihsh)
	ohsh = sdbm_hash(okey+ihsh, block_size+ilen)
	return ohsh

def stoh(s):
	return "".join([hex(ord(c))[2:].rjust(2, '0') for c in s])

m = sys.argv[1] ; l = len(m)
k = sys.argv[2] ; n = len(k)
#print(stoh(sdbm_hash(m, l)), sdbm(m, l), m, l)
print(stoh(sdbm_hmac(m, l, k, n)), sdbm(m, l), m, l, sdbm(k, n), k, n)

$ for x in b c ; do for y in b c ; do python hash.py "$x" "$y" ; echo ; done ; done
('d805c892902cf6ea0c98d1946110a61334a9a1158b430fb7d1c5ba7afb29147d', 98, 'b', 1, 98, 'b', 1)

('6eb38871cc3679cb533f1daeee4ed87ab4f154f953e5ea96d508bda6bb054abd', 98, 'b', 1, 99, 'c', 1)

('0d034f1f7627492fd57bd92a63a8a4f2d977f02c2fdf29b4e49f4b3b21a9e738', 99, 'c', 1, 98, 'b', 1)

('09b2678c7ce46cd7cd112b4a2b6fd05d50c858fe0460d29264c4ea01595968db', 99, 'c', 1, 99, 'c', 1)


$ ./hash "this is a test" "b"
[6b18600ee98449472dd377755dd67cd436c9c143cfc893c9a2181e0567d16cf1] [1655286693] [this is a test] [14] [98] [b] [1]

$ ./hash "this is a test" "c"
[bd3bc1a983e35311e5ea60db5ae8cd28a821386937dfd480eccff95fab8356b9] [1655286693] [this is a test] [14] [99] [c] [1]

$ ./hash "this is a tesu" "b"
[8cddc8b9f5a1a1adf41c78b035bf59b444431e772b9c39824e2cf67e2837d9b0] [1655286694] [this is a tesu] [14] [98] [b] [1]

$ ./hash "this is a tesu" "c"
[2554d7d9ecb47d36b7f62d5e4432a7983490aca1229a91331c16b81567c4edff] [1655286694] [this is a tesu] [14] [99] [c] [1]
#include <stdio.h>
#include <string.h>

unsigned int sdbm(char *inpt, int leng) {
	unsigned int hshs = 0;
	for (int x = 0; x < leng; ++x) {
		hshs = (inpt[x] + (hshs << 6) + (hshs << 16) - hshs);
	}
	return hshs;
}

void sdbm_hash(unsigned char *outp, unsigned char *inpt, int leng) {
	unsigned int mixs[] = {1, 6, 16, 13, 33, 27, 67, 55, 123};
	unsigned int hshs[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	unsigned int less, more = 0;
	int i, mlen = 9, rnds = (4 * mlen);
	for (int x = 0; x < (mlen+leng); ++x) {
		hshs[0] = (mixs[x%mlen] + inpt[x%leng] + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0]);
		more = (more ^ (hshs[0] >> 16));
		i = ((x % (mlen - 1)) + 1);
		less = ((hshs[i] & 0xffff0000) ^ ((hshs[i] & 0xffff) << 16));
		hshs[i] = (less ^ more);
	}
	for (int z = 0; z < rnds; ++z) {
		hshs[0] = (z + more + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0]);
		more = (more ^ (hshs[mlen-1] >> 16));
		for (int y = mlen-1; y > 0; --y) {
			hshs[y] = ((hshs[y] << 16) | (hshs[y-1] >> 16));
			hshs[y-1] = (hshs[y-1] & 0xffff);
		}
	}
	for (int x = 1, y = 0; x < mlen; ++x) {
		for (int z = 3; z > -1; --z, ++y) { 
			outp[y] = ((hshs[x] >> (z * 8)) & 0xff);
		}
	}
}

void sdbm_hmac(unsigned char *outp, unsigned char *mesg, int mlen, unsigned char *skey, int klen) {
	int block_size = 64, hash_size = 32;
	unsigned char inner_pad = 0x36, outer_pad = 0x5C;
	unsigned char ikey[block_size], okey[block_size], ihsh[hash_size], thsh[hash_size];
	unsigned char buff[block_size+mlen+hash_size];
	unsigned char *tkey = skey; int tlen = klen;
	if (klen > block_size) {
		sdbm_hash(thsh, skey, klen);
		tkey = thsh; tlen = hash_size;
	}
	for (int x = 0; x < block_size; ++x) {
		unsigned char padc = 0;
		if (x < tlen) { padc = tkey[x]; }
		ikey[x] = (inner_pad ^ padc);
		okey[x] = (outer_pad ^ padc);
	}
	bcopy(ikey, buff, block_size);
	bcopy(mesg, buff+block_size, mlen);
	sdbm_hash(ihsh, buff, block_size+mlen);
	bcopy(okey, buff, block_size);
	bcopy(ihsh, buff+block_size, hash_size);
	sdbm_hash(outp, buff, block_size+hash_size);
}

void stoh(char *outp, unsigned char *inpt) {
	char *hexs = "0123456789abcdef";
	for (int x = 0, y = 0; x < 32; ++x) {
		outp[y] = hexs[(inpt[x] >> 4) & 0xf]; ++y;
		outp[y] = hexs[inpt[x] & 0xf]; ++y;
	}
}

int main(int argc, char *argv[]) {
	char *m = argv[1]; int l = strlen(m);
	char *k = argv[2]; int n = strlen(k);
	unsigned char h[32]; char o[65]; bzero(o, 65);
	sdbm_hmac(h, (unsigned char *)m, l, (unsigned char *)k, n); stoh(o, h);
	printf("[%s] [%u] [%s] [%d] [%u] [%s] [%d]\n", o, sdbm(m, l), m, l, sdbm(k, n), k, n);
	return 0;
}

Turning the sdbm hash method into an hmac version

Experiment – Turning the SDBM mixing algorithm into a hash function

From this page: http://www.cse.yorku.ca/~oz/hash.html

import os,sys

def tt(ll):
	return (ll & 0xffffffff)

def sdbm(inpt, leng):
	hshs = 0
	for x in range(0, leng):
		hshs = tt(ord(inpt[x]) + tt(hshs << 6) + tt(hshs << 16) - hshs)
	return hshs

def sdbm_hash(inpt, leng):
	mixs = [1, 6, 16, 13, 33, 27, 67, 55, 123]
	hshs = [0, 0, 0, 0, 0, 0, 0, 0, 0]
	more = 0
	rnds = len(hshs)
	for z in range(0, rnds*3):
		hshs[0] = tt((hshs[0] + mixs[z%rnds]) * mixs[z%rnds])
		for x in range(0, leng):
			hshs[0] = (hshs[0] & 0xffff)
			hshs[0] = tt(ord(inpt[x]) + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0])
			more = (more ^ (hshs[rnds-1] >> 16))
			for y in range(rnds-1, 0, -1):
				hshs[y] = tt((hshs[y] << 16) | (hshs[y-1] >> 16))
				hshs[y-1] = (hshs[y-1] & 0xffff)
			hshs[0] = (hshs[0] ^ more)
	o = ""
	for h in hshs[1:]:
		for x in range(3, -1, -1):
			o += chr((h>>(x*8))&0xff)
	return o

def stoh(s):
	return "".join([hex(ord(c))[2:].rjust(2, '0') for c in s])

m = sys.argv[1] ; l = len(m)
print(stoh(sdbm_hash(m, l)), sdbm(m, l), m, l)

$ python hash.py "b" 
('197f907989061db579beba252025bfbf7f4848a47da0e5a9bc8eb73c6fdb8904', 98, 'b', 1)

$ python hash.py "c" 
('f39b31f66506067376f8f88f620e731cd4292a6aeb71da43d297fffc3f5f92c4', 99, 'c', 1)

$ python hash.py "bb" 
('d427d6ba1edaabab2ba3c6ddd70ccf3d0ceff23fcd6de3d2c0af7d0ac95dd62d', 6428800, 'bb', 2)

$ python hash.py "bc" 
('4bfdd409d3de82913ba7405894f444d8858a539abe98e312d64474abf6b68e3a', 6428801, 'bc', 2)

$ ./hash "this is a test"
[dc053c4426ce396ace93a2817b388c465bb5188336d5f8816384a1cef0a2039d] [1655286693] [this is a test] [14]

$ ./hash "this is a tesu"
[cfbb3301ae39658cf1a1874dd48d8dbf9da4aee0831f781e30066be5f67eb819] [1655286694] [this is a tesu] [14]
#include <stdio.h>
#include <string.h>

unsigned int sdbm(char *inpt, int leng) {
	unsigned int hshs = 0;
	for (int x = 0; x < leng; ++x) {
		hshs = (inpt[x] + (hshs << 6) + (hshs << 16) - hshs);
	}
	return hshs;
}

void sdbm_hash(unsigned char *outp, unsigned char *inpt, int leng) {
	unsigned int mixs[] = {1, 6, 16, 13, 33, 27, 67, 55, 123};
	unsigned int hshs[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	unsigned int more = 0;
	int rnds = 9;
	for (int z = 0; z < rnds*3; ++z) {
		hshs[0] = ((hshs[0] + mixs[z%rnds]) * mixs[z%rnds]);
		for (int x = 0; x < leng; ++x) {
			hshs[0] = (hshs[0] & 0xffff);
			hshs[0] = (inpt[x] + (hshs[0] << 6) + (hshs[0] << 16) - hshs[0]);
			more = (more ^ (hshs[rnds-1] >> 16));
			for (int y = rnds-1; y > 0; --y) {
				hshs[y] = ((hshs[y] << 16) | (hshs[y-1] >> 16));
				hshs[y-1] = (hshs[y-1] & 0xffff);
			}
			hshs[0] = (hshs[0] ^ more);
		}
	}
	for (int x = 1, y = 0; x < rnds; ++x) {
		for (int z = 3; z > -1; --z, ++y) { 
			outp[y] = ((hshs[x] >> (z * 8)) & 0xff);
		}
	}
}

void stoh(char *outp, unsigned char *inpt) {
	char *hexs = "0123456789abcdef";
	for (int x = 0, y = 0; x < 32; ++x) {
		outp[y] = hexs[(inpt[x] >> 4) & 0xf]; ++y;
		outp[y] = hexs[inpt[x] & 0xf]; ++y;
	}
}

int main(int argc, char *argv[]) {
	char *m = argv[1]; int l = strlen(m);
	unsigned char h[32]; char o[65]; bzero(o, 65);
	sdbm_hash(h, (unsigned char *)m, l); stoh(o, h);
	printf("[%s] [%u] [%s] [%d]\n", o, sdbm(m, l), m, l);
	return 0;
}

Experiment – Turning the SDBM mixing algorithm into a hash function

Experiment: Running a UDP socket [client/server] ARP broadcaster for each WiFi AP (better client association tracking)

If you are running multiple WiFI APs that are part of the same flat network and bridged together it can be tricky keeping a current list of which client is connected to which AP (sometimes clients can just stop responding on one AP and then wake up and appear on another AP). In addition, the one common question a router needs to know is, is this client connected to me OR are they connected through one of my peer routers and which one is the best one?… I wrote a small service which allows an AP to periodically broadcast out, relay/forward, and receive the current WiFi client associations it has and the best association is chosen to amongst all the APs to be able to keep a common set of ARP entries for each access point! The tables are way cleaner and way more consistent with each other now that I have this thing running. I wrote it in both C and python which are supported on OpenWRT routers and can communicate with each other using a common key/pwd. You simply list which radio interfaces the AP has and the IP address you gave the AP on the flat network and it should be ready to send, forward, & receive ARP related messages. The program will write out a standard file with the following information formatted in it and it can also be used with the relayd service modification post below:

/tmp/arp.txt: [dev] [mac/peer] [ip]
wlan1 00:11:22:dd:ee:ff 192.168.100.123

The code can be found here if you are interested!:

C: https://github.com/stoops/broadarp/blob/init/barp.c
Py2: https://github.com/stoops/broadarp/blob/python/barp.py
relayd-mod: https://github.com/stoops/relayd/compare/master…stoops:merged [blog post]

🙂
Jon C

Experiment: Running a UDP socket [client/server] ARP broadcaster for each WiFi AP (better client association tracking)

Modifying the OpenWRT relayd package C source code to help set better static ARP entries (ATF_PERM)

If you are bridging 2+ wireless networks together, it can get tricky for the ARP mappings to take place efficiently throughout the network as you move around from AP to AP. I wrote a small shell script that runs on each router which reports in its WiFi-AP associations & DHCP entries that are part of the overall /20 network. This client mapping data is sent around to each router to determine who has the proper client and where/how to find them. I noticed that the relayd bridging app was putting in ARP entries that were becoming stale/incorrect over time as you moved around so I modified it’s source code to outsource this work to a file that has an updated master set of static interface entries based on each DHCP/WIFI association for each router on the network.

(I lost access to my fossjon github account due to 2-factor auth being lost)

https://github.com/stoops/relayd/compare/master…stoops:merged

(the steps to compile this on a armv7l router itself are in the README I added)

For example, a shell script running on the router (with all the relevant network information) can determine and write out a small mapping file that this modified framework can use to keep a cleaner and better ARP tableset (/tmp/arp.txt):

wlan1 00:2b:3d:5a:4a:9e 192.168.18.103

The added capability in relayd will then ensure that for any of the bridged interfaces listed, the only ARP entry that will exist is for the one specified in the file and that it will be set to permanent status instead (deletion of incorrect entries added in).

I am running this as a test on my parents home network to see how long it lasts for, modifying C is tricky for me! 🙂

Modifying the OpenWRT relayd package C source code to help set better static ARP entries (ATF_PERM)

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;
}

dnsmasq dhcp mac address hashing algorithm