248 lines
6.4 KiB
Lua
Executable File
248 lines
6.4 KiB
Lua
Executable File
#!/usr/bin/lua
|
|
|
|
local util = require 'gluon.util'
|
|
local wireless = require 'gluon.wireless'
|
|
local site = require 'gluon.site'
|
|
local sysconfig = require 'gluon.sysconfig'
|
|
local iwinfo = require 'iwinfo'
|
|
|
|
local uci = require('simple-uci').cursor()
|
|
|
|
-- Initial
|
|
if not sysconfig.gluon_version then
|
|
uci:delete_all('wireless', 'wifi-iface')
|
|
|
|
-- First count all radios with a fixed frequency band.
|
|
-- This is needed to distribute devices which have radios
|
|
-- capable of operating in the 2.4 GHz and 5 GHz band need
|
|
-- to be distributed evenly.
|
|
local radio_band_count = {band24=0, band5=0}
|
|
wireless.foreach_radio(uci, function(radio)
|
|
local hwmodes = iwinfo.nl80211.hwmodelist(wireless.find_phy(radio))
|
|
if hwmodes.g and not (hwmodes.a or hwmodes.ac) then
|
|
-- 2.4 GHz
|
|
radio_band_count["band24"] = radio_band_count["band24"] + 1
|
|
elseif (hwmodes.a or hwmodes.ac) and not hwmodes.g then
|
|
-- 5 GHz
|
|
radio_band_count["band5"] = radio_band_count["band5"] + 1
|
|
end
|
|
end)
|
|
|
|
-- Use the number of all fixed 2.4G GHz and 5 GHz radios to
|
|
-- distribute dualband radios in this step.
|
|
wireless.foreach_radio(uci, function(radio)
|
|
local radio_name = radio['.name']
|
|
local hwmodes = iwinfo.nl80211.hwmodelist(wireless.find_phy(radio))
|
|
if (hwmodes.a or hwmodes.ac) and hwmodes.g then
|
|
-- Dualband radio
|
|
if radio_band_count["band24"] <= radio_band_count["band5"] then
|
|
-- Assign radio to 2.4GHz band
|
|
radio_band_count["band24"] = radio_band_count["band24"] + 1
|
|
uci:set('wireless', radio_name, 'band', '2g')
|
|
else
|
|
-- Assign radio to 5GHz band
|
|
radio_band_count["band5"] = radio_band_count["band5"] + 1
|
|
uci:set('wireless', radio_name, 'band', '5g')
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function is_outdoor()
|
|
return uci:get_bool('gluon', 'wireless', 'outdoor')
|
|
end
|
|
|
|
local function get_channel(radio, config)
|
|
local channel
|
|
if wireless.preserve_channels(uci) then
|
|
-- preserved channel always wins
|
|
channel = radio.channel
|
|
elseif radio.band == '5g' and is_outdoor() then
|
|
-- actual channel will be picked and probed from chanlist
|
|
channel = 'auto'
|
|
end
|
|
|
|
return channel or config.channel()
|
|
end
|
|
|
|
local function get_htmode(radio)
|
|
if radio.band == '5g' and is_outdoor() then
|
|
local outdoor_htmode = uci:get('gluon', 'wireless', 'outdoor_' .. radio['.name'] .. '_htmode')
|
|
if outdoor_htmode ~= nil then
|
|
return outdoor_htmode
|
|
end
|
|
end
|
|
|
|
local phy = wireless.find_phy(radio)
|
|
if iwinfo.nl80211.hwmodelist(phy).ax then
|
|
return 'HE20'
|
|
end
|
|
|
|
if iwinfo.nl80211.hwmodelist(phy).ac then
|
|
return 'VHT20'
|
|
end
|
|
|
|
return 'HT20'
|
|
end
|
|
|
|
local function is_disabled(name)
|
|
if not uci:get('wireless', name) then
|
|
return nil
|
|
end
|
|
|
|
return uci:get_bool('wireless', name, 'disabled')
|
|
end
|
|
|
|
-- Returns the first argument that is not nil; don't call without any non-nil arguments!
|
|
local function first_non_nil(first, ...)
|
|
if first ~= nil then
|
|
return first
|
|
end
|
|
|
|
return first_non_nil(...)
|
|
end
|
|
|
|
|
|
local function delete_ibss(radio_name)
|
|
local name = 'ibss_' .. radio_name
|
|
|
|
uci:delete('network', name)
|
|
uci:delete('network', name .. '_vlan')
|
|
uci:delete('wireless', name)
|
|
end
|
|
|
|
local function configure_mesh(config, radio, index, suffix, disabled)
|
|
local radio_name = radio['.name']
|
|
local name = 'mesh_' .. radio_name
|
|
|
|
local macfilter = uci:get('wireless', name, 'macfilter')
|
|
local maclist = uci:get('wireless', name, 'maclist')
|
|
|
|
uci:delete('network', name)
|
|
uci:delete('network', name .. '_vlan')
|
|
uci:delete('wireless', name)
|
|
|
|
if not config then
|
|
return
|
|
end
|
|
|
|
local macaddr = wireless.get_wlan_mac(uci, radio, index, 2)
|
|
if not macaddr then
|
|
return
|
|
end
|
|
|
|
uci:section('network', 'interface', name, {
|
|
proto = 'gluon_mesh',
|
|
})
|
|
|
|
uci:section('wireless', 'wifi-iface', name, {
|
|
device = radio_name,
|
|
network = name,
|
|
mode = 'mesh',
|
|
mesh_id = config.id,
|
|
mesh_fwding = false,
|
|
macaddr = macaddr,
|
|
mcast_rate = config.mcast_rate,
|
|
ifname = suffix and 'mesh' .. suffix,
|
|
disabled = disabled,
|
|
macfilter = macfilter,
|
|
maclist = maclist,
|
|
})
|
|
end
|
|
|
|
local function fixup_wan(radio, index)
|
|
local radio_name = radio['.name']
|
|
local name = 'wan_' .. radio_name
|
|
|
|
if not uci:get('wireless', name) then
|
|
return
|
|
end
|
|
|
|
local macaddr = wireless.get_wlan_mac(uci, radio, index, 4)
|
|
if not macaddr then
|
|
return
|
|
end
|
|
|
|
uci:set('wireless', name, 'macaddr', macaddr)
|
|
end
|
|
|
|
local function configure_mesh_wireless(radio, index, config, disabled)
|
|
local radio_name = radio['.name']
|
|
local suffix = radio_name:match('^radio(%d+)$')
|
|
|
|
configure_mesh(config.mesh(), radio, index, suffix,
|
|
first_non_nil(
|
|
disabled,
|
|
is_disabled('mesh_' .. radio_name),
|
|
config.mesh.disabled(false)
|
|
)
|
|
)
|
|
end
|
|
|
|
wireless.foreach_radio(uci, function(radio, index, config)
|
|
local radio_name = radio['.name']
|
|
|
|
delete_ibss(radio_name)
|
|
|
|
if not config() then
|
|
uci:set('wireless', radio_name, 'disabled', true)
|
|
return
|
|
end
|
|
|
|
local suffix = radio_name:match('^radio(%d+)$')
|
|
if not suffix then
|
|
return
|
|
end
|
|
|
|
local channel = get_channel(radio, config)
|
|
local htmode = get_htmode(radio)
|
|
local beacon_interval = config.beacon_interval()
|
|
|
|
uci:delete('wireless', radio_name, 'disabled')
|
|
|
|
uci:set('wireless', radio_name, 'channel', channel)
|
|
uci:set('wireless', radio_name, 'htmode', htmode)
|
|
uci:set('wireless', radio_name, 'country', site.regdom())
|
|
|
|
uci:delete('wireless', radio_name, 'supported_rates')
|
|
uci:delete('wireless', radio_name, 'basic_rate')
|
|
|
|
local band = radio.band
|
|
if band == '2g' then
|
|
uci:set('wireless', radio_name, 'legacy_rates', false)
|
|
configure_mesh_wireless(radio, index, config)
|
|
elseif (band == '5g') then
|
|
if is_outdoor() then
|
|
uci:set('wireless', radio_name, 'channels', config.outdoor_chanlist())
|
|
|
|
-- enforce outdoor channels by filtering the regdom for outdoor channels
|
|
local hostapd_options = uci:get_list('wireless', radio_name, 'hostapd_options')
|
|
util.add_to_set(hostapd_options, 'country3=0x4f')
|
|
uci:set_list('wireless', radio_name, 'hostapd_options', hostapd_options)
|
|
|
|
configure_mesh_wireless(radio, index, config, true)
|
|
else
|
|
uci:delete('wireless', radio_name, 'channels')
|
|
|
|
local hostapd_options = uci:get_list('wireless', radio_name, 'hostapd_options')
|
|
util.remove_from_set(hostapd_options, 'country3=0x4f')
|
|
uci:set_list('wireless', radio_name, 'hostapd_options', hostapd_options)
|
|
|
|
configure_mesh_wireless(radio, index, config)
|
|
end
|
|
end
|
|
|
|
uci:set('wireless', radio_name, 'beacon_int', beacon_interval)
|
|
|
|
fixup_wan(radio, index)
|
|
end)
|
|
|
|
|
|
if uci:get('system', 'rssid_wlan0') then
|
|
uci:set('system', 'rssid_wlan0', 'dev', 'mesh0')
|
|
uci:save('system')
|
|
end
|
|
|
|
uci:save('wireless')
|
|
uci:save('network')
|