diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh index 357277fbf71..7d3ab4dc01b 100644 --- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh +++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh @@ -489,6 +489,7 @@ ${channel_list:+chanlist=$channel_list} ${hostapd_noscan:+noscan=1} ${tx_burst:+tx_queue_data2_burst=$tx_burst} mbssid=$multiple_bssid +#num_global_macaddr=$num_global_macaddr $base_cfg EOF @@ -519,6 +520,7 @@ mac80211_hostapd_setup_bss() { cat >> /var/run/hostapd-$phy.conf < sprintf("%02x", val))); } -function wdev_generate_macaddr(phy, data) +function wdev_macaddr(wdev) { - 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; + return trim(readfile(`/sys/class/net/${wdev}/address`)); +} - let base_addr = phy_sysfs_file(phy, "macaddress"); - if (!base_addr) - return null; +const phy_proto = { + macaddr_init: function(used, options) { + this.macaddr_options = options ?? {}; + this.macaddr_list = {}; - if (!idx && !mbssid) - return base_addr; + 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; - let base_mask = phy_sysfs_file(phy, "address_mask"); - if (!base_mask) - return null; + this.for_each_wdev((wdev) => { + let macaddr = wdev_macaddr(wdev); + this.macaddr_list[macaddr] ??= -1; + }); - if (base_mask == "00:00:00:00:00:00" && idx >= num_global) { - let addrs = split(phy_sysfs_file(phy, "addresses"), "\n"); + return this.macaddr_list; + }, - if (idx < length(addrs)) - return addrs[idx]; + 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; - base_mask = "ff:ff:ff:ff:ff:ff"; - } + let base_addr = phy_sysfs_file(phy, "macaddress"); + if (!base_addr) + return null; - let addr = macaddr_split(base_addr); - let mask = macaddr_split(base_mask); - let type; + if (!idx && !mbssid) + return base_addr; - 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"; + let base_mask = phy_sysfs_file(phy, "address_mask"); + if (!base_mask) + return null; - 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; + 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"; } - break; - } - return macaddr_join(addr); + 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 = { @@ -247,4 +315,4 @@ function vlist_new(cb) { }, vlist_proto); } -export { wdev_remove, wdev_create, wdev_generate_macaddr, is_equal, vlist_new, phy_is_fullmac }; +export { wdev_remove, wdev_create, is_equal, vlist_new, phy_is_fullmac, phy_open }; diff --git a/package/network/services/hostapd/files/hostapd.sh b/package/network/services/hostapd/files/hostapd.sh index a0945fdc53e..271c1f7becd 100644 --- a/package/network/services/hostapd/files/hostapd.sh +++ b/package/network/services/hostapd/files/hostapd.sh @@ -632,8 +632,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_interval" ] && \ append bss_conf "radius_acct_interim_interval=$acct_interval" "$N" diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index 384c5c2eb08..9b356dcc309 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -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(); @@ -41,10 +41,13 @@ 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 += ` @@ -108,8 +111,22 @@ function iface_add(phy, config, phy_status) return iface.start(freq_info) >= 0; } -function iface_restart(phy, config, old_config) +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); @@ -118,6 +135,13 @@ 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) @@ -175,8 +199,64 @@ 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; + + 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; @@ -186,82 +266,231 @@ 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(`Failed to update config data files for bss ${ifname}`); + return false; + } + 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`); + hostapd.printf(`old: ${bss_remove_file_fields(bss_list_cfg[i])}`); + hostapd.printf(`new: ${bss_remove_file_fields(config.bss[i])}`); + if (bss.set_config(config_inline, i) < 0) { + hostapd.printf(`Failed to set config for bss ${ifname}`); return false; } } @@ -269,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]; @@ -278,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) @@ -332,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]); @@ -358,27 +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: { @@ -389,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; @@ -426,7 +695,28 @@ let main_obj = { 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: { @@ -434,7 +724,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; @@ -442,34 +732,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; @@ -479,19 +764,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; - } + }) }, }; diff --git a/package/network/services/hostapd/files/wdev.uc b/package/network/services/hostapd/files/wdev.uc index 8db245cdb73..8a031b40b9e 100644 --- a/package/network/services/hostapd/files/wdev.uc +++ b/package/network/services/hostapd/files/wdev.uc @@ -1,10 +1,13 @@ #!/usr/bin/env ucode 'use strict'; -import { vlist_new, is_equal, wdev_create, wdev_remove, wdev_generate_macaddr } 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 command = shift(ARGV); +let phydev; const mesh_params = [ "mesh_retry_timeout", "mesh_confirm_timeout", "mesh_holding_timeout", "mesh_max_peer_links", @@ -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) @@ -154,6 +162,14 @@ const commands = { 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); @@ -169,7 +185,7 @@ const commands = { data[arg[0]] = arg[1]; } - let macaddr = wdev_generate_macaddr(phy, data); + let macaddr = phydev.macaddr_generate(data); if (!macaddr) { warn(`Could not get MAC address for phy ${phy}\n`); exit(1); @@ -179,12 +195,11 @@ const commands = { }, }; -let command = shift(ARGV); - if (!phy || !command | !commands[command]) usage(); -if (!readfile(`/sys/class/ieee80211/${phy}/index`)) { +phydev = phy_open(phy); +if (!phydev) { warn(`PHY ${phy} does not exist\n`); exit(1); } diff --git a/package/network/services/hostapd/files/wpa_supplicant.uc b/package/network/services/hostapd/files/wpa_supplicant.uc index f8a3fcb5253..cb5f41b1af3 100644 --- a/package/network/services/hostapd/files/wpa_supplicant.uc +++ b/package/network/services/hostapd/files/wpa_supplicant.uc @@ -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); @@ -78,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 = { @@ -111,6 +132,20 @@ 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: ""