Wifimngr
Introduction
Wifimngr is a Linux userspace daemon that provides Radio and Interface objects for status and management of Wi-Fi. In OpenWrt based systems, these Wi-Fi objects are available over UBUS.
Wifimngr reads a "wireless" configuration file (in UCI format [https://openwrt.org/docs/guide-user/network/wifi/basic]) and creates the appropriate objects. The "wireless" configuration file should be OpenWrt compatible. This means that the "wifi-device" and "wifi-iface" sections in the configuration file represent the Wi-Fi Radios and Wi-Fi interfaces respectively.
The wifimngr objects can be used for fetching status and statistics of the Wi-Fi radio and interfaces and perform actions like sending Wi-Fi management frames, start/stop WPS, DPP etc.
Wifimngr is also responsible for passing the received Netlink events from the "libwifi" layer to the system's bus (f.e. UBUS in the Openwrt based systems).
The radio objects are named as "wifi.radio.", while the interface objects are "wifi.ap." or "wifi.bsta." respectively for the AP and STA mode interfaces.
With the Example-1 "wireless"
config below, wifimngr creates a "wifi.radio.radio0"
object for the "wifi-device"
section.
Example-1
config wifi-device "radio0"
option band 'a'
option channel 'auto'
:
For Example-2 config below, wifimngr creates "wifi.ap.wlan0"
and "wifi.bsta.wlan0-1"
objects for the "wifi-iface"
'ap' and 'sta' sections respectively.
Example-2:
config wifi-iface
option device 'radio0'
option ifname 'wlan0'
option mode 'ap'
:
cofnig wifi-iface
option device 'radio0'
option ifname 'wlan0-1'
option mode 'sta'
:
NOTE: With 802.11be and the introduction of Multi-Link Device (MLD) interfaces, the "wireless"
config may additionally contain one or more "wifi-mld"
sections.
These MLD sections represent the Wi-Fi MLD interfaces.
Example-3:
config wifi-iface
option device 'radio0'
option ifname 'wlan0'
option mode 'ap'
option mld 'mld1'
:
cofnig wifi-iface
option device 'radio1'
option ifname 'wlan1'
option mode 'ap'
option mld 'mld1'
:
config wifi-mld 'mld1'
option ifname 'wlan-01'
:
For the config in Example-3 above, wifimngr creates a "wifi.apmld.wlan-01"
object for the Wi-Fi AP MLD interface "wlan-01".
Note the Wi-Fi MLD interface 'wlan-01' has two Affiliated AP interfaces - "wlan0" and "wlan1" from radios "radio0" and "radio1" respectively.
For MLD interface of type STA, wifimngr creates "wifi.bstamld.<mld-ifname>"
object.
Overview
Configuration
The "wireless" configuration file should be in UCI format. See <URL: Openwrt Wi-Fi UCI Config> For more info on the UCI wireless configuration see link
Objects and APIs
The Wi-Fi radio, interface (and mld) objects alongwith the APIs supported by each is given below -
'wifi' @5dffe10f
"status":{}
"debug":{"level":"Integer"}
'wifi.radio.radio0\_band1' @b9359f1b
"status":{}
"stats":{}
"get":{"param":"String"}
"scan":{"ssid":"String","bssid":"String","channel":"Integer","opclass":"Integer"}
"scan_ex":{"ssid":"Array","bssid":"String","opclass":"Array","channel":"Array"}
"scanresults":{"bssid":"String","cache":"Boolean"}
"autochannel":{"exclude_dfs":"Boolean","exclude_6ghz_non_psc":"Boolean","exclude_opclass":"Array","exclude_channels":"Array","exclude_bandwidth":"Array","channels":"Array","bandwidth":"Array"}
"start_cac":{"channel":"Integer","bandwidth":"Integer","method":"Integer"}
"stop_cac":{"channel":"Integer","bandwidth":"Integer","method":"Integer"}
"add_iface":{"args":"Table","config":"Boolean"}
"del_iface":{"ifname":"String","config":"Boolean"}
"channels_info":{}
"channels":{"bandwidth":"Integer"}
"opclass_preferences":{}
"simulate_radar":{"channel":"Integer","bandwidth":"Integer","type":"Integer","subband_mask":"Integer"}
'wifi.ap.wlan01-1' @86d7fe82
"status":{}
"stats":{}
"assoclist":{}
"stations":{"sta":"String"}
"disconnect":{"sta":"String","reason":"Integer"}
"probe_sta":{"sta":"String"}
"monitor_add":{"sta":"String"}
"monitor_del":{"sta":"String"}
"monitor_get":{"sta":"String"}
"subscribe_frame":{"type":"Integer","stype":"Integer"}
"unsubscribe_frame":{"type":"Integer","stype":"Integer"}
"add_neighbor":{"bssid":"String","channel":"Integer","bssid_info":"String","reg":"Integer","phy":"Integer"}
"del_neighbor":{"bssid":"String"}
"list_neighbor":{"ssid":"String","client":"String"}
"request_neighbor":{"client":"String","opclass":"Integer","channel":"Integer","duration":"Integer","mode":"String","bssid":"String","reporting_detail":"Integer","ssid":"String","channel_report":"Array","request_element":"Array"}
"request_btm": {"sta":"String","target_ap":"Array","mode":"Integer","disassoc_tmo":"Integer","validity_int":"Integer","dialog_token":"Integer","bssterm_dur":"Integer","mbo_reason":"Integer","mbo_cell_pref":"Integer","mbo_reassoc_delay":"Integer"}
"request_transition":{"client":"String","bssid":"Array","timeout":"Integer"}
"assoc_control":{"client":"Array","enable":"Integer"}
"add_vendor_ie":{"mgmt":"Integer","oui":"String","data":"String"}
"del_vendor_ie":{"mgmt":"Integer","oui":"String","data":"String"}
"dump_beacon":{}
"chan_switch":{"count":"Integer","channel":"Integer","freq":"Integer","sec_chan_offset":"Integer","cf1":"Integer","cf2":"Integer","bw":"Integer","blocktx":"Boolean","ht":"Boolean","vht":"Boolean","he":"Boolean","eht":"Boolean","auto-ht":"Boolean"}
"measure_link":{"sta":"String"}
"mbo_disallow_assoc":{"reason":"Integer"}
"up":{}
"down":{}
"send_action":{"dst":"String","wait":"Integer","freq":"Integer","frame":"String","src":"String"}
"set_qos_map":{"set":"Array"}
"send_qos_map_conf":{"sta":"String"}
'wifi.apmld.wlan1' @eb47f8ed
"status":{}
"stats":{}
"assoclist":{}
"stations":{"sta":"String"}
"disconnect":{"sta":"String","reason":"Integer"}
'wifi.wps' @4eb8cbd8
"start":{"ifname":"String","mode":"String","role":"String","pin":"String"}
"stop":{}
"status":{"ifname":"String"}
"generate_pin":{}
"validate_pin":{"pin":"String"}
"showpin":{"ifname":"String"}
"setpin":{"ifname":"String","pin":"String"}
For more info on the Ubus API see function specification or go directly to the generated docs.
Table 1. WiFi events over UBUS
Event | Event Data |
---|---|
wifi.radio | {"ifname":"wlan0","event":"ap-disabled", "data": {"bssid":"00:20:07:11:22:33}} |
wifi.radio | {"ifname":"wlan0","event":"ap-enabled", "data": {"bssid":"00:20:07:11:22:33"}} |
wifi.radio | {"ifname":"wlan1","event":"cac-start","data":{"channel":"52","bandwidth":80,"cac_time":60}} |
wifi.radio | {"ifname":"wlan1","event":"cac-end","data":{"channel":"52","bandwidth":80,"success":1}} |
wifi.radio | {"ifname":"wlan1","event":"ap-csa-finished","data":{"freq":"2412", "bandwidth":80}} |
wifi.radio | {"ifname":"wlan1","event":"radar","data":{"channel":"52","bandwidth":80}} |
wifi.radio | {"ifname":"wlan1","event":"nop-end","data":{"channel":"52", "bandwidth":80}} |
wifi.radio | {"ifname":"wlan1","event":"acs-start"} |
wifi.radio | {"ifname":"wlan1","event":"acs-end","data":{"channel":"149", "bandwidth":80}} |
wifi.radio | {"ifname":"wlan1","event":"acs-failed"} |
wifi.iface | {"ifname":"wlan1","event":"up"} |
wifi.iface | {"ifname":"wlan1","event":"down"} |
wifi.iface | _{"ifname":"wlan1","event":"created", "data":{"type":"sta |
wifi.iface | {"ifname":"wlan1","event":"deleted" } |
wifi.iface | {"ifname":"wlan1","event":"wps-start", "data":{"method":"pbc"}} |
wifi.iface | {"ifname":"wlan1","event":"wps-stop", "data":{"method":"pbc"}} |
wifi.iface | {"ifname":"wlan1","event":"wps-success"} |
wifi.iface | {"ifname":"wlan1","event":"wps-success", "data": {"credentials": {"..."}}} |
wifi.iface | {"ifname":"wlan1","event":"wps-timeout"} |
wifi.iface | {"ifname":"wlan1","event":"wps-overlap"} |
wifi.iface | {"ifname":"wlan0","event":"frame-rx","data":{"hexstring":"00003a01...0100"}} |
wifi.sta | {"ifname":"wlan1","event":"connected","data":{"macaddr":"44:d4:37:42:47:bf", "bssid":"00:20:07:11:22:33"}} |
wifi.sta | {"ifname":"wlan1","event":"disconnected","data":{"macaddr":"44:d4:37:42:47:bf", "bssid":"00:20:07:11:22:33"}} |
wifi.sta | {"ifname":"wlan1","event":"wds-station-added","data":{"ifname":"wlan1.sta1","macaddr":"44:d4:37:42:47:bf"}} |
wifi.sta | {"ifname":"wlan1","event":"wds-station-removed","data":{"ifname":"wlan1.sta1","macaddr":"44:d4:37:42:47:bf"}} |
wifi.sta | {"ifname":"wlan1","event":"btm-resp","data":{"macaddr":"30:10:b3:6d:8d:ba","status":"0","target_bssid":"44:d4:37:42:1f:16"}} |
wifi.sta | {"ifname":"wlan0","event":"btm-resp","data":{"macaddr":"30:10:b3:6d:8d:ba","status":"1"}} |
wifi.sta | {"ifname":"wlan0","event":"probe-req","data":{"macaddr":"44:d4:37:42:47:be","rssi":-35}} |
wifi.sta | {"ifname":"wlan0","event":"bcn-resp","data":{"macaddr":"44:d4:37:42:47:be","status":1}} |
wifi.sta | {"ifname":"wlan0","event":"bcn-report","data":{"macaddr":"30:10:b3:6d:8d:ba","token":"1","mode":"00","nbr":[{"bssid":"44:d4:37:42:1f:16","channel":"11","op_class":"81","phy_type":"7","rcpi":"148","rsni":"255"}]}} |
Wifi Statistics
The wifi
object publishes gathered radio and interface statistics.
'wifi' @f2f72310
"status":{}
For info on the wifi
API see link
Access Point
An object will be published on ubus for each interface operating as an access point, managing operations and data for wifi clients, neighbor nodes and interface statistics.
To parse access point interfaces, wifimngr reads the wireless configuration file (/etc/config/wireless
), looking for wifi-iface
sections, with the option mode
specified as ap
.
config wifi-iface
option device 'test5'
option ifname 'test5'
option mode 'ap'
Wifimngr will query libwifi to find available API calls for the driver, in order to dynamically publish available methods to ubus.
root@eagle:~# ubus -v list wifi.ap.test5
'wifi.ap.test5' @a939a75c
"status":{}
"stats":{}
"assoclist":{}
"stations":{"sta":"String"}
"disconnect":{"sta":"String","reason":"Integer"}
"probe_sta":{"sta":"String"}
"monitor_add":{"sta":"String"}
"monitor_del":{"sta":"String"}
"monitor_get":{"sta":"String"}
"subscribe_frame":{"type":"Integer","stype":"Integer"}
"unsubscribe_frame":{"type":"Integer","stype":"Integer"}
"add_neighbor":{"bssid":"String","channel":"Integer","bssid_info":"String","reg":"Integer","phy":"Integer"}
"del_neighbor":{"bssid":"String"}
"list_neighbor":{"ssid":"String","client":"String"}
"request_neighbor":{"client":"String","opclass":"Integer","channel":"Integer","duration":"Integer","mode":"String","bssid":"String","reporting_detail":"Integer","ssid":"String","channel_report":"Array","request_element":"Array"}
"request_transition":{"client":"String","bssid":"Array","timeout":"Integer"}
"assoc_control":{"client":"Array","enable":"Integer"}
"add_vendor_ie":{"mgmt":"Integer","oui":"String","data":"String"}
"del_vendor_ie":{"mgmt":"Integer","oui":"String","data":"String"}
"dump_beacon":{}
"chan_switch":{"count":"Integer","freq":"Integer","sec_chan_offset":"Integer","cf1":"Integer","cf2":"Integer","bw":"Integer","blocktx":"Boolean","ht":"Boolean","vht":"Boolean"}
"measure_link":{"sta":"String"}
"mbo_disallow_assoc":{"reason":"Integer"}
"up":{}
"down":{}
For info on the access point API see link
Radio
Similarily to access point interfaces, wifimngr will publish one object per available radio, parsed from the wireless configuration, by wifi-device
sections. The methods are added dynamically by quering for supported API calls from libwifi.
config wifi-device 'test5'
option channel 'auto'
option hwmode 'auto'
option country 'DE'
option band 'a'
option bandwidth '80'
The radio object manages radio data and statistics, channel selection and wifi scan operations.
root@:/opt/work# ubus -v list wifi.radio.test5
'wifi.radio.test5' @fa7d185d
"status":{}
"stats":{}
"get":{"param":"String"}
"scan":{"ssid":"String","bssid":"String","channel":"Integer"}
"scanresults":{"bssid":"String"}
"autochannel":{"interval":"Integer","algo":"Integer","scans":"Integer"}
For info on the radio API see link
Wifi Protected Setup
Lastly, wifimngr exposes WPS functionality to ubus.
The configuration is read from the /etc/config/wireless
. To enable WPS the wps
option has to be set to 1
.
config wifi-iface
option wps '1'
To see more configuration options see link, under the wifi-iface
section.
root@:/opt/work# ubus -v list wifi.wps
'wifi.wps' @78c52f3b
"start":{"ifname":"String","mode":"String","role":"String","pin":"String"}
"stop":{}
"status":{"ifname":"String"}
"generate_pin":{}
"validate_pin":{"pin":"String"}
"showpin":{"ifname":"String"}
"setpin":{"ifname":"String","pin":"String"}
For info on the WPS API see link
Tests
This section will give a brief overview of the tests for wifimngr, for a more detailed report see test specification
To test wifimngr, the scope of the tests has to be clearly defined, as wifimngr is heavily dependent on libwifi:
- Verify linkage between wifimngr and libwifi APIs
- Verify that the wifi structures are correctly prepared and return data is used correctly by wifimngr
- Verify that API calls successfully reach libwifi and if passed input is of correct format
As the test environment runs in a ubuntu 16.04 docker environment, with little possibility to prepare wifi drivers and kernel version, the easy-soc-libs had to be extended to support APIs to run on a test platform, returning dummy data for getters, and for setters, logging that the call made with timestamp and arguments.
To ensure full coverage, the getters are tested by the ubus-api-validation tool, using json-schemas to validate accurate output. The setters are tested by cmocka tests, creating a libwifimngr.so shared library, invoking its ubus API functions directly, verifying that the call went through, and that the arguments were passed correctly, by parsing the test log file found at /tmp/test.log
.
Additionally, the cmocka tests verify that netlink event callbacks are registered and invoked.
Dependencies
To successfully build wifimngr, the following libraries are needed:
Dependency | Link | License |
---|---|---|
libuci | https://git.openwrt.org/project/uci.git | LGPL 2.1 |
libubox | https://git.openwrt.org/project/libubox.git | BSD |
libubus | https://git.openwrt.org/project/ubus.git | LGPL 2.1 |
libjson-c | https://s3.amazonaws.com/json-c_releases | MIT |
libwifi | https://dev.iopsys.eu/iopsys/easy-soc-libs/tree/devel/libwifi | GNU GPL2 |
libnl3 | ||
libblobmsg_json | ||
libnl-genl |
Additionally, in order to build with the tests, the following libraries are needed:
Dependency | Link | License |
---|---|---|
libjson-schema-validator | https://github.com/pboettch/json-schema-validator | LGPL 2.1 |
libjson-validator | https://dev.iopsys.eu/iopsys/json-schema-validator | |
libjson-editor | https://dev.iopsys.eu/iopsys/json-editor |