Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • firmware/gluon
  • 0x4A6F/gluon
  • patrick/gluon
3 results
Select Git revision
Loading items
Show changes
Showing
with 689 additions and 225 deletions
......@@ -13,6 +13,7 @@ end
local sysconfig = require 'gluon.sysconfig'
local platform = require 'gluon.platform'
local wireless = require 'gluon.wireless'
local config = site.wifi5.outdoors('preset')
local outdoor
......@@ -22,7 +23,7 @@ if sysconfig.gluon_version then
outdoor = false
elseif config == 'preset' then
-- enable outdoor mode through presets on new installs
outdoor = platform.is_outdoor_device() and platform.device_uses_11a(uci)
outdoor = platform.is_outdoor_device() and wireless.device_uses_11a(uci)
else
-- enable/disable outdoor mode unconditionally on new installs
outdoor = config
......
#!/usr/bin/lua
local wireless = require 'gluon.wireless'
local uci = require('simple-uci').cursor()
local preserve_channels = wireless.preserve_channels(uci)
uci:section('gluon', 'wireless', 'wireless', {
preserve_channels = preserve_channels or false,
})
uci:save('gluon')
......@@ -38,11 +38,11 @@ if not sysconfig.gluon_version then
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, 'hwmode', '11g')
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, 'hwmode', '11a')
uci:set('wireless', radio_name, 'band', '5g')
end
end
end)
......@@ -53,20 +53,20 @@ local function is_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.hwmode == '11a' or radio.hwmode == '11na') and is_outdoor() then
if radio.band == '5g' and is_outdoor() then
-- actual channel will be picked and probed from chanlist
channel = 'auto'
return 'auto'
end
return channel or config.channel()
return config.channel()
end
local function get_htmode(radio)
if (radio.hwmode == '11a' or radio.hwmode == '11na') and is_outdoor() then
if wireless.preserve_channels(uci) then
return radio.htmode
end
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
......@@ -74,6 +74,10 @@ local function get_htmode(radio)
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
......@@ -82,28 +86,26 @@ local function get_htmode(radio)
end
local function is_disabled(name)
if uci:get('wireless', name) then
return uci:get_bool('wireless', name, 'disabled')
else
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
else
return first_non_nil(...)
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
......@@ -114,8 +116,6 @@ local function configure_mesh(config, radio, index, suffix, disabled)
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
......@@ -138,6 +138,7 @@ local function configure_mesh(config, radio, index, suffix, disabled)
mesh_id = config.id,
mesh_fwding = false,
macaddr = macaddr,
basic_rate = { config.mcast_rate },
mcast_rate = config.mcast_rate,
ifname = suffix and 'mesh' .. suffix,
disabled = disabled,
......@@ -175,6 +176,20 @@ local function configure_mesh_wireless(radio, index, config, disabled)
)
end
local function set_channels(radio, radio_name, config)
if wireless.preserve_channels(uci) then
return
end
local channel = get_channel(radio, config)
uci:set('wireless', radio_name, 'channel', channel)
local chanlist
if radio.band == '5g' and is_outdoor() then
chanlist = config.outdoor_chanlist()
end
uci:set('wireless', radio_name, 'channels', chanlist)
end
wireless.foreach_radio(uci, function(radio, index, config)
local radio_name = radio['.name']
......@@ -190,40 +205,35 @@ wireless.foreach_radio(uci, function(radio, index, config)
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)
set_channels(radio, radio_name, config)
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 hwmode = radio.hwmode
if hwmode == '11g' or hwmode == '11ng' then
local band = radio.band
if band == '2g' then
uci:set('wireless', radio_name, 'legacy_rates', false)
configure_mesh_wireless(radio, index, config)
elseif (hwmode == '11a' or hwmode == '11na') then
if is_outdoor() then
uci:set('wireless', radio_name, 'channels', config.outdoor_chanlist())
-- enforce outdoor channels by filtering the regdom for outdoor channels
elseif (band == '5g') then
-- ToDo: Remove in v2024.x
local hostapd_options = uci:get_list('wireless', radio_name, 'hostapd_options')
util.add_to_set(hostapd_options, 'country3=0x4f')
util.remove_from_set(hostapd_options, 'country3=0x4f')
uci:set_list('wireless', radio_name, 'hostapd_options', hostapd_options)
if is_outdoor() then
-- enforce outdoor channels by filtering the regdom for outdoor channels
uci:set('wireless', radio_name, 'country3', '0x4f')
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)
uci:delete('wireless', radio_name, 'country3')
configure_mesh_wireless(radio, index, config)
end
end
......@@ -239,5 +249,10 @@ if uci:get('system', 'rssid_wlan0') then
uci:save('system')
end
if uci:get('system', 'rssid_wlan1') then
uci:set('system', 'rssid_wlan1', 'dev', 'mesh1')
uci:save('system')
end
uci:save('wireless')
uci:save('network')
#!/usr/bin/lua
local uci = require('simple-uci').cursor()
local platform = require 'gluon.platform'
-- This is a workaround to result in a configuration where all ports
-- on the DSA switch are connected to the same CPU interface.
--
-- See Gluon issue #3479 for details.
-- https://github.com/freifunk-gluon/gluon/issues/3479
if not platform.match('ipq806x', 'generic', {
'netgear,r7800',
'tplink,c2600',
}) then
return
end
local ports = {
'lan1',
'lan2',
'lan3',
'lan4',
'wan',
}
local cpu_conduit = 'eth0'
for _, port in ipairs(ports) do
local section_name = 'port_' .. port
uci:section('network', 'device', section_name, {
name = port,
conduit = cpu_conduit,
})
end
uci:save('network')
#!/usr/bin/lua
local site = require 'gluon.site'
local uci = require('simple-uci').cursor()
local util = require 'gluon.util'
local mesh_interfaces = util.get_role_interfaces(uci, 'mesh')
local uplink_interfaces = util.get_role_interfaces(uci, 'uplink')
local mesh_interfaces_uplink = {}
local mesh_interfaces_other = {}
for _, iface in ipairs(mesh_interfaces) do
if util.contains(uplink_interfaces, iface) then
table.insert(mesh_interfaces_uplink, iface)
else
table.insert(mesh_interfaces_other, iface)
end
end
if #mesh_interfaces_uplink > 0 then
uci:section('network', 'interface', 'mesh_uplink', {
ifname = 'br-wan',
proto = 'gluon_wired',
index = 0,
vxlan = site.mesh.vxlan(true),
})
end
if #mesh_interfaces_other > 0 then
local iftype, ifname
if #mesh_interfaces_other == 1 then
ifname = mesh_interfaces_other[1]
else
iftype = 'bridge'
ifname = mesh_interfaces_other
for _, iface in ipairs(ifname) do
uci:section('network', 'device', nil, {
name = iface,
isolate = true,
})
end
end
uci:section('network', 'interface', 'mesh_other', {
ifname = ifname,
type = iftype,
igmp_snooping = false,
proto = 'gluon_wired',
index = 4,
vxlan = site.mesh.vxlan(true),
})
end
uci:save('network')
#!/usr/bin/lua
local site = require 'gluon.site'
local uci = require('simple-uci').cursor()
uci:section('network', 'interface', 'mesh_wan', {
ifname = 'br-wan',
proto = 'gluon_wired',
index = 0,
})
local enable = site.mesh_on_wan(false)
local old_auto = uci:get('network', 'mesh_wan', 'auto')
local old_disabled = uci:get('network', 'mesh_wan', 'disabled')
if old_auto ~= nil or old_disabled ~= nil then
enable = old_auto ~= '0' and old_disabled ~= '1'
end
uci:set('network', 'mesh_wan', 'disabled', not enable)
if uci:get('network', 'mesh_wan', 'transitive') == nil then
uci:set('network', 'mesh_wan', 'transitive', true)
end
uci:delete('network', 'mesh_wan', 'auto')
uci:delete('network', 'mesh_wan', 'fixed_mtu')
uci:delete('network', 'mesh_wan', 'legacy')
uci:save('network')
#!/usr/bin/lua
local site = require 'gluon.site'
local util = require 'gluon.util'
local sysconfig = require 'gluon.sysconfig'
local uci = require('simple-uci').cursor()
if not sysconfig.lan_ifname then
os.exit(0)
end
uci:section('network', 'interface', 'mesh_lan', {
ifname = sysconfig.lan_ifname,
igmp_snooping = false,
proto = 'gluon_wired',
index = 4,
})
if sysconfig.lan_ifname:match(' ') then
uci:set('network', 'mesh_lan', 'type', 'bridge')
else
uci:delete('network', 'mesh_lan', 'type')
end
local enable = site.mesh_on_lan(false)
local old_auto = uci:get('network', 'mesh_lan', 'auto')
local old_disabled = uci:get('network', 'mesh_lan', 'disabled')
if old_auto ~= nil or old_disabled ~= nil then
enable = old_auto ~= '0' and old_disabled ~= '1'
end
if enable then
local interfaces = uci:get_list('network', 'client', 'ifname')
if interfaces then
for lanif in sysconfig.lan_ifname:gmatch('%S+') do
if util.contains(interfaces, lanif) then
enable = false
break
end
end
end
end
uci:set('network', 'mesh_lan', 'disabled', not enable)
if uci:get('network', 'mesh_lan', 'transitive') == nil then
uci:set('network', 'mesh_lan', 'transitive', true)
end
uci:delete('network', 'mesh_lan', 'auto')
uci:delete('network', 'mesh_lan', 'fixed_mtu')
uci:delete('network', 'mesh_lan', 'legacy')
uci:save('network')
#!/usr/bin/lua
local uci = require('simple-uci').cursor()
local platform = require 'gluon.platform'
if not platform.is_cellular_device() then
return
end
local function set_or_delete(package, section, option, val)
if val ~= nil and string.len(val) ~= 0 then
uci:set(package, section, option, val)
else
uci:delete(package, section, option)
end
end
local function setup_ncm_qmi(devpath, control_type, delay)
local pdptype = uci:get('gluon', 'cellular', 'pdptype')
local pincode = uci:get('gluon', 'cellular', 'pin')
local username = uci:get('gluon', 'cellular', 'username')
local password = uci:get('gluon', 'cellular', 'password')
local auth = uci:get('gluon', 'cellular', 'auth')
uci:section('network', 'interface', 'cellular', {
proto = control_type,
device = devpath,
disabled = not uci:get_bool('gluon', 'cellular', 'enabled'),
pdptype = pdptype,
peerdns = false,
apn = uci:get('gluon', 'cellular', 'apn'),
})
if pdptype ~= 'IP' then
uci:set('network', 'cellular', 'ipv6', 'auto')
else
uci:delete('network', 'cellular', 'ipv6')
end
set_or_delete('network', 'cellular', 'pincode', pincode)
set_or_delete('network', 'cellular', 'username', username)
set_or_delete('network', 'cellular', 'password', password)
set_or_delete('network', 'cellular', 'auth', auth)
set_or_delete('network', 'cellular', 'delay', delay)
end
if platform.match('ath79', 'nand', {
'glinet,gl-e750',
'glinet,gl-xe300',
}) then
setup_ncm_qmi('/dev/cdc-wdm0', 'qmi', 15)
elseif platform.match('ath79', 'nand', {
'zte,mf281',
}) then
setup_ncm_qmi('/dev/ttyACM0', 'ncm', 15)
elseif platform.match('ipq40xx', 'generic', {
'glinet,gl-ap1300',
'zte,mf289f',
}) then
setup_ncm_qmi('/dev/cdc-wdm0', 'qmi', 15)
elseif platform.match('ramips', 'mt7621', {
'wavlink,ws-wn572hp3-4g',
}) then
setup_ncm_qmi('/dev/ttyUSB2', 'ncm', 15)
elseif platform.match('ramips', 'mt76x8', {
'tplink,tl-mr6400-v5',
}) then
setup_ncm_qmi('/dev/cdc-wdm0', 'qmi', 15)
end
uci:save('network')
#!/usr/bin/lua
local uci = require('simple-uci').cursor()
local platform = require 'gluon.platform'
local defaults = uci:get_first('firewall', 'defaults')
......@@ -16,9 +17,19 @@ local function reject_input_on_wan(zone)
return true
end
local function add_cellular_wan(zone)
if zone.name == 'wan' then
uci:set('firewall', zone['.name'], 'network', {'wan', 'wan6', 'cellular_4', 'cellular_6'})
end
end
uci:foreach('firewall', 'zone', reject_input_on_wan)
for _, zone in ipairs({'mesh', 'local_client', 'wired_mesh'}) do
if platform.is_cellular_device() then
uci:foreach('firewall', 'zone', add_cellular_wan)
end
for _, zone in ipairs({'mesh', 'loc_client', 'wired_mesh'}) do
-- Other packages assign interfaces to these zones
uci:section('firewall', 'zone', zone, {
name = zone,
......@@ -52,13 +63,10 @@ for _, zone in ipairs({'mesh', 'local_client', 'wired_mesh'}) do
family = 'ipv6',
target = 'ACCEPT',
})
-- Can be removed soon: was never in a release
uci:delete('firewall', zone .. '_ICMPv6_out')
end
uci:section('firewall', 'rule', 'local_client_ICMPv4_in', {
src = 'local_client',
uci:section('firewall', 'rule', 'loc_client_ICMPv4_in', {
src = 'loc_client',
proto = 'icmp',
icmp_type = {
'echo-request',
......@@ -67,9 +75,8 @@ uci:section('firewall', 'rule', 'local_client_ICMPv4_in', {
target = 'ACCEPT',
})
-- allow inbound SSH from anywhere
for _, zone in ipairs({ 'wan', 'local_client', 'mesh' }) do
for _, zone in ipairs({ 'wan', 'loc_client', 'mesh' }) do
uci:section('firewall', 'rule', zone .. '_ssh', {
name = zone .. '_ssh',
src = zone,
......@@ -84,9 +91,9 @@ local wired_mesh_ifaces = {}
uci:foreach('network', 'interface',
function(iface)
-- Select all interfaces with proto gluon_wired except for
-- mesh_wan into this zone, as mesh_wan is the same
-- mesh_uplink into this zone, as mesh_uplink is the same
-- interface as wan, which has its own zone
if iface['proto'] == 'gluon_wired' and iface['.name'] ~= 'mesh_wan' then
if iface['proto'] == 'gluon_wired' and iface['.name'] ~= 'mesh_uplink' then
table.insert(wired_mesh_ifaces, iface['.name'])
end
end
......
#!/usr/bin/lua
local uci = require('simple-uci').cursor()
local function migrate_iface(iface)
if iface.proto ~= 'batadv' or iface.mesh ~= 'bat0' then
return
end
local s = iface['.name']
uci:set('network', s, 'proto', 'gluon_mesh')
uci:set('network', s, 'fixed_mtu', true)
if iface.mesh_no_rebroadcast then
uci:set('network', s, 'transitive', iface.mesh_no_rebroadcast)
end
uci:delete('network', s, 'mesh')
uci:delete('network', s, 'mesh_no_rebroadcast')
end
uci:foreach('network', 'interface', migrate_iface)
uci:save('network')
......@@ -12,13 +12,13 @@ uci:set('dhcp', dnsmasq, 'localise_queries', true)
uci:set('dhcp', dnsmasq, 'localservice', false)
uci:set('dhcp', dnsmasq, 'server', dns.servers)
uci:delete('dhcp', dnsmasq, 'cachesize')
uci:set('dhcp', dnsmasq, 'cachesize', dns.cacheentries)
uci:delete('firewall', 'client_dns')
if dns.servers then
-- allow inbound traffic for dns from client zone
uci:section('firewall', 'rule', 'client_dns', {
src = 'local_client',
src = 'loc_client',
dest_port = '53',
proto = 'tcpudp',
target = 'ACCEPT',
......
#!/usr/bin/lua
local uci = require('simple-uci').cursor()
for _, config in ipairs({'system', 'network'}) do
uci:foreach(config .. '_gluon-old', nil, function(s)
if s.gluon_preserve ~= '1' then return end
-- Unnamed sections can't be preserved
if s['.anonymous'] then return end
-- We don't allow overwriting existing sections
if uci:get(config, s['.name']) then return end
uci:section(config, s['.type'], s['.name'], s)
end)
uci:save(config)
end
#!/usr/bin/lua
local info = require 'gluon.info'
local values = info.get_info_pretty()
local padTo = 24
for _, value in ipairs(values) do
local labelLen = string.len(value[1]) + 1
print(value[1] .. ':' .. string.rep(' ', padTo - labelLen), value[2])
end
#!/usr/bin/lua
local unistd = require 'posix.unistd'
local function shift()
table.remove(arg, 1)
end
local reboot = true
if arg[1] == '--no-reboot' then
reboot = false
shift()
end
local setup_mode = unistd.access('/var/gluon/setup-mode') == 0
if #arg ~= 1 then
io.stderr:write('Usage: gluon-switch-domain [--no-reboot] <domain>\n')
os.exit(1)
end
local domain = arg[1]
if not unistd.access('/lib/gluon/domains/') then
io.stderr:write('This Gluon firmware does not support multiple mesh domains.\n')
os.exit(1)
end
local function domain_exists(dom)
return unistd.access('/lib/gluon/domains/' .. dom .. '.json') == 0
end
if not domain_exists(domain) then
io.stderr:write(string.format("Error: invalid mesh domain '%s'\n", domain))
os.exit(1)
end
local uci = require('simple-uci').cursor()
uci:set('gluon', 'core', 'switch_domain', domain)
uci:set('gluon', 'core', 'reconfigure', true)
uci:save('gluon')
local cmd
if setup_mode then
cmd = 'gluon-reconfigure'
elseif reboot then
uci:commit('gluon')
cmd = 'reboot'
else
cmd = 'gluon-reload'
end
unistd.execp(cmd, {[0] = cmd})
local util = require 'gluon.util'
local unistd = require 'posix.unistd'
local dirent = require 'posix.dirent'
local uci = require('simple-uci').cursor()
local M = {}
local function has_devtype(iface_dir, devtype)
return util.file_contains_line(iface_dir..'/uevent', 'DEVTYPE='..devtype)
end
local function is_physical(iface_dir)
return unistd.access(iface_dir .. '/device') == 0
end
local function is_swconfig()
local has = false
uci:foreach("network", "switch", function()
has = true
end)
uci:foreach("network", "switch_vlan", function()
has = true
end)
return has
end
local function interfaces_raw()
local eth_ifaces = {}
local ifaces_dir = '/sys/class/net/'
for iface in dirent.files(ifaces_dir) do
if iface ~= '.' and iface ~= '..' then
local iface_dir = ifaces_dir .. iface
if is_physical(iface_dir) and not has_devtype(iface_dir, 'wlan') then
table.insert(eth_ifaces, iface)
end
end
end
return eth_ifaces
end
-- In comparison to interfaces_raw, this skips non-DSA ports on DSA devices,
-- as for ex. hap ac² has a special eth0 that shouldn't be touched
function M.interfaces()
local intfs = interfaces_raw()
if M.get_switch_type() == 'dsa' then
local new_intfs = {}
for _, intf in ipairs(intfs) do
if has_devtype('/sys/class/net/' .. intf, 'dsa') then
table.insert(new_intfs, intf)
end
end
return new_intfs
end
return intfs
end
function M.is_vlan(intf)
return has_devtype('/sys/class/net/' .. intf, 'vlan')
end
function M.get_switch_type()
if is_swconfig() then
return 'swconfig'
end
for _, intf in ipairs(interfaces_raw()) do
if has_devtype('/sys/class/net/' .. intf, 'dsa') then
return 'dsa'
end
end
return 'none'
end
return M
local uci = require('simple-uci').cursor()
local pretty_hostname = require 'pretty_hostname'
local site = require 'gluon.site'
local sysconfig = require 'gluon.sysconfig'
local platform = require 'gluon.platform'
local util = require 'gluon.util'
local has_vpn, vpn = pcall(require, 'gluon.mesh-vpn')
local ethernet = require 'gluon.ethernet'
local pubkey
if has_vpn and vpn.enabled() then
local _, active_vpn = vpn.get_active_provider()
if active_vpn ~= nil then
pubkey = active_vpn.public_key()
end
end
local M = {}
function M.get_info()
local updater_enabled = uci:get_bool('autoupdater', 'settings', 'enabled')
return {
hostname = pretty_hostname.get(uci),
mac_address = sysconfig.primary_mac,
hardware_model = platform.get_model(),
gluon_version = util.trim(util.readfile('/lib/gluon/gluon-version')),
site_version = util.trim(util.readfile('/lib/gluon/site-version')),
firmware_release = util.trim(util.readfile('/lib/gluon/release')),
site = site.site_name(),
domain = uci:get('gluon', 'core', 'domain'),
public_vpn_key = pubkey,
switch_type = ethernet.get_switch_type(),
updater_branch = updater_enabled and uci:get('autoupdater', 'settings', 'branch'),
}
end
function M.get_info_pretty(i18n)
local _
if i18n then
local pkg_i18n = i18n 'gluon-core'
_ = function(s)
return pkg_i18n.translate(s)
end
else
_ = function(s)
return s
end
end
local data = M.get_info()
return {
{ _('Hostname'), data.hostname },
{ _('MAC address'), data.mac_address },
{ _('Hardware model'), data.hardware_model },
{ _('Gluon version') .. " / " .. _('Site version'), data.gluon_version .. " / " .. data.site_version },
{ _('Firmware release'), data.firmware_release },
{ _('Site'), data.site },
{ _('Domain'), data.domain or 'n/a' },
{ _('Public VPN key'), data.public_vpn_key or _('disabled') },
{ _('Switch type'), data.switch_type },
{ _('Autoupdater branch'), data.updater_branch or _('disabled') },
}
end
return M
local bit = require 'bit'
local bit = require 'bit32'
local M = {}
......
local platform_info = require 'platform_info'
local util = require 'gluon.util'
local wireless = require 'gluon.wireless'
local unistd = require 'posix.unistd'
local M = setmetatable({}, {
......@@ -25,85 +23,89 @@ function M.match(target, subtarget, boards)
end
function M.is_outdoor_device()
if M.match('ar71xx', 'generic', {
'bullet-m',
'cpe210',
'cpe510',
'wbs210',
'wbs510',
'lbe-m5',
'loco-m-xw',
'nanostation-m',
'nanostation-m-xw',
'rocket-m',
'rocket-m-ti',
'rocket-m-xw',
'unifi-outdoor',
'unifi-outdoor-plus',
if M.match('ath79', 'generic', {
'devolo,dvl1750x',
'librerouter,librerouter-v1',
'plasmacloud,pa300',
'plasmacloud,pa300e',
'tplink,cpe210-v1',
'tplink,cpe210-v2',
'tplink,cpe210-v3',
'tplink,cpe220-v3',
'tplink,cpe510-v1',
'tplink,cpe510-v2',
'tplink,cpe510-v3',
'tplink,cpe710-v1',
'tplink,eap225-outdoor-v1',
'tplink,eap225-outdoor-v3',
'tplink,wbs210-v1',
'tplink,wbs210-v2',
'tplink,wbs510-v1',
'ubnt,nanobeam-ac-xc',
'ubnt,nanobeam-m5-xw',
'ubnt,nanostation-loco-m-xw',
'ubnt,nanostation-m-xw',
'ubnt,uk-ultra',
'ubnt,unifi-ap-outdoor-plus',
'ubnt,unifiac-mesh',
'ubnt,unifiac-mesh-pro',
}) then
return true
elseif M.match('ar71xx', 'generic', {'unifiac-lite'}) and
M.get_model() == 'Ubiquiti UniFi-AC-MESH' then
elseif M.match('ath79', 'mikrotik', {
'mikrotik,routerboard-wapr-2nd',
}) then
return true
elseif M.match('ar71xx', 'generic', {'unifiac-pro'}) and
M.get_model() == 'Ubiquiti UniFi-AC-MESH-PRO' then
elseif M.match('ipq40xx', 'generic', {
'aruba,ap-365',
'plasmacloud,pa1200',
}) then
return true
elseif M.match('ath79', 'generic', {
'devolo,dvl1750x',
'tplink,cpe220-v3',
elseif M.match('ipq40xx', 'mikrotik', {
'mikrotik,sxtsq-5-ac',
}) then
return true
elseif M.match('ipq40xx', 'generic', {'engenius,ens620ext'}) then
elseif M.match('mediatek', 'filogic', {
'cudy,ap3000outdoor-v1'
}) then
return true
end
return false
end
function M.device_supports_wpa3()
-- rt2x00 crashes when enabling WPA3 personal / OWE VAP
if M.match('ramips', 'rt305x') then
return false
end
return unistd.access('/lib/gluon/features/wpa3')
end
function M.device_supports_mfp(uci)
local supports_mfp = true
if not M.device_supports_wpa3() then
return false
elseif M.match('ramips', 'mt7621', {
'wavlink,ws-wn572hp3-4g',
'zyxel,nwa55axe',
}) then
return true
end
uci:foreach('wireless', 'wifi-device', function(radio)
local phy = wireless.find_phy(radio)
local phypath = '/sys/kernel/debug/ieee80211/' .. phy .. '/'
if not util.file_contains_line(phypath .. 'hwflags', 'MFP_CAPABLE') then
supports_mfp = false
return false
end
end)
return supports_mfp
function M.is_cellular_device()
if M.match('ath79', 'nand', {
'zte,mf281',
'glinet,gl-e750',
'glinet,gl-xe300',
}) then
return true
elseif M.match('ipq40xx', 'generic', {
'glinet,gl-ap1300',
'zte,mf289f',
}) then
return true
elseif M.match('ramips', 'mt7621', {
'wavlink,ws-wn572hp3-4g',
}) then
return true
elseif M.match('ramips', 'mt76x8', {
'tplink,tl-mr6400-v5',
}) then
return true
end
function M.device_uses_11a(uci)
local ret = false
uci:foreach('wireless', 'wifi-device', function(radio)
if radio.hwmode == '11a' or radio.hwmode == '11na' then
ret = true
return false
end
end)
return ret
end
return M
......@@ -11,6 +11,10 @@ local function get(_, name)
end
local function set(_, name, val)
if val == get(nil, name) then
return
end
if val then
local f = io.open(sysconfigdir .. name, 'w+')
f:write(val, '\n')
......
local bit = require 'bit'
local bit = require 'bit32'
local posix_fcntl = require 'posix.fcntl'
local posix_glob = require 'posix.glob'
local posix_syslog = require 'posix.syslog'
local posix_unistd = require 'posix.unistd'
local hash = require 'hash'
local sysconfig = require 'gluon.sysconfig'
local site = require 'gluon.site'
local unistd = require 'posix.unistd'
local M = {}
......@@ -36,6 +40,10 @@ function M.contains(table, value)
end
function M.file_contains_line(path, value)
if not unistd.access(path) then
return false
end
for line in io.lines(path) do
if line == value then
return true
......@@ -130,10 +138,39 @@ function M.get_mesh_devices(uconn)
return devices
end
-- Returns a list of all interfaces with a given role
--
-- If exclusive is set to true, only interfaces that have no other role
-- are returned; this is used to ensure that the client role is not active
-- at the same time as any other role
function M.get_role_interfaces(uci, role, exclusive)
local ret = {}
local function add(name)
-- Interface names with a / prefix refer to sysconfig interfaces
-- (lan_ifname/wan_ifname/single_ifname)
if string.sub(name, 1, 1) == '/' then
name = sysconfig[string.sub(name, 2) .. '_ifname'] or ''
end
for iface in string.gmatch(name, '%S+') do
M.add_to_set(ret, iface)
end
end
uci:foreach('gluon', 'interface', function(s)
local roles = s.role or {}
if M.contains(roles, role) and (not exclusive or #roles == 1) then
add(s.name)
end
end)
return ret
end
-- Safe glob: returns an empty table when the glob fails because of
-- a non-existing path
function M.glob(pattern)
return posix_glob.glob(pattern) or {}
return posix_glob.glob(pattern, 0) or {}
end
-- Generates a (hopefully) unique MAC address
......@@ -179,4 +216,94 @@ function M.get_uptime()
return tonumber(uptime_file:match('^[^ ]+'))
end
function M.log(message, verbose)
if verbose then
io.stdout:write(message .. '\n')
end
posix_syslog.syslog(posix_syslog.LOG_INFO, message)
end
local function close_fds(fds)
for _, fd in pairs(fds) do
posix_unistd.close(fd)
end
end
M.subprocess = {}
M.subprocess.DEVNULL = -1
M.subprocess.PIPE = 1
-- Execute a program found using command PATH search, like the shell.
-- Return the pid, as well as the I/O streams as pipes or nil on error.
function M.subprocess.popen(path, argt, options)
argt = argt or {}
local childfds = {}
local parentfds = {}
local stdiostreams = {stdin = 0, stdout = 1, stderr = 2}
for iostream in pairs(stdiostreams) do
if options[iostream] == M.subprocess.PIPE then
local piper, pipew = posix_unistd.pipe()
if iostream == "stdin" then
childfds[iostream] = piper
parentfds[iostream] = pipew
else
childfds[iostream] = pipew
parentfds[iostream] = piper
end
end
end
-- childfds: r0, w1, w2
-- parentfds: w0, r1, r2
local pid, errmsg, errnum = posix_unistd.fork()
if pid == nil then
close_fds(parentfds)
close_fds(childfds)
return nil, errmsg, errnum
elseif pid == 0 then
local null = -1
if M.contains(options, M.subprocess.DEVNULL) then
-- only open if there's anything to discard
null = posix_fcntl.open('/dev/null', posix_fcntl.O_RDWR)
end
for iostream, fd in pairs(stdiostreams) do
local option = options[iostream]
if option == M.subprocess.DEVNULL then
posix_unistd.dup2(null, fd)
elseif option == M.subprocess.PIPE then
posix_unistd.dup2(childfds[iostream], fd)
end
end
close_fds(childfds)
close_fds(parentfds)
-- close potential null
if null > 2 then
posix_unistd.close(null)
end
posix_unistd.execp(path, argt)
posix_unistd._exit(127)
end
close_fds(childfds)
return pid, parentfds
end
function M.get_mem_total()
for line in io.lines('/proc/meminfo') do
local match = line:match('^MemTotal:%s+(%d+)')
if match then
return tonumber(match)
end
end
end
return M