mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-29 17:42:41 +00:00
If another host has not set a status, for example when autochannel is disabled, it will not show host_info when remote_hosts is called. This fix adds a check for this condition. Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
330 lines
6.6 KiB
Ucode
Executable File
330 lines
6.6 KiB
Ucode
Executable File
#!/usr/bin/ucode
|
|
let fs = require("fs");
|
|
let ubus = require("ubus");
|
|
let conn = ubus.connect();
|
|
let phys = [];
|
|
let block_list = {
|
|
"2G": {},
|
|
"5G": {}
|
|
};
|
|
let channel_masks = {
|
|
"40": [ 5180, 5220, 5260, 5300, 5500, 5540, 5580, 5620, 5660, 5700, 5745, 5785, 5825 ],
|
|
"80": [ 5180, 5260, 5500, 5580, 5660, 5745 ],
|
|
"160": [ 5180, 5500, ],
|
|
};
|
|
|
|
function uptime_get() {
|
|
let info = conn.call("system", "info");
|
|
|
|
return info.uptime;
|
|
}
|
|
|
|
function remote_info() {
|
|
let info = conn.call("usteer", "remote_info");
|
|
|
|
return info || {};
|
|
}
|
|
|
|
function local_info() {
|
|
let info = conn.call("usteer", "local_info");
|
|
|
|
return info || {};
|
|
}
|
|
|
|
function remote_hosts() {
|
|
let hosts = conn.call("usteer", "remote_hosts");
|
|
|
|
return hosts || {};
|
|
}
|
|
|
|
let uptime = uptime_get();
|
|
let info = local_info();
|
|
let remote = remote_info();
|
|
let hosts = remote_hosts();
|
|
|
|
function state_get() {
|
|
let file = fs.open("/tmp/uchannel.json", "r");
|
|
let state = file ? json(file.read("all")) : {};
|
|
|
|
if (file)
|
|
file.close();
|
|
|
|
return state;
|
|
}
|
|
|
|
function state_set(state) {
|
|
let file = fs.open("/tmp/uchannel.json", "w");
|
|
|
|
state.executed = uptime;
|
|
file.write(state);
|
|
file.close();
|
|
|
|
conn.call("usteer", "set_node_data", {
|
|
node: "*",
|
|
data: {
|
|
status: state.status,
|
|
uptime: state.executed,
|
|
}
|
|
});
|
|
printf("entering %s state\n", state.status);
|
|
}
|
|
|
|
function freq2chan(freq) {
|
|
if (freq == 2484)
|
|
return 14;
|
|
else if (freq < 2484)
|
|
return (freq - 2407) / 5;
|
|
else if (freq >= 4910 && freq <= 4980)
|
|
return (freq - 4000) / 5;
|
|
else if(freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6)
|
|
return (freq - 56160) / 2160;
|
|
else
|
|
return (freq - 5000) / 5;
|
|
}
|
|
|
|
function freq2band(freq) {
|
|
if (freq < 2500)
|
|
return "2G";
|
|
return "5G";
|
|
}
|
|
|
|
function chan2freq(band, channel) {
|
|
if (band == '2G' && channel >= 1 && channel <= 13)
|
|
return 2407 + channel * 5;
|
|
else if (band == '2G' && channel == 14)
|
|
return 2484;
|
|
else if (band == '5G' && channel >= 36 && channel <= 177)
|
|
return 5000 + channel * 5;
|
|
else if (band == '5G' && channel >= 183 && channel <= 196)
|
|
return 4000 + channel * 5;
|
|
else if (band == '60G' && channel >= 1 && channel <= 6)
|
|
return 56160 + channel * 2160;
|
|
|
|
return null;
|
|
}
|
|
|
|
function center_freq(freq, bandwidth) {
|
|
if (bandwidth == 40)
|
|
return +freq + 10;
|
|
if (bandwidth == 80)
|
|
return +freq + 30;
|
|
if (bandwidth == 160)
|
|
return +freq + 70;
|
|
return +freq;
|
|
}
|
|
|
|
function channel_overlap() {
|
|
let overlap = {};
|
|
let peers = {
|
|
"local": {}
|
|
};
|
|
|
|
for (let node, r in remote)
|
|
peers[split(node, "#")] = {};
|
|
|
|
for (let id, i in info) {
|
|
peers.local[i.freq] = true;
|
|
block_list[freq2band(freq)][i.freq] = 0;
|
|
}
|
|
for (let node, r in remote) {
|
|
peers[split(node, "#")][r.freq] = true;
|
|
block_list[freq2band(freq)][r.freq] = 0;
|
|
}
|
|
|
|
for (let id, peer in peers)
|
|
for (let freq, val in peer)
|
|
block_list[freq2band(freq)][freq]++;
|
|
|
|
for (let id, i in info)
|
|
for (let node, r in remote)
|
|
if (i.freq == r.freq) {
|
|
overlap[i.freq] = id;
|
|
break;
|
|
}
|
|
|
|
return overlap;
|
|
}
|
|
|
|
function phy_lookup() {
|
|
let status = conn.call("network.wireless", "status");
|
|
|
|
for (let id, radio in status) {
|
|
let htmode = match(radio.config.htmode, /^([A-Z]+)(.+)$/);
|
|
let phy = {
|
|
path: radio.config.path,
|
|
htmode: lc(htmode[1]),
|
|
bandwidth: htmode[2],
|
|
iface: [],
|
|
sta: false,
|
|
};
|
|
|
|
for (let i, iface in radio.interfaces) {
|
|
|
|
push(phy.iface, iface.ifname);
|
|
if (iface.config.mode != 'ap')
|
|
phy.sta = true;
|
|
}
|
|
push(phys, phy);
|
|
}
|
|
}
|
|
|
|
function phy_find(iface) {
|
|
for (let idx, phy in phys)
|
|
if (index(phy.iface, iface) >= 0)
|
|
return phy;
|
|
return {};
|
|
}
|
|
|
|
function channel_mask(band, bandwidth) {
|
|
if (band == "2G")
|
|
return [ 2412, 2437, 2462 ];
|
|
return channel_masks[bandwidth];
|
|
}
|
|
|
|
function channel_scan(band) {
|
|
conn.call("wifi", "scan", { band });
|
|
sleep(5000);
|
|
|
|
let survey_data = conn.call("wifi", "survey", { band });
|
|
let scan_data = conn.call("wifi", "scan_dump", { band });
|
|
|
|
let channels = {};
|
|
|
|
for (let survey in survey_data.survey) {
|
|
channels[survey.channel] = survey;
|
|
channels[survey.channel].bss = 0;
|
|
}
|
|
|
|
for (let scan in scan_data.scan)
|
|
channels[scan.channel].bss++;
|
|
|
|
return channels;
|
|
}
|
|
|
|
function channel_new(band, channels, mask) {
|
|
let new = [];
|
|
|
|
for (let chan, data in channels) {
|
|
if (data.in_use)
|
|
continue;
|
|
let freq = chan2freq(band, chan);
|
|
if (length(mask) && index(mask, freq) < 0)
|
|
continue;
|
|
if (block_list[band][freq])
|
|
continue;
|
|
push(new, {
|
|
freq,
|
|
bss: data.bss,
|
|
airtime: data.busy_ms * 100 / data.active_ms,
|
|
});
|
|
}
|
|
print("available free channels :" + new + "\n");
|
|
|
|
let best;
|
|
for (let id, data in new) {
|
|
if (!length(best))
|
|
best = data;
|
|
if (best.bss > data.bss)
|
|
best = data;
|
|
else if (best.bss == data.bss &&
|
|
best.airtime > data.airtime)
|
|
best = data;
|
|
}
|
|
return best || {};
|
|
}
|
|
|
|
function channel_balance(band, mask) {
|
|
let lowest = {
|
|
freq: mask[0],
|
|
count: 1000,
|
|
};
|
|
let highest = {
|
|
freq: mask[0],
|
|
count: 0,
|
|
};
|
|
|
|
for (let freq, count in block_list[band]) {
|
|
if (lowest.count > count)
|
|
lowest = { freq, count };
|
|
if (highest.count < count)
|
|
highest = { freq, count };
|
|
}
|
|
|
|
if (highest.count - lowest.cont >= 2)
|
|
return lowest;
|
|
|
|
return {};
|
|
}
|
|
|
|
function youngest() {
|
|
for (let ip, host in hosts) {
|
|
if (host.host_info?.status == "overlap" &&
|
|
host.host_info?.uptime < uptime) {
|
|
print("Found a younger host\n");
|
|
return 1;
|
|
}
|
|
}
|
|
print("We are the youngest host\n");
|
|
return 0;
|
|
}
|
|
|
|
let state = state_get();
|
|
|
|
if (state.status == "waiting" &&
|
|
(uptime - state.changed < (12 * 60 * 60))) {
|
|
state_set(state);
|
|
return;
|
|
}
|
|
|
|
phy_lookup();
|
|
print("discovered devices: " + phys + "\n");
|
|
let overlap = channel_overlap();
|
|
print("list of blocked channels: " + block_list + "\n");
|
|
print("list of overlapping channels: " + overlap + "\n");
|
|
|
|
if (!length(overlap)) {
|
|
state.status = "happy";
|
|
state_set(state);
|
|
return;
|
|
}
|
|
|
|
if (state.status != "overlap" || youngest()) {
|
|
state.status = "overlap";
|
|
state_set(state);
|
|
return;
|
|
}
|
|
|
|
for (let freq, obj in overlap) {
|
|
let phy = phy_find(split(obj, ".")[1]);
|
|
let band = freq2band(freq);
|
|
let channels = channel_scan(band);
|
|
let mask = channel_mask(band, phy.bandwidth);
|
|
let new;
|
|
|
|
if (phy.sta) {
|
|
print("phy has a STA interface cannot change channel\n");
|
|
return;
|
|
}
|
|
|
|
new = channel_new(band, channels, mask);
|
|
|
|
if (!length(new))
|
|
new = channel_balance(band, mask);
|
|
|
|
if (!length(new)) {
|
|
print("no alternative channel found\n");
|
|
continue;
|
|
}
|
|
printf("selected channel: " + new + " for %s\n", obj);
|
|
conn.call(obj, "switch_chan", {
|
|
freq: +new.freq,
|
|
center_freq1: center_freq(new.freq, phy.bandwidth),
|
|
bcn_count: 10,
|
|
force: true
|
|
});
|
|
}
|
|
|
|
state.status = "waiting";
|
|
state.changed = uptime;
|
|
state_set(state);
|