Setting up usteer and band-steering in OpenWrt

usteer is a daemon for sharing wifi client information amongst APs on the same network, and can be used for band steering.

This can be useful for improved WiFi performance when you have a network with multiple APs. Especially on EAP networks, it is highly recommended to set up 802.11r also.

Unfortunately documentation on usteer is sparse, so this wiki stub is for the community to add to.

An summary of WiFi roaming technologies can be found on the roaming page.

The following items are prerequisite to set up usteer on OpenWrt:

  • One or more OpenWrt based APs (and routers)
  • install the full version of wpad
  • usteer package installed on each

To set up usteer:

  • configure 802.11k and 802.11v on all WiFi AP-nodes.
  • install and configure required packages for usteer
  • reboot all nodes (or just restart the network if no wpad packages have been changed).

SSH into each of your wifi/AP nodes and add the following config-lines in /etc/config/wireless to each of your SSIDs:

      option bss_transition '1'
      option ieee80211k '1'

Note: From OpenWrt 18.06.0 to 21.02, the config option ieee80211v was also required. If migrating from an older version, you could remove this option.

We need to install usteer. This is as simple as:

 opkg update && opkg install usteer

At this point you have all the packages you need, but they may be in need of additional configuration.

usteer's config-file /etc/config/usteer specifies which interface it will listen on for other nodes. By default this is lan.

Config options

Name Type Required Default Description
network string yes lan The network interface for inter-AP communication
syslog boolean yes 1 Log messages to syslog (0/1)
ipv6 boolean yes 0 Use IPv6 for remote exchange
debug_level integer yes 2 Minimum level of logged messages. 0 = Fatal, 1 = Info, 2 = Verbose, 3 = some debug messages, 4 = network packet information, 5 = all debug messages
max_neighbour_reports integer no 8 Maximum number of neighbor reports set for a node
sta_block_timeout integer no 30000 Maximum amount of time (ms) a station may be blocked due to policy decisions
local_sta_timeout integer no 120000 Maximum amount of time (ms) a local unconnected station is tracked
measurement_report_timeout integer no 120000 Maximum amount of time (ms) a measurement report is stored
local_sta_update integer no 1000 Local station information update interval (ms)
max_retry_band integer no 5 Maximum number of consecutive times a station may be blocked by policy
seen_policy_timeout integer no 30000 Maximum idle time of a station entry (ms) to be considered for policy decisions
load_balancing_threshold integer no 0 Minimum number of stations delta between APs before load balancing policy is active
band_steering_threshold integer no 5 Minimum number of stations delta between bands before band steering policy is active. Only used if load_balancing_threshold is set.
remote_update_interval integer no 1000 Interval (ms) between sending state updates to other APs
remote_node_timeout integer no 10 Number of remote update intervals after which a remote-node is deleted
assoc_steering boolean no 0 Allow rejecting assoc requests for steering purposes (0/1)
probe_steering boolean no 0 Allow ignoring probe requests for steering purposes (0/1)
min_connect_snr integer no 0 Minimum signal-to-noise ratio or signal level (dBm) to allow connections
min_snr integer no 0 Minimum signal-to-noise ratio or signal level (dBm) to remain connected
min_snr_kick_delay integer no 5000 Timeout after which a station with snr < min_snr will be kicked
roam_process_timeout integer no 5000 Timeout (in ms) after which a association following a disassociation is not seen as a roam
roam_scan_snr integer no 0 Minimum signal-to-noise ratio or signal level (dBm) before attempting to trigger client scans for roam
roam_scan_tries integer no 3 Maximum number of client roaming scan trigger attempts
roam_scan_timeout integer no 0 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
roam_scan_interval integer no 10000 Minimum time (ms) between client roaming scan trigger attempts
roam_trigger_snr integer no 0 Minimum signal-to-noise ratio or signal level (dBm) before attempting to trigger forced client roaming
roam_trigger_interval integer no 60000 Minimum time (ms) between client roaming trigger attempts
roam_kick_delay integer no 100 Timeout (in 100ms beacon intervals) for client roam requests
signal_diff_threshold integer no 0 Minimum signal strength difference until AP steering policy is active
initial_connect_delay integer no 0 Initial delay (ms) before responding to probe requests (to allow other APs to see packets as well)
load_kick_enabled boolean no 0 Enable kicking client on excessive channel load (0/1)
load_kick_threshold integer no 75 Minimum channel load (%) before kicking clients
load_kick_delay integer no 10000 Minimum amount of time (ms) that channel load is above threshold before starting to kick clients
load_kick_min_clients integer no 10 Minimum number of connected clients before kicking based on channel load
load_kick_reason_code integer no 5 Reason code on client kick based on channel load (default: WLAN_REASON_DISASSOC_AP_BUSY)
band_steering_interval integer no 120000 Attempting to steer clients to a higher frequency-band every n ms. A value of 0 disables band-steering.
band_steering_min_snr integer no -60 Minimal SNR or absolute signal a device has to maintain over band_steering_interval to be steered to a higher frequency band.
link_measurement_interval integer no 30000 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.
node_up_script string no blank Script to run after bringing up a node
event_log_types list no blank 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
ssid_list list no blank List of SSIDs to enable steering on

After making any change, reload usteer:

  /etc/init.d/usteer reload

Again, do this on all nodes in your network.

Config gotchas

  • For AP steering to take effect, signal_diff_threshold needs to be set to a value greater than 0.
  • band_steering_threshold does not take effect unless load_balancing_threshold is also set to a value greater than 0.
  • roam_scan_snr needs to be set to trigger client scans for roaming.
  • If roam_scan_snr is unset and roam_trigger_snr is set, then roam_scan_snr will take the value of roam_trigger_snr.

If you prefer to configure usteer using Luci, there is one app available:

  opkg update & opkg install luci-app-usteer

The app shows too information about nodes, clients, signal, etc. that can help to adjust correctly the configuration of usteer and verify that is working correctly.

According to the OpenWrt development mailing list, Intel Wifi cards do not seem to understand the current implementation of usteer because of missing disassociation_timer and disassociation_imminent = true. While a patch is in progress, you might be better of using dawn if you experience your computer constantly switching access points and/or frequencies.

usteer has a ubus interface that can be accessed by:

  ubus call usteer <command> [args]

Ubus commands

Name Arguments Description
local_info none Prints local wifi network information
remote_hosts none Prints information about remote usteer instances
remote_info none Prints remote wifi network information (same as local_info, but for remote APs)
connected_clients none Prints clients that are connected to local wifi interfaces
get_clients none Prints all clients from all APs (local and remote)
get_client_info MAC address (json) Prints information about a specific client, including capabilities and information about roaming attempts
get_config none Prints the local configuration
set_config yes (json) Details required
update_config yes (json) Details required
set_node_data yes (json) Details required
delete_node_data yes (json) Details required

local_info

root@AccessPoint:~# ubus call usteer local_info
{
	"hostapd.wlan1-1": {
		"bssid": "11:22:33:44:55:66",
		"freq": 2412,
		"n_assoc": 0,
		"noise": -95,
		"load": 5,
		"max_assoc": 0,
		"roam_events": {
				"source": 0,
				"target": 0
		},
		"rrm_nr": [
			"11:22:33:44:55:66",
			"OpenWrtSSID",
			"<hex string>"
		]
	}
}

remote_hosts

root@AccessPoint:~# ubus call usteer remote_hosts
{
      "192.168.1.1": {
		"id": -525792052
      }
}

remote_info

root@AccessPoint:~# ubus call usteer remote_info
{
      "192.168.1.1#hostapd.wlan0": {
		"bssid": "11:22:33:44:55:66",
		"freq": 5180,
		"n_assoc": 6,
		"noise": -107,
		"load": 3,
		"max_assoc": 0,
		"roam_events": {
			"source": 0,
			"target": 0
		},
		"rrm_nr": [
			"11:22:33:44:55:66",
			"OpenWrtSSID 5GHz",
			"<hex string>"
		]
      },
      "192.168.1.1#hostapd.wlan1": {
		"bssid": "11:22:33:44:55:67",
		"freq": 2412,
		"n_assoc": 7,
		"noise": -90,
		"load": 13,
		"max_assoc": 0,
		"roam_events": {
			"source": 0,
			"target": 0
		},
		"rrm_nr": [
			"11:22:33:44:55:67",
			"OpenWrtSSID",
			"<hex string>"
		]
      }
}

connected_clients

root@AccessPoint:~# ubus call usteer connected_clients
{
      "hostapd.wlan0": {
		"aa:bb:cc:dd:ee:ff": {
			"signal": -91,
			"created": 604990289,
			"seen": 610827895,
			"last_connected": 610827895,
			"snr-kick": {
				"seen-below": 0
			},
			"load-kick": {
				"count": 0
			},
			"roam-state-machine": {
				"tries": 0,
				"event": 0,
				"kick": 0,
				"scan_start": 0,
				"scan_timeout_start": 0
			},
			"bss-transition-response": {
				"status-code": 0,
				"timestamp": 0
			},
			"beacon-measurement-modes": [
				
			],
			"bss-transition-management": false,
			"measurements": [
				
			]
		},
		...
		"aa:bb:cc:dd:ee:fe": {
			"signal": -81,
			"created": 600282172,
			"seen": 610827895,
			"last_connected": 610827895,
			"snr-kick": {
				"seen-below": 0
			},
			"load-kick": {
				"count": 0
			},
			"roam-state-machine": {
				"tries": 0,
				"event": 0,
				"kick": 0,
				"scan_start": 0,
				"scan_timeout_start": 0
			},
			"bss-transition-response": {
				"status-code": 0,
				"timestamp": 0
			},
			"beacon-measurement-modes": [
				
			],
			"bss-transition-management": false,
			"measurements": [
				
			]
		}
      },
      "hostapd.wlan1": {
		"aa:bb:cc:dd:ee:fd": {
			"signal": -58,
			"created": 600282173,
			"seen": 610827901,
			"last_connected": 610827896,
			"snr-kick": {
				"seen-below": 0
			},
			"load-kick": {
				"count": 0
			},
			"roam-state-machine": {
				"tries": 0,
				"event": 0,
				"kick": 0,
				"scan_start": 0,
				"scan_timeout_start": 0
			},
			"bss-transition-response": {
				"status-code": 0,
				"timestamp": 0
			},
			"beacon-measurement-modes": [
				
			],
			"bss-transition-management": false,
			"measurements": [
				
			]
		}
      }
}

get_clients

root@AccessPoint:~# ubus call usteer get_clients
{
	"aa:bb:cc:dd:ee:ff": {
		"192.168.1.1#hostapd.wlan0": {
			"connected": true,
			"signal": -77
		}
	},
	...
	"aa:bb:cc:dd:ee:fe": {
		"192.168.1.1#hostapd.wlan1": {
			"connected": false,
			"signal": -74
		},
		"hostapd.wlan1-1": {
			"connected": false,
			"signal": -86
		}
	},
	"aa:bb:cc:dd:ee:fd": {
		"192.168.1.1#hostapd.wlan0": {
			"connected": false,
			"signal": -70
		},
		"192.168.1.1#hostapd.wlan1": {
			"connected": false,
			"signal": -81
		},
		"hostapd.wlan1-1": {
			"connected": false,
			"signal": -76
		}
	},
	...
	"aa:bb:cc:dd:ee:fc": {
		"hostapd.wlan0": {
			"connected": true,
			"signal": -74
		}
	}
}

get_client_info

A host that is connected locally

root@AccessPoint:~# ubus call usteer get_client_info "{'address':'aa:bb:cc:dd:ee:ff'}"
{
	"2ghz": true,
	"5ghz": true,
	"nodes": {
		"hostapd.wlan0": {
			"connected": true,
			"signal": -54,
			"stats": {
				"probe": {
					"requests": 5,
					"blocked_cur": 0,
					"blocked_total": 0
				},
				"assoc": {
					"requests": 5,
					"blocked_cur": 0,
					"blocked_total": 0
				},
				"auth": {
					"requests": 5,
					"blocked_cur": 0,
					"blocked_total": 0
				}
			}
		}
	}
}

A host that is connected remotely

root@AccessPoint:~# ubus call usteer get_client_info "{'address':'aa:bb:cc:dd:ee:fe'}"
{
	"2ghz": true,
	"5ghz": false,
	"nodes": {
		"192.168.1.32#hostapd.wlan1": {
			"connected": true,
			"signal": -38,
			"stats": {
				"probe": {
					"requests": 0,
					"blocked_cur": 0,
					"blocked_total": 0
				},
				"assoc": {
					"requests": 0,
					"blocked_cur": 0,
					"blocked_total": 0
				},
				"auth": {
					"requests": 0,
					"blocked_cur": 0,
					"blocked_total": 0
				}
			}
		}
	}
}

get_config

root@AccessPoint:~# ubus call usteer get_config
{
	"syslog": true,
	"debug_level": 2,
	"ipv6": true,
	"local_mode": false,
	"sta_block_timeout": 30000,
	"local_sta_timeout": 120000,
	"local_sta_update": 1000,
	"max_neighbor_reports": 8,
	"max_retry_band": 5,
	"seen_policy_timeout": 30000,
	"measurement_report_timeout": 120000,
	"load_balancing_threshold": 0,
	"band_steering_threshold": 5,
	"remote_update_interval": 5000,
	"remote_node_timeout": 12,
	"assoc_steering": false,
	"min_connect_snr": 0,
	"min_snr": 0,
	"min_snr_kick_delay": 5000,
	"steer_reject_timeout": 60000,
	"roam_process_timeout": 5000,
	"roam_scan_snr": 0,
	"roam_scan_tries": 3,
	"roam_scan_timeout": 0,
	"roam_scan_interval": 10000,
	"roam_trigger_snr": 0,
	"roam_trigger_interval": 60000,
	"roam_kick_delay": 10000,
	"signal_diff_threshold": 0,
	"initial_connect_delay": 0,
	"load_kick_enabled": false,
	"load_kick_threshold": 75,
	"load_kick_delay": 10000,
	"load_kick_min_clients": 10,
	"load_kick_reason_code": 5,
	"band_steering_interval": 20000,
	"band_steering_min_snr": -60,
	"link_measurement_interval": 30000,
	"interfaces": [
		"mesh0",
		"mesh1"
	],
	"event_log_types": [
		
	],
	"ssid_list": [
		"OpenWrtSSID",
		"OpenWrtSSID 5GHz"
	]
}

set_config

Details required

update_config

Details required

set_node_data

Details required

delete_node_data

Details required
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2024/11/20 15:19
  • by humaita