Finally got 802.11v working (FULLY) - Few pointers

So before I begin, I am doing this on my mini PC running Debian up stairs... Not my OpenWRT boxes (living room floor x 1, downstairs entrance x 1). Those will be next.

But it all applies, since I have checked and assuming you are running hostapd full on OpenWRT, it will all work. Im running hostapd-ct upstairs (google search hostapd-ct, reason for this below). Note, I am no wifi expert... Although I have close to two decades of linux experience, so it all went quite quickly...

It's going to be a bit scattered, but it works... I am able to jump from 2.4ghz to 5ghz, and from 5ghz to 2.4ghz at will.


A) Hostapd-ct... The only reason for this is because hostapd-ct's version of hostapd_cli has a command to pull the neighbour report (long string) for your current adapter(s).

show_neighbor = show neighbor database entries

Super helpful, but openwrt's ubus has rrm_nr_get_own which will give you the string so this is not needed on an openwrt + openwrt setup.

B) Populate the neighbor report list fully. Both adapters (5 and 2.4) should contain ALL of the neighbors you wish to jump between. This was the first issue I ran into... Like I said, I don't know much about 802.11 internals... For whatever reason, initially I just populated my 5ghz on my 5ghz adapters, and my 2.4ghz on all my 2.4ghz adapters... Nope, fill them all fully. Essentially on all your adapters, the neighbour list should be the same (full). UNLESS you do not want (like me) the downstairs access point to roam to the upstairs router since there is a router in between. That should make sense.

C) If you are using 802.11r, which you probably are since thats usually everyones first step into 'better roaming', you need to make sure to use ft_over_ds and have rsn_preauth=1 rsn_preauth_interfaces set correctly. Just a pointer for those who dont know, hostapd will use rsn_preauth_interfaces as a 'back channel' to authenticate. So yes, not only does 802.11v work, I am about to FT between 2.4 and 5ghz. This was another issue... Since my mobile devices sit on 5ghz 99% of the time, I didn't bother setting up 802.11r on my 2.4ghz adapters... I could see the BSS working when sending the 802.11v requests, but it would NOT authenticate to the opposite frequency (2.4 to 5, 5 to 2.4)... I couldnt figure out why... Some debugging logging showed that hostapd was failing to authenticate because it was trying to FT and unable to do so... After I enabled 802.11r on my 2.4ghz band, presto.. Works!

D) The commands I am using to force a jump from one freq to the other are:

hostapd_cli -i XXXX bss_tm_req AA:AA:AA:AA:AA:AA neighbor=-BB:BB:BB:BB:BB:BB abridged=1

XXXX = The adapter you wish to switch FROM.
AA:AA... = The MAC of the client you want to transfer over.
BB:BB... = The MAC of the access point you want to EXCLUDE from possible access points where it will roam to.

NOTE: For samsung mobile devices (possibly all of android?) abridged=1 is a STRONG hint that the roam should happen. It states that the access points are part of the same network and gives it 'higher weight'. Note to developers, maybe it would be helpful to expose this via ubus?

NOTE : There is a way to pass a target AP via the neighbour= parameter... But I am thus far unable to figure out how to do so for the 2.4 to 5ghz band... The syntax is (as best I can tell, from the hostapd source):

[TARGET AP MAC],[OP CLASS, as far as I can tell, 2.4ghz=81, 5ghz=122],[CHANNEL],[PHY TYPE, as far as I can tell 7=2.4ghz,9=5ghz],[NEIGHBOUR REPORT LONG STRING]

But like I said, this works for my 5 to 2.4, but not the other way around... Still unsure why...

That's about it, I have this running on my dated Samsung S7 Edge BTW... Switching bands is nearly instant and I don't even notice it on the mobile... Really fast band steering pretty much like one would expect it to be.

Feel free to ask questions sorry for the scattered post, but I'm quite excited about this... Going to bang out a little bash script now to check rssi's and do this on the fly for me... We have a wifi baby monitor and I find it annoying that when I watch it as I move floors it pauses for about 5-10 seconds sometimes requiring an app restart... First world problems :wink:


Is it fully automatic or user action required?

Yes, fully automatic.

Sorry, have you got this working both ways or not? You've contradicted yourself in a few places. Just trying to understand.
A very interesting post nonetheless, and bookmarked for me to come back to.

1 Like


Hi, I'll summarize what I got working so far:


  1. Populate the 802.11k neighbor list with possible roam locations (both 2.4 and 5ghz bands). Note: So on a single router, dual adapter setup each adapter should have a neighbor list that contains 2 neighbors. It self, and the opposite band.

  2. Be close enough to the AP where a connection to the 5ghz band will be possible.

  3. Use the hostapd_cli command in the original post to tell the client to roam to "any AP in the 802.11k neighbor list EXCEPT FOR ____ (notice the minus in front of the neighbor= mac)".

This way the device will roam from 2.4 to 5. Works the other way too, 5 to 2.4.


  1. For 5 to 2.4ghz, use the hostapd_cli command to specify a specific AP (neighbor=MAC,OP_CLASS,CHANNEL,PHY_TYPE) to roam to. Note, so in this case you will be specifying a 2.4ghz AP (OP_CLASS=81, PHY_TYPE=7).

Not Working:

  1. For 5ghz, use the hostapd_cli command to specify a specific AP (neighbor=MAC,OP_CLASS,CHANNEL,PHY_TYPE) to roam to. Note, this is probably because there is an issue wit the OP_CLASS / PHY_TYPE. I pulled those from a post here:


But in my case, the client device does not respond to the roam beacon at all when doing 2.4 to 5. I checked this by running hostapd with the -dd flags and watching the output. When done successfully hostapd will report (among the huge stream of debug info) that the roam request was successful. When I try it with 2.4 to 5ghz, the client doesn't respond to the roam request at all. Silence. Stays connected as is.

Things I have noticed

I got my script banged out for the most part last night, and I noticed a few things.

a) RSSI is a so-so indicator to determine how far the device is from the AP. When the device is in sleep mode (my S7 Edge for example), after a few seconds the wifi will sleep for 10-15 seconds so the RSSI will remain static (for example, -50, right next to my router) even though I have walked away from the AP. Once again, not a wifi internals expert here but I believe there is a way to send a beacon via hostapd and get the client to update their state. ( req_beacon [req_mode=] = send a Beacon report request to a station ). I will look into this later on.

b) The client device (once again, S7 edge in my case) does not like being asked to roam TOO often. Running the hostapd_cli command too many times in sequence causes my S7 to disconnect and switch to LTE.

c) Important, both of the above ONLY APPLY to a sleeping (screen off) device. Screen on? RSSI is updated every second and I can fire off as many roam requests as I wish and the device remains connected.

d) For now what I have up and running... And I understand people want to steer 2.4 to 5ghz, so its not quite that... But what I have setup and running via 2 scripts (one running on the OpenWRT in the living room, one running on my Hytsou H5 mini PC upstairs) is basically when RSSI reaches 70 or less, trigger a roam. For my use case this is perfect. I am able to watch my baby monitor now without any hicups. I get to the bottom of the stairs and RSSI will hit over 70, it'll trigger a roam, and connect to our living room AP. All transparently, without any stop in video or having to restart the (slightly buggy) baby monitor app.

I'm going to figure out the syntax for the "steer to a specific neighbor" and will post here... Thats pretty much the missing piece...

If anyone knows this please post :slight_smile:

Also if anyone knows how to use hostapd_cli to send a beacon to the client device (and get an updated RSSI in return) please post :slight_smile:


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.