diff --git a/files/etc/httpd.conf b/files/etc/httpd.conf
index 8f4655cd..a66f5158 100644
--- a/files/etc/httpd.conf
+++ b/files/etc/httpd.conf
@@ -6,4 +6,4 @@
/cgi-bin/supporttool:root:$p$root
/cgi-bin/advancedconfig:root:$p$root
/cgi-bin/apiprotected:root:$p$root
-
+/cgi-bin/advancednetwork:root:$p$root
diff --git a/files/usr/lib/lua/aredn/nav/admin/65advancednetwork.lua b/files/usr/lib/lua/aredn/nav/admin/65advancednetwork.lua
new file mode 100644
index 00000000..175ff0fc
--- /dev/null
+++ b/files/usr/lib/lua/aredn/nav/admin/65advancednetwork.lua
@@ -0,0 +1,4 @@
+local board = aredn.hardware.get_board_type()
+if board == "mikrotik,hap-ac2" or board == "mikrotik,hap-ac3" then
+ return { href = "advancednetwork", display = "Advanced Network" }
+end
diff --git a/files/www/cgi-bin/advancedconfig b/files/www/cgi-bin/advancedconfig
index c9b2ed8b..25cb656a 100755
--- a/files/www/cgi-bin/advancedconfig
+++ b/files/www/cgi-bin/advancedconfig
@@ -507,6 +507,11 @@ function hasUSB()
end
function supportsVLANChange()
+ -- If we support advanced networking, we dont provide this option here
+ local board = aredn.hardware.get_board_type()
+ if board == "mikrotik,hap-ac2" or board == "mikrotik,hap-ac3" then
+ return false
+ end
local stat = nixio.fs.stat("/etc/aredn_include/swconfig")
-- We always support VLAN changing on devices without switches
if not (stat and stat.size > 0) then
diff --git a/files/www/cgi-bin/advancednetwork b/files/www/cgi-bin/advancednetwork
new file mode 100755
index 00000000..6cfd21ae
--- /dev/null
+++ b/files/www/cgi-bin/advancednetwork
@@ -0,0 +1,680 @@
+#!/usr/bin/lua
+--[[
+
+ Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
+ Copyright (C) 2023 Tim Wilkinson
+ 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 .
+
+ Additional Terms:
+
+ Additional use restrictions exist on the AREDN(TM) 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.hardware")
+require("aredn.http")
+local html = require("aredn.html")
+local aredn_info = require("aredn.info")
+
+local base = "/etc/aredn_include/"
+local xlink_file = "/etc/config.mesh/xlink"
+
+local default_5_port_layout = { ports = { "wan", "lan1", "lan2", "lan3", "lan4" } }
+local layouts = {
+ ["mikrotik,hap-ac2"] = default_5_port_layout,
+ ["mikrotik,hap-ac3"] = default_5_port_layout
+}
+
+local default_5_port_config = {
+ {
+ name = "dtdlink",
+ vlan = 2,
+ ports = { lan4 = { tagged = true } },
+ tagged = true
+ },
+ {
+ name = "lan",
+ vlan = 3,
+ ports = { lan1 = { tagged = false }, lan2 = { tagged = false }, lan3 = { tagged = false } },
+ tagged = false
+ },
+ {
+ name = "wan",
+ vlan = 1,
+ ports = { wan = { tagged = false } },
+ tagged = false
+ }
+}
+local default_configs = {
+ ["mikrotik,hap-ac2"] = default_5_port_config,
+ ["mikrotik,hap-ac3"] = default_5_port_config
+}
+
+function read_user_config(network)
+ local file = base .. network .. ".network.user"
+ if not nixio.fs.stat(file) then
+ return nil
+ end
+ local config = {
+ name = network,
+ vlan = nil,
+ ports = {},
+ tagged = false
+ }
+ local invlan = false
+ for line in io.lines(file)
+ do
+ if line:match("^config%s+bridge%-vlan") then
+ invlan = true
+ elseif line:match("^config") then
+ invlan = false
+ elseif invlan then
+ local m
+ m = line:match("option%s+vlan%s+'(%d+)'")
+ if m then
+ config.vlan = tonumber(m)
+ end
+ m = line:match("list%s+ports%s+'(%S+):u'")
+ if m then
+ config.ports[m] = {
+ tagged = false
+ }
+ end
+ m = line:match("list%s+ports%s+'(%S+):t'")
+ if m then
+ config.ports[m] = {
+ tagged = true
+ }
+ config.tagged = true
+ end
+ end
+ end
+ return config
+end
+
+function read_xlink_config()
+ if not nixio.fs.stat(xlink_file) then
+ return {}
+ end
+ local configs = {}
+ local config = {}
+ local type = "none"
+ for line in io.lines(xlink_file)
+ do
+ if line:match("^config%s+bridge%-vlan") then
+ type = "vlan"
+ config = {
+ name = nil,
+ vlan = nil,
+ ipaddr = nil,
+ peer = nil,
+ weight = 0,
+ port = nil
+ }
+ configs[#configs + 1] = config
+ elseif line:match("^config%s+interface") then
+ type = "interface"
+ config.name = line:match("^config%s+interface%s+'(%S+)'")
+ elseif type == "vlan" then
+ local m
+ m = line:match("option%s+vlan%s+'(%d+)'")
+ if m then
+ config.vlan = tonumber(m)
+ end
+ m = line:match("list%s+ports%s+'(%S+):t'")
+ if m then
+ config.port = m
+ end
+ elseif type == "interface" then
+ local m
+ m = line:match("option%s+ipaddr%s+'([%d%.]+)'")
+ if m then
+ config.ipaddr = m
+ end
+ m = line:match("option%s+peer%s+'([%d%.]+)'")
+ if m then
+ config.peer = m
+ end
+ m = line:match("option%s+weight%s+'([%d]+)'")
+ if m then
+ config.weight = tonumber(m)
+ end
+ end
+ end
+ return configs
+end
+
+function write_user_config(config, variables)
+ local network = config.name
+ local f = io.open(base .. network .. ".network.user", "w")
+ f:write("# Generated by advancednetwork\n")
+ f:write("\nconfig bridge-vlan\n")
+ f:write("\toption device 'br0'\n")
+ f:write("\toption vlan '" .. config.vlan .. "'\n")
+ for name, port in pairs(config.ports)
+ do
+ f:write("\tlist ports '" .. name .. (port.tagged and ":t" or ":u") .. "'\n")
+ end
+ f:write("\nconfig device\n")
+ f:write("\toption name 'br-" .. network .. "'\n")
+ f:write("\toption type 'bridge'\n")
+ f:write("\toption macaddr '<" .. network .. "_mac>'\n")
+ f:write("\tlist ports 'br0." .. config.vlan .. "'\n")
+ f:write("\nconfig interface " .. network .. "\n")
+ f:write("\toption device 'br-" .. network .. "'\n")
+ if network == "dtdlink" then
+ f:write("\toption proto 'static'\n")
+ f:write("\toption ipaddr '<" .. network .. "_ip>'\n")
+ f:write("\toption netmask '255.0.0.0'\n")
+ else
+ if variables[network .. "_proto"] and variables[network .. "_proto"] ~= "" then
+ f:write("\toption proto '<" .. network .. "_proto>'\n")
+ end
+ if variables[network .. "_ip"] and variables[network .. "_ip"] ~= "" then
+ f:write("\toption ipaddr '<" .. network .. "_ip>'\n")
+ end
+ if variables[network .. "_mask"] and variables[network .. "_mask"] ~= "" then
+ f:write("\toption netmask '<" .. network .. "_mask>'\n")
+ end
+ end
+ if network == "lan" then
+ f:write("\toption dns ''\n")
+ end
+ if network == "wan" and variables.wan_gw and variables.wan_gw ~= "" then
+ f:write("\toption gateway ''\n")
+ end
+ f:close()
+end
+
+function update_legacy_wan_vlan(config)
+ local lines = {}
+ for line in io.lines("/etc/config.mesh/_setup")
+ do
+ if not line:match("^wan_intf = ") then
+ lines[#lines + 1] = line
+ end
+ end
+
+ for name, port in pairs(config.ports)
+ do
+ if port.tagged then
+ local wan_intf = ""
+ for dev in aredn.hardware.get_board_network_ifname("wan"):gmatch("%S+")
+ do
+ wan_intf = wan_intf .. " " .. dev:match("^([^%.]+)") .. "." .. config.vlan
+ end
+ if wan_intf ~= "" then
+ lines[#lines + 1] = "wan_intf =" .. wan_intf
+ end
+ break
+ end
+ end
+
+ local f = io.open("/etc/config.mesh/_setup", "w")
+ if f then
+ for _, line in ipairs(lines)
+ do
+ f:write(line .. "\n")
+ end
+ f:close()
+ end
+end
+
+function write_xlink_config(configs)
+ local f = io.open(xlink_file, "w")
+ f:write("# Generated by advancednetwork\n")
+ for _, config in ipairs(configs)
+ do
+ f:write("\nconfig bridge-vlan\n")
+ f:write("\toption device 'br0'\n")
+ f:write("\toption vlan '" .. config.vlan .. "'\n")
+ f:write("\tlist ports '" .. config.port .. ":t'\n")
+ f:write("\nconfig interface '" .. config.name .. "'\n")
+ f:write("\toption ifname 'br0." .. config.vlan .. "'\n")
+ f:write("\toption proto 'static'\n")
+ f:write("\toption ipaddr '" .. config.ipaddr .. "'\n")
+ f:write("\toption netmask '255.255.255.252'\n")
+ f:write("\toption peer '" .. config.peer .. "'\n")
+ f:write("\toption weight '" .. config.weight .. "'\n")
+ end
+ f:close()
+end
+
+function reboot()
+ local node = aredn_info.get_nvram("node")
+ if node == "" then
+ node = "Node"
+ end
+ http_header()
+ html.header(node .. " rebooting", false)
+ html.print("")
+ html.print("
")
+ html.print("
" .. node .. " is rebooting
")
+ html.print("
Your browser should return to this node in 60 seconds. ")
+ html.print("If something goes astray you can try to connect with