ipq807x: backport latest wifi hot-reload to v5.4 kernel

Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
John Crispin
2023-09-17 06:49:46 +02:00
parent befac1e065
commit acd4b79dd4
22 changed files with 3654 additions and 370 deletions

View File

@@ -1,6 +1,6 @@
import * as nl80211 from "nl80211";
import * as rtnl from "rtnl";
import { readfile } from "fs";
import { readfile, glob, basename, readlink } from "fs";
const iftypes = {
ap: nl80211.const.NL80211_IFTYPE_AP,
@@ -94,6 +94,156 @@ function wdev_create(phy, name, data)
return null;
}
function phy_sysfs_file(phy, name)
{
return trim(readfile(`/sys/class/ieee80211/${phy}/${name}`));
}
function macaddr_split(str)
{
return map(split(str, ":"), (val) => hex(val));
}
function macaddr_join(addr)
{
return join(":", map(addr, (val) => sprintf("%02x", val)));
}
function wdev_macaddr(wdev)
{
return trim(readfile(`/sys/class/net/${wdev}/address`));
}
const phy_proto = {
macaddr_init: function(used, options) {
this.macaddr_options = options ?? {};
this.macaddr_list = {};
if (type(used) == "object")
for (let addr in used)
this.macaddr_list[addr] = used[addr];
else
for (let addr in used)
this.macaddr_list[addr] = -1;
this.for_each_wdev((wdev) => {
let macaddr = wdev_macaddr(wdev);
this.macaddr_list[macaddr] ??= -1;
});
return this.macaddr_list;
},
macaddr_generate: function(data) {
let phy = this.name;
let idx = int(data.id ?? 0);
let mbssid = int(data.mbssid ?? 0) > 0;
let num_global = int(data.num_global ?? 1);
let use_global = !mbssid && idx < num_global;
let base_addr = phy_sysfs_file(phy, "macaddress");
if (!base_addr)
return null;
if (!idx && !mbssid)
return base_addr;
let base_mask = phy_sysfs_file(phy, "address_mask");
if (!base_mask)
return null;
if (base_mask == "00:00:00:00:00:00" && idx >= num_global) {
let addrs = split(phy_sysfs_file(phy, "addresses"), "\n");
if (idx < length(addrs))
return addrs[idx];
base_mask = "ff:ff:ff:ff:ff:ff";
}
let addr = macaddr_split(base_addr);
let mask = macaddr_split(base_mask);
let type;
if (mbssid)
type = "b5";
else if (use_global)
type = "add";
else if (mask[0] > 0)
type = "b1";
else if (mask[5] < 0xff)
type = "b5";
else
type = "add";
switch (type) {
case "b1":
if (!(addr[0] & 2))
idx--;
addr[0] |= 2;
addr[0] ^= idx << 2;
break;
case "b5":
if (mbssid)
addr[0] |= 2;
addr[5] ^= idx;
break;
default:
for (let i = 5; i > 0; i--) {
addr[i] += idx;
if (addr[i] < 256)
break;
addr[i] %= 256;
}
break;
}
return macaddr_join(addr);
},
macaddr_next: function(val) {
let data = this.macaddr_options ?? {};
let list = this.macaddr_list;
for (let i = 0; i < 32; i++) {
data.id = i;
let mac = this.macaddr_generate(data);
if (!mac)
return null;
if (list[mac] != null)
continue;
list[mac] = val != null ? val : -1;
return mac;
}
},
for_each_wdev: function(cb) {
let wdevs = glob(`/sys/class/ieee80211/${this.name}/device/net/*`);
wdevs = map(wdevs, (arg) => basename(arg));
for (let wdev in wdevs) {
if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != this.name)
continue;
cb(wdev);
}
}
};
function phy_open(phy)
{
let phyidx = readfile(`/sys/class/ieee80211/${phy}/index`);
if (!phyidx)
return null;
return proto({
name: phy,
idx: int(phyidx)
}, phy_proto);
}
const vlist_proto = {
update: function(values, arg) {
let data = this.data;
@@ -150,7 +300,7 @@ function is_equal(val1, val2) {
if (!is_equal(val1[key], val2[key]))
return false;
for (let key in val2)
if (!val1[key])
if (val1[key] == null)
return false;
return true;
} else {
@@ -165,4 +315,4 @@ function vlist_new(cb) {
}, vlist_proto);
}
export { wdev_remove, wdev_create, is_equal, vlist_new, phy_is_fullmac };
export { wdev_remove, wdev_create, is_equal, vlist_new, phy_is_fullmac, phy_open };

View File

@@ -725,8 +725,7 @@ hostapd_set_bss_options() {
[ -n "$wpa_strict_rekey" ] && append bss_conf "wpa_strict_rekey=$wpa_strict_rekey" "$N"
}
set_default nasid "${macaddr//\:}"
append bss_conf "nas_identifier=$nasid" "$N"
[ -n "$nasid" ] && append bss_conf "nas_identifier=$nasid" "$N"
[ -n "$acct_server" ] && {
append bss_conf "acct_server_addr=$acct_server" "$N"

View File

@@ -1,6 +1,6 @@
let libubus = require("ubus");
import { open, readfile } from "fs";
import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac } from "common";
import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common";
let ubus = libubus.connect();
@@ -31,7 +31,7 @@ function iface_remove(cfg)
wdev_remove(bss.ifname);
}
function iface_gen_config(phy, config)
function iface_gen_config(phy, config, start_disabled)
{
let str = `data:
${join("\n", config.radio.data)}
@@ -41,18 +41,92 @@ channel=${config.radio.channel}
for (let i = 0; i < length(config.bss); i++) {
let bss = config.bss[i];
let type = i > 0 ? "bss" : "interface";
let nasid = bss.nasid ?? replace(bss.bssid, ":", "");
str += `
${type}=${bss.ifname}
bssid=${bss.bssid}
${join("\n", bss.data)}
nas_identifier=${nasid}
`;
if (start_disabled)
str += `
start_disabled=1
`;
}
return str;
}
function iface_restart(phy, config, old_config)
function iface_freq_info(iface, config, params)
{
let freq = params.frequency;
if (!freq)
return null;
let sec_offset = params.sec_chan_offset;
if (sec_offset != -1 && sec_offset != 1)
sec_offset = 0;
let width = 0;
for (let line in config.radio.data) {
if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
sec_offset = null; // auto-detect
continue;
}
let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth)=(\d+)/);
if (!val)
continue;
val = int(val[2]);
if (val > width)
width = val;
}
if (freq < 4000)
width = 0;
return hostapd.freq_info(freq, sec_offset, width);
}
function iface_add(phy, config, phy_status)
{
let config_inline = iface_gen_config(phy, config, !!phy_status);
let bss = config.bss[0];
let ret = hostapd.add_iface(`bss_config=${bss.ifname}:${config_inline}`);
if (ret < 0)
return false;
if (!phy_status)
return true;
let iface = hostapd.interfaces[bss.ifname];
if (!iface)
return false;
let freq_info = iface_freq_info(iface, config, phy_status);
return iface.start(freq_info) >= 0;
}
function iface_config_macaddr_list(config)
{
let macaddr_list = {};
for (let i = 0; i < length(config.bss); i++) {
let bss = config.bss[i];
if (!bss.default_macaddr)
macaddr_list[bss.bssid] = i;
}
return macaddr_list;
}
function iface_restart(phydev, config, old_config)
{
let phy = phydev.name;
iface_remove(old_config);
iface_remove(config);
@@ -61,15 +135,29 @@ function iface_restart(phy, config, old_config)
return;
}
phydev.macaddr_init(iface_config_macaddr_list(config));
for (let i = 0; i < length(config.bss); i++) {
let bss = config.bss[i];
if (bss.default_macaddr)
bss.bssid = phydev.macaddr_next();
}
let bss = config.bss[0];
let err = wdev_create(phy, bss.ifname, { mode: "ap" });
if (err)
hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
let config_inline = iface_gen_config(phy, config);
let ubus = hostapd.data.ubus;
let phy_status = ubus.call("wpa_supplicant", "phy_status", { phy: phy });
if (phy_status && phy_status.state == "COMPLETED") {
if (iface_add(phy, config, phy_status))
return;
hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
}
ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
if (hostapd.add_iface(`bss_config=${bss.ifname}:${config_inline}`) < 0)
if (!iface_add(phy, config))
hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
}
@@ -111,8 +199,65 @@ function bss_reload_psk(bss, config, old_config)
hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
}
function iface_reload_config(phy, config, old_config)
function remove_file_fields(config)
{
return filter(config, (line) => !hostapd.data.file_fields[split(line, "=")[0]]);
}
function bss_remove_file_fields(config)
{
let new_cfg = {};
for (let key in config)
new_cfg[key] = config[key];
new_cfg.data = remove_file_fields(new_cfg.data);
new_cfg.hash = {};
for (let key in config.hash)
new_cfg.hash[key] = config.hash[key];
delete new_cfg.hash.wpa_psk_file;
delete new_cfg.hash.vlan_file;
return new_cfg;
}
function bss_config_hash(config)
{
return hostapd.sha1(remove_file_fields(config) + "");
}
function bss_find_existing(config, prev_config, prev_hash)
{
let hash = bss_config_hash(config.data);
for (let i = 0; i < length(prev_config.bss); i++) {
if (!prev_hash[i] || hash != prev_hash[i])
continue;
prev_hash[i] = null;
return i;
}
return -1;
}
function get_config_bss(config, idx)
{
if (!config.bss[idx]) {
hostapd.printf(`Invalid bss index ${idx}`);
return null;
}
let ifname = config.bss[idx].ifname;
if (!ifname)
hostapd.printf(`Could not find bss ${config.bss[idx].ifname}`);
return hostapd.bss[ifname];
}
function iface_reload_config(phydev, config, old_config)
{
let phy = phydev.name;
if (!old_config || !is_equal(old_config.radio, config.radio))
return false;
@@ -122,82 +267,230 @@ function iface_reload_config(phy, config, old_config)
if (!old_config.bss || !old_config.bss[0])
return false;
if (config.bss[0].ifname != old_config.bss[0].ifname)
return false;
let iface_name = config.bss[0].ifname;
let iface_name = old_config.bss[0].ifname;
let iface = hostapd.interfaces[iface_name];
if (!iface)
if (!iface) {
hostapd.printf(`Could not find previous interface ${iface_name}`);
return false;
}
let first_bss = hostapd.bss[iface_name];
if (!first_bss)
if (!first_bss) {
hostapd.printf(`Could not find bss of previous interface ${iface_name}`);
return false;
}
let macaddr_list = iface_config_macaddr_list(config);
let bss_list = [];
let bss_list_cfg = [];
let prev_bss_hash = [];
for (let bss in old_config.bss) {
let hash = bss_config_hash(bss.data);
push(prev_bss_hash, bss_config_hash(bss.data));
}
// Step 1: find (possibly renamed) interfaces with the same config
// and store them in the new order (with gaps)
for (let i = 0; i < length(config.bss); i++) {
let prev;
// For fullmac devices, the first interface needs to be preserved,
// since it's treated as the master
if (!i && phy_is_fullmac(phy)) {
prev = 0;
prev_bss_hash[0] = null;
} else {
prev = bss_find_existing(config.bss[i], old_config, prev_bss_hash);
}
if (prev < 0)
continue;
let cur_config = config.bss[i];
let prev_config = old_config.bss[prev];
let prev_bss = get_config_bss(old_config, prev);
if (!prev_bss)
return false;
// try to preserve MAC address of this BSS by reassigning another
// BSS if necessary
if (cur_config.default_macaddr &&
!macaddr_list[prev_config.bssid]) {
macaddr_list[prev_config.bssid] = i;
cur_config.bssid = prev_config.bssid;
}
bss_list[i] = prev_bss;
bss_list_cfg[i] = old_config.bss[prev];
}
if (config.mbssid && !bss_list_cfg[0]) {
hostapd.printf("First BSS changed with MBSSID enabled");
return false;
}
// Step 2: if none were found, rename and preserve the first one
if (length(bss_list) == 0) {
// can't change the bssid of the first bss
if (config.bss[0].bssid != old_config.bss[0].bssid) {
if (!config.bss[0].default_macaddr) {
hostapd.printf(`BSSID of first interface changed: ${lc(old_config.bss[0].bssid)} -> ${lc(config.bss[0].bssid)}`);
return false;
}
config.bss[0].bssid = old_config.bss[0].bssid;
}
let prev_bss = get_config_bss(old_config, 0);
if (!prev_bss)
return false;
macaddr_list[config.bss[0].bssid] = 0;
bss_list[0] = prev_bss;
bss_list_cfg[0] = old_config.bss[0];
prev_bss_hash[0] = null;
}
// Step 3: delete all unused old interfaces
for (let i = 0; i < length(prev_bss_hash); i++) {
if (!prev_bss_hash[i])
continue;
let prev_bss = get_config_bss(old_config, i);
if (!prev_bss)
return false;
let ifname = old_config.bss[i].ifname;
hostapd.printf(`Remove bss '${ifname}' on phy '${phy}'`);
prev_bss.delete();
wdev_remove(ifname);
}
// Step 4: rename preserved interfaces, use temporary name on duplicates
let rename_list = [];
for (let i = 0; i < length(bss_list); i++) {
if (!bss_list[i])
continue;
let old_ifname = bss_list_cfg[i].ifname;
let new_ifname = config.bss[i].ifname;
if (old_ifname == new_ifname)
continue;
if (hostapd.bss[new_ifname]) {
new_ifname = "tmp_" + substr(hostapd.sha1(new_ifname), 0, 8);
push(rename_list, i);
}
hostapd.printf(`Rename bss ${old_ifname} to ${new_ifname}`);
if (!bss_list[i].rename(new_ifname)) {
hostapd.printf(`Failed to rename bss ${old_ifname} to ${new_ifname}`);
return false;
}
bss_list_cfg[i].ifname = new_ifname;
}
// Step 5: rename interfaces with temporary names
for (let i in rename_list) {
let new_ifname = config.bss[i].ifname;
if (!bss_list[i].rename(new_ifname)) {
hostapd.printf(`Failed to rename bss to ${new_ifname}`);
return false;
}
bss_list_cfg[i].ifname = new_ifname;
}
// Step 6: assign BSSID for newly created interfaces
let macaddr_data = {
num_global: config.num_global_macaddr ?? 1,
mbssid: config.mbssid ?? 0,
};
macaddr_list = phydev.macaddr_init(macaddr_list, macaddr_data);
for (let i = 0; i < length(config.bss); i++) {
if (bss_list[i])
continue;
let bsscfg = config.bss[i];
let mac_idx = macaddr_list[bsscfg.bssid];
if (mac_idx < 0)
macaddr_list[bsscfg.bssid] = i;
if (mac_idx == i)
continue;
// statically assigned bssid of the new interface is in conflict
// with the bssid of a reused interface. reassign the reused interface
if (!bsscfg.default_macaddr) {
// can't update bssid of the first BSS, need to restart
if (!mac_idx < 0)
return false;
bsscfg = config.bss[mac_idx];
}
let addr = phydev.macaddr_next(i);
if (!addr) {
hostapd.printf(`Failed to generate mac address for phy ${phy}`);
return false;
}
bsscfg.bssid = addr;
}
let config_inline = iface_gen_config(phy, config);
bss_reload_psk(first_bss, config.bss[0], old_config.bss[0]);
if (!is_equal(config.bss[0], old_config.bss[0])) {
if (phy_is_fullmac(phy))
return false;
// Step 7: fill in the gaps with new interfaces
for (let i = 0; i < length(config.bss); i++) {
let ifname = config.bss[i].ifname;
let bss = bss_list[i];
if (config.bss[0].bssid != old_config.bss[0].bssid)
if (bss)
continue;
hostapd.printf(`Add bss ${ifname} on phy ${phy}`);
bss_list[i] = iface.add_bss(config_inline, i);
if (!bss_list[i]) {
hostapd.printf(`Failed to add new bss ${ifname} on phy ${phy}`);
return false;
}
}
// Step 8: update interface bss order
if (!iface.set_bss_order(bss_list)) {
hostapd.printf(`Failed to update BSS order on phy '${phy}'`);
return false;
}
// Step 9: update config
for (let i = 0; i < length(config.bss); i++) {
if (!bss_list_cfg[i])
continue;
let ifname = config.bss[i].ifname;
let bss = bss_list[i];
if (is_equal(config.bss[i], bss_list_cfg[i]))
continue;
if (is_equal(bss_remove_file_fields(config.bss[i]),
bss_remove_file_fields(bss_list_cfg[i]))) {
hostapd.printf(`Update config data files for bss ${ifname}`);
if (bss.set_config(config_inline, i, true) < 0) {
hostapd.printf(`Could not update config data files for bss ${ifname}`);
return false;
} else {
bss.ctrl("RELOAD_WPA_PSK");
continue;
}
}
bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
if (is_equal(config.bss[i], bss_list_cfg[i]))
continue;
hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
if (first_bss.set_config(config_inline, 0) < 0) {
hostapd.printf(`Failed to set config`);
return false;
}
}
let new_cfg = array_to_obj(config.bss, "ifname", 1);
let old_cfg = array_to_obj(old_config.bss, "ifname", 1);
for (let name in old_cfg) {
let bss = hostapd.bss[name];
if (!bss) {
hostapd.printf(`bss '${name}' not found`);
return false;
}
if (!new_cfg[name]) {
hostapd.printf(`Remove bss '${name}' on phy '${phy}'`);
bss.delete();
wdev_remove(name);
continue;
}
let new_cfg_data = new_cfg[name];
delete new_cfg[name];
if (is_equal(old_cfg[name], new_cfg_data))
continue;
hostapd.printf(`Reload config for bss '${name}' on phy '${phy}'`);
let idx = find_array_idx(config.bss, "ifname", name);
if (idx < 0) {
hostapd.printf(`bss index not found`);
return false;
}
if (bss.set_config(config_inline, idx) < 0) {
hostapd.printf(`Failed to set config`);
return false;
}
}
for (let name in new_cfg) {
hostapd.printf(`Add bss '${name}' on phy '${phy}'`);
let idx = find_array_idx(config.bss, "ifname", name);
if (idx < 0) {
hostapd.printf(`bss index not found`);
return false;
}
if (iface.add_bss(config_inline, idx) < 0) {
hostapd.printf(`Failed to add bss`);
if (bss.set_config(config_inline, i) < 0) {
hostapd.printf(`Failed to set config for bss ${ifname}`);
return false;
}
}
@@ -205,6 +498,14 @@ function iface_reload_config(phy, config, old_config)
return true;
}
function iface_update_supplicant_macaddr(phy, config)
{
let macaddr_list = [];
for (let i = 0; i < length(config.bss); i++)
push(macaddr_list, config.bss[i].bssid);
ubus.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
}
function iface_set_config(phy, config)
{
let old_config = hostapd.data.config[phy];
@@ -214,14 +515,28 @@ function iface_set_config(phy, config)
if (!config)
return iface_remove(old_config);
let ret = iface_reload_config(phy, config, old_config);
if (ret) {
hostapd.printf(`Reloaded settings for phy ${phy}`);
return 0;
let phydev = phy_open(phy);
if (!phydev) {
hostapd.printf(`Failed to open phy ${phy}`);
return false;
}
try {
let ret = iface_reload_config(phydev, config, old_config);
if (ret) {
iface_update_supplicant_macaddr(phy, config);
hostapd.printf(`Reloaded settings for phy ${phy}`);
return 0;
}
} catch (e) {
hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
}
hostapd.printf(`Restart interface for phy ${phy}`);
return iface_restart(phy, config, old_config);
let ret = iface_restart(phydev, config, old_config);
iface_update_supplicant_macaddr(phy, config);
return ret;
}
function config_add_bss(config, name)
@@ -268,16 +583,28 @@ function iface_load_config(filename)
continue;
}
if (val[0] == "#num_global_macaddr" ||
val[0] == "mbssid")
config[val[0]] = int(val[1]);
push(config.radio.data, line);
}
while ((line = trim(f.read("line"))) != null) {
if (line == "#default_macaddr")
bss.default_macaddr = true;
let val = split(line, "=", 2);
if (!val[0])
continue;
if (val[0] == "bssid")
bss.bssid = val[1];
if (val[0] == "bssid") {
bss.bssid = lc(val[1]);
continue;
}
if (val[0] == "nas_identifier")
bss.nasid = val[1];
if (val[0] == "bss") {
bss = config_add_bss(config, val[1]);
@@ -294,28 +621,33 @@ function iface_load_config(filename)
return config;
}
function ex_wrap(func) {
return (req) => {
try {
let ret = func(req);
return ret;
} catch(e) {
hostapd.printf(`Exception in ubus function: ${e}\n${e.stacktrace[0].context}`);
}
return libubus.STATUS_UNKNOWN_ERROR;
};
}
let main_obj = {
reload: {
args: {
phy: "",
},
call: function(req) {
try {
let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
for (let phy_name in phy_list) {
let phy = hostapd.data.config[phy_name];
let config = iface_load_config(phy.orig_file);
iface_set_config(phy_name, config);
}
} catch(e) {
hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
return libubus.STATUS_INVALID_ARGUMENT;
call: ex_wrap(function(req) {
let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
for (let phy_name in phy_list) {
let phy = hostapd.data.config[phy_name];
let config = iface_load_config(phy.orig_file);
iface_set_config(phy_name, config);
}
return 0;
}
})
},
apsta_state: {
args: {
@@ -326,7 +658,7 @@ let main_obj = {
csa: true,
csa_count: 0,
},
call: function(req) {
call: ex_wrap(function(req) {
if (req.args.up == null || !req.args.phy)
return libubus.STATUS_INVALID_ARGUMENT;
@@ -344,34 +676,10 @@ let main_obj = {
return 0;
}
let freq = req.args.frequency;
if (!freq)
if (!req.args.frequency)
return libubus.STATUS_INVALID_ARGUMENT;
let sec_offset = req.args.sec_chan_offset;
if (sec_offset != -1 && sec_offset != 1)
sec_offset = 0;
let width = 0;
for (let line in config.radio.data) {
if (!sec_offset && match(line, /^ht_capab=.*HT40/)) {
sec_offset = null; // auto-detect
continue;
}
let val = match(line, /^(vht_oper_chwidth|he_oper_chwidth)=(\d+)/);
if (!val)
continue;
val = int(val[2]);
if (val > width)
width = val;
}
if (freq < 4000)
width = 0;
let freq_info = hostapd.freq_info(freq, sec_offset, width);
let freq_info = iface_freq_info(iface, config, req.args);
if (!freq_info)
return libubus.STATUS_UNKNOWN_ERROR;
@@ -380,14 +688,34 @@ let main_obj = {
freq_info.csa_count = req.args.csa_count ?? 10;
ret = iface.switch_channel(freq_info);
} else {
iface.stop();
ret = iface.start(freq_info);
}
if (!ret)
return libubus.STATUS_UNKNOWN_ERROR;
return 0;
}
})
},
config_get_macaddr_list: {
args: {
phy: ""
},
call: ex_wrap(function(req) {
let phy = req.args.phy;
if (!phy)
return libubus.STATUS_INVALID_ARGUMENT;
let ret = {
macaddr: [],
};
let config = hostapd.data.config[phy];
if (!config)
return ret;
ret.macaddr = map(config.bss, (bss) => bss.bssid);
return ret;
})
},
config_set: {
args: {
@@ -395,7 +723,7 @@ let main_obj = {
config: "",
prev_config: "",
},
call: function(req) {
call: ex_wrap(function(req) {
let phy = req.args.phy;
let file = req.args.config;
let prev_file = req.args.prev_config;
@@ -403,34 +731,29 @@ let main_obj = {
if (!phy)
return libubus.STATUS_INVALID_ARGUMENT;
try {
if (prev_file && !hostapd.data.config[phy]) {
let config = iface_load_config(prev_file);
if (config)
config.radio.data = [];
hostapd.data.config[phy] = config;
}
let config = iface_load_config(file);
hostapd.printf(`Set new config for phy ${phy}: ${file}`);
iface_set_config(phy, config);
} catch(e) {
hostapd.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
return libubus.STATUS_INVALID_ARGUMENT;
if (prev_file && !hostapd.data.config[phy]) {
let config = iface_load_config(prev_file);
if (config)
config.radio.data = [];
hostapd.data.config[phy] = config;
}
let config = iface_load_config(file);
hostapd.printf(`Set new config for phy ${phy}: ${file}`);
iface_set_config(phy, config);
return {
pid: hostapd.getpid()
};
}
})
},
config_add: {
args: {
iface: "",
config: "",
},
call: function(req) {
call: ex_wrap(function(req) {
if (!req.args.iface || !req.args.config)
return libubus.STATUS_INVALID_ARGUMENT;
@@ -440,19 +763,19 @@ let main_obj = {
return {
pid: hostapd.getpid()
};
}
})
},
config_remove: {
args: {
iface: ""
},
call: function(req) {
call: ex_wrap(function(req) {
if (!req.args.iface)
return libubus.STATUS_INVALID_ARGUMENT;
hostapd.remove_iface(req.args.iface);
return 0;
}
})
},
};

View File

@@ -1,11 +1,14 @@
#!/usr/bin/env ucode
'use strict';
import { vlist_new, is_equal, wdev_create, wdev_remove } from "/usr/share/hostap/common.uc";
import { vlist_new, is_equal, wdev_create, wdev_remove, phy_open } from "/usr/share/hostap/common.uc";
import { readfile, writefile, basename, readlink, glob } from "fs";
let libubus = require("ubus");
let keep_devices = {};
let phy = shift(ARGV);
let new_config = shift(ARGV);
let command = shift(ARGV);
let phydev;
const mesh_params = [
"mesh_retry_timeout", "mesh_confirm_timeout", "mesh_holding_timeout", "mesh_max_peer_links",
"mesh_max_retries", "mesh_ttl", "mesh_element_ttl", "mesh_hwmp_max_preq_retries",
@@ -33,6 +36,11 @@ function iface_start(wdev)
system([ "ip", "link", "set", "dev", ifname, "down" ]);
wdev_remove(ifname);
}
let wdev_config = {};
for (let key in wdev)
wdev_config[key] = wdev[key];
if (!wdev_config.macaddr && wdev.mode != "monitor")
wdev_config.macaddr = phydev.macaddr_next();
wdev_create(phy, ifname, wdev);
system([ "ip", "link", "set", "dev", ifname, "up" ]);
if (wdev.freq)
@@ -47,7 +55,7 @@ function iface_start(wdev)
system(cmd);
} else if (wdev.mode == "mesh") {
let cmd = [ "iw", "dev", ifname, "mesh", "join", wdev.ssid, "freq", wdev.freq, wdev.htmode ];
for (let key in [ "beacon-interval", "mcast-rate" ])
for (let key in [ "mcast-rate", "beacon-interval" ])
if (wdev[key])
push(cmd, key, wdev[key]);
system(cmd);
@@ -114,43 +122,86 @@ function add_existing(phy, config)
}
}
function usage()
{
warn(`Usage: ${basename(sourcepath())} <phy> <command> [<arguments>]
let statefile = `/var/run/wdev-${phy}.json`;
for (let dev in ARGV)
keep_devices[dev] = true;
if (!phy || !new_config) {
warn(`Usage: ${basename(sourcepath())} <phy> <config> [<device]...]\n`);
Commands:
set_config <config> [<device]...] - set phy configuration
get_macaddr <id> - get phy MAC address for vif index <id>
`);
exit(1);
}
if (!readfile(`/sys/class/ieee80211/${phy}/index`)) {
const commands = {
set_config: function(args) {
let statefile = `/var/run/wdev-${phy}.json`;
let new_config = shift(args);
for (let dev in ARGV)
keep_devices[dev] = true;
if (!new_config)
usage();
new_config = json(new_config);
if (!new_config) {
warn("Invalid configuration\n");
exit(1);
}
let old_config = readfile(statefile);
if (old_config)
old_config = json(old_config);
let config = vlist_new(iface_cb);
if (type(old_config) == "object")
config.data = old_config;
add_existing(phy, config.data);
add_ifname(config.data);
drop_inactive(config.data);
let ubus = libubus.connect();
let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phy });
let macaddr_list = [];
if (type(data) == "object" && data.macaddr)
macaddr_list = data.macaddr;
ubus.disconnect();
phydev.macaddr_init(macaddr_list);
add_ifname(new_config);
config.update(new_config);
drop_inactive(config.data);
delete_ifname(config.data);
writefile(statefile, sprintf("%J", config.data));
},
get_macaddr: function(args) {
let data = {};
for (let arg in args) {
arg = split(arg, "=", 2);
data[arg[0]] = arg[1];
}
let macaddr = phydev.macaddr_generate(data);
if (!macaddr) {
warn(`Could not get MAC address for phy ${phy}\n`);
exit(1);
}
print(macaddr + "\n");
},
};
if (!phy || !command | !commands[command])
usage();
phydev = phy_open(phy);
if (!phydev) {
warn(`PHY ${phy} does not exist\n`);
exit(1);
}
new_config = json(new_config);
if (!new_config) {
warn("Invalid configuration\n");
exit(1);
}
let old_config = readfile(statefile);
if (old_config)
old_config = json(old_config);
let config = vlist_new(iface_cb);
if (type(old_config) == "object")
config.data = old_config;
add_existing(phy, config.data);
add_ifname(config.data);
drop_inactive(config.data);
add_ifname(new_config);
config.update(new_config);
drop_inactive(config.data);
delete_ifname(config.data);
writefile(statefile, sprintf("%J", config.data));
commands[command](ARGV);

View File

@@ -1,11 +1,12 @@
let libubus = require("ubus");
import { open, readfile } from "fs";
import { wdev_create, wdev_remove, is_equal, vlist_new } from "common";
import { wdev_create, wdev_remove, is_equal, vlist_new, phy_open } from "common";
let ubus = libubus.connect();
wpas.data.config = {};
wpas.data.iface_phy = {};
wpas.data.macaddr_list = {};
function iface_stop(iface)
{
@@ -20,16 +21,23 @@ function iface_stop(iface)
iface.running = false;
}
function iface_start(phy, iface)
function iface_start(phydev, iface, macaddr_list)
{
let phy = phydev.name;
if (iface.running)
return;
let ifname = iface.config.iface;
let wdev_config = {};
for (let field in iface.config)
wdev_config[field] = iface.config[field];
if (!wdev_config.macaddr)
wdev_config.macaddr = phydev.macaddr_next();
wpas.data.iface_phy[ifname] = phy;
wdev_remove(ifname);
let ret = wdev_create(phy, ifname, iface.config);
let ret = wdev_create(phy, ifname, wdev_config);
if (ret)
wpas.printf(`Failed to create device ${ifname}: ${ret}`);
wpas.add_iface(iface.config);
@@ -43,6 +51,11 @@ function iface_cb(new_if, old_if)
return;
}
if (new_if && old_if)
wpas.printf(`Update configuration for interface ${old_if.config.iface}`);
else if (old_if)
wpas.printf(`Remove interface ${old_if.config.iface}`);
if (old_if)
iface_stop(old_if);
}
@@ -73,9 +86,22 @@ function set_config(phy_name, config_list)
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);
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);
for (let ifname in phy.data)
iface_start(phy_name, phy.data[ifname]);
iface_start(phydev, phy.data[ifname]);
}
let main_obj = {
@@ -106,6 +132,55 @@ let main_obj = {
return 0;
}
},
phy_set_macaddr_list: {
args: {
phy: "",
macaddr: [],
},
call: function(req) {
let phy = req.args.phy;
if (!phy)
return libubus.STATUS_INVALID_ARGUMENT;
wpas.data.macaddr_list[phy] = req.args.macaddr;
return 0;
}
},
phy_status: {
args: {
phy: ""
},
call: function(req) {
if (!req.args.phy)
return libubus.STATUS_INVALID_ARGUMENT;
let phy = wpas.data.config[req.args.phy];
if (!phy)
return libubus.STATUS_NOT_FOUND;
for (let ifname in phy.data) {
try {
let iface = wpas.interfaces[ifname];
if (!iface)
continue;
let status = iface.status();
if (!status)
continue;
if (status.state == "INTERFACE_DISABLED")
continue;
status.ifname = ifname;
return status;
} catch (e) {
continue;
}
}
return libubus.STATUS_NOT_FOUND;
}
},
config_set: {
args: {
phy: "",
@@ -116,6 +191,7 @@ let main_obj = {
if (!req.args.phy)
return libubus.STATUS_INVALID_ARGUMENT;
wpas.printf(`Set new config for phy ${req.args.phy}`);
try {
if (req.args.config)
set_config(req.args.phy, req.args.config);
@@ -188,6 +264,7 @@ function iface_hostapd_notify(phy, ifname, iface, state)
switch (state) {
case "DISCONNECTED":
case "AUTHENTICATING":
case "SCANNING":
msg.up = false;
break;
case "INTERFACE_DISABLED":

View File

@@ -0,0 +1,43 @@
From: Harshitha Prem <quic_hprem@quicinc.com>
Date: Wed, 22 Feb 2023 09:29:01 +0530
Subject: [PATCH] nl80211: Add frequency info in start AP command
When ACS is configured in multiple BSS case, sometimes a virtual AP
interface does not come up as the channel context information between
different BSSs of the same band does not match.
Same behavior is observed in case of multiple band/hardware under a
single wiphy, when we bring up multiple virtual interface in various
bands simultaneously and the kernel maps a random channel as it has more
than one channel context, e.g., say a 2.4 GHz channel to a 5 GHz virtual
AP interface when the start AP command is sent. This is because the
frequency information is not present in the command.
Add the frequency information into the start AP netlink command so that
the kernel maps the appropriate channel context by parsing it instead of
using a previous set channel information.
Signed-off-by: Harshitha Prem <quic_hprem@quicinc.com>
---
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -167,6 +167,8 @@ static int nl80211_send_frame_cmd(struct
const u16 *csa_offs, size_t csa_offs_len);
static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
int report);
+static int nl80211_put_freq_params(struct nl_msg *msg,
+ const struct hostapd_freq_params *freq);
#define IFIDX_ANY -1
@@ -4717,6 +4719,9 @@ static int wpa_driver_nl80211_set_ap(voi
nla_nest_end(msg, spr);
}
+ if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
+ goto fail;
+
if (params->freq && params->freq->he_enabled) {
struct nlattr *bss_color;

View File

@@ -0,0 +1,20 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Thu, 14 Sep 2023 10:53:50 +0200
Subject: [PATCH] driver_nl80211: fix setting QoS map on secondary BSSs
The setting is per-BSS, not per PHY
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -10045,7 +10045,7 @@ static int nl80211_set_qos_map(void *pri
wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
qos_map_set, qos_map_set_len);
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_QOS_MAP)) ||
nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
nlmsg_free(msg);
return -ENOBUFS;

View File

@@ -0,0 +1,18 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Thu, 14 Sep 2023 11:28:03 +0200
Subject: [PATCH] driver_nl80211: update drv->ifindex on removing the first
BSS
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -8003,6 +8003,7 @@ static int wpa_driver_nl80211_if_remove(
if (drv->first_bss->next) {
drv->first_bss = drv->first_bss->next;
drv->ctx = drv->first_bss->ctx;
+ drv->ifindex = drv->first_bss->ifindex;
os_free(bss);
} else {
wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");

View File

@@ -287,7 +287,33 @@
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -5827,6 +5827,7 @@ union wpa_event_data {
@@ -3357,6 +3357,25 @@ struct wpa_driver_ops {
const char *ifname);
/**
+ * if_rename - Rename a virtual interface
+ * @priv: Private driver interface data
+ * @type: Interface type
+ * @ifname: Interface name of the virtual interface to be renamed
+ * (NULL when renaming the AP BSS interface)
+ * @new_name: New interface name of the virtual interface
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*if_rename)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, const char *new_name);
+
+ /**
+ * set_first_bss - Make a virtual interface the first (primary) bss
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_first_bss)(void *priv);
+
+ /**
* set_sta_vlan - Bind a station into a specific interface (AP only)
* @priv: Private driver interface data
* @ifname: Interface (main or virtual BSS or VLAN)
@@ -5827,6 +5846,7 @@ union wpa_event_data {
/**
* struct ch_switch
@@ -295,7 +321,7 @@
* @freq: Frequency of new channel in MHz
* @ht_enabled: Whether this is an HT channel
* @ch_offset: Secondary channel offset
@@ -5835,6 +5836,7 @@ union wpa_event_data {
@@ -5835,6 +5855,7 @@ union wpa_event_data {
* @cf2: Center frequency 2
*/
struct ch_switch {
@@ -348,3 +374,187 @@
switch (event) {
case EVENT_AUTH:
#ifdef CONFIG_FST
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -367,6 +367,23 @@ static inline int hostapd_drv_stop_ap(st
return hapd->driver->stop_ap(hapd->drv_priv);
}
+static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
+ enum wpa_driver_if_type type,
+ const char *ifname,
+ const char *new_name)
+{
+ if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
+ return -1;
+ return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
+}
+
+static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
+{
+ if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->set_first_bss(hapd->drv_priv);
+}
+
static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
struct wpa_channel_info *ci)
{
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1222,7 +1222,7 @@ static void wpa_driver_nl80211_event_rtm
}
wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
namebuf, ifname);
- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+ if (drv->first_bss->ifindex != ifi->ifi_index) {
wpa_printf(MSG_DEBUG,
"nl80211: Not the main interface (%s) - do not indicate interface down",
drv->first_bss->ifname);
@@ -1258,7 +1258,7 @@ static void wpa_driver_nl80211_event_rtm
}
wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
namebuf, ifname);
- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+ if (drv->first_bss->ifindex != ifi->ifi_index) {
wpa_printf(MSG_DEBUG,
"nl80211: Not the main interface (%s) - do not indicate interface up",
drv->first_bss->ifname);
@@ -7609,6 +7609,7 @@ static void *i802_init(struct hostapd_da
char master_ifname[IFNAMSIZ];
int ifindex, br_ifindex = 0;
int br_added = 0;
+ int err;
bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
params->global_priv, 1,
@@ -7668,21 +7669,17 @@ static void *i802_init(struct hostapd_da
(params->num_bridge == 0 || !params->bridge[0]))
add_ifidx(drv, br_ifindex, drv->ifindex);
- if (bss->added_if_into_bridge || bss->already_in_bridge) {
- int err;
-
- drv->rtnl_sk = nl_socket_alloc();
- if (drv->rtnl_sk == NULL) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
- goto failed;
- }
+ drv->rtnl_sk = nl_socket_alloc();
+ if (drv->rtnl_sk == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+ goto failed;
+ }
- err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
- if (err) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
- nl_geterror(err));
- goto failed;
- }
+ err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
+ if (err) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+ nl_geterror(err));
+ goto failed;
}
if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
@@ -8041,6 +8038,50 @@ static int wpa_driver_nl80211_if_remove(
return 0;
}
+static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
+ enum wpa_driver_if_type type,
+ const char *ifname, const char *new_name)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ifinfomsg ifi = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = bss->ifindex,
+ };
+ struct nl_msg *msg;
+ int res = -ENOMEM;
+
+ if (ifname)
+ ifi.ifi_index = if_nametoindex(ifname);
+
+ msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
+ if (!msg)
+ return res;
+
+ if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
+ goto out;
+
+ if (nla_put_string(msg, IFLA_IFNAME, new_name))
+ goto out;
+
+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
+ if (res < 0)
+ goto out;
+
+ res = nl_wait_for_ack(drv->rtnl_sk);
+ if (res) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Renaming device %s to %s failed: %s",
+ ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
+ goto out;
+ }
+
+ if (type == WPA_IF_AP_BSS && !ifname)
+ os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
+
+out:
+ nlmsg_free(msg);
+ return res;
+}
static int cookie_handler(struct nl_msg *msg, void *arg)
{
@@ -9385,6 +9426,37 @@ static int driver_nl80211_if_remove(void
}
+static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, const char *new_name)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
+}
+
+
+static int driver_nl80211_set_first_bss(void *priv)
+{
+ struct i802_bss *bss = priv, *tbss;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (drv->first_bss == bss)
+ return 0;
+
+ for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
+ if (tbss->next != bss)
+ continue;
+
+ tbss->next = bss->next;
+ bss->next = drv->first_bss;
+ drv->first_bss = bss;
+ drv->ctx = bss->ctx;
+ return 0;
+ }
+
+ return -1;
+}
+
+
static int driver_nl80211_send_mlme(void *priv, const u8 *data,
size_t data_len, int noack,
unsigned int freq,
@@ -11967,6 +12039,8 @@ const struct wpa_driver_ops wpa_driver_n
.set_acl = wpa_driver_nl80211_set_acl,
.if_add = wpa_driver_nl80211_if_add,
.if_remove = driver_nl80211_if_remove,
+ .if_rename = driver_nl80211_if_rename,
+ .set_first_bss = driver_nl80211_set_first_bss,
.send_mlme = driver_nl80211_send_mlme,
.get_hw_feature_data = nl80211_get_hw_feature_data,
.sta_add = wpa_driver_nl80211_sta_add,

View File

@@ -19,18 +19,6 @@
enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
--- a/src/ap/ubus.c
+++ b/src/ap/ubus.c
@@ -424,6 +424,9 @@ hostapd_bss_get_status(struct ubus_conte
hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
blobmsg_close_table(&b, dfs_table);
+ if (hapd->conf->uci_section)
+ blobmsg_add_string(&b, "uci_section", hapd->conf->uci_section);
+
ubus_send_reply(ctx, req, b.head);
return 0;
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -785,6 +785,7 @@ void hostapd_config_free_bss(struct host

View File

@@ -50,42 +50,4 @@
return NL_SKIP;
}
--- a/src/ap/ubus.c
+++ b/src/ap/ubus.c
@@ -306,6 +306,36 @@ hostapd_bss_get_clients(struct ubus_cont
blobmsg_add_u32(&b, "tx", sta_driver_data.current_tx_rate * 100);
blobmsg_close_table(&b, r);
blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
+
+ r = blobmsg_open_table(&b, "mcs");
+ if (sta_driver_data.rx_hemcs) {
+ blobmsg_add_u32(&b, "he", 1);
+ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_hemcs);
+ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_hemcs);
+ } else if (sta_driver_data.rx_vhtmcs) {
+ blobmsg_add_u32(&b, "vht", 1);
+ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_vhtmcs);
+ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_vhtmcs);
+ } else {
+ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_mcs);
+ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_mcs);
+ }
+ blobmsg_close_table(&b, r);
+
+ r = blobmsg_open_table(&b, "nss");
+ if (sta_driver_data.rx_he_nss) {
+ blobmsg_add_u32(&b, "he", 1);
+ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_he_nss);
+ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_he_nss);
+ } else if (sta_driver_data.rx_vht_nss) {
+ blobmsg_add_u32(&b, "vht", 1);
+ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_vht_nss);
+ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_vht_nss);
+ } else {
+ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_mcs);
+ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_mcs);
+ }
+ blobmsg_close_table(&b, r);
}
hostapd_parse_capab_blobmsg(sta);

View File

@@ -61,28 +61,6 @@
};
--- a/src/ap/ubus.c
+++ b/src/ap/ubus.c
@@ -336,6 +336,9 @@ hostapd_bss_get_clients(struct ubus_cont
blobmsg_add_u32(&b, "tx", sta_driver_data.tx_mcs);
}
blobmsg_close_table(&b, r);
+
+ if (sta->signal_mgmt)
+ blobmsg_add_u32(&b, "signal_mgmt", sta->signal_mgmt);
}
hostapd_parse_capab_blobmsg(sta);
@@ -457,6 +460,9 @@ hostapd_bss_get_status(struct ubus_conte
if (hapd->conf->uci_section)
blobmsg_add_string(&b, "uci_section", hapd->conf->uci_section);
+ if (hapd->signal_mgmt)
+ blobmsg_add_u32(&b, "signal_mgmt", hapd->signal_mgmt);
+
ubus_send_reply(ctx, req, b.head);
return 0;
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -451,6 +451,7 @@ struct hostapd_data {

View File

@@ -306,6 +306,39 @@ hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
blobmsg_add_u32(&b, "tx", sta_driver_data.current_tx_rate * 100);
blobmsg_close_table(&b, r);
blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
r = blobmsg_open_table(&b, "mcs");
if (sta_driver_data.rx_hemcs) {
blobmsg_add_u32(&b, "he", 1);
blobmsg_add_u32(&b, "rx", sta_driver_data.rx_hemcs);
blobmsg_add_u32(&b, "tx", sta_driver_data.tx_hemcs);
} else if (sta_driver_data.rx_vhtmcs) {
blobmsg_add_u32(&b, "vht", 1);
blobmsg_add_u32(&b, "rx", sta_driver_data.rx_vhtmcs);
blobmsg_add_u32(&b, "tx", sta_driver_data.tx_vhtmcs);
} else {
blobmsg_add_u32(&b, "rx", sta_driver_data.rx_mcs);
blobmsg_add_u32(&b, "tx", sta_driver_data.tx_mcs);
}
blobmsg_close_table(&b, r);
r = blobmsg_open_table(&b, "nss");
if (sta_driver_data.rx_he_nss) {
blobmsg_add_u32(&b, "he", 1);
blobmsg_add_u32(&b, "rx", sta_driver_data.rx_he_nss);
blobmsg_add_u32(&b, "tx", sta_driver_data.tx_he_nss);
} else if (sta_driver_data.rx_vht_nss) {
blobmsg_add_u32(&b, "vht", 1);
blobmsg_add_u32(&b, "rx", sta_driver_data.rx_vht_nss);
blobmsg_add_u32(&b, "tx", sta_driver_data.tx_vht_nss);
} else {
blobmsg_add_u32(&b, "rx", sta_driver_data.rx_mcs);
blobmsg_add_u32(&b, "tx", sta_driver_data.tx_mcs);
}
blobmsg_close_table(&b, r);
if (sta->signal_mgmt)
blobmsg_add_u32(&b, "signal_mgmt", sta->signal_mgmt);
}
hostapd_parse_capab_blobmsg(sta);
@@ -424,6 +457,12 @@ hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
blobmsg_close_table(&b, dfs_table);
if (hapd->conf->uci_section)
blobmsg_add_string(&b, "uci_section", hapd->conf->uci_section);
if (hapd->signal_mgmt)
blobmsg_add_u32(&b, "signal_mgmt", hapd->signal_mgmt);
ubus_send_reply(ctx, req, b.head);
return 0;

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,8 @@
#include "beacon.h"
#include "hw_features.h"
#include "ap_drv_ops.h"
#include "dfs.h"
#include "acs.h"
#include <libubox/uloop.h>
static uc_resource_type_t *global_type, *bss_type, *iface_type;
@@ -109,6 +111,94 @@ uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs)
return NULL;
}
static struct hostapd_vlan *
bss_conf_find_vlan(struct hostapd_bss_config *bss, int id)
{
struct hostapd_vlan *vlan;
for (vlan = bss->vlan; vlan; vlan = vlan->next)
if (vlan->vlan_id == id)
return vlan;
return NULL;
}
static int
bss_conf_rename_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
const char *ifname)
{
if (!strcmp(ifname, vlan->ifname))
return 0;
hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, vlan->ifname, ifname);
os_strlcpy(vlan->ifname, ifname, sizeof(vlan->ifname));
return 0;
}
static int
bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss)
{
struct hostapd_bss_config *old_bss = hapd->conf;
struct hostapd_vlan *vlan, *vlan_new, *wildcard;
char ifname[IFNAMSIZ + 1], vlan_ifname[IFNAMSIZ + 1], *pos;
int ret;
vlan = bss_conf_find_vlan(old_bss, VLAN_ID_WILDCARD);
wildcard = bss_conf_find_vlan(bss, VLAN_ID_WILDCARD);
if (!!vlan != !!wildcard)
return -1;
if (vlan && wildcard && strcmp(vlan->ifname, wildcard->ifname) != 0)
strcpy(vlan->ifname, wildcard->ifname);
else
wildcard = NULL;
for (vlan = bss->vlan; vlan; vlan = vlan->next) {
if (vlan->vlan_id == VLAN_ID_WILDCARD ||
vlan->dynamic_vlan > 0)
continue;
if (!bss_conf_find_vlan(old_bss, vlan->vlan_id))
return -1;
}
for (vlan = old_bss->vlan; vlan; vlan = vlan->next) {
if (vlan->vlan_id == VLAN_ID_WILDCARD)
continue;
if (vlan->dynamic_vlan == 0) {
vlan_new = bss_conf_find_vlan(bss, vlan->vlan_id);
if (!vlan_new)
return -1;
if (bss_conf_rename_vlan(hapd, vlan, vlan_new->ifname))
return -1;
continue;
}
if (!wildcard)
continue;
os_strlcpy(ifname, wildcard->ifname, sizeof(ifname));
pos = os_strchr(ifname, '#');
if (!pos)
return -1;
*pos++ = '\0';
ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s%d%s",
ifname, vlan->vlan_id, pos);
if (os_snprintf_error(sizeof(vlan_ifname), ret))
return -1;
if (bss_conf_rename_vlan(hapd, vlan, vlan_ifname))
return -1;
}
return 0;
}
static uc_value_t *
uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
{
@@ -118,6 +208,7 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
struct hostapd_config *conf;
uc_value_t *file = uc_fn_arg(0);
uc_value_t *index = uc_fn_arg(1);
uc_value_t *files_only = uc_fn_arg(2);
unsigned int i, idx = 0;
int ret = -1;
@@ -129,9 +220,28 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
iface = hapd->iface;
conf = interfaces->config_read_cb(ucv_string_get(file));
if (!conf || idx > conf->num_bss || !conf->bss[idx])
if (!conf)
goto out;
if (idx > conf->num_bss || !conf->bss[idx])
goto free;
if (ucv_boolean_get(files_only)) {
struct hostapd_bss_config *bss = conf->bss[idx];
struct hostapd_bss_config *old_bss = hapd->conf;
#define swap_field(name) \
do { \
void *ptr = old_bss->name; \
old_bss->name = bss->name; \
bss->name = ptr; \
} while (0)
swap_field(ssid.wpa_psk_file);
ret = bss_reload_vlans(hapd, bss);
goto done;
}
hostapd_bss_deinit_no_free(hapd);
hostapd_drv_stop_ap(hapd);
hostapd_free_hapd_data(hapd);
@@ -142,12 +252,14 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
iface->conf->bss[i] = conf->bss[idx];
hapd->conf = conf->bss[idx];
conf->bss[idx] = old_bss;
hostapd_config_free(conf);
hostapd_setup_bss(hapd, hapd == iface->bss[0], !iface->conf->multiple_bssid);
hostapd_setup_bss(hapd, hapd == iface->bss[0], true);
hostapd_ucode_update_interfaces();
done:
ret = 0;
free:
hostapd_config_free(conf);
out:
return ucv_int64_new(ret);
}
@@ -178,10 +290,15 @@ uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
struct hostapd_iface *iface;
int i, idx;
if (!hapd || hapd == hapd->iface->bss[0])
if (!hapd)
return NULL;
iface = hapd->iface;
if (iface->num_bss == 1) {
wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface);
return NULL;
}
for (idx = 0; idx < iface->num_bss; idx++)
if (iface->bss[idx] == hapd)
break;
@@ -191,8 +308,13 @@ uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
for (i = idx + 1; i < iface->num_bss; i++)
iface->bss[i - 1] = iface->bss[i];
iface->num_bss--;
iface->bss[0]->interface_added = 0;
hostapd_drv_set_first_bss(iface->bss[0]);
hapd->interface_added = 1;
hostapd_drv_stop_ap(hapd);
hostapd_bss_deinit(hapd);
hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
@@ -266,6 +388,58 @@ out:
return ret;
}
static uc_value_t *
uc_hostapd_iface_set_bss_order(uc_vm_t *vm, size_t nargs)
{
struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
uc_value_t *bss_list = uc_fn_arg(0);
struct hostapd_data **new_bss;
struct hostapd_bss_config **new_conf;
if (!iface)
return NULL;
if (ucv_type(bss_list) != UC_ARRAY ||
ucv_array_length(bss_list) != iface->num_bss)
return NULL;
new_bss = calloc(iface->num_bss, sizeof(*new_bss));
new_conf = calloc(iface->num_bss, sizeof(*new_conf));
for (size_t i = 0; i < iface->num_bss; i++) {
struct hostapd_data *bss;
bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss");
if (bss->iface != iface)
goto free;
for (size_t k = 0; k < i; k++)
if (new_bss[k] == bss)
goto free;
new_bss[i] = bss;
new_conf[i] = bss->conf;
}
new_bss[0]->interface_added = 0;
for (size_t i = 1; i < iface->num_bss; i++)
new_bss[i]->interface_added = 1;
free(iface->bss);
iface->bss = new_bss;
free(iface->conf->bss);
iface->conf->bss = new_conf;
iface->conf->num_bss = iface->num_bss;
hostapd_drv_set_first_bss(iface->bss[0]);
return ucv_boolean_new(true);
free:
free(new_bss);
free(new_conf);
return NULL;
}
static uc_value_t *
uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
{
@@ -297,12 +471,32 @@ uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
int i;
switch (iface->state) {
case HAPD_IFACE_ENABLED:
case HAPD_IFACE_DISABLED:
break;
#ifdef CONFIG_ACS
case HAPD_IFACE_ACS:
acs_cleanup(iface);
iface->scan_cb = NULL;
/* fallthrough */
#endif
default:
hostapd_disable_iface(iface);
break;
}
if (iface->state != HAPD_IFACE_ENABLED)
hostapd_disable_iface(iface);
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *hapd = iface->bss[i];
hostapd_drv_stop_ap(hapd);
hapd->started = 0;
hapd->beacon_set_done = 0;
}
return NULL;
}
static uc_value_t *
@@ -311,67 +505,85 @@ uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
uc_value_t *info = uc_fn_arg(0);
struct hostapd_config *conf;
bool changed = false;
uint64_t intval;
int i;
if (!iface)
return NULL;
if (!info)
if (!info) {
iface->freq = 0;
goto out;
}
if (ucv_type(info) != UC_OBJECT)
return NULL;
#define UPDATE_VAL(field, name) \
if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) && \
!errno && intval != conf->field) do { \
conf->field = intval; \
changed = true; \
} while(0)
conf = iface->conf;
if ((intval = ucv_int64_get(ucv_object_get(info, "op_class", NULL))) && !errno)
conf->op_class = intval;
if ((intval = ucv_int64_get(ucv_object_get(info, "hw_mode", NULL))) && !errno)
conf->hw_mode = intval;
if ((intval = ucv_int64_get(ucv_object_get(info, "channel", NULL))) && !errno)
conf->channel = intval;
if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
conf->secondary_channel = intval;
#ifdef CONFIG_IEEE80211AC
if ((intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL))) && !errno) {
conf->vht_oper_centr_freq_seg0_idx = intval;
#ifdef CONFIG_IEEE80211AX
conf->he_oper_centr_freq_seg0_idx = intval;
#endif
#ifdef CONFIG_IEEE80211BE
conf->eht_oper_centr_freq_seg0_idx = intval;
#endif
}
if ((intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL))) && !errno) {
conf->vht_oper_centr_freq_seg1_idx = intval;
#ifdef CONFIG_IEEE80211AX
conf->he_oper_centr_freq_seg1_idx = intval;
#endif
#ifdef CONFIG_IEEE80211BE
conf->eht_oper_centr_freq_seg1_idx = intval;
#endif
}
UPDATE_VAL(op_class, "op_class");
UPDATE_VAL(hw_mode, "hw_mode");
UPDATE_VAL(channel, "channel");
UPDATE_VAL(secondary_channel, "sec_channel");
if (!changed &&
(iface->bss[0]->beacon_set_done ||
iface->state == HAPD_IFACE_DFS))
return ucv_boolean_new(true);
intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL));
if (!errno)
hostapd_set_oper_centr_freq_seg0_idx(conf, intval);
intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL));
if (!errno)
hostapd_set_oper_centr_freq_seg1_idx(conf, intval);
intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
if (!errno) {
conf->vht_oper_chwidth = intval;
#ifdef CONFIG_IEEE80211AX
conf->he_oper_chwidth = intval;
#endif
#ifdef CONFIG_IEEE80211BE
conf->eht_oper_chwidth = intval;
#endif
}
#endif
if (!errno)
hostapd_set_oper_chwidth(conf, intval);
intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL));
if (!errno)
iface->freq = intval;
else
iface->freq = 0;
conf->acs = 0;
out:
if (conf->channel)
switch (iface->state) {
case HAPD_IFACE_DISABLED:
break;
case HAPD_IFACE_ENABLED:
if (!hostapd_is_dfs_required(iface) ||
hostapd_is_dfs_chan_available(iface))
break;
wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
/* fallthrough */
default:
hostapd_disable_iface(iface);
break;
}
if (conf->channel && !iface->freq)
iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
if (iface->state != HAPD_IFACE_ENABLED) {
hostapd_enable_iface(iface);
return ucv_boolean_new(true);
}
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *hapd = iface->bss[i];
int ret;
hapd->started = 1;
hapd->conf->start_disabled = 0;
hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
conf->channel,
conf->enable_edmg,
@@ -436,6 +648,55 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
return ucv_boolean_new(!ret);
}
static uc_value_t *
uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
{
struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
uc_value_t *ifname_arg = uc_fn_arg(0);
char prev_ifname[IFNAMSIZ + 1];
struct sta_info *sta;
const char *ifname;
int ret;
if (!hapd || ucv_type(ifname_arg) != UC_STRING)
return NULL;
os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname));
ifname = ucv_string_get(ifname_arg);
hostapd_ubus_free_bss(hapd);
if (interfaces->ctrl_iface_deinit)
interfaces->ctrl_iface_deinit(hapd);
ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
if (ret)
goto out;
for (sta = hapd->sta_list; sta; sta = sta->next) {
char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];
if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable)
continue;
snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid);
snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid);
hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name);
}
if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan)))
os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan));
os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface));
hostapd_ubus_add_bss(hapd);
hostapd_ucode_update_interfaces();
out:
if (interfaces->ctrl_iface_init)
interfaces->ctrl_iface_init(hapd);
return ret ? NULL : ucv_boolean_new(true);
}
int hostapd_ucode_init(struct hapd_interfaces *ifaces)
{
static const uc_function_list_t global_fns[] = {
@@ -449,9 +710,11 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
static const uc_function_list_t bss_fns[] = {
{ "ctrl", uc_hostapd_bss_ctrl },
{ "set_config", uc_hostapd_bss_set_config },
{ "rename", uc_hostapd_bss_rename },
{ "delete", uc_hostapd_bss_delete },
};
static const uc_function_list_t iface_fns[] = {
{ "set_bss_order", uc_hostapd_iface_set_bss_order },
{ "add_bss", uc_hostapd_iface_add_bss },
{ "stop", uc_hostapd_iface_stop },
{ "start", uc_hostapd_iface_start },

View File

@@ -129,7 +129,10 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
tmp_channel &= ~((8 << width) - 1);
center_idx = tmp_channel + center_ofs + (4 << width) - 1;
ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
if (freq_val < 3000)
ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0));
else
ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
center_idx = (center_idx - channel) * 5 + freq_val;
ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
@@ -295,9 +298,15 @@ uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
{
uc_value_t *val = wpa_ucode_registry_get(reg, idx);
void **dataptr;
if (val)
ucv_array_set(reg, idx - 1, NULL);
if (!val)
return NULL;
ucv_array_set(reg, idx - 1, NULL);
dataptr = ucv_resource_dataptr(val, NULL);
if (dataptr)
*dataptr = NULL;
return val;
}

View File

@@ -2,6 +2,7 @@
#include "utils/common.h"
#include "utils/ucode.h"
#include "drivers/driver.h"
#include "ap/hostapd.h"
#include "wpa_supplicant_i.h"
#include "wps_supplicant.h"
#include "bss.h"
@@ -211,12 +212,13 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
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);
if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
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 (ht_oper->ht_param &
HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
sec_chan = -1;
}
@@ -224,6 +226,15 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
}
#ifdef CONFIG_MESH
if (wpa_s->ifmsh) {
struct hostapd_iface *ifmsh = wpa_s->ifmsh;
ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(ifmsh->conf->secondary_channel));
ucv_object_add(ret, "frequency", ucv_int64_new(ifmsh->freq));
}
#endif
return ret;
}

View File

@@ -0,0 +1,60 @@
From: Andy Ren <andy.ren@getcruise.com>
Date: Mon, 7 Nov 2022 09:42:42 -0800
Subject: [PATCH] net/core: Allow live renaming when an interface is up
Allow a network interface to be renamed when the interface
is up.
As described in the netconsole documentation [1], when netconsole is
used as a built-in, it will bring up the specified interface as soon as
possible. As a result, user space will not be able to rename the
interface since the kernel disallows renaming of interfaces that are
administratively up unless the 'IFF_LIVE_RENAME_OK' private flag was set
by the kernel.
The original solution [2] to this problem was to add a new parameter to
the netconsole configuration parameters that allows renaming of
the interface used by netconsole while it is administratively up.
However, during the discussion that followed, it became apparent that we
have no reason to keep the current restriction and instead we should
allow user space to rename interfaces regardless of their administrative
state:
1. The restriction was put in place over 20 years ago when renaming was
only possible via IOCTL and before rtnetlink started notifying user
space about such changes like it does today.
2. The 'IFF_LIVE_RENAME_OK' flag was added over 3 years ago in version
5.2 and no regressions were reported.
3. In-kernel listeners to 'NETDEV_CHANGENAME' do not seem to care about
the administrative state of interface.
Therefore, allow user space to rename running interfaces by removing the
restriction and the associated 'IFF_LIVE_RENAME_OK' flag. Help in
possible triage by emitting a message to the kernel log that an
interface was renamed while UP.
[1] https://www.kernel.org/doc/Documentation/networking/netconsole.rst
[2] https://lore.kernel.org/netdev/20221102002420.2613004-1-andy.ren@getcruise.com/
Signed-off-by: Andy Ren <andy.ren@getcruise.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
Index: linux-5.4.164-qsdk-26349818b464f8c7b52d59ce73579d9f3dd6bd5d/net/core/dev.c
===================================================================
--- linux-5.4.164-qsdk-26349818b464f8c7b52d59ce73579d9f3dd6bd5d.orig/net/core/dev.c
+++ linux-5.4.164-qsdk-26349818b464f8c7b52d59ce73579d9f3dd6bd5d/net/core/dev.c
@@ -1125,7 +1125,8 @@ int dev_change_name(struct net_device *d
}
if (oldname[0] && !strchr(oldname, '%'))
- netdev_info(dev, "renamed from %s\n", oldname);
+ netdev_info(dev, "renamed from %s%s\n", oldname,
+ dev->flags & IFF_UP ? " (while UP)" : "");
old_assign_type = dev->name_assign_type;
dev->name_assign_type = NET_NAME_RENAMED;

View File

@@ -0,0 +1,67 @@
From: Andy Ren <andy.ren@getcruise.com>
Date: Mon, 7 Nov 2022 09:42:42 -0800
Subject: [PATCH] net/core: Allow live renaming when an interface is up
Allow a network interface to be renamed when the interface
is up.
As described in the netconsole documentation [1], when netconsole is
used as a built-in, it will bring up the specified interface as soon as
possible. As a result, user space will not be able to rename the
interface since the kernel disallows renaming of interfaces that are
administratively up unless the 'IFF_LIVE_RENAME_OK' private flag was set
by the kernel.
The original solution [2] to this problem was to add a new parameter to
the netconsole configuration parameters that allows renaming of
the interface used by netconsole while it is administratively up.
However, during the discussion that followed, it became apparent that we
have no reason to keep the current restriction and instead we should
allow user space to rename interfaces regardless of their administrative
state:
1. The restriction was put in place over 20 years ago when renaming was
only possible via IOCTL and before rtnetlink started notifying user
space about such changes like it does today.
2. The 'IFF_LIVE_RENAME_OK' flag was added over 3 years ago in version
5.2 and no regressions were reported.
3. In-kernel listeners to 'NETDEV_CHANGENAME' do not seem to care about
the administrative state of interface.
Therefore, allow user space to rename running interfaces by removing the
restriction and the associated 'IFF_LIVE_RENAME_OK' flag. Help in
possible triage by emitting a message to the kernel log that an
interface was renamed while UP.
[1] https://www.kernel.org/doc/Documentation/networking/netconsole.rst
[2] https://lore.kernel.org/netdev/20221102002420.2613004-1-andy.ren@getcruise.com/
Signed-off-by: Andy Ren <andy.ren@getcruise.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1152,8 +1152,6 @@ int dev_change_name(struct net_device *d
BUG_ON(!dev_net(dev));
net = dev_net(dev);
- if (dev->flags & IFF_UP)
- return -EBUSY;
write_seqcount_begin(&devnet_rename_seq);
@@ -1171,7 +1169,8 @@ int dev_change_name(struct net_device *d
}
if (oldname[0] && !strchr(oldname, '%'))
- netdev_info(dev, "renamed from %s\n", oldname);
+ netdev_info(dev, "renamed from %s%s\n", oldname,
+ dev->flags & IFF_UP ? " (while UP)" : "");
old_assign_type = dev->name_assign_type;
dev->name_assign_type = NET_NAME_RENAMED;

View File

@@ -0,0 +1,67 @@
From: Andy Ren <andy.ren@getcruise.com>
Date: Mon, 7 Nov 2022 09:42:42 -0800
Subject: [PATCH] net/core: Allow live renaming when an interface is up
Allow a network interface to be renamed when the interface
is up.
As described in the netconsole documentation [1], when netconsole is
used as a built-in, it will bring up the specified interface as soon as
possible. As a result, user space will not be able to rename the
interface since the kernel disallows renaming of interfaces that are
administratively up unless the 'IFF_LIVE_RENAME_OK' private flag was set
by the kernel.
The original solution [2] to this problem was to add a new parameter to
the netconsole configuration parameters that allows renaming of
the interface used by netconsole while it is administratively up.
However, during the discussion that followed, it became apparent that we
have no reason to keep the current restriction and instead we should
allow user space to rename interfaces regardless of their administrative
state:
1. The restriction was put in place over 20 years ago when renaming was
only possible via IOCTL and before rtnetlink started notifying user
space about such changes like it does today.
2. The 'IFF_LIVE_RENAME_OK' flag was added over 3 years ago in version
5.2 and no regressions were reported.
3. In-kernel listeners to 'NETDEV_CHANGENAME' do not seem to care about
the administrative state of interface.
Therefore, allow user space to rename running interfaces by removing the
restriction and the associated 'IFF_LIVE_RENAME_OK' flag. Help in
possible triage by emitting a message to the kernel log that an
interface was renamed while UP.
[1] https://www.kernel.org/doc/Documentation/networking/netconsole.rst
[2] https://lore.kernel.org/netdev/20221102002420.2613004-1-andy.ren@getcruise.com/
Signed-off-by: Andy Ren <andy.ren@getcruise.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1152,8 +1152,6 @@ int dev_change_name(struct net_device *d
BUG_ON(!dev_net(dev));
net = dev_net(dev);
- if (dev->flags & IFF_UP)
- return -EBUSY;
write_seqcount_begin(&devnet_rename_seq);
@@ -1171,7 +1169,8 @@ int dev_change_name(struct net_device *d
}
if (oldname[0] && !strchr(oldname, '%'))
- netdev_info(dev, "renamed from %s\n", oldname);
+ netdev_info(dev, "renamed from %s%s\n", oldname,
+ dev->flags & IFF_UP ? " (while UP)" : "");
old_assign_type = dev->name_assign_type;
dev->name_assign_type = NET_NAME_RENAMED;

View File

@@ -488,6 +488,7 @@ ${channel:+channel=$channel}
${channel_list:+chanlist=$channel_list}
${hostapd_noscan:+noscan=1}
${tx_burst:+tx_queue_data2_burst=$tx_burst}
#num_global_macaddr=$num_global_macaddr
$base_cfg
EOF
@@ -528,6 +529,7 @@ mac80211_hostapd_setup_bss() {
cat >> /var/run/hostapd-$phy.conf <<EOF
$hostapd_cfg
bssid=$macaddr
${default_macaddr:+#default_macaddr}
${dtim_period:+dtim_period=$dtim_period}
${max_listen_int:+max_listen_interval=$max_listen_int}
EOF
@@ -542,57 +544,9 @@ mac80211_get_addr() {
mac80211_generate_mac() {
local phy="$1"
local multiple_bssid="$2"
local id="${macidx:-0}"
local ref="$(cat /sys/class/ieee80211/${phy}/macaddress)"
local mask="$(cat /sys/class/ieee80211/${phy}/address_mask)"
[ "$mask" = "00:00:00:00:00:00" -a "$multiple_bssid" != 1 ] && {
mask="ff:ff:ff:ff:ff:ff";
[ "$(wc -l < /sys/class/ieee80211/${phy}/addresses)" -gt $id ] && {
addr="$(mac80211_get_addr "$phy" "$id")"
[ -n "$addr" ] && {
echo "$addr"
return
}
}
}
local oIFS="$IFS"; IFS=":"; set -- $mask; IFS="$oIFS"
local mask1=$1
local mask6=$6
local oIFS="$IFS"; IFS=":"; set -- $ref; IFS="$oIFS"
[ "$multiple_bssid" -eq 1 ] && {
printf "02:%s:%s:%s:%s:%02x" $b1 $2 $3 $4 $5 $macidx
return
}
macidx=$(($id + 1))
local use_global=0
[ "$id" -gt 0 -a "$macidx" -le "$num_global_macaddr" ] && use_global=1
[ "$((0x$mask1))" -gt 0 -a "$use_global" -lt 1 ] && {
b1="0x$1"
[ "$id" -gt 0 ] && \
b1=$(($b1 ^ ((($id - !($b1 & 2)) << 2)) | 0x2))
printf "%02x:%s:%s:%s:%s:%s" $b1 $2 $3 $4 $5 $6
return
}
[ "$((0x$mask6))" -lt 255 -a "$use_global" -gt 0 ] && {
printf "%s:%s:%s:%s:%s:%02x" $1 $2 $3 $4 $5 $(( 0x$6 ^ $id ))
return
}
off2=$(( (0x$6 + $id) / 0x100 ))
printf "%s:%s:%s:%s:%02x:%02x" \
$1 $2 $3 $4 \
$(( (0x$5 + $off2) % 0x100 )) \
$(( (0x$6 + $id) % 0x100 ))
wdev_tool "$phy" get_macaddr id=$id num_global=$num_global_macaddr mbssid=$multiple_bssid
}
find_phy() {
@@ -626,11 +580,14 @@ mac80211_prepare_vif() {
set_default powersave 0
json_add_string _ifname "$ifname"
default_macaddr=
[ -n "$macaddr" ] || {
macaddr="$(mac80211_generate_mac $phy $multiple_bssid)"
macaddr="$(mac80211_generate_mac $phy)"
macidx="$(($macidx + 1))"
default_macaddr=1
}
json_add_string _macaddr "$macaddr"
json_add_string _default_macaddr "$default_macaddr"
json_select ..
@@ -754,7 +711,7 @@ mac80211_setup_adhoc() {
json_add_object "$ifname"
json_add_string mode adhoc
json_add_string macaddr "$macaddr"
[ -n "$default_macaddr" ] || json_add_string macaddr "$macaddr"
json_add_string ssid "$ssid"
json_add_string freq "$freq"
json_add_string htmode "$iw_htmode"
@@ -780,7 +737,7 @@ mac80211_setup_mesh() {
json_add_object "$ifname"
json_add_string mode mesh
json_add_string macaddr "$macaddr"
[ -n "$default_macaddr" ] || json_add_string macaddr "$macaddr"
json_add_string ssid "$ssid"
json_add_string freq "$freq"
json_add_string htmode "$iw_htmode"
@@ -831,7 +788,6 @@ wpa_supplicant_init_config() {
wpa_supplicant_add_interface() {
local ifname="$1"
local mode="$2"
local hostapd_ctrl="$3"
local prev
_wpa_supplicant_common "$ifname"
@@ -843,9 +799,8 @@ wpa_supplicant_add_interface() {
json_add_string iface "$ifname"
json_add_string mode "$mode"
json_add_string config "$_config"
json_add_string macaddr "$macaddr"
[ -n "$default_macaddr" ] || json_add_string macaddr "$macaddr"
[ -n "$network_bridge" ] && json_add_string bridge "$network_bridge"
[ -n "$hostapd_ctrl" ] && json_add_string hostapd_ctrl "$hostapd_ctrl"
[ -n "$wds" ] && json_add_boolean 4addr "$wds"
json_add_boolean powersave "$powersave"
[ "$mode" = "mesh" ] && mac80211_add_mesh_params
@@ -920,7 +875,7 @@ mac80211_setup_supplicant() {
wpa_supplicant_add_network "$ifname" "$freq" "$htmode" "$noscan"
fi
wpa_supplicant_add_interface "$ifname" "$mode" "$hostapd_ctrl"
wpa_supplicant_add_interface "$ifname" "$mode"
return 0
}
@@ -932,6 +887,7 @@ mac80211_setup_vif() {
json_select config
json_get_var ifname _ifname
json_get_var macaddr _macaddr
json_get_var default_macaddr _default_macaddr
json_get_vars mode wds powersave
set_default powersave 0
@@ -1014,7 +970,7 @@ mac80211_reset_config() {
hostapd_conf_file="/var/run/hostapd-$phy.conf"
ubus call hostapd config_set '{ "phy": "'"$phy"'", "config": "", "prev_config": "'"$hostapd_conf_file"'" }' > /dev/null
ubus call wpa_supplicant config_set '{ "phy": "'"$phy"'", "config": [] }' > /dev/null
wdev_tool "$phy" '{}'
wdev_tool "$phy" set_config '{}'
}
drv_mac80211_setup() {
@@ -1116,7 +1072,7 @@ drv_mac80211_setup() {
mac80211_prepare_iw_htmode
active_ifnames=
for_each_interface "ap sta adhoc mesh monitor" mac80211_prepare_vif ${multiple_bssid}
for_each_interface "ap sta adhoc mesh monitor" mac80211_prepare_vif
for_each_interface "ap sta adhoc mesh monitor" mac80211_setup_vif
[ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_set_config "$phy"
@@ -1125,7 +1081,7 @@ drv_mac80211_setup() {
[ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_start "$phy"
json_set_namespace wdev_uc prev
wdev_tool "$phy" "$(json_dump)" $active_ifnames
wdev_tool "$phy" set_config "$(json_dump)" $active_ifnames
json_set_namespace "$prev"
for_each_interface "ap sta adhoc mesh monitor" mac80211_set_vif_txpower

View File

@@ -0,0 +1,148 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Thu, 14 Sep 2023 13:17:16 +0200
Subject: [PATCH] cfg80211: allow grace period for DFS available after beacon
shutdown
Fixes reconfiguring an AP on a DFS channel in non-ETSI regdomain
Fixes: b35a51c7dd25 ("cfg80211: Make pre-CAC results valid only for ETSI domain")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -162,6 +162,8 @@ enum ieee80211_channel_flags {
* @dfs_state: current state of this channel. Only relevant if radar is required
* on this channel.
* @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
+ * @dfs_state_last_available: timestamp (jiffies) of the last time when the
+ * channel was available.
* @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
*/
struct ieee80211_channel {
@@ -178,6 +180,7 @@ struct ieee80211_channel {
int orig_mag, orig_mpwr;
enum nl80211_dfs_state dfs_state;
unsigned long dfs_state_entered;
+ unsigned long dfs_state_last_available;
unsigned int dfs_cac_ms;
};
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -25,6 +25,8 @@ int __cfg80211_stop_ap(struct cfg80211_r
if (!wdev->beacon_interval)
return -ENOENT;
+ cfg80211_update_last_available(wdev->wiphy, &wdev->chandef);
+
err = rdev_stop_ap(rdev, dev);
if (!err) {
wdev->conn_owner_nlportid = 0;
@@ -35,9 +37,6 @@ int __cfg80211_stop_ap(struct cfg80211_r
if (notify)
nl80211_send_ap_stopped(wdev);
- /* Should we apply the grace period during beaconing interface
- * shutdown also?
- */
cfg80211_sched_dfs_chan_update(rdev);
}
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -411,6 +411,8 @@ static void cfg80211_set_chans_dfs_state
c->dfs_state = dfs_state;
c->dfs_state_entered = jiffies;
+ if (dfs_state == NL80211_DFS_AVAILABLE)
+ c->dfs_state_last_available = jiffies;
}
}
@@ -769,6 +771,49 @@ static bool cfg80211_get_chans_dfs_avail
return true;
}
+static void
+__cfg80211_update_last_available(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ /*
+ * Check entire range of channels for the bandwidth.
+ * If any channel in between is disabled or has not
+ * had gone through CAC return false
+ */
+ for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
+ c = ieee80211_get_channel_khz(wiphy, freq);
+ if (!c)
+ return;
+
+ c->dfs_state_last_available = jiffies;
+ }
+}
+
+void cfg80211_update_last_available(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return;
+
+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
+ width);
+ if (chandef->width != NL80211_CHAN_WIDTH_80P80)
+ return;
+
+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
+ width);
+}
+
static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef)
{
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -483,6 +483,8 @@ void cfg80211_set_dfs_state(struct wiphy
enum nl80211_dfs_state dfs_state);
void cfg80211_dfs_channels_update_work(struct work_struct *work);
+void cfg80211_update_last_available(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef);
unsigned int
cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -858,6 +858,8 @@ void cfg80211_dfs_channels_update_work(s
if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
radar_event = NL80211_RADAR_NOP_FINISHED;
+ timeout = c->dfs_state_entered +
+ msecs_to_jiffies(time_dfs_update);
} else {
if (regulatory_pre_cac_allowed(wiphy) ||
cfg80211_any_wiphy_oper_chan(wiphy, c))
@@ -865,11 +867,10 @@ void cfg80211_dfs_channels_update_work(s
time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
+ timeout = c->dfs_state_last_available +
+ msecs_to_jiffies(time_dfs_update);
}
- timeout = c->dfs_state_entered +
- msecs_to_jiffies(time_dfs_update);
-
if (time_after_eq(jiffies, timeout)) {
c->dfs_state = NL80211_DFS_USABLE;
c->dfs_state_entered = jiffies;