Now I've another question. I made my wireless adapter to monitor mode and used wireshark to capture beacon frames. As this article, I should capture action frames, but I could't capture them. So I can't sure the neighbor report runs correctly.
I assume you're using iwcap to capture the data. The neighbour reports are only requested when the client decides to do that, so will depend on your clients.
I'll have a go here and see if I can capture something. A little busy today so I might take a little while getting back to you.
Thanks @PolynomialDivision - that was quite helpful in that it prompted me to come up with a way to easily test the neighbour reports. First set up a wpa_supplicant hanging off the AP with the neighbour reports active. Then use the wpa_cli command "neighbor_rep_request" to trigger a request. The mac addresses have been modified.
@sotux Seems to be some sort of issue on restarting the AP. The script doesn't populate the neighbour list properly. I'll have a bit of think as to why - not sure at this stage. It's fine when started manually after everything is up.
Hi, @muddyfeet
Now I use another way to auto update neighbor list.
Below is the service script, put this file into the /etc/inid.d directory and name it as "neighbor_report".
#!/bin/sh /etc/rc.common
START=90
USE_PROCD=1
radios=$(ubus list | grep hostapd.wlan)
start_service() {
local rrm_own
OIFS=$IFS
IFS=$'\n'
for value in ${radios}
do
rrm_own=${rrm_own}",$(ubus call ${value} rrm_nr_get_own | jsonfilter -e '$.value')"
done
IFS=$OIFS
procd_open_instance
procd_set_param command /bin/sh "/usr/bin/rrm"
procd_add_mdns "rrm" "nr" "" "${rrm_own:1}"
procd_close_instance
}
service_triggers()
{
procd_add_reload_trigger wireless
}
And then is rrm file, put it in the /usr/bin directory and name it "rrm". It will be called by the serivce script. This script do a while loop and update neighbor list every 60s.
#!/bin/sh
radios=$(ubus list | grep hostapd.wlan)
_discover_neighbors() {
local rrm_neighbors
ubus call umdns update
sleep 2
rrm_neighbors=$(ubus call umdns browse | jsonfilter -e '@["_rrm._nr"][*].txt')
for value in ${rrm_neighbors}
do
rrm_nr_lists=${rrm_nr_lists}",${value}"
done
}
_append_own() {
for value in ${radios}
do
rrm_nr_lists=${rrm_nr_lists}",$(ubus call ${value} rrm_nr_get_own | jsonfilter -e '$.value')"
done
}
while true
do
OIFS=$IFS
IFS=$'\n'
rrm_nr_lists=""
_discover_neighbors
_append_own
for value in ${radios}
do
ubus call ${value} bss_mgmt_enable '{"neighbor_report": true}'
eval "ubus call ${value} rrm_nr_set '{ \"list\": [ ${rrm_nr_lists:1} ] }'"
done
sleep 60
IFS=$OIFS
done
exit 0
Still have the problem on startup. It appears to be that my 5 GHz radio is doing the CAC and is not listed when you enumerate the radios in the init script. The 2.4 GHz radio is fine.
I've made a couple of changes to make it work but you might want to find a more elegant way.
One thing I have noticed (but doesn't interfere with the operation) is when using a couple of iPhone bonjour browsers, they can't cope with the "nr" protocol. One crashes completely and the other just refuses to display the published neighbours.
#!/bin/sh /etc/rc.common
START=90
USE_PROCD=1
start_service() {
local rrm_own
OIFS=$IFS
IFS=$'\n'
sleep 120
radios=$(ubus list | grep hostapd.wlan)
for value in ${radios}
do
rrm_own=${rrm_own}",$(ubus call ${value} rrm_nr_get_own | jsonfilter -e '$.value')"
done
IFS=$OIFS
procd_open_instance
procd_set_param command /bin/sh "/etc/scripts/rrm"
procd_add_mdns "rrm" "nr" "" "${rrm_own:1}"
procd_close_instance
}
service_triggers()
{
procd_add_reload_trigger wireless
}
and
#!/bin/sh
_discover_neighbors() {
local rrm_neighbors
ubus call umdns update
sleep 2
rrm_neighbors=$(ubus call umdns browse | jsonfilter -e '@["_rrm._nr"][*].txt')
for value in ${rrm_neighbors}
do
rrm_nr_lists=${rrm_nr_lists}",${value}"
done
}
_append_own() {
radios=$(ubus list | grep hostapd.wlan)
for value in ${radios}
do
rrm_nr_lists=${rrm_nr_lists}",$(ubus call ${value} rrm_nr_get_own | jsonfilter -e '$.value')"
done
}
while true
do
OIFS=$IFS
IFS=$'\n'
rrm_nr_lists=""
_discover_neighbors
_append_own
for value in ${radios}
do
ubus call ${value} bss_mgmt_enable '{"neighbor_report": true}'
eval "ubus call ${value} rrm_nr_set '{ \"list\": [ ${rrm_nr_lists:1} ] }'"
done
sleep 60
IFS=$OIFS
done
exit 0
Also, just in case you don't notice it, I put the rrm script in /etc/scripts on my APs as I back that up on system upgrades (see the procd_set_param_command line in my version of the first script above). You may want to change it back in your version.
For hostapd, you'd (probably) have to upstream it first before OpenWRT will accept it i.e. contact and attach the patch to https://lists.infradead.org/mailman/listinfo/hostap. @hauke will probably be the reviewer once it's upstreamed and you open up a PR backporting the patch to OpenWrt.
Thanks a lot. I'm only exposing the values through ubus allowing me to collect them by another daemon to do fancy hearing map stuff. Sadly, I have to use the Nexus 5X of a friend to develop and test. Is there maybe some cheap device I could use for this?
Hi @PolynomialDivision
Yes, I saw what you did with the beacon responses - that was good stuff.
I think there is some improvement yet to make on this. The sleep 120 in my version blocks the restart on the init script when done from Luci which in turn causes a webpage timeout. It would be better to find some more elegant way to refresh things when radios come and go - maybe something using "procd_set_param watch hostapd". I'm not across how this works so will need to leave it to better programmers than me. Also, the sleep 120 deals with the normal CAC of 60 seconds for DFS channels but there are some channels (5600-5650MHz sub-band) where the CAC is 10 minutes.
Also, the change from_nr to _udp did fix the problem on the mdns browsers.
Now steer a client from wlan0 to wlan1 or or the other way around
ubus call hostapd.wlan0 wnm_disassoc_imminent '{"addr":"00:xx:xx:xx:xx:xx", "duration": 120, "neighbors":["[THE STRING OF THE NEIGHBOR REPORT FROM WLAN0 OR WLAN1]"]}'
I'm not sure if the client gets deauthed, or it switches the interface on it's own. I have to increase the debug level.
Thanks, this script is working (modded it to include udp instead of nr). I can see a successful Neighbor Request and Neighbor Response on ath10k-ct-htt, hostapd-2.9 with openssl. The setup includes 2x Archer A7s Dumb APs with a WRT1900ACS router (disabled wireless functionality) β all three have OpenWrt built from the master branch.
Additional roaming settings were: 802.11r with FT-over-the-Air, 802.11v with BSS transition, 802.11k with list populated by your script.
Because when the script staring, the radio may not started yet. So the umdns can't broadcast the local AP. I always restart the script after all radios have started.
If you add a sleep in the script to wait radios, LUCI may report timeout in the startup page as @muddyfeet said.
I discovered that devices sometimes roam, and somtimes not...
I upgraded my controller DAWN, to use disassoc_imm... function, instead of deauthing clients.
But I think, something is still broken. I have to look into this again, when I have time. And I call ubus methods on each interface. That's not good...