How does rrm work?

Hello friends,
As per 18.06 logs , 802.11k and 802.11v are introduced in latest version of OpenWrt.I found rrm fields in ubus hostapd.wlan* list. Can anyone brief me how to use them and how do they work?

"bss_mgmt_enable":{"neighbor_report":"Boolean","beacon_report":"Boolean","bss_transition":"Boolean"}
   "rrm_nr_get_own":{}
   "rrm_nr_list":{}
   "rrm_nr_set":{"list":"Array"}
   "rrm_beacon_req":{"addr":"String","mode":"Integer","op_class":"Integer","channel":"Integer","duration":"Integer","bssid":"String","ssid":"String"}
   "wnm_disassoc_imminent":{"addr":"String","duration":"Integer","neighbors":"Array"}

Hi there

I have 2 x dual band access points located in the house (tplink archer C7). I'm using 802.11k neighbour lists sourced via the script below.

This script runs on each AP via a cronjob every 5 minutes and polls the other AP and itself for the neighbour information which is then populated into the neighbour list. Both APs run the same SSID on both bands. Obviously the remotehost variable points to other AP. The script is clunky but does the job for my situation. SSH keys are setup on each AP to allow access from each other.

Hope this helps.

remotehost="192.168.5.20"
nljson="'{ \"list\": [ "
nljson=$nljson"$(ubus call hostapd.wlan0 rrm_nr_get_own | jsonfilter -e '$.value'),"
error=$?
nljson=$nljson"$(ubus call hostapd.wlan1 rrm_nr_get_own | jsonfilter -e '$.value'),"
error=$(( $error + $? ))
nljson=$nljson"$(ssh root@$remotehost ubus call hostapd.wlan0 rrm_nr_get_own | jsonfilter -e '$.value'),"
error=$(( $error + $? ))
nljson=$nljson"$(ssh root@$remotehost ubus call hostapd.wlan1 rrm_nr_get_own | jsonfilter -e '$.value')"
error=$(( $error + $? ))
nljson=$nljson" ] }'"
if [ "$error" == 0 ]; then
	ubus call hostapd.wlan0 bss_mgmt_enable '{"neighbor_report": true}'
	error=$(( $error + $? ))
	ubus call hostapd.wlan1 bss_mgmt_enable '{"neighbor_report": true}'
	error=$(( $error + $? ))
	eval "ubus call hostapd.wlan0 rrm_nr_set $nljson"
	error=$(( $error + $? ))
	eval "ubus call hostapd.wlan1 rrm_nr_set $nljson"
	error=$(( $error + $? ))
else
	ubus call hostapd.wlan0 bss_mgmt_enable '{"neighbor_report": false}'
	error=$(( $error + $? ))
	ubus call hostapd.wlan1 bss_mgmt_enable '{"neighbor_report": false}'
	error=$(( $error + $? ))
fi
if [ "$error" == 0 ]; then
	logger "SUCCESS: 802.11k neighbours set ok"
else
	logger "ERROR: 802.11k neighbours not set"
fi
1 Like

Thank you for the reply @muddyfeet.
do we need to add bssid,ssid and nr everytime? isnt any way who find neighbor ap and add it auto?
after adding this how can we verify its effect on bss transition ?

Apologies if you understand this already but for anyone else reading...

The neighbour lists in 802.11k are requested by the client via a neighbour request (if it supports 802.11k and when and if it feels like it). They are used primarily to narrow down the list of available APs to scan when it is evaluating a handover to another AP - it speeds up the scanning but not the actual handovers. The 802.11r standard shortens the actual handover time - 802.11r is built into the Luci GUI under the Wireless Security tab.

There is nothing built into Openwrt or in the 802.11k standard to handle the construction of neighbour lists.These need to be built by either a AP controller (have a look at the OpenWisp or DAWN - these do not yet support 802.11k) or via discovery using a method like what I have done.

The neighbour lists must contain BSSID, BSSID information, Operating class, channel number. Have a look at https://blogs.cisco.com/wireless/why-the-802-11k-and-neighbor-report-are-important

If you run "ubus call hostapd.wlan0 rrm_nr_get_own" from an Openwrt command prompt you can see the format of the information stored.

If the above script is run and a neighbour list populated you can see the list by running "ubus call hostapd.wlan0 rrm_nr_list"

Change wlan0 to match your wlan interface names.

As for evaluating the performance, you will need to do an on air capture (using an external tool or iwcap on Openwrt) and run it through wireshark, and hope you observe a client requesting the neighbour report from the AP in the pcap file.

802.11v can be used to force a bss transition but will require a controller to manage each client attached to the AP. The DAWN controller implements this based on AP channel utilization.

My script is just a hack to work with a small number of APs (2 in my case) where I don't mind manually editing each version on the different APs.

@muddyfeet Thank you so much for the reply!
It helped me a lot. :grinning:

Hi, I stumbled upon this while searching on how to get 802.11k working.
What else except full wpad and 802.11v does need to get configured?
I get weird 167 error and when manually trying the same command it does not spit any error at all.

I'm using wpad-basic. There's nothing special required. The script will require certificates for ssh login to be installed on each access point. I'm guessing the 167 error is related to that. The following link might be helpful to set all that up.

https://yorkspace.wordpress.com/2009/04/08/using-public-keys-with-dropbear-ssh-client/

Hm, but I manually started the script and entered the password.
Will give setting certificates a go.

Wow. Thanks for your script. :slight_smile: I will look at this.
How do u use this script? Actually the most important information is the RSSI of the clients to the roaming target. And that is very complicated. :S I thought 802.11k would help by sending a request from AP to client, that it should scan another channel. But you do the opposite? So sending the client information about other APs. But with only 2radios, the AP can not know, if the client has sufficient signal strength to connect to the others.

I read this Cisco stuff. Cisco devices have often a 3rd radio to allow to scan through all other channels to get the signal strength information to other clients. So the controller knows everything.

Hi there

All this script does is just collect a list of what neighbour APs the client should look at for a handover. It is still up to the client to go and measure the RSSI of those neighbours and make it's own decision. The benefit of the neighbour list is that it means the client doesn't have to scan all 2.4 and 5 GHz channels, just the 3 channels provided by the script above - saving time and battery life.

The script runs via a cron job every 5 min on each of the 2 dual channel APs that I have. For example with the 2.4GHz channel on AP1 it will populate a neighbour list with the 5GHz channel on AP1 and the 2.4GHz and 5GHZ channels on AP2. It also turns on the neighbour reporting. Each AP has ssh access to the other AP.

If you never change the frequency of the APs you could just run the commands manually and set the neighbour lists without the cron job - they shouldn't change (except for restarts etc.)

The 802.11k standard also has the client RSSI reporting in it - that's the beacon reports. As there is no controller involved it is pointless doing these. If there is a controller it can can use 802.11v bss transition to direct a handover on the basis of these beacon reports.

Bundling the neighbour reports + beacon reports + 802.11v bss transition together you end up with something close to what happens on mobile phone networks with sensible handover decisions (I'm a mobile network engineer). But to do that you need a controller which there isn't one yet for openwrt and I'm not a software engineer as you can tell from the bodgy code above, so I'm not volunteering to write one!

Hope that is helpful.

Cheers

Hi, @muddyfeet, @PolynomialDivision

I'm very interested in this topic and I have an idea. I read about the docs of ubus and rpcd and I want to do these steps below.

Supose I have 3 APs, one as a contoller AP and others as general APs.

  1. Add an acl config file on the general APs for rpcd (in /usr/share/rpcd/acl.d) and let superuser can access ubus hostapd.wlan* methods.
  2. Install package uhttpd-mod-ubus on general APs.
  3. Add a config file on the contoller AP (e.g: ap.config in /etc/config), and provide username/password for each general APs.
  4. Use a bash script on the master AP to login to each slave AP and call ubus method rrm_nr_get_own() via rpcd, and also call it on local, and generate the neighbour list. Then set on each AP.
  5. Maybe can make a luci app to set username/password for each general AP and shows their info.

How do you think about it?
Thanks.

Was exactly how I was thinking that this should be implemented - just didn't have the time to figure it all out. Hence the clunky script.

Another way might be to use mdns to publish the results from rrm_nr_get_own that other APs can discover and create a neighbour list from.

I think this would be a great addition to Openwrt.

Happy to be a tester if you get round to creating something.

Using mdns is a good method, I've tried it, but failed. I don't know why. Below is my scirpt.

#!/bin/sh /etc/rc.common

START=90

USE_PROCD=1

start_service() {
	local rrm_own
	local radios

	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
	
	procd_open_instance
	procd_add_mdns "rrm" "tcp" "" "value=${rrm_own:1}"
	procd_close_instance
}

service_triggers()
{
    procd_add_reload_trigger wireless
}

exec

ubus call service list | jsonfilter -e '@[*].instances[*].data.mdns'

can see the umdns advertise but the

ubus call umdns browse

didn't show it.

Mybe something is wrong in my script. And I'm not sure whether the port number is necessary.
Does it need a daemon running in the background?

I've had a play with your init script above. The change below makes it work.

procd_open_instance
procd_set_param command /bin/sleep
procd_append_param command 10 
procd_add_mdns "rrm" "tcp" "" "value=${rrm_own:1}"
procd_close_instance

Also ubus call umdns browse will only pick up remote services. Doesn't appear to pick up local ones.

Thank You!!! How many clients are supporting this? Then it comes to 802.11k and 802.11v legacy devices are so annoying... >.>

No idea how many clients are supporting these features. I've had no issue with any of my devices with 802.11r (FT) and 802.11k (Neighbour reports) - Microsoft, Apple, Android and various other odds and sods. Although Microsoft did break 802.11r support for a month with a driver update on my MS Surface in October.

This list might be helpful: https://clients.mikealbano.com/home

It's implemented in Windows 10 (subject to driver support), Apple devices since ios7, not sure about Android. See links below.

https://docs.microsoft.com/en-us/windows-hardware/drivers/network/fast-roaming-with-802-11k--802-11v--and-802-11r

https://support.apple.com/en-us/HT202628

And for Intel wireless adapters on Windows:

https://www.intel.com/content/www/us/en/support/articles/000021562/network-and-io/wireless-networking.html

The other complication as you probably know is that these standards contain many things. Plucking the list of features below from https://d2cpnw0u24fjm4.cloudfront.net/wp-content/uploads/WLPC-802.11kvr-Perry-Correll-wlpc-2019.pdf. Client devices may have implemented only some of the features yet advertise that they support 802.11k and 802.11v. However, it is likely that the ones we want are available in the most minimal implementation of them.

The good thing about the neighbour reports is the client can ignore them if it doesn't support the feature as it is up to the client to request a neighbour report from the AP, whereas the BSS transition originates from the AP to the client. This is where legacy devices are more likely to be a problem.

802.11k

• Neighbor Report
• Beacon (client measures and reports)
• Frame (AP counters)
• Channel Load
• Noise Histogram
• STA Statistics (STA counters)
• Location
• Configuration Information
• Link Measurement
• Transmit Stream/Category Measurement
•Transmit Power Control

802.11v:

• BSS Transition Management
• BSS Max idle period management
• Channel usage
• Collocated interference reporting
• Diagnostic reporting
• Directed multicast service (DMS)
• Event reporting
• Flexible multicast service (FMS)
• Location services
• Multicast diagnostic reporting
• Multiple BSSID capability
• Proxy ARP
• QoS traffic capability
• SSID list
• Triggered STA statistics
• TIM broadcast
• Timing measurement
• Traffic filtering service
• U-APSD Coexistence
• WNM-Notification
• WNM-Sleep mode

1 Like

Thanks @muddyfeet, I've made an update for my script.

#!/bin/sh /etc/rc.common

START=90

USE_PROCD=1

radios=$(ubus list | grep hostapd.wlan)

_broadcast_own() {
	local rrm_own

	for value in ${radios}
	do
		rrm_own=${rrm_own}",$(ubus call ${value} rrm_nr_get_own | jsonfilter -e '$.value')"
	done

	procd_open_instance
	procd_set_param command /bin/sleep
	procd_append_param command 60
	procd_add_mdns "rrm" "nr" "" "${rrm_own:1}"
	procd_close_instance

	rrm_nr_lists=${rrm_own}
}

_discover_neighbours() {
	local rrm_neighbours

	ubus call umdns update
	sleep 2

	rrm_neighbours=$(ubus call umdns browse | jsonfilter -e '@["_rrm._nr"][*].txt')

	for value in ${rrm_neighbours}
	do
		rrm_nr_lists=${rrm_nr_lists}",${value}"
	done
}

start_service() {
	OIFS=$IFS
	IFS=$'\n'

	_broadcast_own
	_discover_neighbours

	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

	IFS=$OIFS
}

service_triggers()
{
	procd_add_reload_trigger wireless
}
1 Like

Would u like to package that script?

Hi @sotux

Very nice! I'm really pleased someone with a bit more programming experience has had a crack at this. This is great. I'll have a play and let you know of any improvements I can think of.

Here's my first thoughts:

One thing that might be required if this is to be used more widely is to have a /etc/config file that contains WLANs to exclude from the neighbour reporting (if there is more than one on the local LAN). This will allow it to exclude guest WLANs etc. if that is required.

A simple tick box in Luci to turn on/off and a list of the excluded WLANs would really polish it.

You may not need the service trigger, an updated neighbour list will be created 60 seconds after any change. Although, if CAC is being done on a 5 GHz DFS channel it might delay the availability of channel information for a longer period - not sure how this will affect it but may mean the service trigger is needed.

Finally, it would be good to check whether there is a clash with avahi if that is installed as your script requires umdns which does the same function.

Good work!

Sorry @PolynomialDivision , I think this script is not perfect now. So I'll not package it.

I think the script should do a while for every 60s to check other AP whether they are changed or not and update the nr_list automatically. The script should do some error check also.