2022-02-22 18:25:01 -07:00
#! /usr/bin/lua
--[[
2023-01-15 00:07:22 -07:00
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
2023-12-12 21:01:23 -07:00
Copyright (C) 2021-2023 Tim Wilkinson
2023-01-15 00:07:22 -07:00
Orignal Perl Copyright (C) 2015 Conrad Lara
See Contributors file for additional contributors
2022-02-22 18:25:01 -07:00
2023-01-15 00:07:22 -07:00
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 3 of the License.
2022-02-22 18:25:01 -07:00
2023-01-15 00:07:22 -07:00
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
2022-02-22 18:25:01 -07:00
2023-01-15 00:07:22 -07:00
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
2022-02-22 18:25:01 -07:00
2023-01-15 00:07:22 -07:00
Additional Terms:
2022-02-22 18:25:01 -07:00
2023-01-15 00:07:22 -07:00
Additional use restrictions exist on the AREDN(TM) trademark and logo.
See AREDNLicense.txt for more info.
2022-02-22 18:25:01 -07:00
2023-01-15 00:07:22 -07:00
Attributions to the AREDN Project must be retained in the source code.
If importing this code into a new or existing project attribution
to the AREDN project must be added to the source code.
2022-02-22 18:25:01 -07:00
2023-01-15 00:07:22 -07:00
You must not misrepresent the origin of the material contained within.
2022-02-22 18:25:01 -07:00
2023-01-15 00:07:22 -07:00
Modified versions must be modified to attribute to the original source
and be marked in reasonable ways as differentiate it from the original
version
2022-02-22 18:25:01 -07:00
--]]
require("nixio")
require("aredn.utils")
2023-12-12 21:01:23 -07:00
require('aredn.info')
2022-02-22 18:25:01 -07:00
require("aredn.hardware")
2023-01-15 00:07:22 -07:00
require("iwinfo")
2023-12-12 21:01:23 -07:00
require("aredn.services")
2022-02-22 18:25:01 -07:00
-- helpers start
function is_null(v)
2022-02-27 16:57:51 -07:00
if not v or v == "" or v == 0 or v == "0" then
2022-02-22 18:25:01 -07:00
return true
else
return false
end
end
2023-01-15 00:07:22 -07:00
local function h2s(hex)
local s = ""
if hex then
for i = 1,#hex,2
do
local p = hex:sub(i, i+1)
if p:match("[0-9a-f][0-9a-f]") then
s = s .. string.char(tonumber(p, 16))
else
s = s .. p
end
end
end
return s
end
2022-02-22 18:25:01 -07:00
-- helpers end
2023-06-21 22:07:21 -06:00
local c = uci.cursor()
2023-12-06 12:39:23 -07:00
local cm = uci.cursor("/etc/config.mesh")
2023-06-21 22:07:21 -06:00
2023-01-15 00:07:22 -07:00
local ieee80211 = "/sys/class/ieee80211/"
2022-12-22 13:22:49 -07:00
local lanintf = aredn.hardware.get_board_network_ifname("lan")
2023-12-12 21:01:23 -07:00
local node = aredn.info.get_nvram("node")
local tactical = aredn.info.get_nvram("tactical")
local mac2 = aredn.info.get_nvram("mac2")
local dtdmac = aredn.info.get_nvram("dtdmac")
2022-02-22 18:25:01 -07:00
2023-12-12 21:01:23 -07:00
local wifi_mon_enable = false
2022-02-22 18:25:01 -07:00
local deleteme = {}
local cfg = {
lan_intf = lanintf,
2022-12-22 13:22:49 -07:00
wan_intf = aredn.hardware.get_board_network_ifname("wan"),
2023-01-15 00:07:22 -07:00
bridge_network_config = "",
lan_network_config = "",
wan_network_config = "",
dtdlink_network_config = "",
2023-09-19 21:06:09 -06:00
wifi_network_config = "",
2023-12-06 12:39:23 -07:00
olsrd_dtd_interface_mode = "ether",
tun_network_config = "",
2023-12-12 21:01:23 -07:00
wireguard_network_config = "",
olsrd_pollrate = "0.05",
tun_devices_config = ""
}
-- Track the changes so we can make better decissions about what to restart/reboot
local changes = {
reboot = false,
system = false,
manager = false,
network = false,
olsr = false,
dnsmasq = false,
firewall = false,
tunnels = false,
wireless = false
2013-11-14 23:11:16 -07:00
}
2023-01-15 22:50:39 -07:00
function expand_vars(lines)
local nlines = {}
for line in lines:gmatch("([^\n]*\n?)")
do
local inc = line:match("^include%s+(%S+)%s*")
if inc then
if nixio.fs.stat(inc) then
line = expand_vars(read_all(inc))
else
line = nil
end
elseif line:match("^[^#]") then
for parm in line:gmatch("<([^%s]*)>")
do
if deleteme[parm] then
line = nil
elseif parm == "NODE" then
line = line:gsub("<NODE>", node)
elseif parm == "MAC2" then
line = line:gsub("<MAC2>", mac2)
elseif parm == "DTDMAC" then
line = line:gsub("<DTDMAC>", dtdmac)
2023-02-07 20:57:20 -07:00
elseif cfg[parm] then
2023-01-15 22:50:39 -07:00
line = line:gsub("<" .. parm .. ">", cfg[parm])
2023-02-07 20:57:20 -07:00
else
line = nil
2023-01-15 22:50:39 -07:00
end
end
end
if line then
nlines[#nlines + 1] = line
end
end
return table.concat(nlines, "")
end
2022-02-22 18:25:01 -07:00
-- load the verify the selected configuration
for line in io.lines("/etc/config.mesh/_setup")
do
if not (line:match("^%s*#") or line:match("^%s*$")) then
line = line:gsub("<NODE>", node):gsub("<MAC2>", mac2):gsub("<DTDMAC>", dtdmac)
local k, v = line:match("^([^%s]*)%s*=%s*(.*)%s*$")
cfg[k] = v
end
end
if cfg.wifi_enable == "1" then
2023-05-25 22:27:59 -06:00
if not cfg.wifi_intf or cfg.wifi_intf == "" then
cfg.wifi_intf = aredn.hardware.get_board_network_ifname("wifi"):match("^(%S+)")
end
2023-06-22 18:17:43 -06:00
local mtu = c:get("aredn", "@lqm[0]", "mtu")
mtu = tonumber(mtu)
2023-06-21 22:07:21 -06:00
if mtu and mtu >= 256 and mtu <= 1500 then
cfg.wifi_mtu = mtu
end
2018-12-12 14:16:07 -07:00
else
2023-01-15 00:07:22 -07:00
cfg.wifi_intf = "br-nomesh"
2022-02-22 18:25:01 -07:00
end
2023-09-19 21:06:09 -06:00
-- Supernode options
2023-12-06 12:39:23 -07:00
local is_supernode = (cm:get("aredn", "@supernode[0]", "enable") == "1")
2023-09-19 21:06:09 -06:00
if is_supernode then
cfg.olsrd_dtd_interface_mode = "isolated"
2023-12-06 13:14:17 -07:00
cfg.olsrd_pollrate = "0.01"
2023-09-19 21:06:09 -06:00
end
2022-02-22 18:25:01 -07:00
-- delete some config lines if necessary
if cfg.wan_proto == "dhcp" then
deleteme.wan_ip = true
deleteme.wan_gw = true
deleteme.wan_mask = true
end
2022-02-27 16:57:51 -07:00
if not is_null(cfg.dmz_mode) or cfg.wan_proto ~= "disabled" then
2022-02-22 18:25:01 -07:00
deleteme.lan_gw = true
end
-- lan_dhcp sense is inverted in the dhcp config file
-- and it is a checkbox so it may not be defined - this fixes that
if cfg.lan_dhcp == "1" then
cfg.lan_dhcp = 0
else
cfg.lan_dhcp = 1
end
2023-12-06 13:19:18 -07:00
-- handle possible remote syslog
local remote_log = c:get("aredn", "@remotelog[0]", "url") or ""
local proto, ip, port = remote_log:match("^(.+)://(%d+%.%d+%.%d+%.%d+):(%d+)$")
port = tonumber(port)
if proto and (proto == "tcp" or proto == "udp") and (port > 0 and port < 65536) and validate_ip(ip) then
cfg.remote_log_ip = ip
cfg.remote_log_port = port
cfg.remote_log_proto = proto
else
deleteme.remote_log_ip = true
deleteme.remote_log_port = true
deleteme.remote_log_proto = true
end
2022-02-22 18:25:01 -07:00
-- verify that we have all the variables we need
for file in nixio.fs.glob("/etc/config.mesh/*")
do
for line in io.lines(file)
do
if line:match("^[^#]") then
for parm in line:gmatch("<([^%s]*)>")
do
if parm:upper() == parm then
-- nvram variable
2023-12-12 21:01:23 -07:00
if aredn.info.get_nvram(parm:lower()) == "" then
2022-02-22 18:25:01 -07:00
print ("nv parameter '" .. parm .. "' in file '" .. file .. "' does not exist")
return -1
end
else
if not cfg[parm] and not deleteme[parm] then
print ("parameter '" .. parm .. "' in file '" .. file .. "' does not exist")
return -1
end
end
end
end
end
end
-- sensible dmz_mode default
if is_null(cfg.dmz_mode) then
2022-02-27 16:57:51 -07:00
cfg.dmz_mode = "0"
2022-02-22 18:25:01 -07:00
end
-- switch to dmz values if needed
if not is_null(cfg.dmz_mode) then
cfg.lan_ip = cfg.dmz_lan_ip
cfg.lan_mask = cfg.dmz_lan_mask
cfg.dhcp_start = cfg.dmz_dhcp_start
cfg.dhcp_end = cfg.dmz_dhcp_end
cfg.dhcp_limit = cfg.dmz_dhcp_limit
end
-- select ports and dhcp files based on mode
local portfile = "/etc/config.mesh/_setup.ports"
local dhcpfile = "/etc/config.mesh/_setup.dhcp"
local aliasfile = "/etc/config.mesh/aliases"
local servfile = "/etc/config.mesh/_setup.services"
2022-02-27 16:57:51 -07:00
if is_null(cfg.dmz_mode) then
2022-02-22 18:25:01 -07:00
portfile = portfile .. ".nat"
dhcpfile = dhcpfile .. ".nat"
aliasfile = aliasfile .. ".nat"
servfile = servfile .. ".nat"
else
portfile = portfile .. ".dmz"
dhcpfile = dhcpfile .. ".dmz"
aliasfile = aliasfile .. ".dmz"
servfile = servfile .. ".dmz"
end
-- check for old aliases file, copy it to .dmz and create symlink
-- just in case anyone is already using the file for some script or something
if not nixio.fs.readlink("/etc/config.mesh/aliases") then
if nixio.fs.stat("/etc/config.mesh/aliases") then
filecopy("/etc/config.mesh/aliases", "/etc/config.mesh/aliases.dmz")
os.remove("/etc/config.mesh/aliases")
else
2022-03-17 13:26:52 -06:00
io.open("/etc/config.mesh/aliases.dmz", "a"):close()
2022-02-22 18:25:01 -07:00
end
nixio.fs.symlink("aliases.dmz", "/etc/config.mesh/aliases")
end
2023-12-12 21:01:23 -07:00
-- generate the new school bridge configuration
if nixio.fs.stat("/etc/aredn_include/bridge.network.user") then
cfg.bridge_network_config = expand_vars(read_all("/etc/aredn_include/bridge.network.user"))
else
local list = {}
for _, net in ipairs({ "lan", "wan", "dtdlink" })
do
local ports = aredn.hardware.get_board_network_ifname(net):split(" ")
for _, port in ipairs(ports)
2023-01-15 00:07:22 -07:00
do
2023-12-12 21:01:23 -07:00
list[port:gsub("%..*$", "")] = true
2023-01-15 00:07:22 -07:00
end
end
2023-12-12 21:01:23 -07:00
local config = "config device\n option name 'br0'\n option type 'bridge'\n option vlan_filtering '1'\n"
for port, _ in pairs(list)
2023-01-15 00:07:22 -07:00
do
2023-12-12 21:01:23 -07:00
config = config .. " list ports '" .. port .. "'\n"
end
cfg.bridge_network_config = config
end
-- generate the network configurations
for _, net in ipairs({ "lan", "wan", "dtdlink", "wifi" })
do
local wireless = false
local config = ""
-- user override
if nixio.fs.stat("/etc/aredn_include/" .. net .. ".network.user") then
if nixio.fs.stat("/etc/aredn_include/fixedmac." .. net) then
for line in io.lines("/etc/aredn_include/fixedmac." .. net)
do
local m = line:match("option%s+macaddr%s+(%S+)")
if m then
cfg[net .. "_mac"] = m
end
end
end
config = expand_vars(read_all("/etc/aredn_include/" .. net .. ".network.user"))
else
-- generate a complete config
local vlan = nil
local ports = aredn.hardware.get_board_network_ifname(net):split(" ")
if net == "lan" then
-- If the LAN has be given a vlan to use (usually by a switch) we use that here,
-- otherwise we just use '3'
local lvlan = ports[1]:match("%.(%d+)$")
if lvlan then
vlan = lvlan .. ":t"
else
vlan = "3:u"
end
elseif net == "wan" then
-- wifi can be used for the WAN (we become a wifi client)
if cfg.wifi3_enable == "1" then
-- WAN uses wifi
wireless = true
ports = { "wlan0" }
for devname in nixio.fs.dir(ieee80211)
2023-02-07 20:57:20 -07:00
do
2023-12-12 21:01:23 -07:00
local hwmode = "11g"
if iwinfo.nl80211.freqlist(devname)[1].mhz > 5000 then
hwmode="11a"
end
if hwmode == cfg.wifi3_hwmode then
ports = { "wlan" .. devname:match("^phy(%d+)$") }
break
2023-02-07 20:57:20 -07:00
end
end
2023-12-12 21:01:23 -07:00
else
-- If the WAN has been given a vlan to use (usually 1) we use that here.
local wvlan = ports[1]:match("%.(%d+)$")
if wvlan then
vlan = wvlan .. ":t"
2023-01-15 00:07:22 -07:00
else
2023-12-12 21:01:23 -07:00
vlan = "4:u"
2023-01-15 00:07:22 -07:00
end
2023-12-12 21:01:23 -07:00
-- handle wan vlan override
for line in io.lines("/etc/config.mesh/_setup")
do
local wport, wvlan = line:match("^wan_intf = (%w+%.)(%d+)")
2023-01-15 00:07:22 -07:00
if wvlan then
vlan = wvlan .. ":t"
2023-12-12 21:01:23 -07:00
ports = { wport .. wvlan }
break
2023-01-15 00:07:22 -07:00
end
end
end
2023-12-12 21:01:23 -07:00
elseif net == "dtdlink" then
-- Always vlan 2
vlan = "2:t"
cfg.dtdlink_proto = "static"
cfg.dtdlink_mask = "255.0.0.0"
elseif net == "wifi" then
wireless = true
ports = { cfg.wifi_intf }
end
2023-01-15 00:07:22 -07:00
2023-12-12 21:01:23 -07:00
if vlan then
-- new school vlan configuration
local v, t = vlan:match("(.*):(.*)")
config = config .. "\nconfig bridge-vlan\n option device 'br0'\n option vlan '" .. v .. "'\n"
for _, port in ipairs(ports)
do
config = config .. " list ports '" .. port:gsub("%..*$", "") .. ":" .. t .. "'\n"
2023-01-15 00:07:22 -07:00
end
2023-12-12 21:01:23 -07:00
config = config .. "\n"
ports = { "br0." .. v }
end
2023-01-15 00:07:22 -07:00
2023-12-12 21:01:23 -07:00
local proto = cfg[net .. "_proto"] or ""
local ipaddr = cfg[net .. "_ip"] or ""
local netmask = cfg[net .. "_mask"] or ""
local mtu = cfg[net .. "_mtu"] or ""
local dns1 = ""
local dns2 = ""
if net == "lan" then
dns1 = cfg.wan_dns1 or ""
dns2 = cfg.wan_dns2 or ""
end
local gateway = cfg[net .. "_gw"] or ""
2023-01-15 00:07:22 -07:00
2023-12-12 21:01:23 -07:00
config = config .. "config device\n"
if not wireless then
config = config .. " option name 'br-" .. net .. "'\n option type 'bridge'\n"
elseif cfg.wifi_enable ~= "1" then
netmask = "255.255.255.255"
end
if nixio.fs.stat("/etc/aredn_include/fixedmac." .. net) then
config = config .. read_all("/etc/aredn_include/fixedmac." .. net)
end
2023-01-15 00:07:22 -07:00
2023-12-12 21:01:23 -07:00
if not wireless then
if #ports == 0 then
config = config .. " option bridge_empty '1'\n"
2023-01-15 00:07:22 -07:00
else
2023-12-12 21:01:23 -07:00
for _, port in ipairs(ports)
do
config = config .. " list ports '" .. port .. "'\n"
2023-01-15 00:07:22 -07:00
end
end
2023-12-12 21:01:23 -07:00
else
config = config .. " option name '" .. ports[1] .. "'\n"
if not ports[1]:match("^wlan") then
config = config .. " option type 'bridge'\n option bridge_empty '1'\n"
2023-01-15 00:07:22 -07:00
end
2022-12-22 13:22:49 -07:00
end
2023-12-12 21:01:23 -07:00
config = config .. "\nconfig interface " .. net .. "\n"
if wireless then
config = config .. " option device '" .. ports[1] .. "'\n"
else
config = config .. " option device 'br-" .. net .. "'\n"
2023-12-06 12:39:23 -07:00
end
2023-12-12 21:01:23 -07:00
if proto ~= "" then
config = config .. " option proto '" .. proto .. "'\n"
2023-12-06 12:39:23 -07:00
end
2023-12-12 21:01:23 -07:00
if mtu ~= "" then
config = config .. " option mtu '" .. mtu .. "'\n"
2023-12-06 12:39:23 -07:00
end
2023-12-12 21:01:23 -07:00
if ipaddr ~= "" then
config = config .. " option ipaddr '" .. ipaddr .. "'\n"
2023-12-06 12:39:23 -07:00
end
2023-12-12 21:01:23 -07:00
if netmask ~= "" then
config = config .. " option netmask '" .. netmask .. "'\n"
2023-12-06 12:39:23 -07:00
end
2023-12-12 21:01:23 -07:00
if dns1 ~= "" or dns2 ~= "" then
config = config .. " option dns '" .. (dns1 or "") .. (dns1 and dns2 and " " or "") .. (dns2 or "") .. "'\n"
end
if gateway ~= "" then
config = config .. " option gateway '" .. gateway .. "'\n"
2022-02-22 18:25:01 -07:00
end
end
2023-12-12 21:01:23 -07:00
cfg[net .. "_network_config"] = config
end
2022-02-22 18:25:01 -07:00
2023-12-12 21:01:23 -07:00
-- Generate the tunnel configurations
local tun_port = cm:get("vtun", "@options[0]", "port")
if tun_port then
cfg.tun_network_config = cfg.tun_network_config .. "config options\n\toption port '" .. tun_port .. "'\n\n"
end
local tun_start = cm:get("vtun", "@network[0]", "start")
local tun_dns = cm:get("vtun", "@network[0]", "dns")
if tun_start or tun_dns then
cfg.tun_network_config = cfg.tun_network_config .. "config network\n"
if tun_start then
cfg.tun_network_config = cfg.tun_network_config .. "\toption start '" .. tun_start .. "'\n"
2022-02-22 18:25:01 -07:00
end
2023-12-12 21:01:23 -07:00
if tun_dns then
cfg.tun_network_config = cfg.tun_network_config .. "\toption dns '" .. tun_dns .. "'\n"
2022-02-22 18:25:01 -07:00
end
2023-12-12 21:01:23 -07:00
cfg.tun_network_config = cfg.tun_network_config .. "\n"
2022-02-22 18:25:01 -07:00
end
2023-12-13 00:26:27 -07:00
local vtunclients = 0
2023-12-12 21:01:23 -07:00
cm:foreach("vtun", "client",
function(s)
if s.enabled == "1" then
cfg.tun_network_config = cfg.tun_network_config .. string.format("config client\n\toption enabled '1'\n\toption node '%s'\n\toption passwd '%s'\n\toption clientip '%s'\n\toption serverip '%s'\n\toption netip '%s'\n\n",
s.node, s.passwd, s.clientip, s.serverip, s.netip)
2023-12-13 00:26:27 -07:00
vtunclients = vtunclients + 1
2023-12-12 21:01:23 -07:00
end
2022-02-22 18:25:01 -07:00
end
2023-12-12 21:01:23 -07:00
)
local wgclients = 0
cm:foreach("wireguard", "client",
function(s)
if s.enabled == "1" then
local client_priv, client_pub = s.key:match("^(.+=)(.+=)$")
cfg.wireguard_network_config = cfg.wireguard_network_config ..
string.format("config wireguard_wgc\n\toption public_key '%s'\n\toption persistent_keepalive '25'\n\tlist allowed_ips '0.0.0.0/0'\n\n",
client_pub)
wgclients = wgclients + 1
end
2022-02-22 18:25:01 -07:00
end
2023-12-12 21:01:23 -07:00
)
if wgclients > 0 then
local private = cm:get("wireguard", "@wireguard_server[0]", "private")
local mask_size = tonumber(cm:get("wireguard", "@wireguard_server[0]", "masksize") or 26)
local ab, c, d = tun_start:match("^(%d+%.%d+%.)(%d+)%.(%d+)$")
c = tonumber(c) + 1
if c > 255 then
c = 0
2022-02-22 18:25:01 -07:00
end
2023-12-12 21:01:23 -07:00
d = nixio.bit.band(tonumber(d), nixio.bit.lshift(255, 32 - mask_size)) + 1
cfg.wireguard_network_config =
string.format("config interface 'wgc'\n\toption proto 'wireguard'\n\toption private_key '%s'\n\toption nohostroute '1'\n\toption listen_port '%s'\n\tlist addresses '%s'\n\n",
private, (tun_port or 5525), (ab .. c .. "." .. d)) ..
cfg.wireguard_network_config
end
2023-12-13 00:26:27 -07:00
local vtunservers = 0
2023-12-12 21:01:23 -07:00
local wgservers = 0
cm:foreach("vtun", "server",
function(s)
if s.enabled == "1" then
if s.netip:match("/") then
local server_pub, client_priv, client_pub = s.passwd:match("^(.+=)(.+=)(.+=)$")
cfg.wireguard_network_config = cfg.wireguard_network_config ..
string.format("config interface 'wgs%d'\n\toption proto 'wireguard'\n\toption private_key '%s'\n\toption nohostroute '1'\n\tlist addresses '%s'\n\n",
wgservers, client_priv, s.netip:match("^(.+)/"))
cfg.wireguard_network_config = cfg.wireguard_network_config ..
string.format("config wireguard_wgs%d\n\toption public_key '%s'\n\toption endpoint_host '%s'\n\toption endpoint_port '%s'\n\toption persistent_keepalive '25'\n\tlist allowed_ips '0.0.0.0/0'\n\n",
wgservers, server_pub, s.host, (tun_port or 5525))
wgservers = wgservers + 1
else
cfg.tun_network_config = cfg.tun_network_config ..
string.format("config server\n\toption enabled '1'\n\toption host '%s'\n\toption node '%s'\n\toption passwd '%s'\n\toption clientip '%s'\n\toption serverip '%s'\n\toption netip '%s'\n\n",
s.host, s.node, s.passwd, s.clientip, s.serverip, s.netip)
2023-12-13 00:26:27 -07:00
vtunservers = vtunservers + 1
2022-02-22 18:25:01 -07:00
end
end
end
2023-12-12 21:01:23 -07:00
)
-- Create the tunnel devices we need
2023-12-13 14:59:54 -07:00
local maxclients = 0
local maxservers = 0
2023-12-12 21:01:23 -07:00
-- No tunnel devices without the vtund app
2023-12-13 14:59:54 -07:00
if nixio.fs.stat("/usr/sbin/vtund") then
maxclients = 10 * math.ceil(vtunclients / 10)
maxservers = 10 * math.ceil(vtunservers / 10)
2023-12-12 21:01:23 -07:00
end
2023-12-13 00:57:36 -07:00
for dev = 50, 50 + maxclients + maxservers - 1
2023-12-12 21:01:23 -07:00
do
cfg.tun_devices_config = cfg.tun_devices_config ..
string.format("config interface 'tun%d'\n\toption ifname 'tun%d'\n\toption proto 'none'\n\n", dev, dev)
end
-- Not wireguard devices without the wg app
if not nixio.fs.stat("/usr/bin/wg") then
2023-12-13 14:59:54 -07:00
wgclients = 0
wgservers = 0
end
if wgclients + wgservers > 0 then
local maxwgservers = math.min(10 * math.ceil(wgservers / 10), nixio.bit.lshift(1, 32 - tonumber(cm:get("wireguard", "@wireguard_server[0]", "masksize") or 26)))
2023-12-12 21:01:23 -07:00
cfg.tun_devices_config = cfg.tun_devices_config ..
string.format("config interface 'wgc'\n\toption ifname 'wgc'\n\toption proto 'none'\n\n")
2023-12-13 14:59:54 -07:00
for dev = 0, maxwgservers - 1
2023-12-12 21:01:23 -07:00
do
cfg.tun_devices_config = cfg.tun_devices_config ..
string.format("config interface 'wgs%d'\n\toption ifname 'wgs%d'\n\toption proto 'none'\n\n", dev, dev)
2022-02-22 18:25:01 -07:00
end
2023-12-12 21:01:23 -07:00
end
2022-02-22 18:25:01 -07:00
2023-12-12 21:01:23 -07:00
remove_all("/tmp/new_config")
nixio.fs.mkdir("/tmp/new_config")
2022-02-22 18:25:01 -07:00
2023-12-12 21:01:23 -07:00
for file in nixio.fs.glob("/etc/config.mesh/*")
do
local bfile = nixio.fs.basename(file)
if bfile == "vtun" or bfile == "wireguard" or bfile == "xlink" or bfile == "olsrd" or bfile == "firewall.user" or bfile:match("^_setup") or bfile:match("^aliases") then
-- Dont copy these
else
local f = io.open("/tmp/new_config/" .. bfile, "w")
if f then
f:write(expand_vars(read_all(file)))
f:close()
2022-02-22 18:25:01 -07:00
end
end
end
2023-12-12 21:01:23 -07:00
-- Tunnels
write_all("/tmp/new_config/vtun", expand_vars("<tun_network_config>"))
local nc = uci.cursor("/tmp/new_config")
2022-02-22 18:25:01 -07:00
-- append to firewall
local add_masq = false
2023-12-12 21:01:23 -07:00
local fw = io.open("/tmp/new_config/firewall", "a")
2022-02-22 18:25:01 -07:00
if fw then
if not is_null(cfg.dmz_mode) then
fw:write("\nconfig forwarding\n option src wifi\n option dest lan\n")
fw:write("\nconfig forwarding\n option src dtdlink\n option dest lan\n")
add_masq = true
else
fw:write("\nconfig 'include'\n option 'path' '/etc/firewall.natmode'\n option 'reload' '1'\n")
end
2022-08-11 01:19:33 -06:00
if c:get("aredn", "@wan[0]", "olsrd_gw") == "1" then
2022-02-22 18:25:01 -07:00
fw:write("\nconfig forwarding\n option src wifi\n option dest wan\n")
fw:write("\nconfig forwarding\n option src dtdlink\n option dest wan\n")
end
2022-02-26 13:49:12 -07:00
if nixio.fs.access(portfile) then
for line in io.lines(portfile)
do
if not (line:match("^%s*#") or line:match("^%s*$")) then
local dip = line:match("dmz_ip = (%w+)")
if dip and cfg.dmz_mode ~= 0 then
fw:write("\nconfig redirect\n option src wifi\n option proto tcp\n option src_dip " .. cfg.wifi_ip .. "\n option dest_ip " .. dip .. "\n")
fw:write("\nconfig redirect\n option src wifi\n option proto udp\n option src_dip " .. cfg.wifi_ip .. "\n option dest_ip " .. dip .. "\n")
else
local intf, type, oport, host, iport, enable = line:match("(.*):(.*):(.*):(.*):(.*):(.*)")
if enable == "1" then
local match = " option src_dport " .. oport .. "\n"
if type == "tcp" then
match = match .. " option proto tcp\n"
elseif type == "udp" then
match = match .. " option proto udp\n"
end
-- uci the host and then
-- set the inside port unless the rule uses an outside port range
host = "option dest_ip " .. host .. "\n"
2022-05-22 20:05:08 -06:00
if not oport:match("-") then
2022-02-26 13:49:12 -07:00
host = host .. " option dest_port " .. iport .. "\n"
end
if not is_null(cfg.dmz_mode) and intf == "both" then
intf = "wan"
end
if intf == "both" then
fw:write("\nconfig redirect\n option src wifi\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
fw:write("\nconfig redirect\n option src dtdlink\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
fw:write("config redirect\n option src wan\n " .. match .. " " .. host .. "\n")
elseif intf == "wifi" and is_null(cfg.dmz_mode) then
fw:write("\nconfig redirect\n option src dtdlink\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
fw:write("\nconfig redirect\n option src wifi\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
elseif intf == "wan" then
fw:write("\nconfig redirect\n option src dtdlink\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
fw:write("config redirect\n option src wan\n " .. match .. " " .. host .. "\n")
end
2022-02-22 18:25:01 -07:00
end
end
end
end
end
fw:close();
end
if add_masq then
2023-12-12 21:01:23 -07:00
nc:set("firewall", "@zone[2]", "masq", "0")
nc:commit("firewall")
2022-02-22 18:25:01 -07:00
end
-- setup node lan dhcp
2023-12-12 21:01:23 -07:00
if nc:get("aredn", "@wan[0]", "lan_dhcp_route") == "1" or nc:get("aredn", "@wan[0]", "lan_dhcp_defaultroute") == "1" then
2022-08-11 01:19:33 -06:00
-- Provide stateless routes and default route
2023-12-12 21:01:23 -07:00
nc:set("dhcp", "@dhcp[0]", "dhcp_option", {
2022-08-14 10:57:11 -06:00
"121,10.0.0.0/8," .. cfg.lan_ip .. ",0.0.0.0/0," .. cfg.lan_ip,
"249,10.0.0.0/8," .. cfg.lan_ip .. ",0.0.0.0/0," .. cfg.lan_ip
2022-02-22 18:25:01 -07:00
})
else
2022-08-11 01:19:33 -06:00
-- Provide stateless routes to the mesh, and a blank default route (option 3 has no values) to
-- surpress default route being sent
2023-12-12 21:01:23 -07:00
nc:set("dhcp", "@dhcp[0]", "dhcp_option", {
2022-08-14 10:57:11 -06:00
"121,10.0.0.0/8," .. cfg.lan_ip,
"249,10.0.0.0/8," .. cfg.lan_ip,
2022-08-11 01:19:33 -06:00
"3"
2022-02-22 18:25:01 -07:00
})
end
2023-12-12 21:01:23 -07:00
nc:commit("dhcp")
2022-02-22 18:25:01 -07:00
-- generate the wireless config file
2023-01-15 00:07:22 -07:00
local config = ""
local ifacenum = 0
local ifacecount = 0
2023-12-06 11:12:11 -07:00
local devpaths = {}
2023-04-28 12:01:58 -06:00
if nixio.fs.stat(ieee80211) then
for devname in nixio.fs.dir(ieee80211)
do
ifacecount = ifacecount + 1
2023-01-15 00:07:22 -07:00
end
2023-04-28 12:01:58 -06:00
for devname in nixio.fs.dir(ieee80211)
do
local dev = devname:match("^phy(%d+)$")
local radio = "radio" .. dev
local wlan = "wlan" .. dev
local devpath = nixio.fs.realpath(ieee80211 .. nixio.fs.readlink(ieee80211 .. devname)):match("^/sys/devices/(.*)/ieee802.*$")
if devpath:match("^platform.*/pci.*") then
devpath = devpath:match("^platform/(.*)")
end
2023-12-06 11:12:11 -07:00
local devpathc = devpaths[devpath] or 0
devpaths[devpath] = devpathc + 1
if devpathc > 0 then
devpath = devpath .. "+" .. devpathc
end
2023-04-28 12:01:58 -06:00
local is_mesh_rf = false
local htmode = "HT20"
local disabled = "0"
local chanbw = nil
local country = nil
local channel = nil
local distance = nil
local hwmode = "11g"
if iwinfo.nl80211.freqlist(devname)[1].mhz > 5000 then
hwmode="11a"
end
local network = nil
local mode = nil
local ssid = nil
local encryption = nil
local key = nil
if wlan == cfg.wifi_intf then
-- mesh RF adhoc configuration
is_mesh_rf = true
channel = cfg.wifi_channel
chanbw = cfg.wifi_chanbw
country = "HX"
distance = cfg.wifi_distance
ssid = cfg.wifi_ssid .. "-" .. chanbw .. "-v3"
mode = "adhoc"
encryption = "none"
network = "wifi"
elseif cfg.wifi2_enable == "1" and (ifacecount == 1 or (ifacecount > 1 and hwmode == cfg.wifi2_hwmode)) then
-- lan AP interface
channel = cfg.wifi2_channel
ssid = h2s(cfg.wifi2_ssid)
mode = "ap"
encryption = cfg.wifi2_encryption
key = h2s(cfg.wifi2_key)
network = "lan"
elseif cfg.wifi3_enable == "1" and (ifacecount == 1 or (ifacecount > 1 and hwmode == cfg.wifi3_hwmode)) then
-- wan client
ssid = h2s(cfg.wifi3_ssid)
mode = "sta"
if cfg.wifi3_key and cfg.wifi3_key ~= "" then
encryption = "psk2"
key = h2s(cfg.wifi3_key)
else
enable = "none"
end
network = "wan"
htmode = nil
2023-01-15 00:07:22 -07:00
else
2023-04-28 12:01:58 -06:00
disabled = "1"
2023-01-15 00:07:22 -07:00
end
2023-04-28 12:01:58 -06:00
config = config .. "config wifi-device '" .. radio .. "'\n option type 'mac80211'\n"
config = config .. " option disabled '" .. disabled .. "'\n"
if channel then
config = config .. " option channel '" .. channel .. "'\n"
end
if chanbw then
config = config .. " option chanbw '" .. chanbw .. "'\n"
end
if country then
config = config .. " option country '" .. country .. "'\n"
end
if distance then
config = config .. " option distance '" .. distance .. "'\n"
end
config = config .. " option hwmode '" .. hwmode .. "'\n"
if htmode then
config = config .. " option htmode '" .. htmode .. "'\n"
end
config = config .. " option path '" .. devpath .. "'\n\n"
2023-01-15 00:07:22 -07:00
2023-04-28 12:01:58 -06:00
config = config .. "config wifi-iface\n"
config = config .. " option ifname '" .. wlan .. "'\n"
2023-01-15 00:07:22 -07:00
config = config .. " option device '" .. radio .. "'\n"
2023-04-28 12:01:58 -06:00
if network then
config = config .. " option network '" .. network .. "'\n"
end
if mode then
config = config .. " option mode '" .. mode .. "'\n"
end
if ssid then
config = config .. " option ssid '" .. ssid .. "'\n"
end
if encryption then
config = config .. " option encryption '" .. encryption .. "'\n"
end
if key then
config = config .. " option key '" .. key .. "'\n"
end
config = config .. "\n"
2023-12-06 12:59:47 -07:00
if is_mesh_rf and wifi_mon_enable then
2023-04-28 12:01:58 -06:00
config = config .. "config wifi-iface\n"
2023-12-06 12:59:47 -07:00
config = config .. " option ifname '" .. wlan .. "-1'\n"
config = config .. " option device '" .. radio .. "'\n"
config = config .. " option network 'wifi_mon'\n option mode 'monitor'\n\n"
2023-04-28 12:01:58 -06:00
end
2023-01-15 00:07:22 -07:00
2023-04-28 12:01:58 -06:00
ifacenum = ifacenum + 1
end
2023-01-15 00:07:22 -07:00
end
2023-12-12 21:01:23 -07:00
write_all("/tmp/new_config/wireless", config)
-- indicate whether lan is running in dmz mode
nc:set("aredn", "@dmz[0]", "mode", cfg.dmz_mode)
nc:commit("aredn")
-- generate the host and ethers files
local h = io.open("/etc/hosts", "w")
local e = io.open("/etc/ethers", "w")
if h and e then
h:write("# automatically generated file - do not edit\n")
h:write("# use /etc/hosts.user for custom entries\n")
h:write("127.0.0.1\tlocalhost\n")
if not is_null(cfg.wifi_ip) then
h:write(cfg.lan_ip .. "\tlocalnode\n")
h:write(cfg.wifi_ip .. "\t" .. node .. " " .. tactical .. "\n")
else
h:write(cfg.lan_ip .. "\tlocalnode " .. node .. " " .. tactical .. "\n")
end
if not is_null(cfg.dtdlink_ip) then
h:write(cfg.dtdlink_ip .. "\tdtdlink." .. node .. ".local.mesh dtdlink." .. node .."\n")
end
if is_null(cfg.dmz_mode) then
h:write(decimal_to_ip(ip_to_decimal(cfg.lan_ip) + 1) .. "\tlocalap\n")
end
e:write("# automatically generated file - do not edit\n")
e:write("# use /etc/ethers.user for custom entries\n")
2022-02-22 18:25:01 -07:00
2023-12-12 21:01:23 -07:00
local netaddr = nixio.bit.band(ip_to_decimal(cfg.lan_ip), ip_to_decimal(cfg.lan_mask))
if nixio.fs.access(dhcpfile) then
for line in io.lines(dhcpfile)
do
if not (line:match("^%s*#") or line:match("^%s*$")) then
local mac, ip, host, noprop = line:match("(%S+)%s+(%S+)%s+(%S+)%s*(%S*)")
if mac and ip and host and noprop then
ip = decimal_to_ip(netaddr + ip)
if validate_same_subnet(ip, cfg.lan_ip, cfg.lan_mask) and validate_ip_netmask(ip, cfg.lan_mask) then
h:write(ip .. "\t" .. host .. " " .. noprop .. "\n")
e:write(mac .. "\t" .. ip .. "\n")
end
end
end
end
end
-- aliases need to ba added to /etc/hosts or they will now show up on the localnode
-- nor will the services thehy offer
-- also add a comment to the hosts file so we can display the aliases differently if needed
local f = io.open(aliasfile, "r")
if f then
for line in f:lines()
do
if not (line:match("^%s*#") or line:match("^%s*$")) then
local ip, host = line:match("(%S+)%s+(%S+)")
if ip then
if host:match("%.") and not host:match("%.local%.mesh$") then
host = host .. ".local.mesh"
end
h:write(ip .. "\t" .. host .. " #ALIAS\n")
end
end
end
f:close()
end
h:write("\n")
if nixio.fs.access("/etc/hosts.user", "r") then
for line in io.lines("/etc/hosts.user")
do
h:write(line .. "\n")
end
end
if nixio.fs.access("/etc/ethers.user", "r") then
for line in io.lines("/etc/ethers.user")
do
e:write(line .. "\n")
end
end
h:close()
e:close()
end
-- generate olsrd.conf
if nixio.fs.access("/etc/config.mesh/olsrd", "r") then
local of = io.open("/tmp/new_config/olsrd", "w")
if of then
for line in io.lines("/etc/config.mesh/olsrd")
do
if line:match("<olsrd_bridge>") then
if is_null(cfg.olsrd_bridge) then
line = line:gsub("<olsrd_bridge>", '"wifi" "lan"')
else
line = line:gsub("<olsrd_bridge>", '"lan"')
end
elseif line:match("^[^#]") then
for parm in line:gmatch("<([^%s]*)>")
do
line = line:gsub("<" .. parm .. ">", cfg[parm])
end
end
of:write(line .. "\n")
end
if not is_null(cfg.dmz_mode) then
local a, b, c, d = cfg.dmz_lan_ip:match("(.*)%.(.*)%.(.*)%.(.*)")
of:write(string.format("\nconfig Hna4\n\toption netaddr %s.%s.%s.%d\n\toption netmask 255.255.255.%d\n\n", a, b, c, d - 1, nixio.bit.band(255 * 2 ^ cfg.dmz_mode, 255)))
end
if cfg.wifi_enable ~= "1" and not is_null(cfg.wifi_ip) then
of:write(string.format("config Hna4\n\toption netaddr %s\n\toption netmask 255.255.255.255\n\n", cfg.wifi_ip))
end
if is_supernode then
of:write("config Hna4\n\toption netaddr 10.0.0.0\n\toption netmask 255.0.0.0\n\n")
end
if nc:get("aredn", "@wan[0]", "olsrd_gw") == "1" then
of:write("config LoadPlugin\n\toption library 'olsrd_dyn_gw.so.0.5'\n\toption Interval '60'\n\tlist Ping '8.8.8.8'\n\tlist Ping '8.8.4.4'\n\n")
end
of:write("config LoadPlugin 'nameservice'\n\toption library 'olsrd_nameservice.so.0.4'\n\toption interval '30'\n\toption timeout '300'\n\toption sighup_pid_file '/var/run/dnsmasq/dnsmasq.pid'\n\toption name_change_script '/usr/local/bin/olsrd-namechange'\n")
aredn.services.reset_validation()
local names, hosts, services = aredn.services.get()
for _, name in ipairs(names)
do
of:write("\tlist name '" .. name .. "'\n")
end
for _, host in ipairs(hosts)
do
if host.host ~= "" then
2023-12-13 23:49:28 -07:00
of:write("\tlist hosts '" .. host.ip .. " " .. host.host .. "'\n")
2023-12-12 21:01:23 -07:00
end
end
for _, service in ipairs(services)
do
of:write("\tlist service '" .. service .. "'\n")
end
of:write("\n")
-- add all the tunnel interfaces
2023-12-13 00:26:27 -07:00
if vtunclients + vtunservers + wgclients + wgservers > 0 then
of:write("config Interface\n")
2023-12-13 00:57:36 -07:00
for dev = 50, 50 + vtunclients - 1
do
of:write("\tlist interface 'tun" .. dev .. "'\n")
end
for dev = 50 + maxclients, 50 + maxclients + vtunservers - 1
2023-12-13 00:26:27 -07:00
do
of:write("\tlist interface 'tun" .. dev .. "'\n")
end
if wgclients + wgservers > 0 then
of:write("\tlist interface 'wgc'\n")
for dev = 0, wgservers - 1
do
of:write("\tlist interface 'wgs" .. dev .. "'\n")
end
end
of:write("\toption Ip4Broadcast '255.255.255.255'\n")
local tun_weight = tonumber(nc:get("aredn", "@tunnel[0]", "weight") or 1)
local is_supernode = nc:get("aredn", "@supernode[0]", "enable") == "1"
if not tun_weight or tun_weight < 1 or is_supernode then
of:write("\toption Mode 'ether'\n")
elseif tun_weight > 1 then
of:write("\toption LinkQualityMult 'default " .. (1 / tun_weight) .. "'\n")
end
2023-12-12 21:01:23 -07:00
end
2023-12-13 14:59:54 -07:00
nc:set("aredn", "@tunnel[0]", "maxclients", maxclients)
nc:set("aredn", "@tunnel[0]", "maxservers", maxservers)
2023-12-12 21:01:23 -07:00
-- add xlink interfaces
if nixio.fs.stat("/etc/config.mesh/xlink") then
uci.cursor("/etc/config.mesh"):foreach("xlink", "interface",
function(section)
2023-12-13 23:49:28 -07:00
of:write("\nconfig Interface\n\tlist interface '" .. section[".name"] .. "'\n")
2023-12-12 21:01:23 -07:00
if section.peer then
of:write("\toption Ip4Broadcast '" .. section.peer .. "'\n")
else
of:write("\toption Ip4Broadcast '255.255.255.255'\n")
end
local weight = tonumber(section.weight or 0)
if weight then
if weight > 1 then
of:write("\toption LinkQualityMult 'default " .. (1 / weight) .. "'\n")
elseif weight < 1 then
2023-12-13 23:49:28 -07:00
of:write("\toption Mode 'ether'\n")
2023-12-12 21:01:23 -07:00
end
else
of:write("\toption Mode 'ether'\n")
end
end
)
end
of:close()
end
end
-- generate the services file
local sf = io.open("/tmp/new_config/services", "w")
if sf then
if nixio.fs.access(servfile) then
for line in io.lines(servfile)
do
if not (line:match("^%s*#") or line:match("^%s*$")) then
local name, link, proto, host, port, sffx = line:match("(.*)|(.*)|(.*)|(.*)|(.*)|(.*)")
if name and name ~= "" and host ~= "" then
if proto == "" then
proto = "http"
end
if link == "0" then
port = "0"
end
sf:write(string.format("%s://%s:%s/%s|tcp|%s\n", proto, host, port, sffx, name))
end
end
end
end
sf:close()
end
-- Update user firewall
local _, diff = filecopy("/etc/config.mesh/firewall.user", "/etc/firewall.user", true)
if diff then
changes.firewall = true
end
-- Update services script
local sf = io.open("/tmp/local_services", "w")
if sf then
sf:write("#!/bin/sh\n")
if cfg.wifi_proto ~= "disabled" then
local wifi_channel = tonumber(cfg.wifi_channel)
if is_null(cfg.wifi_txpower) or tonumber(cfg.wifi_txpower) > aredn.hardware.wifi_maxpower(cfg.wifi_intf, wifi_channel) then
cfg.wifi_txpower = aredn.hardware.wifi_maxpower(cfg.wifi_intf, wifi_channel)
elseif tonumber(cfg.wifi_txpower) < 1 then
cfg.wifi_txpower = 1
end
if cfg.wifi_enable == "1" then
sf:write("/usr/sbin/iw dev " .. cfg.wifi_intf .. " set txpower fixed " .. cfg.wifi_txpower .. "00\n")
end
if not is_null(cfg.aprs_lat) and not is_null(cfg.aprs_lon) then
nc:set("aredn", "@location[0]", "lat", cfg.aprs_lat)
nc:set("aredn", "@location[0]", "lon", cfg.aprs_lon)
nc:commit("aredn")
end
end
sf:close()
local _, diff = filecopy("/tmp/local_services", "/etc/local/services", true)
if diff then
changes.reboot = true
end
os.remove("/tmp/local_services")
nixio.fs.chmod("/etc/local/services", "777")
end
---
--- Make it official
---
-- Handle /etc/config/aredn specially because only some changes require a restart/reboot
local config_aredn = {
lqm_enable = c:get("aredn", "@lqm[0]", "enable"),
tunnel_weight = c:get("aredn", "@tunnel[0]", "weight"),
supernode_enable = c:get("aredn", "@supernode[0]", "enable")
}
local nfiles = {}
for file in nixio.fs.glob("/tmp/new_config/*")
do
nfiles[nixio.fs.basename(file)] = true
end
-- Remove files we no longer need
for file in nixio.fs.glob("/etc/config/*")
do
if not nfiles[nixio.fs.basename(file)] then
nixio.fs.remove(file)
changes.reboot = true
end
end
for file, _ in pairs(nfiles)
do
local ffile = "/tmp/new_config/" .. file
local _, diff = filecopy(ffile, "/etc/config/" .. file, true)
if diff then
if file == "system" then
changes.system = true
elseif file == "aredn" then
local oc = uci:cursor()
if oc:get("aredn", "@lqm[0]", "enable") ~= config_aredn.lqm_enable then
changes.manager = true
end
if oc:get("aredn", "@tunnel[0]", "weight") ~= config_aredn.tunnel_weight then
changes.olsrd = true
end
if oc:get("aredn", "@supernode[0]", "enable") ~= config_aredn.supernode_enable then
changes.reboot = true
end
elseif file == "network" then
changes.network = true
elseif file == "dhcp" then
changes.dnsmasq = true
elseif file == "olsrd" then
changes.olsrd = true
elseif file == "firewall" then
changes.firewall = true
elseif file == "wireless" then
changes.wireless = true
elseif file == "vtun" then
changes.tunnels = true
else
changes.reboot = true
end
end
nixio.fs.remove(ffile)
end
nixio.fs.rmdir("/tmp/new_config")
aredn.info.set_nvram("config", "mesh")
aredn.info.set_nvram("node", node)
aredn.info.set_nvram("tactical", tactical)
-- Set file flags for whichever parts of the system require a restart/reboot
for k, v in pairs(changes)
do
if v then
nixio.fs.mkdir("/tmp/reboot-required")
io.open("/tmp/reboot-required/" .. k, "w"):close()
end
2022-02-22 18:25:01 -07:00
end
return 0