Proper configuration of 802.11k and 802.11v

Deleted post because my config also had the same problem.

I believe that the root cause is that the clients are not forced to scan other APs when the signal is still good enough. However, I can't recommend a concrete configuration.

cc @rowra

I tried running Dawn again and found myself frustrated with it, once again. It feels "heavy" and just doesn't seem to function well with my setup and/or devices. I kept noticing that some of my devices were appearing as associated with multiple APs simultaneously in the luci wireless view. Odd.

I moved back to Usteer again and actually made a more concerted effort to configure some additional settings. Based on lots of good feedback/configs from multiple other forum posts, I am currently running this config and it's working pretty darn well with my primarily Apple device household. This is the config on each of my three APs:

WIP Usteer Config
config usteer
	# The network interface for inter-AP communication
	option 'network' 'lan'

	# Log messages to syslog (0/1)
	option 'syslog' '1'

	# Disable network communication (0/1)
	option local_mode '0'

	# Use IPv6 for remote exchange
	option 'ipv6' '0'

	# Minimum level of logged messages
	# 0 = fatal
	# 1 = info
	# 2 = verbose
	# 3 = some debug messages
	# 4 = network packet information
	# 5 = all debug messages
	option 'debug_level' '2'

	# Maximum number of neighbor reports set for a node
	#option max_neighbor_reports 8

	# Maximum amount of time (ms) a station may be blocked due to policy decisions
	#option sta_block_timeout 30000

	# Maximum amount of time (ms) a local unconnected station is tracked
	#option local_sta_timeout 120000

	# Maximum amount of time (ms) a measurement report is stored
	#option measurement_report_timeout 120000

	# Local station information update interval (ms)
	#option local_sta_update 1000

	# Maximum number of consecutive times a station may be blocked by policy
	#option max_retry_band 5

	# Maximum idle time of a station entry (ms) to be considered for policy decisions
	#option seen_policy_timeout 30000

	# Minimum number of stations delta between APs before load balancing policy is active
	#option load_balancing_threshold 0

	# Minimum number of stations delta between bands before band steering policy is active
	#option band_steering_threshold 5

	# Interval (ms) between sending state updates to other APs
	#option remote_update_interval 1000

	# Number of remote update intervals after which a remote-node is deleted
	#option remote_node_timeout 10

	# Allow rejecting assoc requests for steering purposes (0/1)
	option assoc_steering 0

	# Allow ignoring probe requests for steering purposes (0/1)
	#option probe_steering 0

	# Minimum signal-to-noise ratio or signal level (dBm) to allow connections
	option min_connect_snr -70

	# Minimum signal-to-noise ratio or signal level (dBm) to remain connected
	option min_snr -70

	# Timeout after which a station with snr < min_snr will be kicked
	option min_snr_kick_delay 10000

	# Timeout (ms) for which a client will not be steered after rejecting a BSS-transition-request
	option steer_reject_timeout 10000

	# Timeout (in ms) after which a association following a disassociation is not seen
	# as a roam
	#option roam_process_timeout 5000

	# Minimum signal-to-noise ratio or signal level (dBm) before attempting to trigger
	# client scans for roaming
	option roam_scan_snr -55

	# Maximum number of client roaming scan trigger attempts
	option roam_scan_tries 2

	# Retry scanning when roam_scan_tries is exceeded after this timeout (in ms)
	# In case this option is set to 0, the client is kicked instead
	#option roam_scan_timeout 0

	# Minimum time (ms) between client roaming scan trigger attempts
	option roam_scan_interval 1000

	# Minimum signal-to-noise ratio or signal level (dBm) before attempting to trigger
	# forced client roaming
	#option roam_trigger_snr -60

	# Minimum time (ms) between client roaming trigger attempts
	option roam_trigger_interval 1000

	# Timeout (ms) for client roam requests. usteer will kick the client after this times out.
	option roam_kick_delay 5000

	# Minimum signal strength difference until AP steering policy is active
	option signal_diff_threshold 10

	# Initial delay (ms) before responding to probe requests (to allow other APs to see packets as well)
	#option initial_connect_delay 0

	# Enable kicking client on excessive channel load (0/1)
	#option load_kick_enabled 0

	# Minimum channel load (%) before kicking clients
	#option load_kick_threshold 75

	# Minimum amount of time (ms) that channel load is above threshold before starting to kick clients
	#option load_kick_delay 10000

	# Minimum number of connected clients before kicking based on channel load
	#option load_kick_min_clients 10

	# Reason code on client kick based on channel load (default: WLAN_REASON_DISASSOC_AP_BUSY)
	#option load_kick_reason_code 5

	# Attempting to steer clients to a higher frequency-band every n ms.
	# A value of 0 disabled band-steering.
	option band_steering_interval 5000

	# Minimal SNR or absolute signal a device has to maintain over band_steering_interval to be
	# steered to a higher frequency band
	option band_steering_min_snr -70

	# Interval (ms) the device is sent a link-measurement request to help assess
	# the bi-directional link quality. Setting the interval to 0 disables link-measurements.
	#option link_measurement_interval 30000

	# Script to run after bringing up a node
	#option node_up_script ''

	# Message types to include in log
	# Available types:
	# - probe_req_accept
	# - probe_req_deny
	# - auth_req_accept
	# - auth_req_deny
	# - assoc_req_accept
	# - assoc_req_deny
	# - load_kick_trigger
	# - load_kick_reset
	# - load_kick_min_clients
	# - load_kick_no_client
	# - load_kick_client
	# - signal_kick
	list event_log_types 'signal_kick'
	list event_log_types 'auth_req_accept'
	list event_log_types 'auth_req_deny'
	list event_log_types 'assoc_req_accept'
	list event_log_types 'assoc_req_deny'

	# List of SSIDs to enable steering on
	list ssid_list '<SSID 1 HERE>'
	list ssid_list '<SSID 2 HERE>'
	list ssid_list '<SSID 3 HERE>'

One really important thing I found is that I had always set assoc_steering to 1 per several recommendations from others. However, what I noticed last night is that setting being enabled was actually preventing steering from 2.4Ghz to 5Ghz for devices on the same physical AP (!!!). This, if I understand correctly, was due to Usteer assuming a stronger signal on 2.4Ghz is better than a lesser signal on 5Ghz.

For example:

usteer event=assoc_req_deny node=hostapd.wl1-ap0 sta=bc:d0:74:xx:xx:xx reason=better_candidate signal=-52 assoc=6 load=12 select_reason=signal remote=hostapd.wl0-ap0 remote_signal=-38 remote_assoc=1 remote_load=18

As soon as I realized that, I disabled assoc_steering (as you'll see in my config) and immediately devices that prefer 5Ghz (which Apple does) happily jumped off of 2.4Ghz and back to 5Ghz.

I'm anxious to see if others are willing to try this config and give feedback. Let me know!

3 Likes

Thank you for this update. Have you setup the above-described static-neighbor-report service with usteer? Is that necessary? Is there a way to check that usteer is actually doing anything since some reports I've read question that.

This has been a great thread so far. Enjoying reading all the posts on it.

My understanding is that it is unnecessary. I am basing it on the fact that ubus call usteer remote_info happily returns all of my APs/SSIDs. Further, for each of the results, the output includes "rrm_nr" which lists the BSSID, SSID, and Neighbor Report value.

Also, based on this, https://github.com/openwrt/usteer#functions, I am [perhaps naively] assuming "Synchronization of Neighbor Reports between multiple APs" is fulfilling the role that static-neighbor-report would otherwise play.

If anyone knows that static-neighbor-report is actually needed with Usteer, I'll be glad to be corrected on that for the sake of science. :slight_smile:

1 Like

Sure is! That's the purpose of the enabled event log types (list event_log_types ...) in my config. Running logread -f will allow you to see a lot of what Usteer is doing.

3 Likes

Making some more interesting discoveries on this topic. Any iPhone users who also happen to be somewhat Wireshark savvy--could you help test something with me?

I just ran a wireless sniffer capture from my MacBook. If you're not familiar, this is a built-in Mac tool that does a low-level (i.e. raw) capture from your wireless card. It gets all the 802.11 packets you don't typically see in a Wireshark capture on a Mac. Here's the process: https://osxdaily.com/2015/04/23/sniff-packet-capture-packet-trace-mac-os-x-wireless-diagnostics/

When analyzing the sniffer capture in Wireshark (again, post-capture), I found two of my iPhones (an iPhone 11 and an iPhone 12 Pro Max) are not indicating support for neighbor reports in their RM capabilities when sending an association request to the AP. Supposedly these iPhones support 802.11k, but the association request says otherwise.

Example:

When the AP sends the association response back, it indicates it supports neighbor report (hooray for 802.11k), but also states AP channel report capability is disabled:

Because the iPhones state they don't have neighbor report enabled, they never send a Neighbor Report Request, thus the AP does not send a Neighbor Report Response back.

Not sure what to make of all this yet, but wanted to float the info out there to the smarter brains who might have some thoughts about it.

For anyone willing to test this with me, the following display filter in Wireshark should show the packets we're interested in here:

Display Filter:
wlan.fc.type_subtype==0x0 || wlan.fc.type_subtype==0x1 || wlan.fixed.action_code==4 || wlan.fixed.action_code==5

Key:
wlan.fc.type_subtype==0x0 = Association Request
wlan.fc.type_subtype==0x1 = Association Response
wlan.fixed.action_code==4 = Neighbor Report Request
wlan.fixed.action_code==5 = Neighbor Report Response

3 Likes

With this usteer configuration, I believe I fixed my roaming "issues". I see no more 4 way handshake and Viber do not disconnect while walking around my apartment. I need to test it with MS Teams. I needed just to comment the min snr -70, as when I am smoking on the terrace I got disconnected from the AP.
Thanks. Will continue testing

Glad to hear a positive report back on the config, though I'm a little confused by your mention of no more 4-way handshakes. That's not necessarily a good thing, TBH. But maybe I misunderstood what you were saying.

Would you mind elaborating on that point some more?

As far as I understood it from other topics like https://forum.openwrt.org/t/any-way-to-monitor-if-802-11r-is-working/73504/2?u=underworld with "k" roaming you should not have again 4 way handshakes, as the clients are already authenticated and you should see something like this auth_alg=ft. And in fact now I do not see 4 way mesages and roaming seems to work.
Regards

Ah, yes! So in an ideal configuration, you would be seeing much more of this in your logs:

WPA: FT authentication already completed - do not start 4-way handshake

And just for the sake of not leading anyone astray on this thread, that is a feature/function of 802.11r, not specifically 802.11k/v. Though obviously when configured properly, they all play together for a great end-user experience. :slight_smile:

2 Likes

Yep but I never saw : WPA: FT authentication already completed - do not start 4-way handshake, in my 4-5 years of experiments. I am glad however that I do not see anymore 4-way handshake messages.

Continuing this discovery... I confirmed that without Usteer or Dawn, beacon frames from the AP do not have Link Measurement enabled. This screen capture is what the beacon looks like when Usteer or Dawn are enabled (I tested with each enabled independently):

And the beacon without either Usteer or Dawn:

1 Like

Any thoughts on this:

In yet another interesting twist, I found an older iPhone 7 which is running iOS 15.7.3. It actually sends a Neighbor Report Request:

However, the AP does not reply with a Neighbor Report Response.

I have a single Android device (not by choice) in my house and it properly exchanges Neighbor Report Requests and Neighbor Report Responses with my APs.

Is this an iOS issue? Does Apple not use NRRs anymore?

In relation to my previous post, I have submitted a bug report with Apple to make them aware of what I am seeing. Not going to hold my breath on getting actionable responses back, but they may surprise me.

3 Likes

Here is a question, mindful that both DAWN and Usteer have various snags. Perhaps @patrakov, @account4538 or @eR2022 can offer some insight.

Would you expect enabling 802.11k and 802.11v without doing anything else to cause harm? If so why? And what minimum extra step(s) should be undertaken to make things work well with having enabled 802.11k and/or 802.11v? For example, above there was mention of static-neighbor-report.

No harm in enabling any of these options by themselves.

Here is a DAWN config that works for me with WPA2 - but note that this is just a test setup, and I don't really need the second AP. The test clients are two Linux laptops with Intel AX200 and AX210 cards, and Samsung Galaxy A02 phone. I know that there are lots of options that should better be removed.

And note: this has been tested on the 2.4 GHz only, as band steering inevitably adds latency spikes while in the gray area. I could not get 802.11r to work reliably - sometimes it does roam with FT, sometimes it does a full 4-way handshake.

/etc/config/wireless fragment on Linksys E8450
config wifi-device 'radio0'
	option type 'mac80211'
	option path 'platform/18000000.wmac'
	option band '2g'
	option country 'PH'
	option htmode 'HT40'
	option channel '13'
	option noscan '1'
	option local_pwr_constraint '0'
	option cell_density '1'
	option txpower '20'
	option log_level '1'

config wifi-iface 'wifinet5'
	option device 'radio0'
	option mode 'ap'
	option ssid 'Backbone'
	option encryption 'psk2+ccmp'
	option wds '1'
	option key 'PASSWORD'
	option ieee80211w '2'
	option wpa_disable_eapol_key_retries '1'
	option network 'lan'
	option ieee80211r '1'
	option mobility_domain 'af15'
	option ft_psk_generate_local '0'
	option bss_transition '1'
	option wnm_sleep_mode '1'
	option time_advertisement '2'
	option time_zone 'GMT0'
	option ieee80211k '1'
	option ft_over_ds '0'
	option reassociation_deadline '20000'
/etc/config/wireless fragment on Netgear WAX202 (used as a repeater)
config wifi-device 'radio0'
	option type 'mac80211'
	option path '1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0'
	option band '2g'
	option country 'PH'
	option noscan '1'
	option htmode 'HT40'
	option channel '13'
	option cell_density '1'
	option txpower '20'
	option log_level '1'

config wifi-iface 'wifinet1'
	option device 'radio0'
	option mode 'sta'
	option ssid 'Backbone'
	option bssid 'EA:9F:80:D4:9E:C5'
	option key 'PASSWORD'
	option wds '1'
	option encryption 'psk2+ccmp'
	option ieee80211w '2'
	option network 'lan'

config wifi-iface 'wifinet2'
	option device 'radio0'
	option mode 'ap'
	option ssid 'Backbone'
	option encryption 'psk2+ccmp'
	option wds '1'
	option key 'PASSWORD'
	option ieee80211r '1'
	option mobility_domain 'af15'
	option ft_psk_generate_local '1'
	option ieee80211w '2'
	option wpa_disable_eapol_key_retries '1'
	option network 'lan'
	option bss_transition '1'
	option wnm_sleep_mode '1'
	option time_advertisement '2'
	option time_zone 'GMT0'
	option ieee80211k '1'
	option ft_over_ds '0'
	option reassociation_deadline '20000'
/etc/config/dawn, identical on both devices
config local
	option loglevel '1'

config network
	option broadcast_ip '192.168.10.255'
	option broadcast_port '1025'
	option tcp_port '1026'
	option network_option '2'
	option shared_key 'PASSWORDpassword'
	option iv 'PASSWORDpassword'
	option use_symm_enc '1'
	option collision_domain '-1'
	option bandwidth '-1'

config hostapd
	option hostapd_dir '/var/run/hostapd'

config times
	option con_timeout '60'
	option update_client '10'
	option remove_client '15'
	option remove_probe '30'
	option remove_ap '460'
	option update_hostapd '10'
	option update_tcp_con '10'
	option update_chan_util '5'
	option update_beacon_reports '20'

config metric 'global'
	option min_probe_count '2'
	option bandwidth_threshold '0'
	option kicking_threshold '10'
	option min_number_to_kick '3'
	option use_station_count '0'
	option max_station_diff '1'
	option eval_probe_req '0'
	option eval_auth_req '0'
	option eval_assoc_req '0'
	option deny_auth_reason '1'
	option deny_assoc_reason '17'
	option chan_util_avg_period '3'
	option duration '150'
	option set_hostapd_nr '2'
	option rrm_mode 'apt'
	option kicking '1'

config metric '802_11g'
	option initial_score '70'
	option ht_support '0'
	option vht_support '0'
	option rssi '0'
	option low_rssi_val '-80'
	option low_rssi '-15'
	option chan_util '0'
	option max_chan_util '0'
	option rssi_weight '1'
	option no_ht_support '0'
	option no_vht_support '0'
	option rssi_val '-60'
	option chan_util_val '140'
	option max_chan_util_val '170'
	option rssi_center '-50'

config metric '802_11a'
	option initial_score '100'
	option ht_support '0'
	option vht_support '0'
	option rssi '0'
	option low_rssi_val '-80'
	option low_rssi '-15'
	option chan_util '0'
	option max_chan_util '-15'
	option no_ht_support '0'
	option no_vht_support '0'
	option rssi_val '-60'
	option chan_util_val '140'
	option max_chan_util_val '170'
	option rssi_weight '1'
	option rssi_center '-70'

The key for reliable kicking was to enable active probing.

1 Like

Thanks for posting that config!

How do you test? I found the android phone app Ubiquiti WiFiman app helpful because it shows you transitions in real-time with continuously plotting graph of signal strength. I haven't figured out a good alternative for Apple iPhones or iPads. Or perhaps with DAWN you can look in the DAWN logs.

The motivation behind my question was just that it seems nice to be able to be able to keep things simple. I think that was perhaps @account4538's approach.

I also test using that, but also note that it produces some false reports about roaming. I.e. it draws an arrow without the word "Disconnected", while in the hostapd log on the AP there is clearly a 4-way handshake.

Screenshot falsely indicating a successful roam but also a huge latency spike:

Hostapd log on the target AP, clearly indicating that FT did not work this time:

Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c IEEE 802.11: authentication OK (open system)
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c MLME: MLME-AUTHENTICATE.indication(c8:51:42:23:d4:0c, OPEN_SYSTEM)
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c MLME: MLME-DELETEKEYS.request(c8:51:42:23:d4:0c)
Wed Mar  8 09:00:28 2023 daemon.info hostapd: wlan0-1: STA c8:51:42:23:d4:0c IEEE 802.11: authenticated
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c IEEE 802.11: association OK (aid 1)
Wed Mar  8 09:00:28 2023 daemon.info hostapd: wlan0-1: STA c8:51:42:23:d4:0c IEEE 802.11: associated (aid 1)
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c MLME: MLME-ASSOCIATE.indication(c8:51:42:23:d4:0c)
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c MLME: MLME-DELETEKEYS.request(c8:51:42:23:d4:0c)
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c IEEE 802.11: binding station to interface 'wlan0-1'
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c WPA: event 1 notification
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c WPA: start authentication
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c IEEE 802.1X: unauthorizing port
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c WPA: sending 1/4 msg of 4-Way Handshake
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c WPA: received EAPOL-Key frame (2/4 Pairwise)
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c WPA: sending 3/4 msg of 4-Way Handshake
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c WPA: received EAPOL-Key frame (4/4 Pairwise)
Wed Mar  8 09:00:28 2023 daemon.notice hostapd: wlan0-1: AP-STA-CONNECTED c8:51:42:23:d4:0c
Wed Mar  8 09:00:28 2023 daemon.debug hostapd: wlan0-1: STA c8:51:42:23:d4:0c IEEE 802.1X: authorizing port
Wed Mar  8 09:00:28 2023 daemon.info hostapd: wlan0-1: STA c8:51:42:23:d4:0c RADIUS: starting accounting session D6904079BFEF1D64
Wed Mar  8 09:00:28 2023 daemon.info hostapd: wlan0-1: STA c8:51:42:23:d4:0c WPA: pairwise key handshake completed (RSN)
Wed Mar  8 09:00:28 2023 daemon.notice hostapd: EAPOL-4WAY-HS-COMPLETED c8:51:42:23:d4:0c
Wed Mar  8 09:00:38 2023 daemon.notice hostapd: wlan0-1: BEACON-REQ-TX-STATUS c8:51:42:23:d4:0c 32 ack=1
Wed Mar  8 09:00:54 2023 daemon.notice hostapd: wlan0-1: BEACON-REQ-TX-STATUS c8:51:42:23:d4:0c 33 ack=1
Wed Mar  8 09:00:58 2023 daemon.notice hostapd: wlan0-1: BEACON-REQ-TX-STATUS c8:51:42:23:d4:0c 34 ack=1
Wed Mar  8 09:01:00 2023 cron.err crond[1769]: USER root pid 25360 cmd scp root@192.168.10.1:/tmp/dhcp.leases /tmp/dhcp.leases.new && mv /tmp/dhcp.leases.new /tmp/dhcp.leases
Wed Mar  8 09:01:14 2023 daemon.notice hostapd: wlan0-1: BEACON-REQ-TX-STATUS c8:51:42:23:d4:0c 35 ack=1
1 Like

You can test if 802.11k neighbor report is working by checking to see if the neighbor list is populated:

ubus call hostapd.wlan1 rrm_nr_list (or wlan0, wlan1-1, etc...)

I only use the neighbor report and 802.11r fast transition and both Apple and Android phones FT without issue and I don't have any devices that cannot connect.

I'm using WPA2-PSK, no 802.11w frame protection, KRACK countermeasures off, reassociation deadline 65535, FT over the air, generate PMK locally, and Time interval for rekeying GTK=0

1 Like