Finally got 802.11v working (FULLY) - Few pointers

Small update, I now know how to update the RSSI of the device at will... This takes care of the issue of having a stale RSSI (when my phone screen is off, sleep mode, this can be delayed up to 15 seconds)...

Note, this is probably horrible for my battery life :wink:

hostapd_cli - sa_query = send SA Query to a station

I have this set in my script for 2 seconds:

Every 2 seconds check if the device has been inactive for 2000ms or longer?

If so, send a sq_query packet.

The delimma with 2.4 and 5ghz is that the radio physics are different. Roaming between two APs with two different radio bands makes it difficult because you can’t ideally place an AP for both bands, you either pick one band or you find a compromise location.

Share your script once you have it in good shape. Myself and I bet many others would love to have an automatic 802.11v script. I’m running two r7800s on hynman’s latest master build. Personally all my faster devices are wired or 5ghz only. My two APs were positioned based off of 5ghz RSSIs for a clean roam. All my clutter of smart devices are on 2.4ghz only.

Having the idiot proof 802.11r toggle in luci has been great. Hope your work produces a similar result for 802.11v!


Whats working 100% now is (I live in a 3 floor home):

RSSI fairly fresh, 2 seconds or less. We'll see how this affects my battery life.

Upstairs -> Middle Floor

Force a roam when RSSI gets to 75 (close to the bottom of the stairs), let the client decide where to roam to but use BSS + 802.11k so its quite a bit smoother then deauthing. At this point it will 99% roam to the 2ghz band but on the proper floor.

Once on the middle floor, check 2ghz RSSI every second, once it gets to 60 or less, force a roam via BSS. The client is fairly good at picking the 5ghz band if the signal is close enough to the 2ghz band.

Middle floor -> Upstairs

Essentially the same thing as above.

The client tends to pick the 2ghz band when I reach the point where the middle floor AP is @ 75 RSSI or greater, but the upstairs AP script takes over and starts to check for RSSI 60 or less and asks for a roam (via BSS again) when 60 or less, it switches to the 5ghz band quickly.

Essentially in my case what this is all doing is giving me greater control when to notify the client to roam.

And since its all done via BSS with the neighbor list (802.11k) the transitions are very quick. From what I saw when doing it via deauths is the client device would drop out completly and reconnect within a second or so. This is seamless.

We had the kiddo sleep for 2 hours today (nice!) and I did a bunch of walking between floors to test and found for the most part the hiccups I was experiencing in the baby monitor app were all but gone.

Just need to polish the script a bit more and will post it online when I feel its ready. The main thing now is not to keep asking for a roam when the previous one yielded no change (eg: when I exit onto the patio).

Hostapd when configured with the proper build and runtime conf does dump the BSS reply from the client, which contains target_bssid... So maybe I will watch for the in the logs to see if the device actually roamed or not.

Will keep this thread up to date with my progress.

Thanks for this, I've been wanting to get ft_over_ds instead of "over the air" working for a while now and setting rsn_preauth to 1 actually helped (rsn_preauth_interfaces was also automatically added, with br-lan as the network interface).

Here is some more clarity, and everything working 100%. I was wrong though...

The minus in front of the target mac IS NOT to exclude it from the possible candidates. Its to specify it as the target bssid candidate.

And is only available in the hostapd-ct release, although the patch is 2 weeks old so it'll make its way into OpenWRT eventually.

Here is a basic dump for all to see... It works flawlessly... 2.4ghz to 5.8ghz, vice versa.

I'll leave it at this, if you want true 802.11v someone needs to port over hostapd-ct (I'll probably do that one my self) and integrate it all into OpenWRT... That I do not have time for... I will however fix up my script and post it here once its running 100%...

(wlan0 = 2.4, wlan1 = 5.8)

root@UPSTAIRS:~/# iw dev wlan1 station get [My S7 MAC ADDR] | grep bit ;
        tx bitrate:     866.7 MBit/s VHT-MCS 9 80MHz short GI VHT-NSS 2
        rx bitrate:     866.7 MBit/s VHT-MCS 9 80MHz short GI VHT-NSS 2
root@UPSTAIRS:~/# /usr/sbin/hostapd_cli -i wlan1 bss_tm_req [My S7 MAC ADDR] neighbor=-[My 2.4ghz AP MAC ADDR] abridged=1 pref=1 ;
root@UPSTAIRS:~/# iw dev wlan0 station get [My S7 MAC ADDR] | grep bit ;
        tx bitrate:     144.4 MBit/s MCS 15 short GI
        rx bitrate:     130.0 MBit/s MCS 15
root@UPSTAIRS:~/# /usr/sbin/hostapd_cli -i wlan0 bss_tm_req [My S7 MAC ADDR] neighbor=-[My 5.8ghz AP MAC ADDR] abridged=1 pref=1 ;
root@UPSTAIRS:~/# iw dev wlan1 station get [My S7 MAC ADDR] | grep bit ;
        tx bitrate:     866.7 MBit/s VHT-MCS 9 80MHz short GI VHT-NSS 2
        rx bitrate:     866.7 MBit/s VHT-MCS 9 80MHz short GI VHT-NSS 2

Hi! I am using 802.11r and would very much like to know more about 802.11v. Would you mind answering couple of short questions?

  1. Do I gain something with 802.11v if I already use 802.11r and have 5GHz AP's only?
  2. If installing hostapd, can hostapd-common be removed?
  3. is hostapd-ct a must if I want to try 802.11v (as far as I can see, there is no such package for Archer C7 v2 target)


Do I gain something with 802.11v if I already use 802.11r and have 5GHz AP's only?

Yes, but you need a script / software written on your own, as far as I know nothing is available yet. Assuming your client device supports it (most android and iOS devices do for example), you gain the ability to steer the client to a specific access point or to initiate a roam. I am using this to check if the RSSI is 80 or worst, which is close to the limits and to force the client to do a roam. Quite often its sooner then the device would have decided to do so on its own.

Although not applicable to you, I check if the RSSI is 65 or less, and if it is and the client is on 2.4ghz, I force it to switch over to the 5ghz band.

If installing hostapd, can hostapd-common be removed?

Not 100% sure but I would assume that hostapd-common is common so probably required.

Is hostapd-ct a must if I want to try 802.11v (as far as I can see, there is no such package for Archer C7 v2 target)

Probably not, I see lots of the hooks available via ubus but since I work out of my office upstairs where I have a Debian mini PC doing my wifi and routing, I have been setting all of this up on a raw hostapd setup up here.

My setup thus far (2 days in, doing it when I have free time, which is not often) seams to be an improvement over what I had before. Not even steering to a specific AP, but just having the ability to signal to the cellphone that he should try to roam has improved things here quite a bit. Like I said above, no more disconnects from my baby monitor when I move between floors.

And as an added bonus, I now have my 2.4ghz band unblocked for my mobile phone (this is how I was forcing it to use 5ghz before)... So this alone makes 'initial' connections or when ever the client does roam on its own faster as it avoids to try -> failed, try next step in the connection flow. Yeah I know, this is probably cached somewhere on the client device but this feels much cleaner. Connect to 2.4ghz, when the signal is good, switch him over to 5ghz.

OK, so as far as I understand it, you are basically running a scheduled script that polls client RSSI and if it is too low, trigger an enforced roam to some of devices in the list, is this correct?

How often is this script run?

Essentially yes, but the script is not scheduled its running all the time. Its pooling the RSSI via iw every 0.5 secs and acting on that.

So far I have a bit more then that going on, eg: wait until the client has been connected at least 10 seconds before doing anything. Try not to force a roam TOO often, try to force an RSSI update if its been 2000ms or more since the last one, etc etc etc....

I have made quite a bit of progress on this, I now know the hostapd_cli command syntax.

802.11v works 100% as long as you are ready to setup your own script.

The syntax for the bss_tm_req neighbor field is:


I am able to jump between access points at will. I can move my Samsung S7 to the downstairs 2.4ghz band from my desk in my office even though there is a 2.4ghz access point 1 meter away from me.


Also, for a list of op classes:

They are listed in the file but for me:

81 = All my 2.4ghz access points
128 = All my 5ghz access points (running on channel 36 at 80mhz)

Also, the phy_type's I am using are:

2.4ghz = 7
5ghz = 9

Also, what I found works best is to roam between access points at 2.4ghz (that is, neighbor=some 2.4g access point), and on the roamed-to access point, switch over to 5ghz when rssi is acceptable.

Also, important, you can specify multiple neighbor=... This becomes up to the device but it SHOULD pick the best choice... I have 4 access points in my home, so for example the middle floor has an option (depending on where I am walking) to roam to upstairs, downstairs or when I am on my patio the laundry room ap (simple TPL 902AC in repeater) picks up best... Its not 100%, but I try to order the neighbor= elements in the 'most likely choice' order... It tends to work quite well.

Just a short update... I have it working pretty much flawlessly.

To make it work 100% one needs to use req_beacon (hostapd_cli, there is a ubus hook for it though), mode 1, and it will send back a beacon report to the AP.

Once you have captured the beacon report and fed it back into your infinite loop script, you can act based on that and send out a BSS-TM request if necessary.

Note : This works quite a bit better on QCM based adapters... On my mediatek based repeater on my middle floor, it would roam to or from it no problem, but there was always a bit of a pause during authentication (even with 802.11r)... I had a mediatek based RE650 there, but had an older RE450 in the drawer which is QCM based... So I swapped it out and its flawless.

Essentially you need to have 2 scripts:

A) One that receives the beacon report, executed via hostapd_cli -a SCRIPTNAME. Save the beacon report somewhere, I chose in /tmp.

B) The main script which besides sending out BSS-TM requests when you deem necessary, but also sends OUT the beacon report requests and waits for a response (in my case, to be saved into /tmp/....).

Yeah its complex, but once its all together not that bad...

Once its all polished I will put my script up on github with a quick wiki how it works.

And finally here is the log of 3 of my APs working together as I go from upstairs to down stairs and back up.


2020-03-13 13:55:51 | Sent AP-STA-CONNECTED [My S7 Edge MAC] wlan0 events
2020-03-13 13:56:14 | Candidate [Middle floor AP MAC] has rssi of 59, forced roam via hostapd_cli -i wlan0 bss_tm_req [My S7 Edge MAC] neighbor=[Middle floor AP MAC],0x0000,128,36,9 pref=1 abridged=1 2>&1 [OK]


2020-03-13 13:55:42 | Sent AP-STA-CONNECTED [My S7 Edge MAC] wlan0 events
2020-03-13 13:55:51 | Candidate [Ground floor AP MAC] has rssi of 59, forced roam via hostapd_cli -i wlan0 bss_tm_req [My S7 Edge MAC] neighbor=[Ground floor AP MAC],0x0000,128,36,9 pref=1 abridged=1 2>&1 [OK]
2020-03-13 13:56:15 | Sent AP-STA-CONNECTED [My S7 Edge MAC] wlan0 events
2020-03-13 13:56:28 | Candidate [Upstairs floor AP MAC] has rssi of 63, forced roam via hostapd_cli -i wlan0 bss_tm_req [My S7 Edge MAC] neighbor=[Upstairs floor AP MAC],0x0000,128,36,9 pref=1 abridged=1 2>&1 [OK]


2020-03-13 13:55:41 | Candidate [Middle floor AP MAC] has rssi of 65, forced roam via hostapd_cli -i wlan1 bss_tm_req [My S7 Edge MAC] neighbor=[Middle floor AP MAC],0x0000,128,36,9 pref=1 abridged=1 2>&1 [OK]

Final note, at least with my S7 Edge... The beacon report responses are pretty much instant. Even when scanning the opposite frequency... They are 'live' as well... I'm sure this is horrible for battery life, but I have it set up so it only sends out the beacon requests when the RSSI reported by iw dev wlanX station get is low enough to warrant a roam. Which, when the screen is on anyways, is updated every xxx ms, so it is fairly fresh.

So there it is, 802.11v band steering / assisted roaming, 100% working on 2xOpenWRT + 1xDebian (errr... hostapd to be quite honest...)...


Congrats for the working environment!
I am looking for the same solution. Have you managed to publish the scripts on github?

Looking forward.

@PolynomialDivision ping

@LGA1150 Thanks for pointing me to this thread.

Since, I have to stay at home, I just began to go further with the stuff I already did.

Please, up-vote my PR.

Further, I wrote DAWN. And now I start to work on this issue. So I just fix and refactor my daemon to get things into official openwrt repository.

On some branch, I already exchanged neighbor reports automatically, and implemented bss transition. I will go on with it, after bringing the legacy version into the package tree.


Hi all, I will post my script in a short while (2-3 days).

A couple of notes, just so everyone is clear.

A) Its written in PHP. I am most fluent and things just go quickly for me in PHP so I chose PHP. I know its not optimal for OpenWRT but someone can port it fairly easily if they desire. Knowing I would be sharing it I have commented it heavily.

B) My script is sort of tailored to my setup here at home, although the important settings are fed via a config file.

As previously mentioned, but I will mention again:

I live in a 3 floor home that is concrete.
Each floor has an dual band AP that is wired via ethernet to a switch downstairs.

What does this mean? That on each floor, I have a good strong 5ghz signal from that floors AP.

So the name of the game is fairly straight forward:

  1. If the client device has a strong RSSI at 2ghz, then roam to 5ghz via BSS-TM ASAP. This will happen quickly, under 1 second after a strong RSSI is detected.

  2. When the 5ghz signal starts to drop below a set value (-65 dbi is what I use for this), start sending out beacon requests and see if we can roam to a 5ghz AP on a different floor. Once the connected to AP is at -70dbm, but another candidate has a better signal, roam over to the stronger AP. This works in my case because even at 5ghz there is always some overlap with my 5ghz access points.

This works very well, about 99% of the time my script will steer the device as I am walking. I am able to have skype video conversations jumping between APs with zero cut outs or lag or stalls. The only time it doesn't work is if the client device is more aggressive in the roaming then my script.

I have 2 spots in my home where sitting on 2ghz is optimal. I leave that to the client to decide on its own. On my patio and terrace upstairs I just let the phone roam on its own from 5ghz to 2ghz there.

C) Initially I had this all setup so that even when the screen was off on the mobile device it would steer the roaming. It is possible to keep the wifi adapter awake on my S7 Edge (probably most Android devices) by sending a pool request. In the end I opted not to do this. No point in wasting my battery. I check if the device has the screen on AND is actively using wifi by checking the inactive field in the

iw dev wlanX station get CLIENTMAC

dump. If its over 1 second, I skip the loop and check again, until its under 1 second.

D) NOTE : This works best with Qualcomm based (ath9k / ath10k) adapters. As mentioned somewhere above I have a Mediatek based RE650 as well... It appears that the chipset in that device (all mt based devices?) does NOT bridge the wireless and ethernet ports correctly and the broadcast packets are dropped / somehow not handled fully on roam. I am unsure as to the cause / solution to this as I just swapped it out for my Qualcomm based RE450 and off I went.

E) Of course you will also want to setup 802.11r to make the auths quicker. In my case 802.11r works even between bands (2.4 <-> 5.8). While you are doing it might as well get 802.11k done as well for the times you want the device to roam on its own.

Will post in a couple days when its up on github.


Okay, I made a PR.
I will add the luci-app-dawn after the controller is accepted, cause I did a workaround to have that in a sepertaed feed.

Maybe I just upload some ipkg to github.


Alright, I have posted what I use on github.

Like I said, its not perfect but for my use case it works great. I documented as much as I had time to, heavily commented it and assuming you have a fairly standard setup on OpenWRT, you should be able to get it up and running.


When initially documenting things I was rushing, so there were a few (small) errors here and there. I had a few minutes now to clean it up so it should be a little more clear.

I packed my daemon.
Install instructions to add my feed into opkg:

Currenty only mips_24kc.

1 Like