From: Felix Fietkau Date: Sun, 21 Sep 2025 13:39:35 +0000 (+0200) Subject: wpa_supplicant: add MLO client support X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=9aca8a97d7911230211d98a24e4dc4b0a0173ec2;p=openwrt%2Fstaging%2Fthess.git wpa_supplicant: add MLO client support Can also be used for a client mode interface that is able to connect on multiple bands individually, while handling hostapd state for the correct band. Signed-off-by: Felix Fietkau --- diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc index 30e196ddce..3ef150694f 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc @@ -248,6 +248,8 @@ export function generate(config_list, data, interface) { iface: interface.config.ifname, config: file_name, '4addr': !!interface.config.wds, + mlo: !!interface.config.mlo, + freq_list: data.config.scan_list, powersave: false }; diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc index d793ef8bfe..fa9a5faafb 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc @@ -17,6 +17,16 @@ let wdev_handler = {}; let wdev_script_task, wdev_script_timeout; let handler_timer; +function supplicant_start_mlo() +{ + ubus.call({ + object: "wpa_supplicant", + method: "mld_start", + return: "ignore", + data: { }, + }); +} + function delete_wdev(name) { delete netifd.wireless.devices[name]; @@ -216,6 +226,9 @@ function run_next_handler() { while (!wdev_cur && length(wdev_handler) > 0) __run_next_handler(); + + if (!wdev_cur && !length(wdev_handler)) + supplicant_start_mlo(); } function run_handler(wdev, op, cb) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc index 360fe2503e..19c38d11e5 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc @@ -17,12 +17,12 @@ let wireless = netifd.wireless = { path: realpath(netifd.main_path + "/wireless"), }; -function hostapd_update_mlo() +function wpad_update_mlo(service, mode) { let config = {}; for (let ifname, data in wireless.mlo) { - if (data.mode != "ap") + if (data.mode != mode) continue; data.phy = find_phy(data.radio_config[0], true); @@ -33,17 +33,28 @@ function hostapd_update_mlo() } ubus.call({ - object: "hostapd", + object: service, method: "mld_set", return: "ignore", data: { config }, }); } +function hostapd_update_mlo() +{ + wpad_update_mlo("hostapd", "ap"); +} + +function supplicant_update_mlo() +{ + wpad_update_mlo("wpa_supplicant", "sta"); +} + function update_config(new_devices, mlo_vifs) { wireless.mlo = mlo_vifs; hostapd_update_mlo(); + supplicant_update_mlo(); for (let name, dev in wireless.devices) if (!new_devices[name]) @@ -516,6 +527,8 @@ wireless.obj = ubus.publish("network.wireless", ubus_obj); wireless.listener = ubus.listener("ubus.object.add", (event, msg) => { if (msg.path == "hostapd") hostapd_update_mlo(); + else if (msg.path == "wpa_supplicant") + supplicant_update_mlo(); }); return { diff --git a/package/network/services/hostapd/files/wpa_supplicant.uc b/package/network/services/hostapd/files/wpa_supplicant.uc index dd371154e1..b4cafe80a6 100644 --- a/package/network/services/hostapd/files/wpa_supplicant.uc +++ b/package/network/services/hostapd/files/wpa_supplicant.uc @@ -13,6 +13,7 @@ function ex_handler(e) } libubus.guard(ex_handler); +wpas.data.mld = {}; wpas.data.config = {}; wpas.data.iface_phy = {}; wpas.data.macaddr_list = {}; @@ -77,50 +78,39 @@ function prepare_config(config, radio) return { config }; } -function set_config(config_name, phy_name, radio, num_global_macaddr, macaddr_base, config_list) +function phy_dev_open(phy_name) { - let phy = wpas.data.config[config_name]; - - if (radio < 0) - radio = null; - + let phy = wpas.data.config[phy_name]; if (!phy) { - phy = vlist_new(iface_cb, false); - phy.name = phy_name; - wpas.data.config[config_name] = phy; + warn(`Missing phy config for ${phy_name}\n`); + return; } - phy.radio = radio; - phy.num_global_macaddr = num_global_macaddr; - phy.macaddr_base = macaddr_base; + let phydev = phy_open(phy.name, phy.radio); + if (!phydev) + return; - let values = []; - for (let config in config_list) - push(values, [ config.iface, prepare_config(config) ]); + let macaddr_list = wpas.data.macaddr_list[phy_name]; + phydev.macaddr_init(macaddr_list, { + num_global: phy.num_global_macaddr, + macaddr_base: phy.macaddr_base, + }); - phy.update(values); + return phydev; } function start_pending(phy_name) { let phy = wpas.data.config[phy_name]; - let ubus = wpas.data.ubus; - if (!phy || !phy.data) return; - let phydev = phy_open(phy.name, phy.radio); + let phydev = phy_dev_open(phy_name); if (!phydev) { wpas.printf(`Could not open phy ${phy_name}`); return; } - let macaddr_list = wpas.data.macaddr_list[phy_name]; - phydev.macaddr_init(macaddr_list, { - num_global: phy.num_global_macaddr, - macaddr_base: phy.macaddr_base, - }); - for (let ifname in phy.data) iface_start(phydev, phy.data[ifname]); } @@ -136,6 +126,235 @@ function phy_name(phy, radio) return phy; } +function mld_remove(data) +{ + if (!data.radio_mask_up) + return; + + let name = data.name; + wpas.printf(`Remove MLD interface ${name}`); + wpas.remove_iface(name); + wdev_remove(name); + data.radio_mask_up = 0; +} + +function mld_first_phy(data) +{ + let mask = data.radio_mask_present; + + for (let i = 0; mask; i++, mask >>= 1) + if (mask & 1) + return i; +} + +function mld_radio_index(data, freq) +{ + let phys = data.phy_config; + for (let i = 0; i < length(phys); i++) + if (phys[i] && index(phys[i].freq_list, freq) >= 0) + return i; +} + +function mld_add(data, phy_list) +{ + let name = data.name; + phy_list ??= []; + + wpas.printf(`Add MLD interface ${name}`); + + let radio = mld_first_phy(data); + if (radio == null) + return; + + let phy_name = data.phy + '.' + radio; + let phydev = phy_list[phy_name]; + if (!phydev) { + phydev = phy_dev_open(phy_name); + if (!phydev) + return; + + phy_list[phy_name] = phydev; + } + + let wdev_config = { ...data.config, radio_mask: data.radio_mask }; + let ret = phydev.wdev_add(name, wdev_config); + if (ret) + wpas.printf(`Failed to create device ${name}: ${ret}`); + + let first_config = data.phy_config[radio]; + + wdev_set_up(name, true); + wpas.add_iface(first_config); + + let iface = wpas.interfaces[name]; + if (!iface) { + wpas.printf(`Interface ${name} not found after adding\n`); + wpas.remove_iface(name); + wdev_remove(name); + return; + } + + if (length(data.freq_list) > 0) + iface.config('freq_list', data.freq_list); + + data.radio_mask_up = data.radio_mask_present; +} + +function mld_remove_links(data) +{ + // TODO + mld_remove(data); +} + +function mld_add_links(data) +{ + // TODO: incremental update + mld_remove(data); + mld_add(data); +} + +function mld_set_config(config) +{ + let prev_mld = { ...wpas.data.mld }; + let new_mld = {}; + let phy_list = {}; + let new_config = !length(prev_mld); + + wpas.printf(`Set MLD config: ${keys(config)}`); + + for (let name, data in config) { + let prev = prev_mld[name]; + if (prev && is_equal(prev.config, data)) { + new_mld[name] = prev; + delete prev_mld[name]; + continue; + } + + let radio_mask = 0; + for (let r in data.radios) + if (r != null) + radio_mask |= 1 << r; + + new_mld[name] = { + name, + config: data, + phy: data.phy, + phy_config: [], + radio_mask, + radio_mask_up: 0, + radio_mask_present: 0, + }; + } + + for (let name, data in prev_mld) + mld_remove(data); + + wpas.data.mld = new_mld; + +} + +function mld_set_iface_config(name, data, radio, config) +{ + wpas.printf(`Set MLD interface ${name} radio ${radio} config: ${keys(config)}`); + + data.phy_config[radio] = config; + if (config) + data.radio_mask_present |= 1 << radio; + else + data.radio_mask_present &= ~(1 << radio); + + let freq_list; + for (let config in data.phy_config) { + if (!config || !config.freq_list) + continue; + if (!freq_list) + freq_list = [ ...config.freq_list ]; + else + push(freq_list, ...config.freq_list); + } + + data.freq_list = freq_list; +} + +function mld_update_iface(name, data) { + if (!data.radio_mask_up) + return; + + if (!data.radio_mask_present) { + mld_remove(data); + return; + } + + let mask = data.radio_mask_up & ~data.radio_mask_present; + if (!mask) + return; + + mld_remove_links(data); +} + +function mld_update_phy(phy, ifaces) { + for (let name, data in wpas.data.mld) { + if (data.phy != phy.name) + continue; + + mld_set_iface_config(name, data, phy.radio, ifaces[name]); + mld_update_iface(name, data); + } +} + +function mld_start() { + wpas.printf(`Start pending MLD interfaces\n`); + + let phy_list = {}; + for (let name, data in wpas.data.mld) { + wpas.printf(`MLD interface ${name} present=${data.radio_mask_present} up=${data.radio_mask_up}`); + let add_mask = data.radio_mask_present & ~data.radio_mask_up; + if (!add_mask) + continue; + + if (!data.radio_mask_up) + mld_add(data, phy_list); + else + mld_add_links(data); + } +} + +function mld_bss_allowed(data, bss) { + if (!data.freq_list) + return true; + + return index(data.freq_list, bss.freq) >= 0; +} + +function set_config(config_name, phy_name, radio, num_global_macaddr, macaddr_base, config_list) +{ + let phy = wpas.data.config[config_name]; + + if (radio < 0) + radio = null; + + if (!phy) { + phy = vlist_new(iface_cb, false); + phy.name = phy_name; + wpas.data.config[config_name] = phy; + } + + phy.radio = radio; + phy.num_global_macaddr = num_global_macaddr; + phy.macaddr_base = macaddr_base; + + let values = []; + let mlo_ifaces = {}; + for (let config in config_list) + if (config.mlo) + mlo_ifaces[config.iface] = config; + else + push(values, [ config.iface, prepare_config(config) ]); + + mld_update_phy(phy, mlo_ifaces); + phy.update(values); +} + let main_obj = { phy_set_state: { args: { @@ -214,6 +433,25 @@ let main_obj = { return libubus.STATUS_NOT_FOUND; } }, + mld_set: { + args: { + config: {} + }, + call: function(req) { + if (!req.args.config) + return libubus.STATUS_INVALID_ARGUMENT; + + mld_set_config(req.args.config); + return 0; + } + }, + mld_start: { + args: {}, + call: function(req) { + mld_start(); + return 0; + } + }, config_set: { args: { phy: "", @@ -315,12 +553,30 @@ function iface_event(type, name, data) { ubus.call("service", "event", { type: `wpa_supplicant.${name}.${type}`, data: {} }); } -function iface_hostapd_notify(phy, ifname, iface, state) +function iface_hostapd_fill_radio_link(mld, radio, msg, link) +{ + let config = mld.phy_config[radio]; + if (!config) + return; + + let freq_list = config.freq_list; + if (!freq_list) + return; + + if (!link || index(freq_list, link.frequency) < 0) + return; + + msg.frequency = link.frequency; + msg.sec_chan_offset = link.sec_chan_offset; +} + +function iface_hostapd_notify(ifname, iface, state) { - let ubus = wpas.data.ubus; let status = iface.status(); - let msg = { phy: phy }; + let ubus = wpas.data.ubus; + let msg = {}; + let mld = wpas.data.mld[ifname]; switch (state) { case "DISCONNECTED": case "AUTHENTICATING": @@ -333,26 +589,76 @@ function iface_hostapd_notify(phy, ifname, iface, state) break; case "COMPLETED": msg.up = true; - msg.frequency = status.frequency; - msg.sec_chan_offset = status.sec_chan_offset; + if (!mld) { + msg.frequency = status.frequency; + msg.sec_chan_offset = status.sec_chan_offset; + } break; default: return; } - ubus.call("hostapd", "apsta_state", msg); + if (!mld) { + msg.phy = wpas.data.iface_phy[ifname]; + if (!phy) { + wpas.printf(`no PHY for ifname ${ifname}`); + return; + } + ubus.call("hostapd", "apsta_state", msg); + return; + } + + let radio_mask = mld.radio_mask; + for (let i = 0; radio_mask; i++, radio_mask >>= 1) { + if (!(radio_mask & 1)) { + wpas.printf(`skip radio ${i}`); + continue; + } + + let radio_msg = { + ...msg, + phy: mld.phy, + radio: i, + }; + + if (state == "COMPLETED") { + if (status.links) + for (let link in status.links) + iface_hostapd_fill_radio_link(mld, i, radio_msg, link); + else + iface_hostapd_fill_radio_link(mld, i, radio_msg, status); + } + + ubus.call("hostapd", "apsta_state", radio_msg); + } } -function iface_channel_switch(phy, ifname, iface, info) +function iface_channel_switch(ifname, iface, info) { let msg = { - phy: phy, up: true, csa: true, csa_count: info.csa_count ? info.csa_count - 1 : 0, frequency: info.frequency, sec_chan_offset: info.sec_chan_offset, }; + + let mld = wpas.data.mld[ifname]; + if (mld) { + msg.phy = mld.phy; + msg.radio = mld_radio_index(mld, info.frequency); + if (msg.radio == null) { + wpas.printf(`PHY ${mld.phy} radio for frequency ${info.frequency} not found`); + return; + } + } else { + msg.phy = wpas.data.iface_phy[ifname]; + if (!msg.phy) { + wpas.printf(`no PHY for ifname ${ifname}`); + return; + } + } + ubus.call("hostapd", "apsta_state", msg); } @@ -362,6 +668,13 @@ return { set_config(phy, []); wpas.ubus.disconnect(); }, + bss_allowed: function(ifname, bss) { + let mld = wpas.data.mld[ifname]; + if (!mld) + return true; + + return mld_bss_allowed(mld, bss); + }, iface_add: function(name, obj) { iface_event("add", name); }, @@ -369,39 +682,35 @@ return { iface_event("remove", name); }, state: function(ifname, iface, state) { - let phy = wpas.data.iface_phy[ifname]; - if (!phy) { - wpas.printf(`no PHY for ifname ${ifname}`); - return; - } + try { + iface_hostapd_notify(ifname, iface, state); - iface_hostapd_notify(phy, ifname, iface, state); + if (state != "COMPLETED") + return; - if (state != "COMPLETED") - return; + let phy = wpas.data.iface_phy[ifname]; + if (!phy) + return; - let phy_data = wpas.data.config[phy]; - if (!phy_data) - return; + let phy_data = wpas.data.config[phy]; + if (!phy_data) + return; - let iface_data = phy_data.data[ifname]; - if (!iface_data) - return; + let iface_data = phy_data.data[ifname]; + if (!iface_data) + return; - let wdev_config = iface_data.config; - if (!wdev_config || wdev_config.mode != "mesh") - return; + let wdev_config = iface_data.config; + if (!wdev_config || wdev_config.mode != "mesh") + return; - wdev_set_mesh_params(ifname, wdev_config); + wdev_set_mesh_params(ifname, wdev_config); + } catch (e) { + ex_handler(e); + } }, event: function(ifname, iface, ev, info) { - let phy = wpas.data.iface_phy[ifname]; - if (!phy) { - wpas.printf(`no PHY for ifname ${ifname}`); - return; - } - if (ev == "CH_SWITCH_STARTED") - iface_channel_switch(phy, ifname, iface, info); + iface_channel_switch(ifname, iface, info); } }; diff --git a/package/network/services/hostapd/patches/601-ucode_support.patch b/package/network/services/hostapd/patches/601-ucode_support.patch index 30780ca76e..436acf9220 100644 --- a/package/network/services/hostapd/patches/601-ucode_support.patch +++ b/package/network/services/hostapd/patches/601-ucode_support.patch @@ -702,7 +702,28 @@ as adding/removing interfaces. CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c -@@ -6293,6 +6293,7 @@ void supplicant_event(void *ctx, enum wp +@@ -53,6 +53,7 @@ + #include "wmm_ac.h" + #include "nan_usd.h" + #include "dpp_supplicant.h" ++#include "ucode.h" + + + #define MAX_OWE_TRANSITION_BSS_SELECT_COUNT 5 +@@ -1706,6 +1707,12 @@ struct wpa_ssid * wpa_scan_res_match(str + return NULL; + } + ++ if (!wpas_ucode_bss_allowed(wpa_s, bss)) { ++ if (debug_print) ++ wpa_dbg(wpa_s, MSG_DEBUG, " skip - denied by ucode handler"); ++ return NULL; ++ } ++ + for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) { + if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len, + bss, bssid_ignore_count, debug_print, link)) +@@ -6293,6 +6300,7 @@ void supplicant_event(void *ctx, enum wp event_to_string(event), event); #endif /* CONFIG_NO_STDOUT_DEBUG */ diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.c b/package/network/services/hostapd/src/wpa_supplicant/ucode.c index 88d63e0b21..7f0249a423 100644 --- a/package/network/services/hostapd/src/wpa_supplicant/ucode.c +++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.c @@ -6,6 +6,7 @@ #include "wpa_supplicant_i.h" #include "wps_supplicant.h" #include "ctrl_iface.h" +#include "config.h" #include "bss.h" #include "ucode.h" @@ -41,6 +42,21 @@ wpas_ucode_update_interfaces(void) ucv_object_add(ucv_prototype_get(global), "interfaces", ifs); } +static uc_value_t * +wpas_ucode_bss_get_uval(struct wpa_bss *bss) +{ + uc_value_t *val; + + val = ucv_object_new(vm); + ucv_object_add(val, "freq", ucv_int64_new(bss->freq)); + ucv_object_add(val, "ssid", ucv_string_new_length(bss->ssid, bss->ssid_len)); + ucv_object_add(val, "snr", ucv_int64_new(bss->snr)); + ucv_object_add(val, "signal", ucv_int64_new(bss->level)); + ucv_object_add(val, "noise", ucv_int64_new(bss->noise)); + + return val; +} + void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s) { uc_value_t *val; @@ -71,6 +87,25 @@ void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s) ucv_put(val); } +bool wpas_ucode_bss_allowed(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + uc_value_t *val; + bool ret = true; + + if (wpa_ucode_call_prepare("bss_allowed")) + return true; + + uc_value_push(ucv_string_new(wpa_s->ifname)); + uc_value_push(wpas_ucode_bss_get_uval(bss)); + val = wpa_ucode_call(2); + + if (ucv_type(val) == UC_BOOLEAN) + ret = ucv_boolean_get(val); + ucv_put(val); + + return ret; +} + void wpas_ucode_update_state(struct wpa_supplicant *wpa_s) { const char *state; @@ -203,6 +238,29 @@ out: return ucv_int64_new(ret); } +static void +uc_wpas_iface_status_bss(uc_value_t *ret, struct wpa_bss *bss) +{ + int sec_chan = 0; + const u8 *ie; + + ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= 2) { + const struct ieee80211_ht_operation *ht_oper; + int sec; + + ht_oper = (const void *) (ie + 2); + sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + sec_chan = 1; + else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + sec_chan = -1; + } + + ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan)); + ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq)); +} + static uc_value_t * uc_wpas_iface_status(uc_vm_t *vm, size_t nargs) { @@ -218,25 +276,29 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs) ucv_object_add(ret, "state", ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state))); bss = wpa_s->current_bss; - if (bss) { - int sec_chan = 0; - const u8 *ie; - - ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); - if (ie && ie[1] >= 2) { - const struct ieee80211_ht_operation *ht_oper; - int sec; - - ht_oper = (const void *) (ie + 2); - sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; - if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) - sec_chan = 1; - else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) - sec_chan = -1; + if (bss) + uc_wpas_iface_status_bss(ret, bss); + + if (wpa_s->valid_links) { + unsigned int valid_links = wpa_s->valid_links; + uc_value_t *link, *links; + + links = ucv_array_new(vm); + + for (size_t i = 0; + valid_links && i < ARRAY_SIZE(wpa_s->links); + i++, valid_links >>= 1) { + bss = wpa_s->links[i].bss; + + if (!(valid_links & 1) || !bss) + continue; + + link = ucv_object_new(vm); + uc_wpas_iface_status_bss(link, bss); + ucv_array_set(links, i, link); } - ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan)); - ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq)); + ucv_object_add(ret, "links", links); } #ifdef CONFIG_MESH @@ -276,6 +338,58 @@ uc_wpas_iface_ctrl(uc_vm_t *vm, size_t nargs) return ret; } +static uc_value_t * +uc_wpas_iface_config(uc_vm_t *vm, size_t nargs) +{ + struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface"); + uc_value_t *arg = uc_fn_arg(0); + uc_value_t *val = uc_fn_arg(1); + uc_value_t *ret = NULL; + bool get = nargs == 1; + const char *name; + size_t len = 0; + + if (!wpa_s || ucv_type(arg) != UC_STRING) + return NULL; + + name = ucv_string_get(arg); + if (!strcmp(name, "freq_list")) { + if (get) { + int *cur = wpa_s->conf->freq_list; + if (!cur) + return NULL; + + ret = ucv_array_new(vm); + while (*cur) + ucv_array_set(ret, len++, ucv_int64_new(*(cur++))); + } else { + size_t len = ucv_array_length(val); + int *freq_list; + + if (ucv_type(val) != UC_ARRAY) + return NULL; + + freq_list = calloc(len + 1, sizeof(*freq_list)); + for (size_t i = 0; i < len; i++) { + uc_value_t *cur = ucv_array_get(val, i); + + if (ucv_type(cur) != UC_INTEGER) { + free(freq_list); + return NULL; + } + + freq_list[i] = ucv_int64_get(cur); + } + + free(wpa_s->conf->freq_list); + wpa_s->conf->freq_list = freq_list; + ret = ucv_boolean_new(true); + } + } + + return ret; +} + int wpas_ucode_init(struct wpa_global *gl) { static const uc_function_list_t global_fns[] = { @@ -288,6 +402,7 @@ int wpas_ucode_init(struct wpa_global *gl) static const uc_function_list_t iface_fns[] = { { "status", uc_wpas_iface_status }, { "ctrl", uc_wpas_iface_ctrl }, + { "config", uc_wpas_iface_config }, }; uc_value_t *data, *proto; diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.h b/package/network/services/hostapd/src/wpa_supplicant/ucode.h index a429a0ed87..fd339fa3e9 100644 --- a/package/network/services/hostapd/src/wpa_supplicant/ucode.h +++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.h @@ -20,6 +20,7 @@ void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s); void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s); void wpas_ucode_update_state(struct wpa_supplicant *wpa_s); void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data); +bool wpas_ucode_bss_allowed(struct wpa_supplicant *wpa_s, struct wpa_bss *bss); #else static inline int wpas_ucode_init(struct wpa_global *gl) { @@ -44,6 +45,10 @@ static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, uni { } +static inline bool wpas_ucode_bss_allowed(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + return true; +} #endif #endif