mirror of
				https://github.com/Telecominfraproject/wlan-ap.git
				synced 2025-10-29 17:42:41 +00:00 
			
		
		
		
	ipq807x: backport latest wifi hot-reload to v5.4 kernel
Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
		| @@ -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 }; | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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; | ||||
| 		} | ||||
| 		}) | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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": | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
| @@ -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"); | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
							
								
								
									
										1845
									
								
								feeds/ipq807x_v5.4/hostapd/src/src/ap/ubus.c.orig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1845
									
								
								feeds/ipq807x_v5.4/hostapd/src/src/ap/ubus.c.orig
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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 }, | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| @@ -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; | ||||
| @@ -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; | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
		Reference in New Issue
	
	Block a user
	 John Crispin
					John Crispin