Skip to content
Snippets Groups Projects

Wifimngr

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:

  1. Verify linkage between wifimngr and libwifi APIs
  2. Verify that the wifi structures are correctly prepared and return data is used correctly by wifimngr
  3. 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