mirror of https://github.com/aredn/aredn.git
1398 lines
52 KiB
Lua
Executable File
1398 lines
52 KiB
Lua
Executable File
#! /usr/bin/lua
|
|
--[[
|
|
|
|
Part of AREDN® -- Used for creating Amateur Radio Emergency Data Networks
|
|
Copyright (C) 2021-2023 Tim Wilkinson
|
|
Orignal Perl Copyright (C) 2015 Conrad Lara
|
|
See Contributors file for additional contributors
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
Additional Terms:
|
|
|
|
Additional use restrictions exist on the AREDN® trademark and logo.
|
|
See AREDNLicense.txt for more info.
|
|
|
|
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.
|
|
|
|
You must not misrepresent the origin of the material contained within.
|
|
|
|
Modified versions must be modified to attribute to the original source
|
|
and be marked in reasonable ways as differentiate it from the original
|
|
version
|
|
|
|
--]]
|
|
|
|
require("nixio")
|
|
require("aredn.utils")
|
|
require('aredn.info')
|
|
require("aredn.hardware")
|
|
require("iwinfo")
|
|
require("aredn.services")
|
|
|
|
-- helpers start
|
|
|
|
function is_null(v)
|
|
if not v or v == "" or v == 0 or v == "0" then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
function is_notnull(v)
|
|
return not is_null(v)
|
|
end
|
|
|
|
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
|
|
|
|
local function tablesize(t)
|
|
local len = 0
|
|
for _ in pairs(t)
|
|
do
|
|
len = len + 1
|
|
end
|
|
return len
|
|
end
|
|
|
|
local function get_subtable(container, key)
|
|
local subtable = container[key]
|
|
if not subtable then
|
|
subtable = {}
|
|
container[key] = subtable
|
|
end
|
|
return subtable
|
|
end
|
|
|
|
-- helpers end
|
|
|
|
local FORCED = "force"
|
|
local UNFORCED = "onrequest"
|
|
|
|
local c = uci.cursor()
|
|
local cm = uci.cursor("/etc/config.mesh")
|
|
|
|
local ieee80211 = "/sys/class/ieee80211/"
|
|
local lanintf = aredn.hardware.get_board_network_ifname("lan")
|
|
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")
|
|
|
|
local wifi_mon_enable = false
|
|
local deleteme = {}
|
|
local cfg = {
|
|
lan_intf = lanintf,
|
|
wan_intf = aredn.hardware.get_board_network_ifname("wan"),
|
|
bridge_network_config = "",
|
|
lan_network_config = "",
|
|
wan_network_config = "",
|
|
dtdlink_network_config = "",
|
|
wifi_network_config = "",
|
|
olsrd_dtd_interface_mode = "ether",
|
|
tun_network_config = "",
|
|
wireguard_network_config = "",
|
|
dtdlink_interfaces = "\tlist network 'dtdlink'",
|
|
vpn_interfaces = "",
|
|
olsrd_pollrate = "0.05",
|
|
tun_devices_config = "",
|
|
hello_interval = "4.0",
|
|
tc_interval = "10.0",
|
|
mid_interval = "10.0",
|
|
hna_interval = "10.0"
|
|
}
|
|
-- 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,
|
|
localservices = false,
|
|
wpad = false
|
|
}
|
|
|
|
function valid_config(config)
|
|
remove_all("/tmp/uci_validate")
|
|
nixio.fs.mkdir("/tmp/uci_validate")
|
|
write_all("/tmp/uci_validate/config", config)
|
|
local r = os.execute("/sbin/uci -c /tmp/uci_validate import dummy < /tmp/uci_validate/config")
|
|
remove_all("/tmp/uci_validate")
|
|
return r == 0 and true or false
|
|
end
|
|
|
|
function is_nat_mode()
|
|
return is_null(cfg.dmz_mode)
|
|
end
|
|
function is_dmz_mode()
|
|
return is_notnull(cfg.dmz_mode) and cfg.dmz_mode ~= "1"
|
|
end
|
|
function is_altnet_mode()
|
|
return cfg.dmz_mode == "1"
|
|
end
|
|
|
|
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))
|
|
if not valid_config(line) then
|
|
print("Invalid config fragment: " .. inc)
|
|
os.exit(1)
|
|
end
|
|
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)
|
|
elseif cfg[parm] then
|
|
line = line:gsub("<" .. parm .. ">", cfg[parm])
|
|
else
|
|
line = nil
|
|
end
|
|
end
|
|
end
|
|
if line then
|
|
nlines[#nlines + 1] = line
|
|
end
|
|
end
|
|
return table.concat(nlines, "")
|
|
end
|
|
|
|
-- 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
|
|
if not cfg.wifi_intf or cfg.wifi_intf == "" then
|
|
cfg.wifi_intf = aredn.hardware.get_board_network_ifname("wifi"):match("^(%S+)")
|
|
end
|
|
local mtu = c:get("aredn", "@lqm[0]", "mtu")
|
|
mtu = tonumber(mtu)
|
|
if mtu and mtu >= 256 and mtu <= 1500 then
|
|
cfg.wifi_mtu = mtu
|
|
end
|
|
else
|
|
cfg.wifi_intf = "br-nomesh"
|
|
end
|
|
|
|
-- Supernode options
|
|
|
|
local is_supernode = (cm:get("aredn", "@supernode[0]", "enable") == "1")
|
|
if is_supernode then
|
|
cfg.olsrd_dtd_interface_mode = "isolated"
|
|
cfg.olsrd_pollrate = "0.01"
|
|
end
|
|
|
|
-- 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
|
|
if is_dmz_mode() or is_altnet_mode() or cfg.wan_proto ~= "disabled" then
|
|
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
|
|
|
|
-- handle possible remote syslog
|
|
local remote_log = cm: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
|
|
|
|
-- 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
|
|
if aredn.info.get_nvram(parm:lower()) == "" then
|
|
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
|
|
cfg.dmz_mode = "0"
|
|
end
|
|
|
|
-- switch to dmz values if needed
|
|
if is_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 dhcptagsfile = "/etc/config.mesh/_setup.dhcptags"
|
|
local dhcpoptionsfile = "/etc/config.mesh/_setup.dhcpoptions"
|
|
local aliasfile = "/etc/config.mesh/aliases"
|
|
local servfile = "/etc/config.mesh/_setup.services"
|
|
if is_nat_mode() then
|
|
portfile = portfile .. ".nat"
|
|
dhcpfile = dhcpfile .. ".nat"
|
|
dhcptagsfile = dhcptagsfile .. ".nat"
|
|
dhcpoptionsfile = dhcpoptionsfile .. ".nat"
|
|
aliasfile = aliasfile .. ".nat"
|
|
servfile = servfile .. ".nat"
|
|
else
|
|
portfile = portfile .. ".dmz"
|
|
dhcpfile = dhcpfile .. ".dmz"
|
|
dhcptagsfile = dhcptagsfile .. ".dmz"
|
|
dhcpoptionsfile = dhcpoptionsfile .. ".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
|
|
io.open("/etc/config.mesh/aliases.dmz", "a"):close()
|
|
end
|
|
nixio.fs.symlink("aliases.dmz", "/etc/config.mesh/aliases")
|
|
end
|
|
|
|
-- 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"))
|
|
if not valid_config(cfg.bridge_network_config) then
|
|
print("Invalid config fragment: /etc/aredn_include/bridge.network.user")
|
|
os.exit(1)
|
|
end
|
|
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)
|
|
do
|
|
list[port:gsub("%..*$", "")] = true
|
|
end
|
|
end
|
|
local config = "config device\n option name 'br0'\n option type 'bridge'\n option vlan_filtering '1'\n"
|
|
for port, _ in pairs(list)
|
|
do
|
|
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"))
|
|
if not valid_config(config) then
|
|
print("Invalid config fragment: /etc/aredn_include/" .. net .. ".network.user")
|
|
os.exit(1)
|
|
end
|
|
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)
|
|
do
|
|
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
|
|
end
|
|
end
|
|
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"
|
|
else
|
|
vlan = "4:u"
|
|
end
|
|
-- handle wan vlan override
|
|
for line in io.lines("/etc/config.mesh/_setup")
|
|
do
|
|
local wport, wvlan = line:match("^wan_intf = (%w+%.)(%d+)")
|
|
if wvlan then
|
|
vlan = wvlan .. ":t"
|
|
ports = { wport .. wvlan }
|
|
break
|
|
end
|
|
end
|
|
end
|
|
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
|
|
|
|
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"
|
|
end
|
|
config = config .. "\n"
|
|
ports = { "br0." .. v }
|
|
end
|
|
|
|
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 ""
|
|
|
|
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
|
|
|
|
if not wireless then
|
|
if #ports == 0 then
|
|
config = config .. " option bridge_empty '1'\n"
|
|
else
|
|
for _, port in ipairs(ports)
|
|
do
|
|
config = config .. " list ports '" .. port .. "'\n"
|
|
end
|
|
end
|
|
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"
|
|
end
|
|
end
|
|
|
|
config = config .. "\nconfig interface " .. net .. "\n"
|
|
if wireless then
|
|
config = config .. " option device '" .. ports[1] .. "'\n"
|
|
else
|
|
config = config .. " option device 'br-" .. net .. "'\n"
|
|
end
|
|
if proto ~= "" then
|
|
config = config .. " option proto '" .. proto .. "'\n"
|
|
end
|
|
if mtu ~= "" then
|
|
config = config .. " option mtu '" .. mtu .. "'\n"
|
|
end
|
|
if ipaddr ~= "" then
|
|
config = config .. " option ipaddr '" .. ipaddr .. "'\n"
|
|
end
|
|
if netmask ~= "" then
|
|
config = config .. " option netmask '" .. netmask .. "'\n"
|
|
end
|
|
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"
|
|
end
|
|
end
|
|
cfg[net .. "_network_config"] = config
|
|
end
|
|
|
|
-- 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"
|
|
end
|
|
if tun_dns then
|
|
cfg.tun_network_config = cfg.tun_network_config .. "\toption dns '" .. tun_dns .. "'\n"
|
|
end
|
|
cfg.tun_network_config = cfg.tun_network_config .. "\n"
|
|
end
|
|
local def_tun_weight = tonumber(cm:get("aredn", "@tunnel[0]", "weight") or 1) or 0
|
|
local is_supernode = cm:get("aredn", "@supernode[0]", "enable") == "1"
|
|
if is_supernode then
|
|
def_tun_weight = 0
|
|
end
|
|
local tun_weights = {}
|
|
local vtunclients = 0
|
|
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:upper(), s.passwd, s.clientip, s.serverip, s.netip)
|
|
local w = s.weight or def_tun_weight
|
|
if not tun_weights[w] then
|
|
tun_weights[w] = {}
|
|
end
|
|
table.insert(tun_weights[w], string.format("tun%d", 50 + vtunclients))
|
|
vtunclients = vtunclients + 1
|
|
end
|
|
end
|
|
)
|
|
local wgclients = 0
|
|
cm:foreach("wireguard", "client",
|
|
function(s)
|
|
if s.enabled == "1" then
|
|
local server_priv, _, _, client_pub = s.key:match("^(.+=)(.+=)(.+=)(.+=)$")
|
|
local addr, port = s.clientip:match("^(%d+%.%d+%.%d+%.%d+):(%d+)$")
|
|
cfg.wireguard_network_config = cfg.wireguard_network_config ..
|
|
string.format("config interface 'wgc%d'\n\toption proto 'wireguard'\n\toption private_key '%s'\n\toption nohostroute '1'\n\toption listen_port '%s'\n\tlist addresses '%s'\n\n",
|
|
wgclients, server_priv, port, addr)
|
|
cfg.wireguard_network_config = cfg.wireguard_network_config ..
|
|
string.format("config wireguard_wgc%d\n\toption public_key '%s'\n\toption persistent_keepalive '25'\n\tlist allowed_ips '0.0.0.0/0'\n\n",
|
|
wgclients, client_pub)
|
|
local w = s.weight or def_tun_weight
|
|
if not tun_weights[w] then
|
|
tun_weights[w] = {}
|
|
end
|
|
table.insert(tun_weights[w], string.format("wgc%d", wgclients))
|
|
wgclients = wgclients + 1
|
|
end
|
|
end
|
|
)
|
|
local vtunservers = 0
|
|
local wgservers = 0
|
|
local vtunclients_roundup = 10 * math.ceil(vtunclients / 10)
|
|
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("^(.+=)(.+=)(.+=)$")
|
|
local abc, d, p = s.netip:match("^(%d+%.%d+%.%d+)%.(%d+):(%d+)")
|
|
d = tonumber(d) + 1
|
|
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, (abc .. "." .. d))
|
|
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, p)
|
|
local w = s.weight or def_tun_weight
|
|
if not tun_weights[w] then
|
|
tun_weights[w] = {}
|
|
end
|
|
table.insert(tun_weights[w], string.format("wgs%d", wgservers))
|
|
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:upper(), s.passwd, s.clientip, s.serverip, s.netip)
|
|
local w = s.weight or def_tun_weight
|
|
if not tun_weights[w] then
|
|
tun_weights[w] = {}
|
|
end
|
|
table.insert(tun_weights[w], string.format("tun%d", 50 + vtunclients_roundup + vtunservers))
|
|
vtunservers = vtunservers + 1
|
|
end
|
|
end
|
|
end
|
|
)
|
|
|
|
-- Create the tunnel devices we need
|
|
local maxclients = 0
|
|
local maxservers = 0
|
|
-- No tunnel devices without the vtund app
|
|
if nixio.fs.stat("/usr/sbin/vtund") then
|
|
maxclients = 10 * math.ceil(vtunclients / 10)
|
|
maxservers = 10 * math.ceil(vtunservers / 10)
|
|
end
|
|
for dev = 50, 50 + maxclients + maxservers - 1
|
|
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
|
|
wgclients = 0
|
|
wgservers = 0
|
|
end
|
|
|
|
-- Put the tunnels into the vpn zone
|
|
local vpnzone = false
|
|
if maxclients + maxservers + wgclients + wgservers > 0 then
|
|
vpnzone = true
|
|
for i = 50, 50 + maxclients + maxservers - 1
|
|
do
|
|
cfg.vpn_interfaces = cfg.vpn_interfaces .. "\tlist network 'tun" .. i .. "'\n"
|
|
end
|
|
for i = 0, wgclients-1
|
|
do
|
|
cfg.vpn_interfaces = cfg.vpn_interfaces .. "\tlist network 'wgc" .. i .. "'\n"
|
|
end
|
|
for i = 0, wgservers-1
|
|
do
|
|
cfg.vpn_interfaces = cfg.vpn_interfaces .. "\tlist network 'wgs" .. i .. "'\n"
|
|
end
|
|
end
|
|
|
|
-- Put xlinks into dtdlink zone
|
|
if nixio.fs.stat("/etc/config.mesh/xlink") then
|
|
uci.cursor("/etc/config.mesh"):foreach("xlink", "interface",
|
|
function(section)
|
|
cfg.dtdlink_interfaces = cfg.dtdlink_interfaces .. "\n\tlist network '" .. section[".name"] .. "'"
|
|
end
|
|
)
|
|
end
|
|
|
|
remove_all("/tmp/new_config")
|
|
nixio.fs.mkdir("/tmp/new_config")
|
|
|
|
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()
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Tunnels
|
|
write_all("/tmp/new_config/vtun", expand_vars("<tun_network_config>"))
|
|
|
|
local nc = uci.cursor("/tmp/new_config")
|
|
|
|
-- append to firewall
|
|
local fw = io.open("/tmp/new_config/firewall", "a")
|
|
if fw then
|
|
if not is_nat_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")
|
|
if vpnzone then
|
|
fw:write("\nconfig forwarding\n option src vpn\n option dest lan\n")
|
|
end
|
|
end
|
|
|
|
if nc:get("aredn", "@wan[0]", "olsrd_gw") == "1" then
|
|
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")
|
|
if vpnzone then
|
|
fw:write("\nconfig forwarding\n option src vpn\n option dest wan\n")
|
|
end
|
|
end
|
|
|
|
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 is_dmz_mode() 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"
|
|
if not oport:match("-") then
|
|
host = host .. " option dest_port " .. iport .. "\n"
|
|
end
|
|
if is_dmz_mode() and intf == "both" then
|
|
intf = "wan"
|
|
end
|
|
if intf == "both" then
|
|
fw:write("\nconfig redirect\n option src wifi\n option dest lan\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
|
|
fw:write("\nconfig redirect\n option src dtdlink\n option dest lan\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
|
|
if vpnzone then
|
|
fw:write("\nconfig redirect\n option src vpn\n option dest lan\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
|
|
end
|
|
fw:write("config redirect\n option src wan\n option dest lan\n " .. match .. " " .. host .. "\n")
|
|
elseif intf == "wifi" and is_nat_mode() then
|
|
fw:write("\nconfig redirect\n option src dtdlink\n option dest lan\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
|
|
fw:write("\nconfig redirect\n option src wifi\n option dest lan\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
|
|
if vpnzone then
|
|
fw:write("\nconfig redirect\n option src vpn\n option dest lan\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
|
|
end
|
|
elseif intf == "wan" then
|
|
fw:write("\nconfig redirect\n option src dtdlink\n option dest lan\n " .. match .. " option src_dip " .. cfg.wifi_ip .. "\n " .. host .. "\n")
|
|
fw:write("config redirect\n option src wan\n option dest lan\n " .. match .. " " .. host .. "\n")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
fw:close();
|
|
end
|
|
|
|
-- setup nat
|
|
if is_nat_mode() then
|
|
-- zone[0] = lan, zone[1] = wan, zone[2] = wifi, zone[3] = dtdlink, zone[4] = vpn
|
|
local masq_src = cfg.lan_ip .. "/" .. netmask_to_cidr(cfg.lan_mask)
|
|
for z = 2, 4
|
|
do
|
|
nc:set("firewall", "@zone[" .. z .. "]", "masq", "1")
|
|
nc:set("firewall", "@zone[" .. z .. "]", "masq_src", masq_src)
|
|
end
|
|
nc:commit("firewall")
|
|
end
|
|
|
|
-- setup node lan dhcp
|
|
local function load_dhcp_tags(dhcptagsfile)
|
|
local dhcp_tags = {}
|
|
|
|
for line in io.lines(dhcptagsfile)
|
|
do
|
|
if not (line:match("^%s*#") or line:match("^%s*$")) then
|
|
local name, condition, pattern = line:match("(%S+)%s+(%S+)%s+(.*)")
|
|
if pattern then
|
|
table.insert(get_subtable(dhcp_tags, condition),
|
|
{name = name, pattern = pattern})
|
|
end
|
|
end
|
|
end
|
|
return dhcp_tags
|
|
end
|
|
|
|
local function load_dhcp_options(dhcpoptionsfile)
|
|
local dhcp_options = {}
|
|
if nixio.fs.access(dhcpoptionsfile) then
|
|
for line in io.lines(dhcpoptionsfile)
|
|
do
|
|
if not (line:match("^%s*#") or line:match("^%s*$")) then
|
|
local tag, force, opt_num, opt_val = line:match("(%S*)%s+(%w+)%s+(%d+)%s+(.*)")
|
|
if opt_val then
|
|
local by_tag = get_subtable(dhcp_options, tag)
|
|
if tag == "" and force == FORCED then
|
|
force = UNFORCED -- force is unsupported for untagged options
|
|
end
|
|
table.insert(get_subtable(by_tag, force),
|
|
{num = opt_num, val = opt_val})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return dhcp_options
|
|
end
|
|
|
|
local function option_item(tag, option)
|
|
local parts = {}
|
|
if tag ~= "" then
|
|
table.insert(parts, "tag:" .. tag)
|
|
end
|
|
table.insert(parts, option.num)
|
|
table.insert(parts, option.val)
|
|
return table.concat(parts, ",")
|
|
end
|
|
|
|
local function create_classifying_section(condition, cond_list)
|
|
for i, props in ipairs(cond_list)
|
|
do
|
|
local secname = condition
|
|
local pat = props.pattern
|
|
if (condition == "subscriberid") then
|
|
secname = "subscrid"
|
|
pat = '"' .. pat:gsub('"', '\\"') .. '"'
|
|
print(props.pattern, "->", pat)
|
|
end
|
|
nc:add("dhcp", secname)
|
|
local section_ref = string.format("@%s[%d]", secname, i-1)
|
|
nc:set("dhcp", section_ref, "networkid", props.name)
|
|
nc:set("dhcp", section_ref, condition, pat)
|
|
end
|
|
end
|
|
|
|
local function create_tag_section(tag, force, optlist)
|
|
if tag ~= "" then
|
|
nc:set("dhcp", tag, "tag")
|
|
if force == FORCED then
|
|
nc:set("dhcp", tag, "force", 1)
|
|
end
|
|
local options = {}
|
|
for _, option in ipairs(optlist) do
|
|
table.insert(options, option_item("", option)) -- tag is on section
|
|
end
|
|
nc:set("dhcp", tag, "dhcp_option", options)
|
|
end
|
|
end
|
|
|
|
do
|
|
local dhcp_option_list = {}
|
|
if nc:get("aredn", "@wan[0]", "lan_dhcp_route") == "1" or nc:get("aredn", "@wan[0]", "lan_dhcp_defaultroute") == "1" then
|
|
-- Provide stateless routes and default route
|
|
table.insert(dhcp_option_list, "121,10.0.0.0/8," .. cfg.lan_ip .. ",0.0.0.0/0," .. cfg.lan_ip)
|
|
table.insert(dhcp_option_list, "249,10.0.0.0/8," .. cfg.lan_ip .. ",0.0.0.0/0," .. cfg.lan_ip)
|
|
else
|
|
-- Provide stateless routes to the mesh, and a blank default route (option 3 has no values) to
|
|
-- suppress default route being sent
|
|
table.insert(dhcp_option_list, "121,10.0.0.0/8," .. cfg.lan_ip)
|
|
table.insert(dhcp_option_list, "249,10.0.0.0/8," .. cfg.lan_ip)
|
|
table.insert(dhcp_option_list, "3")
|
|
end
|
|
|
|
local advanced_options = load_dhcp_options(dhcpoptionsfile)
|
|
|
|
if nixio.fs.access(dhcptagsfile) then
|
|
for condition, cond_list in pairs(load_dhcp_tags(dhcptagsfile))
|
|
do
|
|
create_classifying_section(condition, cond_list)
|
|
end
|
|
end
|
|
|
|
for tag, forcelist in pairs(advanced_options)
|
|
do
|
|
if forcelist then
|
|
if tag == "" -- tag-section name cannot be empty
|
|
or tablesize(forcelist) > 1 -- section name must be unique
|
|
then -- place unforced options in the anonymous section
|
|
for _, option in ipairs(forcelist[UNFORCED]) do
|
|
table.insert(dhcp_option_list, option_item(tag, option))
|
|
end
|
|
forcelist[UNFORCED] = nil
|
|
end
|
|
for force, optlist in pairs(forcelist)
|
|
do
|
|
create_tag_section(tag, force, optlist)
|
|
end
|
|
end
|
|
end
|
|
|
|
if #dhcp_option_list > 0 then
|
|
nc:set("dhcp", "@dhcp[0]", "dhcp_option", dhcp_option_list)
|
|
end
|
|
end
|
|
|
|
nc:commit("dhcp")
|
|
|
|
-- generate the wireless config file
|
|
local config = ""
|
|
local ifacecount = aredn.hardware.get_radio_count()
|
|
local devpaths = {}
|
|
for dev = 0, ifacecount - 1
|
|
do
|
|
local devname = "phy" .. dev
|
|
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
|
|
local devpathc = devpaths[devpath] or 0
|
|
devpaths[devpath] = devpathc + 1
|
|
if devpathc > 0 then
|
|
devpath = devpath .. "+" .. devpathc
|
|
end
|
|
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
|
|
encryption = "none"
|
|
end
|
|
network = "wan"
|
|
htmode = nil
|
|
else
|
|
disabled = "1"
|
|
end
|
|
|
|
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"
|
|
|
|
config = config .. "config wifi-iface\n"
|
|
config = config .. " option ifname '" .. wlan .. "'\n"
|
|
config = config .. " option device '" .. radio .. "'\n"
|
|
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"
|
|
|
|
if is_mesh_rf and wifi_mon_enable then
|
|
config = config .. "config wifi-iface\n"
|
|
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"
|
|
end
|
|
end
|
|
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 is_notnull(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 is_notnull(cfg.dtdlink_ip) then
|
|
h:write(cfg.dtdlink_ip .. "\tdtdlink." .. node .. ".local.mesh dtdlink." .. node .."\n")
|
|
end
|
|
if is_nat_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")
|
|
|
|
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 is_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 is_altnet_mode() then
|
|
local a, b, c, d = cfg.lan_ip:match("(.*)%.(.*)%.(.*)%.(.*)")
|
|
of:write(string.format("\nconfig Hna4\n\toption netaddr %s.%s.%s.%d\n\toption netmask %s\n\n", a, b, c, d - 1, cfg.lan_mask))
|
|
end
|
|
|
|
if cfg.wifi_enable ~= "1" and is_notnull(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")
|
|
local altnetwork = nc:get("aredn", "@supernode[0]", "altnetwork")
|
|
local altnetmask = nc:get("aredn", "@supernode[0]", "altnetmask")
|
|
if altnetwork and altnetmask then
|
|
of:write("config Hna4\n\toption netaddr " .. altnetwork .. "\n\toption netmask " .. altnetmask .. "\n\n")
|
|
end
|
|
end
|
|
|
|
if nixio.fs.stat("/etc/config.mesh/xlink") then
|
|
uci.cursor("/etc/config.mesh"):foreach("xlink", "interface",
|
|
function(section)
|
|
if section.netmask ~= "255.255.255.255" then
|
|
local addr = decimal_to_ip(nixio.bit.band(ip_to_decimal(section.ipaddr), ip_to_decimal(section.netmask)))
|
|
of:write(string.format("config Hna4\n\toption netaddr %s\n\toption netmask %s\n\n", addr, section.netmask))
|
|
end
|
|
end
|
|
)
|
|
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
|
|
of:write("\tlist hosts '" .. host.ip .. " " .. host.host .. "'\n")
|
|
end
|
|
end
|
|
for _, service in ipairs(services)
|
|
do
|
|
of:write("\tlist service '" .. service .. "'\n")
|
|
end
|
|
of:write("\n")
|
|
|
|
-- add all the tunnel interfaces
|
|
if vtunclients + vtunservers + wgclients + wgservers > 0 then
|
|
for weight, ifaces in pairs(tun_weights)
|
|
do
|
|
of:write("\nconfig Interface\n")
|
|
for _, iface in ipairs(ifaces)
|
|
do
|
|
of:write("\tlist interface '" .. iface .. "'\n")
|
|
end
|
|
of:write("\toption Ip4Broadcast '255.255.255.255'\n")
|
|
weight = tonumber(weight)
|
|
if weight < 1 then
|
|
of:write("\toption Mode 'ether'\n")
|
|
elseif weight > 1 then
|
|
of:write("\toption LinkQualityMult 'default " .. (1 / weight) .. "'\n")
|
|
end
|
|
of:write("\toption HelloInterval '" .. cfg.hello_interval .. "'\n")
|
|
of:write("\toption TcInterval '" .. cfg.tc_interval .. "'\n")
|
|
of:write("\toption MidInterval '" .. cfg.mid_interval .. "'\n")
|
|
of:write("\toption HnaInterval '" .. cfg.hna_interval .. "'\n")
|
|
end
|
|
end
|
|
nc:set("aredn", "@tunnel[0]", "maxclients", maxclients)
|
|
nc:set("aredn", "@tunnel[0]", "maxservers", maxservers)
|
|
nc:commit("aredn")
|
|
|
|
-- add xlink interfaces
|
|
if nixio.fs.stat("/etc/config.mesh/xlink") then
|
|
uci.cursor("/etc/config.mesh"):foreach("xlink", "interface",
|
|
function(section)
|
|
of:write("\nconfig Interface\n\tlist interface '" .. section[".name"] .. "'\n")
|
|
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
|
|
of:write("\toption Mode 'ether'\n")
|
|
end
|
|
else
|
|
of:write("\toption Mode 'ether'\n")
|
|
end
|
|
of:write("\toption HelloInterval '" .. cfg.hello_interval .. "'\n")
|
|
of:write("\toption TcInterval '" .. cfg.tc_interval .. "'\n")
|
|
of:write("\toption MidInterval '" .. cfg.mid_interval .. "'\n")
|
|
of:write("\toption HnaInterval '" .. cfg.hna_interval .. "'\n")
|
|
end
|
|
)
|
|
end
|
|
|
|
-- OLSRD user extras
|
|
if nixio.fs.stat("/etc/aredn_include/olsrd.user") then
|
|
of:write("\n")
|
|
of:write(expand_vars(read_all("/etc/aredn_include/olsrd.user")))
|
|
end
|
|
|
|
of:close()
|
|
end
|
|
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 is_notnull(cfg.aprs_lat) and is_notnull(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.localservices = true
|
|
end
|
|
os.remove("/tmp/local_services")
|
|
nixio.fs.chmod("/etc/local/services", "777")
|
|
end
|
|
|
|
---
|
|
--- Make it official
|
|
---
|
|
|
|
-- Handle special cases
|
|
local config_special = {
|
|
dmz_mode = c:get("aredn", "@dmz[0]", "mode"),
|
|
lqm_enable = c:get("aredn", "@lqm[0]", "enable"),
|
|
tunnel_weight = c:get("aredn", "@tunnel[0]", "weight"),
|
|
supernode_enable = c:get("aredn", "@supernode[0]", "enable"),
|
|
watchdog_enable = c:get("aredn", "@watchdog[0]", "enable"),
|
|
watchdog_pings = c:get("aredn", "@watchdog[0]", "ping_addresses"),
|
|
watchdog_daily = c:get("aredn", "@watchdog[0]", "daily"),
|
|
web_access = c:get("aredn", "@wan[0]", "web_access"),
|
|
ssh_access = c:get("aredn", "@wan[0]", "ssh_access"),
|
|
telnet_access = c:get("aredn", "@wan[0]", "telnet_access"),
|
|
wifi_mode_0 = c:get("wireless", "@wifi-iface[0]", "mode"),
|
|
wifi_mode_1 = c:get("wireless", "@wifi-iface[1]", "mode"),
|
|
wifi_channel_0 = c:get("wireless", "@wifi-device[0]", "channel"),
|
|
wifi_channel_1 = c:get("wireless", "@wifi-device[1]", "channel")
|
|
}
|
|
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.log = true
|
|
changes.system = true
|
|
elseif file == "aredn" then
|
|
local oc = uci:cursor()
|
|
if oc:get("aredn", "@dmz[0]", "mode") ~= config_special.dmz_mode then
|
|
changes.reboot = true
|
|
end
|
|
if oc:get("aredn", "@lqm[0]", "enable") ~= config_special.lqm_enable then
|
|
changes.manager = true
|
|
end
|
|
if oc:get("aredn", "@tunnel[0]", "weight") ~= config_special.tunnel_weight then
|
|
changes.olsrd = true
|
|
end
|
|
if oc:get("aredn", "@supernode[0]", "enable") ~= config_special.supernode_enable then
|
|
changes.reboot = true
|
|
end
|
|
if oc:get("aredn", "@watchdog[0]", "enable") ~= config_special.watchdog_enable then
|
|
changes.reboot = true
|
|
end
|
|
if oc:get("aredn", "@watchdog[0]", "ping_addresses") ~= config_special.watchdog_pings then
|
|
changes.manager = true
|
|
end
|
|
if oc:get("aredn", "@watchdog[0]", "daily") ~= config_special.watchdog_daily then
|
|
changes.manager = true
|
|
end
|
|
if oc:get("aredn", "@wan[0]", "web_access") ~= config_special.web_access or oc:get("aredn", "@wan[0]", "ssh_access") ~= config_special.ssh_access or oc:get("aredn", "@wan[0]", "telnet_access") ~= config_special.telnet_access then
|
|
changes.firewall = true
|
|
end
|
|
elseif file == "network" then
|
|
changes.network = true
|
|
changes.tunnels = true -- restarting network devices requires tunnels to restart
|
|
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
|
|
local oc = uci:cursor()
|
|
if oc:get("wireless", "@wifi-device[0]", "channel") ~= config_special.wifi_channel_0 or oc:get("wireless", "@wifi-device[1]", "channel") ~= config_special.wifi_channel_1 then
|
|
changes.manager = true
|
|
end
|
|
if oc:get("wireless", "@wifi-iface[0]", "mode") ~= config_special.wifi_mode_0 or oc:get("wireless", "@wifi-iface[1]", "mode") ~= config_special.wifi_mode_1 then
|
|
-- Only start the hostapd (etc) if we need to. This doesn't change what is currently running
|
|
-- only what automatically runs in the future
|
|
if oc:get("wireless", "@wifi-iface[0]", "mode") == "ap" or oc:get("wireless", "@wifi-iface[1]", "mode") == "ap" then
|
|
os.execute("/etc/init.d/wpad enable > /dev/null 2>&1")
|
|
else
|
|
os.execute("/etc/init.d/wpad disable > /dev/null 2>&1")
|
|
end
|
|
changes.reboot = true
|
|
else
|
|
changes.wireless = true
|
|
end
|
|
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
|
|
end
|
|
|
|
return 0
|