mirror of
https://git.openwrt.org/openwrt/openwrt.git/
synced 2025-10-06 02:52:47 +02:00
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 <nbd@nbd.name>
This commit is contained in:
@@ -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
|
||||
};
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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 {
|
||||
|
@@ -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,6 +78,254 @@ function prepare_config(config, radio)
|
||||
return { config };
|
||||
}
|
||||
|
||||
function phy_dev_open(phy_name)
|
||||
{
|
||||
let phy = wpas.data.config[phy_name];
|
||||
if (!phy) {
|
||||
warn(`Missing phy config for ${phy_name}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
let phydev = phy_open(phy.name, phy.radio);
|
||||
if (!phydev)
|
||||
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,
|
||||
});
|
||||
|
||||
return phydev;
|
||||
}
|
||||
|
||||
function start_pending(phy_name)
|
||||
{
|
||||
let phy = wpas.data.config[phy_name];
|
||||
if (!phy || !phy.data)
|
||||
return;
|
||||
|
||||
let phydev = phy_dev_open(phy_name);
|
||||
if (!phydev) {
|
||||
wpas.printf(`Could not open phy ${phy_name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let ifname in phy.data)
|
||||
iface_start(phydev, phy.data[ifname]);
|
||||
}
|
||||
|
||||
function phy_name(phy, radio)
|
||||
{
|
||||
if (!phy)
|
||||
return null;
|
||||
|
||||
if (radio != null && radio >= 0)
|
||||
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];
|
||||
@@ -95,47 +344,17 @@ function set_config(config_name, phy_name, radio, num_global_macaddr, macaddr_ba
|
||||
phy.macaddr_base = macaddr_base;
|
||||
|
||||
let values = [];
|
||||
let mlo_ifaces = {};
|
||||
for (let config in config_list)
|
||||
push(values, [ config.iface, prepare_config(config) ]);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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]);
|
||||
}
|
||||
|
||||
function phy_name(phy, radio)
|
||||
{
|
||||
if (!phy)
|
||||
return null;
|
||||
|
||||
if (radio != null && radio >= 0)
|
||||
phy += "." + radio;
|
||||
|
||||
return phy;
|
||||
}
|
||||
|
||||
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 ubus = wpas.data.ubus;
|
||||
let status = iface.status();
|
||||
let msg = { phy: phy };
|
||||
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 status = iface.status();
|
||||
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);
|
||||
|
||||
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 iface_data = phy_data.data[ifname];
|
||||
if (!iface_data)
|
||||
return;
|
||||
|
||||
let wdev_config = iface_data.config;
|
||||
if (!wdev_config || wdev_config.mode != "mesh")
|
||||
return;
|
||||
|
||||
wdev_set_mesh_params(ifname, wdev_config);
|
||||
} catch (e) {
|
||||
ex_handler(e);
|
||||
}
|
||||
|
||||
iface_hostapd_notify(phy, ifname, iface, state);
|
||||
|
||||
if (state != "COMPLETED")
|
||||
return;
|
||||
|
||||
let phy_data = wpas.data.config[phy];
|
||||
if (!phy_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;
|
||||
|
||||
wdev_set_mesh_params(ifname, wdev_config);
|
||||
},
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@@ -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 */
|
||||
|
||||
|
@@ -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;
|
||||
if (bss)
|
||||
uc_wpas_iface_status_bss(ret, bss);
|
||||
|
||||
ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
|
||||
if (ie && ie[1] >= 2) {
|
||||
const struct ieee80211_ht_operation *ht_oper;
|
||||
int sec;
|
||||
if (wpa_s->valid_links) {
|
||||
unsigned int valid_links = wpa_s->valid_links;
|
||||
uc_value_t *link, *links;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user