mirror of https://github.com/aredn/aredn.git
1872 lines
67 KiB
Lua
Executable File
1872 lines
67 KiB
Lua
Executable File
#!/usr/bin/lua
|
|
--[[
|
|
|
|
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
|
Copyright (C) 2021 Tim Wilkinson
|
|
Original 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(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")
|
|
require("aredn.utils")
|
|
require("aredn.html")
|
|
require("uci")
|
|
require("aredn.info")
|
|
require("aredn.olsr")
|
|
|
|
local html = aredn.html
|
|
|
|
local cursor = uci.cursor()
|
|
|
|
local config = aredn.info.get_nvram("config")
|
|
local node = aredn.info.get_nvram("node")
|
|
if not node or node == "" then
|
|
node = "NOCALL"
|
|
end
|
|
local tactical = aredn.info.get_nvram("tactical")
|
|
if not tactical then
|
|
tactical = ""
|
|
end
|
|
|
|
-- post_data
|
|
local parms = {}
|
|
if os.getenv("REQUEST_METHOD") == "POST" then
|
|
require('luci.http')
|
|
local request = luci.http.Request(nixio.getenv(),
|
|
function()
|
|
local v = io.read(1024)
|
|
if not v then
|
|
io.close()
|
|
end
|
|
return v
|
|
end
|
|
)
|
|
parms = request:formvalue()
|
|
end
|
|
|
|
local function validate_service_name(name)
|
|
if not name or name == "" or name:match("[:-\"|<>]") then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function validate_service_protocol(proto)
|
|
if not proto or proto == "" or proto:match("[:-\"|<>]") or not proto:match("^%w+") then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function validate_service_suffix(suffix)
|
|
if not suffix or suffix:match("[:-\"|<>]") or not suffix:match("^[%w/?&._=#-]*$") then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
local mac_pattern = "%x%x:%x%x:%x%x:%x%x:%x%x:%x%x"
|
|
local mac_wildcard = "*:*:*:*:*:*"
|
|
|
|
-- accepts nonempty strings
|
|
local function validate_nonempty_matcher(matcher)
|
|
return matcher ~= ""
|
|
end
|
|
|
|
-- accepts MAC addresses with optional bytewise wildcard substitution
|
|
local function validate_mac_matcher(matcher)
|
|
return matcher:gsub("%x%x", "*") == mac_wildcard
|
|
end
|
|
|
|
-- accepts sequences of one or more hexadecimal digit pairs separated by colons
|
|
local function validate_hexbytes_matcher(matcher)
|
|
return matcher:gsub("[XY]", "Z"):gsub("%x%x:", "X"):gsub("%x%x", "Y"):match("^X*Y$")
|
|
end
|
|
|
|
-- accepts host names with optional trailing wildcard
|
|
local function validate_host_matcher(matcher)
|
|
return matcher:match("^%w[%w.-]*%*?$")
|
|
end
|
|
|
|
-- accepts Net-ASCII (RFC 854) with escapes for supported control characters
|
|
local function validate_net_ascii(matcher)
|
|
local no_escapes = matcher:gsub("\\[\\benrt]", "")
|
|
return not no_escapes:match("[^ -Z^-~%[%]]")
|
|
end
|
|
|
|
local FORCE_SUPPORTED_HINT = "Send option even when not requested by client"
|
|
local FORCE_UNSUPPORTED_HINT = "Only options with an explicit tag can be sent always."
|
|
|
|
local dhcp_tag_conds = {
|
|
{ key = "vendorclass",
|
|
name = "Vendor Class",
|
|
validator = validate_nonempty_matcher,
|
|
hint = "all or part of the class value sent by the client",
|
|
jsPlaceholder = "subpart of class string",
|
|
},
|
|
{ key = "userclass",
|
|
name = "User Class",
|
|
validator = validate_nonempty_matcher,
|
|
hint = "all or part of the class value sent by the client",
|
|
jsPlaceholder = "subpart of class string",
|
|
},
|
|
{ key = "mac",
|
|
name = "MAC Address",
|
|
validator = validate_mac_matcher,
|
|
hint = "six bytes, each either two hexadecimal digits or a single asterisk, separated by colons",
|
|
jsPattern = [[^(?:(?:[0-9a-fA-F]{2}|\\*):){5}(?:[0-9a-fA-F]{2}|\\*)$]],
|
|
jsPlaceholder = "*:*:*:*:*:*",
|
|
},
|
|
{ key = "circuitid",
|
|
name = "Agent Circuit ID",
|
|
validator = validate_hexbytes_matcher,
|
|
hint = "one or more bytes, each two hexadecimal digits, separated by colons",
|
|
jsPattern = [[^(?:(?:[0-9a-fA-F]{2}|\\*):)*(?:[0-9a-fA-F]{2}|\\*)$]],
|
|
jsPlaceholder = "12:3a:bc",
|
|
},
|
|
{ key = "remoteid",
|
|
name = "Agent Remote ID",
|
|
validator = validate_hexbytes_matcher,
|
|
hint = "one or more bytes, each two hexadecimal digits, separated by colons",
|
|
jsPattern = [[^(?:(?:[0-9a-fA-F]{2}|\\*):)*(?:[0-9a-fA-F]{2}|\\*)$]],
|
|
jsPlaceholder = "12:3a:bc",
|
|
},
|
|
{ key = "subscriberid",
|
|
name = "Subscriber-ID",
|
|
validator = validate_net_ascii,
|
|
hint = [[ASCII printing characters and \\ escapes; use \\\\ \\b \\e \\n \\r or \\t for \\, BS, ESC, LF, CR, or HT, respectively]],
|
|
jsPattern = [[^(?:[ -\\[\\]-~]|\\\\[\\\\benrt])+$]],
|
|
jsPlaceholder = "ASCII string\\\\r\\\\n& escapes",
|
|
},
|
|
}
|
|
|
|
local dhcp_tag_validators = {}
|
|
for _, cond in ipairs(dhcp_tag_conds)
|
|
do
|
|
dhcp_tag_validators[cond.key] = {
|
|
validator = cond.validator,
|
|
hint = cond.hint,
|
|
jsPattern = cond.jsPattern,
|
|
jsPlaceholder = cond.jsPlaceholder}
|
|
end
|
|
|
|
local function html_safe(s)
|
|
return s:gsub("&", "&"):gsub(">", ">"):gsub("<", "<")
|
|
end
|
|
|
|
local serv_err = {}
|
|
local function serverr(msg)
|
|
serv_err[#serv_err + 1] = msg:gsub(">", ">"):gsub("<", "<")
|
|
end
|
|
local port_err = {}
|
|
local function porterr(msg)
|
|
port_err[#port_err + 1] = msg
|
|
end
|
|
|
|
local dmz_err = {}
|
|
local function dmzerr(msg)
|
|
dmz_err[#dmz_err + 1] = msg
|
|
end
|
|
local dhcp_err = {}
|
|
local function dhcperr(msg)
|
|
dhcp_err[#dhcp_err + 1] = msg
|
|
end
|
|
local dhcptag_err = {}
|
|
local function dhcptagerr(msg)
|
|
dhcptag_err[#dhcptag_err + 1] = msg
|
|
end
|
|
local dhcpopt_err = {}
|
|
local function dhcpopterr(msg)
|
|
dhcpopt_err[#dhcpopt_err + 1] = msg
|
|
end
|
|
local alias_err = {}
|
|
local function aliaserr(msg)
|
|
alias_err[#alias_err + 1] = msg
|
|
end
|
|
local errors = {}
|
|
local function err(msg)
|
|
errors[#errors + 1] = msg
|
|
end
|
|
local hidden = {}
|
|
local function hide(m)
|
|
hidden[#hidden + 1] = m
|
|
end
|
|
|
|
local hosts = {}
|
|
local addrs = {}
|
|
local macs = {}
|
|
|
|
if config == "" or nixio.fs.stat("/tmp/reboot-required") then
|
|
http_header()
|
|
html.header(node .. " setup", true)
|
|
html.print("<body><center>")
|
|
html.alert_banner()
|
|
html.navbar_admin("ports")
|
|
html.print("<table width=820><tr><td align=center><br><b>")
|
|
if config == "" then
|
|
html.print("This page is not available until the configuration has been set.")
|
|
else
|
|
html.print("The specified configuration is invalid, try flushing your browser cache or reboot the mesh node.")
|
|
end
|
|
html.print("</b></td></tr>")
|
|
html.print("</table></center>")
|
|
html.footer();
|
|
html.print("</body></html>")
|
|
http_footer()
|
|
os.exit()
|
|
end
|
|
|
|
-- check for dmz mode
|
|
local dmz_mode = tonumber(cursor:get("aredn", "@dmz[0]", "mode"))
|
|
if not dmz_mode then
|
|
dmz_mode = 2
|
|
end
|
|
|
|
local lanip, lanbcast, lanmask = aredn.hardware.get_interface_ip4(aredn.hardware.get_iface_name("lan"))
|
|
local lannet_d = nixio.bit.band(ip_to_decimal(lanip), ip_to_decimal(lanmask))
|
|
|
|
local tmpdir = "/tmp/web/ports"
|
|
if not parms.reload then
|
|
remove_all(tmpdir)
|
|
end
|
|
nixio.fs.mkdir(tmpdir)
|
|
|
|
local fsuffix = dmz_mode == 0 and ".nat" or ".dmz"
|
|
local portfile = "/etc/config.mesh/_setup.ports" .. fsuffix
|
|
local dhcpfile = "/etc/config.mesh/_setup.dhcp" .. fsuffix
|
|
local dhcptagsfile = "/etc/config.mesh/_setup.dhcptags" .. fsuffix
|
|
local dhcpoptionsfile = "/etc/config.mesh/_setup.dhcpoptions" .. fsuffix
|
|
local servfile = "/etc/config.mesh/_setup.services" .. fsuffix
|
|
local aliasfile = "/etc/config.mesh/aliases" .. fsuffix
|
|
|
|
-- if a reset or a first time page load
|
|
-- read the data from the config files
|
|
if parms.button_reset or not parms.reload then
|
|
local i = 0
|
|
if nixio.fs.stat(portfile) then
|
|
for line in io.lines(portfile)
|
|
do
|
|
if not (line:match("^%s*#") or line:match("^%s*$")) then
|
|
local k, v = line:match("(%S+)%s+=%s+(%S+)")
|
|
if v then
|
|
parms[k] = v
|
|
else
|
|
local intf, type, out, ip, _in, enable = line:match("(.*):(.*):(.*):(.*):(.*):(.*)")
|
|
if intf then
|
|
i = i + 1
|
|
parms["port" .. i .. "_intf"] = intf
|
|
parms["port" .. i .. "_type"] = type
|
|
parms["port" .. i .. "_out"] = out
|
|
parms["port" .. i .. "_ip"] = ip
|
|
parms["port" .. i .. "_in"] = _in
|
|
parms["port" .. i .. "_enable"] = enable
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
parms.port_num = i
|
|
|
|
-- set dhcp reservations
|
|
-- ip addresses are stored as offsets from the lan network address
|
|
i = 0
|
|
if nixio.fs.stat(dhcpfile) then
|
|
for line in io.lines(dhcpfile)
|
|
do
|
|
if not (line:match("^%s*#") or line:match("^%s*$")) then
|
|
local a, b, x = line:match("(%S*)%s+(%S*)%s+(.*)")
|
|
if x then
|
|
local c, d = x:match("(%S*)%s+(%S*)")
|
|
if not c then
|
|
c = x
|
|
d = ""
|
|
end
|
|
i = i + 1
|
|
local prefix = "dhcp" .. i .. "_"
|
|
parms[prefix .. "mac"] = a
|
|
parms[prefix .. "ip"] = decimal_to_ip(lannet_d + tonumber(b))
|
|
parms[prefix .. "host"] = c
|
|
parms[prefix .. "noprop"] = d
|
|
end
|
|
end
|
|
end
|
|
end
|
|
parms.dhcp_num = i
|
|
|
|
-- set dhcp tags
|
|
i = 0
|
|
if nixio.fs.stat(dhcptagsfile) then
|
|
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
|
|
i = i + 1
|
|
local prefix = "dhcptag" .. i .. "_"
|
|
parms[prefix .. "name"] = name
|
|
parms[prefix .. "cond"] = condition
|
|
parms[prefix .. "pat"] = pattern
|
|
end
|
|
end
|
|
end
|
|
end
|
|
parms.dhcptags_num = i
|
|
|
|
-- set dhcp options
|
|
i = 0
|
|
if nixio.fs.stat(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 force then
|
|
i = i + 1
|
|
local prefix = "dhcpopt" .. i .. "_"
|
|
parms[prefix .. "tag"] = tag
|
|
parms[prefix .. "force"] = force
|
|
parms[prefix .. "num"] = opt_num
|
|
parms[prefix .. "val"] = opt_val
|
|
end
|
|
end
|
|
end
|
|
end
|
|
parms.dhcpoptions_num = i
|
|
|
|
-- services
|
|
i = 0
|
|
if nixio.fs.stat(servfile) then
|
|
for line in io.lines(servfile)
|
|
do
|
|
if not (line:match("^%s*#") or line:match("^%s*$")) then
|
|
local a, b, c, d, x = line:match("([^|]*)|([^|]*)|([^|]*)|([^|]*)|(.*)")
|
|
if x then
|
|
local e, f = x:match("(.*)|(.*)")
|
|
if not e then
|
|
e = x
|
|
f = ""
|
|
end
|
|
i = i + 1
|
|
local prefix = "serv" .. i .. "_"
|
|
parms[prefix .. "name"] = a
|
|
parms[prefix .. "link"] = b
|
|
parms[prefix .. "proto"] = c
|
|
parms[prefix .. "host"] = d
|
|
parms[prefix .. "port"] = e
|
|
parms[prefix .. "suffix"] = f
|
|
end
|
|
end
|
|
end
|
|
end
|
|
parms.serv_num = i
|
|
|
|
-- aliases
|
|
i = 0
|
|
if nixio.fs.stat(aliasfile) then
|
|
for line in io.lines(aliasfile)
|
|
do
|
|
if not (line:match("^%s*#") or line:match("^%s*$")) then
|
|
local a, b = line:match("(.*)%s+(.*)")
|
|
if b then
|
|
i = i + 1
|
|
parms["alias" .. i .. "_ip"] = a
|
|
parms["alias" .. i .. "_host"] = b
|
|
end
|
|
end
|
|
end
|
|
end
|
|
parms.alias_num = i
|
|
|
|
-- sanitize the 'add' values
|
|
parms.port_add_intf = dmz_mode ~= 0 and "wan" or "wifi"
|
|
parms.port_add_type = "tcp"
|
|
if not parms.dmz_ip then
|
|
parms.dmz_ip = ""
|
|
end
|
|
parms.port_add_out = ""
|
|
parms.port_add_ip = ""
|
|
parms.port_add_in = ""
|
|
parms.dhcp_add_host = ""
|
|
parms.dhcp_add_ip = ""
|
|
parms.dhcp_add_mac = ""
|
|
parms.dhcp_add_noprop = ""
|
|
parms.dhcptag_add_name = ""
|
|
parms.dhcptag_add_cond = ""
|
|
parms.dhcptag_add_pat = ""
|
|
parms.dhcpopt_add_tag = ""
|
|
parms.dhcpopt_add_num = ""
|
|
parms.dhcpopt_add_val = ""
|
|
parms.serv_add_name = ""
|
|
parms.serv_add_proto = ""
|
|
parms.serv_add_host = ""
|
|
parms.serv_add_port = ""
|
|
parms.serv_add_suffix = ""
|
|
parms.alias_add_host = ""
|
|
parms.alias_add_ip = ""
|
|
end
|
|
|
|
local dhcp_start = tonumber(cursor:get("dhcp", "@dhcp[0]", "start"))
|
|
local dhcp_limit = tonumber(cursor:get("dhcp", "@dhcp[0]", "limit"))
|
|
local dhcp_end = dhcp_start + dhcp_limit - 1
|
|
|
|
-- load and validate the ports
|
|
|
|
local function make_addable_list(max_row)
|
|
local list = {}
|
|
for i = 1,max_row
|
|
do
|
|
list[#list + 1] = i
|
|
end
|
|
list[#list + 1] = "_add"
|
|
return list
|
|
end
|
|
|
|
local list = make_addable_list(parms.port_num)
|
|
local port_num = 0
|
|
local usedports = {}
|
|
|
|
local f = io.open(tmpdir .. "/ports", "w")
|
|
if f then
|
|
local vars = { "_intf", "_type", "_out", "_ip", "_in" }
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _ = 1,1 -- a non-loop so we can break out to continue the parent loop
|
|
do
|
|
for _, var in ipairs(vars)
|
|
do
|
|
local varname = "port" .. val .. var
|
|
local v = parms[varname]
|
|
if v then
|
|
v = v:gsub("^%s+", ""):gsub("%s+$", "")
|
|
else
|
|
v = ""
|
|
end
|
|
parms[varname] = v
|
|
_G[var] = v
|
|
end
|
|
local varname = "port" .. val .. "_enable"
|
|
if not parms[varname] then
|
|
parms[varname] = "0"
|
|
end
|
|
_enable = parms[varname]
|
|
|
|
if val == "_add" then
|
|
_enable = "1"
|
|
end
|
|
|
|
local continue = false
|
|
if val == "_add" then
|
|
if not ((_out ~= "" or _ip ~= "" or _in ~= "") and (parms.port_add or parms.button_save)) then
|
|
break
|
|
end
|
|
else
|
|
if parms["port" .. val .. "_del"] then
|
|
break
|
|
end
|
|
end
|
|
|
|
if val == "_add" and parms.button_save then
|
|
porterr(val .. " this rule must be added or cleared out before saving changes")
|
|
break
|
|
end
|
|
|
|
if _out:match("-") then
|
|
if validate_port_range(_out) then
|
|
_in = _out:match("^%s*(%d+)")
|
|
else
|
|
porterr(val .. "'" .. _out .. "' is not a valid port range")
|
|
end
|
|
else
|
|
if _out == "" then
|
|
porterr(val .. " an outside port is required")
|
|
elseif not validate_port(_out) then
|
|
porterr(val .. "'" .. _out .. "' is not a valid port")
|
|
end
|
|
end
|
|
if _ip == "" then
|
|
porterr(val .. " an address must be selected")
|
|
elseif not validate_ip(_ip) then
|
|
porterr(val .. "'" .. _ip .. "' is not a valid address")
|
|
end
|
|
if _in == "" then
|
|
porterr(val .. "a LAN port is required")
|
|
elseif not validate_port(_in) then
|
|
porterr(val .. "'" .. _in .. "' is not a valid port")
|
|
end
|
|
if val == "_add" and #port_err > 0 then
|
|
break
|
|
end
|
|
|
|
-- commit the data for this rule
|
|
port_num = port_num + 1
|
|
usedports[_out] = true
|
|
if _type ~= "tcp" and _type ~= "udp" then
|
|
_type = "both"
|
|
end
|
|
|
|
f:write(_intf .. ":" .. _type .. ":" .. _out .. ":" .. _ip .. ":" .. _in .. ":" .. _enable .. "\n")
|
|
|
|
for _, var in ipairs(vars)
|
|
do
|
|
parms["port" .. port_num .. var] = _G[var]
|
|
end
|
|
|
|
if val == "_add" then
|
|
parms.port_add_intf = "wifi"
|
|
parms.port_add_out = ""
|
|
parms.port_add_ip = ""
|
|
parms.port_add_in = ""
|
|
end
|
|
end
|
|
end
|
|
|
|
if parms.dmz_ip and parms.dmz_ip ~= "" then
|
|
f:write("dmz_ip = " .. parms.dmz_ip .. "\n")
|
|
if not validate_ip(parms.dmz_ip) then
|
|
dmzerr(parms.dmz_ip .. " is not a valid address")
|
|
end
|
|
end
|
|
|
|
f:close()
|
|
end
|
|
parms.port_num = port_num
|
|
|
|
-- load and validate the dhcp reservations
|
|
|
|
local list = make_addable_list(parms.dhcp_num)
|
|
local dhcp_num = 0
|
|
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _ = 1,1
|
|
do
|
|
local host = parms["dhcp" .. val .. "_host"]
|
|
local ip = parms["dhcp" .. val .. "_ip"]
|
|
local mac = parms["dhcp" .. val .. "_mac"]
|
|
local noprop = parms["dhcp" .. val .. "_noprop"]
|
|
if not noprop then
|
|
noprop = ""
|
|
end
|
|
|
|
local foundhost = false
|
|
if val == "_add" then
|
|
if host ~= "" then
|
|
local pattern = "%s" .. host .. "%s"
|
|
for line in aredn.olsr.getHostAsLines()
|
|
do
|
|
if line:lower():match(pattern) then
|
|
foundhost = true
|
|
dhcperr(val .. [[ <font color='red'>Warning!</font> ']] .. host .. [[ is already in use!<br>
|
|
Please choose another hostname.<br>
|
|
Prefixing the hostname with your callsign will help prevent duplicates on the network.
|
|
]])
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if not ((host ~= "" or ip ~= "" or mac ~= "" or foundhost) and (parms.dhcp_add or parms.button_save)) then
|
|
break
|
|
end
|
|
elseif parms["dhcp" .. val .. "_del"] then
|
|
break
|
|
end
|
|
|
|
if val == "_add" and parms.button_save then
|
|
dhcperr(val .. " this reservation must be added or cleared before saving changes")
|
|
break
|
|
end
|
|
|
|
if validate_hostname(host) then
|
|
if not foundhost then
|
|
local lhost = host:lower()
|
|
if lhost == node:lower() or lhost == tactical:lower() then
|
|
dhcperr(val .. " hostname '" .. host .. "' is already in use")
|
|
end
|
|
for key, _ in pairs(hosts)
|
|
do
|
|
if key:lower() == lhost then
|
|
dhcperr(val .. " hostname '" .. host .. "' is already in use")
|
|
break
|
|
end
|
|
end
|
|
end
|
|
else
|
|
if host then
|
|
dhcperr(val .. " '" .. host .. "' is not a valid hostname")
|
|
else
|
|
dhcperr(val .. " a hostname is required")
|
|
end
|
|
end
|
|
|
|
if validate_ip(ip) then
|
|
if addrs[ip] then
|
|
dhcperr(val .. " '" .. ip .. "' is already in use")
|
|
elseif ip == lanip or not validate_same_subnet(ip, lanip, lanmask) or not validate_ip_netmask(ip, lanmask) then
|
|
dhcperr(val .. " '" .. ip .. "' is not a valid LAN address")
|
|
end
|
|
else
|
|
if ip then
|
|
dhcperr(val .. " '" .. ip .. "' is not a valid address")
|
|
else
|
|
dhcperr(val .. " an IP Address must be selected")
|
|
end
|
|
end
|
|
|
|
if mac:match(mac_pattern) then
|
|
if macs[mac] then
|
|
dhcperr(val .. " MAC " .. mac .. " is already in use")
|
|
end
|
|
else
|
|
if mac then
|
|
dhcperr(val .. " '" .. mac .. "' is not a valid mac address")
|
|
else
|
|
dhcperr(val .. " a MAC address is required")
|
|
end
|
|
end
|
|
|
|
if val == "_add" and #dhcp_err > 0 then
|
|
break
|
|
end
|
|
|
|
dhcp_num = dhcp_num + 1
|
|
local prefix = "dhcp" .. dhcp_num .. "_"
|
|
parms[prefix .. "host"] = host
|
|
parms[prefix .. "ip"] = ip
|
|
parms[prefix .. "mac"] = mac
|
|
parms[prefix .. "noprop"] = noprop
|
|
|
|
hosts[host] = true
|
|
addrs[ip] = true
|
|
macs[mac] = true
|
|
|
|
if val == "_add" then
|
|
parms.dhcp_add_host = ""
|
|
parms.dhcp_add_ip = ""
|
|
parms.dhcp_add_mac = ""
|
|
parms.dhcp_add_noprop = ""
|
|
end
|
|
end
|
|
end
|
|
|
|
-- add existing leases
|
|
for lease, lval in pairs(parms)
|
|
do
|
|
local n = lease:match("^lease(%d+)_add$")
|
|
if n and lval ~= "" then
|
|
local found = false
|
|
for k, v in pairs(parms)
|
|
do
|
|
if k:match("dhcp%d+_mac") then
|
|
if v == parms["lease" .. n .. "_mac"] then
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if not found then
|
|
dhcp_num = dhcp_num + 1
|
|
local prefix1 = "lease" .. n .. "_"
|
|
local prefix2 = "dhcp" .. dhcp_num .. "_"
|
|
local host = parms[prefix1 .. "host"]
|
|
local ip = parms[prefix1 .. "ip"]
|
|
local mac = parms[prefix1 .. "mac"]
|
|
local noprop = parms[prefix1 .. "noprop"]
|
|
|
|
parms[prefix2 .. "host"] = host
|
|
parms[prefix2 .. "ip"] = ip
|
|
parms[prefix2 .. "mac"] = mac
|
|
parms[prefix2 .. "noprop"] = noprop and noprop or "0"
|
|
local lhost = host:lower()
|
|
if lhost == node:lower() or lhost == tactical:lower() then
|
|
dhcperr(dhcp_num .. " hostname '" .. host .. "' is already in use")
|
|
end
|
|
for key, _ in pairs(hosts)
|
|
do
|
|
if key:lower() == lhost then
|
|
dhcperr(dhcp_num .. " hostname '" .. host .. "' is already in use")
|
|
break
|
|
end
|
|
end
|
|
if addrs[ip] then
|
|
dhcperr(dhcp_num .. " is already in use")
|
|
end
|
|
if macs[mac] then
|
|
dhcperr(dhcp_num .. " MAC " .. mac .. " is already in use")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
parms.dhcp_num = dhcp_num
|
|
dhcphosts = {}
|
|
dhcphosts[lanip] = "localhost"
|
|
|
|
-- replace "blank" dhcp hostname and save the dhcp info into the tmpdir
|
|
|
|
local f = io.open(tmpdir .. "/dhcp", "w")
|
|
if f then
|
|
local nn = 1
|
|
for i = 1,parms.dhcp_num
|
|
do
|
|
if parms["dhcp" .. i .. "_host"] == "*" then
|
|
while hosts["noname" .. nn]
|
|
do
|
|
nn = nn + 1
|
|
end
|
|
parms["dhcp" .. i .. "_host"] = "noname" .. nn
|
|
hosts["noname" .. nn] = true
|
|
end
|
|
f:write(parms["dhcp" .. i .. "_mac"] .. " " .. (ip_to_decimal(parms["dhcp" .. i .. "_ip"]) - lannet_d) .. " " .. parms["dhcp" .. i .. "_host"] .. " " .. parms["dhcp" .. i .. "_noprop"] .. "\n")
|
|
if not (dhcphosts[parms["dhcp" .. i .. "_ip"]]) then
|
|
dhcphosts[parms["dhcp" .. i .. "_ip"]] = parms["dhcp" .. i .. "_host"]
|
|
end
|
|
end
|
|
f:close()
|
|
end
|
|
|
|
local function write_temp_records(filename, group_name, record_count, fields)
|
|
local f = io.open(tmpdir .. "/" .. filename, "w")
|
|
if f then
|
|
for i = 1,record_count
|
|
do
|
|
local prefix = group_name .. i .. "_"
|
|
local record = {}
|
|
for _, field in ipairs(fields)
|
|
do
|
|
table.insert(record, parms[prefix .. field])
|
|
end
|
|
f:write(table.concat(record, " ") .. "\n")
|
|
end
|
|
f:close()
|
|
end
|
|
end
|
|
|
|
-- DHCP tags
|
|
local list = make_addable_list(parms.dhcptags_num)
|
|
local dhcptags_num = 0
|
|
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _ = 1,1
|
|
do
|
|
local prefix = "dhcptag" .. val .. "_"
|
|
local name = parms[prefix .. "name"]
|
|
local cond_key = parms[prefix .. "cond"]
|
|
local matcher = parms[prefix .. "pat"]
|
|
|
|
if val == "_add" then
|
|
if not ((name ~= "" or cond_key ~= "" or matcher ~= "") and (parms.dhcptag_add or parms.button_save)) then
|
|
break
|
|
end
|
|
elseif parms[prefix .. "del"] then
|
|
break
|
|
end
|
|
if val == "_add" and parms.button_save then
|
|
dhcptagerr(val .. " this tag must be added or cleared out before saving changes")
|
|
break
|
|
end
|
|
if name and not name:match("^%w+$") then
|
|
dhcptagerr(val .. [[ <font color='red'>Warning!</font> The tag name "]] .. html_safe(name)
|
|
.. [[" is invalid; tag names must contain only letters and digits.]])
|
|
break
|
|
end
|
|
if cond_key == "" then
|
|
dhcptagerr(val .. [[ <font color='red'>Warning!</font> Please choose a client parameter to match.]])
|
|
break
|
|
end
|
|
if matcher == "" then
|
|
dhcptagerr(val .. [[ <font color='red'>Warning!</font> Please supply a value to match to the client parameter.]])
|
|
break
|
|
else
|
|
local tag_validator = dhcp_tag_validators[cond_key]
|
|
if not tag_validator then
|
|
dhcptagerr(string.format([[%s <font color='red'>Warning!</font> "%s" is not a known DHCP parameter.]], val, cond_key))
|
|
break
|
|
end
|
|
if not tag_validator.validator(matcher) then
|
|
dhcptagerr(string.format([[Line %s: <font color='red'>Warning!</font> "%s" is not a valid match for %s; must be %s]],
|
|
val, html_safe(matcher), cond_key, (tag_validator.hint:gsub("\\\\", "\\"))))
|
|
break
|
|
end
|
|
end
|
|
|
|
if val == "_add" and #dhcptag_err > 0 and dhcptag_err[#dhcptag_err]:match("^" .. val .. " ") then
|
|
break
|
|
end
|
|
|
|
dhcptags_num = dhcptags_num + 1
|
|
prefix = "dhcptag" .. dhcptags_num .. "_"
|
|
parms[prefix .. "name"] = name
|
|
parms[prefix .. "cond"] = cond_key
|
|
parms[prefix .. "pat"] = matcher
|
|
if val == "_add" then
|
|
parms.dhcptag_add_name = ""
|
|
parms.dhcptag_add_cond = ""
|
|
parms.dhcptag_add_pat = ""
|
|
end
|
|
end
|
|
end
|
|
|
|
-- write to temp file
|
|
write_temp_records("dhcptags", "dhcptag", dhcptags_num, {"name", "cond", "pat"})
|
|
parms.dhcptags_num = dhcptags_num
|
|
|
|
-- DHCP options
|
|
local list = make_addable_list(parms.dhcpoptions_num)
|
|
local dhcpoptions_num = 0
|
|
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _ = 1,1
|
|
do
|
|
local prefix = "dhcpopt" .. val .. "_"
|
|
local tag = parms[prefix .. "tag"]
|
|
local force = parms[prefix .. "force"]
|
|
if force ~= "force" then
|
|
force = "onrequest"
|
|
end
|
|
local opt_num = parms[prefix .. "num"]
|
|
local opt_val = parms[prefix .. "val"]
|
|
|
|
if val == "_add" then
|
|
if not ((tag ~= "" or opt_num ~= "" or opt_val ~= "") and (parms.dhcpopt_add or parms.button_save)) then
|
|
break
|
|
end
|
|
elseif parms[prefix .. "del"] then
|
|
break
|
|
end
|
|
if val == "_add" and parms.button_save then
|
|
dhcpopterr(val .. " this option must be added or cleared out before saving changes")
|
|
break
|
|
end
|
|
if tag ~= "" and not tag:match("^%w+$") then
|
|
dhcpopterr(val .. [[ <font color='red'>Warning!</font> The tag name "]] .. html_safe(tag) .. [[" is invalid; tag names must contain only letters and digits.]])
|
|
break
|
|
end
|
|
local nnum = tonumber(opt_num)
|
|
if not nnum or nnum < 1 or nnum > 254 then
|
|
dhcpopterr(val .. [[ <font color='red'>Warning!</font> Option number must be an integer in the range [1..254].]])
|
|
break
|
|
end
|
|
-- TODO check opt_val for suitability for opt_num
|
|
if opt_val == "" then
|
|
dhcpopterr(val .. [[ <font color='red'>Warning!</font> Please supply a value to send for this option.]])
|
|
break
|
|
end
|
|
|
|
if val == "_add" and #dhcpopt_err > 0 and dhcpopt_err[#dhcpopt_err]:match("^" .. val .. " ") then
|
|
--if val == "_add" and #dhcpopt_err > 0 then
|
|
break
|
|
end
|
|
|
|
dhcpoptions_num = dhcpoptions_num + 1
|
|
prefix = "dhcpopt" .. dhcpoptions_num .. "_"
|
|
parms[prefix .. "tag"] = tag
|
|
parms[prefix .. "force"] = force
|
|
parms[prefix .. "num"] = opt_num
|
|
parms[prefix .. "val"] = opt_val
|
|
if val == "_add" then
|
|
parms.dhcpopt_add_tag = ""
|
|
parms.dhcpopt_add_force = ""
|
|
parms.dhcpopt_add_num = ""
|
|
parms.dhcpopt_add_val = ""
|
|
end
|
|
end
|
|
end
|
|
|
|
-- write to temp file
|
|
write_temp_records("dhcpoptions", "dhcpopt", dhcpoptions_num, {"tag", "force", "num", "val"})
|
|
parms.dhcpoptions_num = dhcpoptions_num
|
|
|
|
-- aliases
|
|
local list = make_addable_list(parms.alias_num)
|
|
local alias_num = 0
|
|
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _ = 1,1
|
|
do
|
|
local foundhost = false
|
|
local host = parms["alias" .. val .. "_host"]
|
|
local ip = parms["alias" .. val .. "_ip"]
|
|
-- if adding aliases check the name is not already in use,
|
|
-- also check that it dos not contain anything that will be weird on the mesh
|
|
if val == "_add" then
|
|
if host ~= "" then
|
|
local pattern = "%s" .. host .. "%s"
|
|
for line in aredn.olsr.getHostAsLines()
|
|
do
|
|
if line:lower():match(pattern) then
|
|
foundhost = true
|
|
aliaserr(val .. [[ <font color='red'>Warning!</font> ']] .. host .. [[ is already in use!<br>
|
|
Please choose another hostname.<br>
|
|
Prefixing the hostname with your callsign will help prevent duplicates on the network.
|
|
]])
|
|
break
|
|
end
|
|
end
|
|
if not host:match("^%*%.") and (host:match("_") or not host:match("^[%w%-%.]+$")) then
|
|
aliaserr(val .. " <font color='red'>Warning!</font> The alias name: '" .. host .."' is invalid")
|
|
end
|
|
end
|
|
if not ((host ~= "" or ip ~= "" or foundhost) and (parms.alias_add or parms.button_save)) then
|
|
break
|
|
end
|
|
elseif parms["alias" .. val .. "_del"] then
|
|
break
|
|
end
|
|
if val == "_add" and parms.button_save then
|
|
aliaserr(val .. " this alias must be added or cleared out before saving changes")
|
|
break
|
|
end
|
|
if val == "_add" and #alias_err > 0 and alias_err[#alias_err]:match("^" .. val .. " ") then
|
|
break
|
|
end
|
|
|
|
alias_num = alias_num + 1
|
|
parms["alias" .. alias_num .. "_host"] = host
|
|
parms["alias" .. alias_num .. "_ip"] = ip
|
|
hosts[host] = true
|
|
if val == "_add" then
|
|
parms.alias_add_host = ""
|
|
parms.alias_add_ip = ""
|
|
end
|
|
end
|
|
end
|
|
|
|
-- write to temp file
|
|
write_temp_records("aliases", "alias", alias_num, {"ip", "host"})
|
|
parms.alias_num = alias_num
|
|
|
|
-- load and validate services
|
|
local list = make_addable_list(parms.serv_num)
|
|
local serv_num = 0
|
|
hosts[""] = true
|
|
hosts[node] = true
|
|
usedports[""] = true
|
|
local servicenames = {}
|
|
|
|
local vars = { "name", "link", "proto", "host", "port", "suffix" }
|
|
local f = io.open(tmpdir .. "/services", "w")
|
|
if f then
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _ = 1,1
|
|
do
|
|
for _, var in ipairs(vars)
|
|
do
|
|
local varname = "serv" .. val .. "_" .. var
|
|
local v = parms[varname]
|
|
if v then
|
|
v = v:gsub("^%s+", ""):gsub("%s+$", "")
|
|
else
|
|
v = ""
|
|
end
|
|
parms[varname] = v
|
|
_G[var] = v
|
|
end
|
|
|
|
if link == "" then
|
|
link = "0"
|
|
end
|
|
|
|
if dmz_mode == 0 then
|
|
host = node
|
|
end
|
|
|
|
-- remove services that have had their host or port deleted
|
|
if val ~= "_add" and not (dmz_mode == 0 and true or hosts[host]) then
|
|
break
|
|
end
|
|
|
|
if val == "_add" then
|
|
if not ((name ~= "" or proto ~= "" or port ~= "" or suffix ~= "") and (parms.serv_add or parms.button_save)) then
|
|
break
|
|
end
|
|
else
|
|
if parms["serv" .. val .. "_del"] or not (name ~= "" or proto ~= "" or port ~= "" or suffix ~= "") then
|
|
break
|
|
end
|
|
end
|
|
|
|
if val == "_add" and parms.button_save then
|
|
serverr(val .. " this service must be added or cleared out before saving changes")
|
|
break
|
|
end
|
|
|
|
if name == "" then
|
|
serverr(val .. " a name is required")
|
|
elseif not validate_service_name(name) then
|
|
serverr(val .. " '" .. name .. "' is not a valid service name")
|
|
elseif servicenames[name] then
|
|
serverr(val .. " the name '" .. name .. "' is already in use")
|
|
end
|
|
|
|
if link == "1" then
|
|
if proto == "" then
|
|
proto = "http"
|
|
end
|
|
parms["serv" .. val .. "_proto"] = proto
|
|
if not validate_service_protocol(proto) then
|
|
serverr(val .. " '" .. proto .. "' is not a valid protocol")
|
|
elseif port == "" then
|
|
serverr(val .. " a port number is required")
|
|
elseif not validate_port(port) then
|
|
serverr(val .. " '" .. port .. "' is not a valid port")
|
|
elseif not validate_service_suffix(suffix) then
|
|
serverr(val .. " '" .. suffix .. "' is not a valid service suffix")
|
|
end
|
|
elseif val == "_add" then
|
|
proto = ""
|
|
port = ""
|
|
suffix = ""
|
|
end
|
|
|
|
if val == "_add" and #serv_err > 0 then
|
|
break
|
|
end
|
|
|
|
-- commit the data for this service
|
|
serv_num = serv_num + 1
|
|
servicenames[name] = true
|
|
|
|
f:write(name .. "|" .. link .. "|" .. proto .. "|" .. host .. "|" .. port .. "|" .. suffix .. "\n")
|
|
|
|
for _, var in ipairs(vars)
|
|
do
|
|
parms["serv" .. serv_num .. "_" .. var] = _G[var]
|
|
end
|
|
|
|
if val == "_add" then
|
|
parms.serv_add_name = ""
|
|
parms.serv_add_link = ""
|
|
parms.serv_add_proto = ""
|
|
parms.serv_add_host = ""
|
|
parms.serv_add_port = ""
|
|
parms.serv_add_suffix = ""
|
|
end
|
|
end
|
|
end
|
|
f:close()
|
|
end
|
|
parms.serv_num = serv_num
|
|
|
|
-- save configuration
|
|
if parms.button_save and not (#port_err > 0 or #dhcp_err > 0 or #dhcptag_err > 0 or #dhcpopt_err > 0 or #dmz_err > 0 or #serv_err > 0 or #alias_err > 0) then
|
|
filecopy(tmpdir .. "/ports", portfile)
|
|
filecopy(tmpdir .. "/dhcp", dhcpfile)
|
|
filecopy(tmpdir .. "/dhcptags", dhcptagsfile)
|
|
filecopy(tmpdir .. "/dhcpoptions", dhcpoptionsfile)
|
|
filecopy(tmpdir .. "/services", servfile)
|
|
filecopy(tmpdir .. "/aliases", aliasfile)
|
|
|
|
os.remove("/tmp/service-validation-state")
|
|
|
|
if os.execute("/usr/local/bin/node-setup > /dev/null 2>&1") ~= 0 then
|
|
err("problem with configuration")
|
|
else
|
|
os.execute("/usr/local/bin/restart-services.sh dnsmasq firewall olsrd > /dev/null 2>&1")
|
|
end
|
|
end
|
|
|
|
-- generate the page
|
|
|
|
http_header()
|
|
html.header(node .. " setup", false)
|
|
|
|
do
|
|
local function generateValidatorCaseJS(cond)
|
|
local caseTab = {' case "',
|
|
cond.key,
|
|
'":\n newPlaceholder = "',
|
|
cond.jsPlaceholder,
|
|
'";\n newTitle = "',
|
|
cond.hint}
|
|
if cond.jsPattern then
|
|
table.insert(caseTab, '";\n newPattern = "')
|
|
table.insert(caseTab, cond.jsPattern)
|
|
end
|
|
table.insert(caseTab, '";\n break;\n')
|
|
return table.concat(caseTab, '')
|
|
end
|
|
|
|
-- Note: toggleField comes from https://jsfiddle.net/6nq7w/4/
|
|
local scriptTab = {[[
|
|
<script>
|
|
function toggleField(hideObj,showObj){
|
|
hideObj.disabled=true;
|
|
hideObj.style.display='none';
|
|
showObj.disabled=false;
|
|
showObj.style.display='inline';
|
|
showObj.focus();
|
|
}
|
|
function setOrRemoveAttribute(elem, attrName, value) {
|
|
if (value == null)
|
|
elem.removeAttribute(attrName);
|
|
else
|
|
elem.setAttribute(attrName, value);
|
|
}
|
|
function allowForceOption(forceID, tagValue) {
|
|
const forceElem = document.getElementById(forceID);
|
|
if (tagValue == "") {
|
|
forceElem.checked = false;
|
|
forceElem.disabled = true;
|
|
forceElem.title = "]] .. FORCE_UNSUPPORTED_HINT .. [[";
|
|
}
|
|
else
|
|
forceElem.disabled = false;
|
|
forceElem.title = "]] .. FORCE_SUPPORTED_HINT .. [[";
|
|
}
|
|
function setMatcherValidator(inputId, matcherType) {
|
|
const matcherElem = document.getElementById(inputId);
|
|
newPattern = null;
|
|
newPlaceholder = null;
|
|
newTitle = null;
|
|
switch (matcherType) {
|
|
]]}
|
|
for _, cond in ipairs(dhcp_tag_conds)
|
|
do
|
|
table.insert(scriptTab, generateValidatorCaseJS(cond))
|
|
end
|
|
table.insert(scriptTab, [[
|
|
default:
|
|
break;
|
|
}
|
|
setOrRemoveAttribute(matcherElem, "placeholder", newPlaceholder);
|
|
setOrRemoveAttribute(matcherElem, "pattern", newPattern);
|
|
setOrRemoveAttribute(matcherElem, "title", newTitle);
|
|
}
|
|
</script>
|
|
<style>
|
|
input:invalid {
|
|
border: 2px dashed red;
|
|
}
|
|
</style>]])
|
|
html.print(table.concat(scriptTab, ''))
|
|
end
|
|
html.print("</head>")
|
|
html.print("<body><center>")
|
|
html.alert_banner()
|
|
html.print("<form method=post action=/cgi-bin/ports enctype='multipart/form-data'>")
|
|
html.navbar_admin("ports")
|
|
html.print("<table width=820>")
|
|
|
|
-- control buttons
|
|
html.print([[<tr><td align=center>
|
|
<a href='/help.html#ports' target='_blank'>Help</a>
|
|
|
|
<input type=submit name=button_save value='Save Changes' title='Save and use these settings now (takes about 20 seconds)'>
|
|
<input type=submit name=button_reset value='Reset Values' title='Revert to the last saved settings'>
|
|
<input type=button name=button_refresh value='Refresh' title='Refresh this page' onclick='window.location.reload();'>
|
|
<tr><td> </td></tr>]])
|
|
hide("<input type=hidden name=reload value=1>")
|
|
|
|
-- messages
|
|
|
|
if parms.button_save then
|
|
if #port_err > 0 or #dhcp_err > 0 or #dhcptag_err > 0 or #dhcpopt_err > 0 or #dmz_err > 0 or #serv_err > 0 then
|
|
html.print("<tr><td align=center><b>Configuration NOT saved!</b></td></tr>")
|
|
elseif #errors > 0 then
|
|
html.print("<tr><td align=center><b>Configuration saved, however:<br>")
|
|
for _, err in ipairs(errors)
|
|
do
|
|
html.print(err .. "<br>")
|
|
end
|
|
html.print("</b></td></tr>")
|
|
else
|
|
html.print("<tr><td align=center><b>Configuration saved and is now active.</b></td></tr>")
|
|
end
|
|
html.print("<tr><td> </td></tr>")
|
|
end
|
|
|
|
-- everything else
|
|
|
|
local js_mac_pattern = [[ pattern='^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$' ]]
|
|
local js_host_pattern = [[ pattern='^[a-zA-Z0-9][a-zA-Z0-9\-]*$' ]]
|
|
local js_alias_pattern = [[ pattern='^(\*\.|)?[a-zA-Z0-9][a-zA-Z0-9\-]*$' ]]
|
|
local js_port_range_pattern = [[ pattern='^\s*\d{1,5}(?:\s*-\s*\d{1,5})?\s*$' ]]
|
|
|
|
local function print_heading_vsep()
|
|
html.print("<tr><td colspan=4 height=5></td></tr>")
|
|
end
|
|
|
|
local function print_new_entry_vsep(val, list, columns)
|
|
if val == "_add" and #list > 1 then
|
|
html.print("<tr><td colspan=" .. columns .. " height=10></td></tr>")
|
|
end
|
|
end
|
|
|
|
-- mark input fields of already-added entries as "required"
|
|
local function require_existing(val)
|
|
return val == "_add" and "" or " required "
|
|
end
|
|
|
|
local function print_errors(error_list)
|
|
for _, e in ipairs(error_list)
|
|
do
|
|
html.print("<tr><th colspan=8>" .. e .. "</th></tr>")
|
|
end
|
|
end
|
|
|
|
local function print_reservations()
|
|
html.print("<table cellpadding=0 cellspacing=0><tr><th colspan=4>DHCP Address Reservations</th></tr>")
|
|
print_heading_vsep()
|
|
html.print("<tr><td align=center>Hostname</td><td align=center>IP Address</td><td align=center>MAC Address</td>")
|
|
|
|
if dmz_mode ~= 0 then
|
|
html.print("<td align=center style='font-size:10px;'>Do Not<br>Propagate</td><td></td></tr>")
|
|
else
|
|
html.print("<td></td><td></td></tr>")
|
|
end
|
|
print_heading_vsep()
|
|
|
|
local list = make_addable_list(parms.dhcp_num)
|
|
|
|
local mac_list = {}
|
|
for _, val in ipairs(list)
|
|
do
|
|
local host = parms["dhcp" .. val .. "_host"]
|
|
local ip = parms["dhcp" .. val .. "_ip"]
|
|
local mac = parms["dhcp" .. val .. "_mac"]:lower()
|
|
local noprop = parms["dhcp" .. val .. "_noprop"]
|
|
mac_list[mac] = true
|
|
|
|
print_new_entry_vsep(val, list, 5)
|
|
html.print("<tr><td><input style='width=180;' type=text placeholder='host-name' title='alphanumerics and embedded hyphens' name=dhcp" .. val .. "_host value='" .. host .. "'" .. js_host_pattern .. require_existing(val) .. "></td>")
|
|
html.print("<td align=center style='padding:0 4px'><select style='width:130px' name=dhcp" .. val .. "_ip>")
|
|
if val == "_add" then
|
|
html.print("<option value=''>- IP Address -</option>\n")
|
|
end
|
|
for i = dhcp_start,dhcp_end
|
|
do
|
|
local selip = decimal_to_ip(lannet_d + i - lannet_d % 256)
|
|
if selip ~= lanip then
|
|
local ipname = dhcphosts[selip]
|
|
if not ipname or selip == ip then
|
|
ipname = selip
|
|
end
|
|
html.print("<option " .. (ip == selip and "selected" or "") .. " value='" .. selip .. "'>" .. ipname .. "</option>")
|
|
end
|
|
end
|
|
html.print("</select></td>")
|
|
html.print("<td><input style='width:120;' type=text placeholder='aa:aa:aa:aa:aa:aa' name=dhcp" .. val .. "_mac value='" .. mac .. "'" .. js_mac_pattern .. require_existing(val) .. "></td>")
|
|
if dmz_mode ~= 0 then
|
|
html.print("<td align=center><input type=checkbox id=dhcp" .. val .. "_noprop name=dhcp" .. val .. "_noprop value='#NOPROP'"
|
|
.. (noprop == "#NOPROP" and " checked" or "") .. "></td>")
|
|
else
|
|
html.print("<td></td>")
|
|
end
|
|
|
|
html.print("<td><nobr> <input type=submit name=")
|
|
|
|
if val == "_add" then
|
|
html.print("dhcp_add value=Add title='Add this as a DHCP reservation'")
|
|
else
|
|
html.print("dhcp" .. val .. "_del value='Del ' title='Remove this reservation'")
|
|
end
|
|
html.print("></nobr></td></tr>")
|
|
|
|
while #dhcp_err > 0 and dhcp_err[1]:match("^" .. val .. " ")
|
|
do
|
|
html.print("<tr><th colspan=4>" .. dhcp_err[1]:gsub("^%S+ ", "") .. "</th></tr>")
|
|
table.remove(dhcp_err, 1)
|
|
end
|
|
|
|
html.print("<tr><td height=5></td></tr>")
|
|
end
|
|
|
|
html.print("<tr><td> </td></tr>")
|
|
html.print("<tr><th colspan=4>Current DHCP Leases</th></tr>\n<tr>")
|
|
|
|
local i = 0
|
|
if nixio.fs.stat("/tmp/dhcp.leases") then
|
|
for line in io.lines("/tmp/dhcp.leases")
|
|
do
|
|
i = i + 1
|
|
local _, mac, ip, host = line:match("(%S+)%s+(%S+)%s+(%S+)%s+(%S+)")
|
|
html.print("<tr><td height=5></td></tr>")
|
|
html.print("<tr><td align=center>" .. host .. "</td><td align=center><small>" .. ip .. "</small></td>")
|
|
html.print("<td align=center><small>" .. mac .. "</small></td><td></td><td><nobr> <input " .. (mac_list[mac] and "disabled " or "") .. "type=submit name=lease" .. i .. "_add value=Add ")
|
|
html.print("title='Use these values as an address reservation'></nobr></td></tr>")
|
|
hide("<input type=hidden name=lease" .. i .. "_host value=" .. host .. ">")
|
|
hide("<input type=hidden name=lease" .. i .. "_ip value=" .. ip .. ">")
|
|
hide("<input type=hidden name=lease" .. i .. "_mac value=" .. mac .. ">")
|
|
end
|
|
end
|
|
|
|
if i == 0 then
|
|
html.print("<tr><td align=center colspan=4>there are no active leases</td></tr>")
|
|
end
|
|
html.print("</table>")
|
|
end
|
|
|
|
local function print_forwarding()
|
|
html.print("<table cellpadding=0 cellspacing=0>")
|
|
html.print("<tr><th colspan=6>Port Forwarding</th></tr>")
|
|
html.print("<tr><td colspan=6 height=5></td></tr>")
|
|
|
|
html.print("<tr><td align=center>Interface</td><td align=center>Type</td>")
|
|
html.print("<td align=center>Outside Port</td><td align=center>LAN IP</td>")
|
|
html.print("<td align=center>LAN Port</td><td> </td></tr>")
|
|
html.print("<tr><td colspan=6 height=5></td></tr>")
|
|
|
|
local list = make_addable_list(parms.port_num)
|
|
local vars = { "_intf", "_type", "_out", "_ip", "_in", "_enable", "_adv", "_link", "_proto", "_suffix", "_name" }
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _, var in ipairs(vars)
|
|
do
|
|
_G[var] = parms["port" .. val .. var]
|
|
end
|
|
|
|
print_new_entry_vsep(val, list, 6)
|
|
|
|
html.print("<tr>")
|
|
hide("<input style='width:90;' type=hidden name=port" .. val .. "_enable value=1>")
|
|
|
|
-- port forwarding settings
|
|
html.print("<td align=center valign=top><select name=port" .. val .. "_intf title='forward inbound packets from this interface'>")
|
|
if dmz_mode == 0 then
|
|
html.print("<option " .. (_intf == "wifi" and "selected" or "") .. " value='wifi'>WiFi</option>")
|
|
html.print("<option " .. (_intf == "wan" and "selected" or "") .. " value='wan'>WAN</option>")
|
|
html.print("<option " .. (_intf == "both" and "selected" or "") .. " value='both'>Both</option>")
|
|
else
|
|
html.print("<option " .. (_intf == "wan" and "selected" or "") .. " value='wan'>WAN</option>")
|
|
end
|
|
html.print("</select></td>")
|
|
|
|
html.print("<td align=center valign=top><select name=port" .. val .. "_type>")
|
|
html.print("<option " .. (_type == "tcp" and "selected" or "") .. " value='tcp'>TCP</option>")
|
|
html.print("<option " .. (_type == "udp" and "selected" or "") .. " value='udp'>UDP</option>")
|
|
html.print("<option " .. (_type == "both" and "selected" or "") .. " value='both'>Both</option>")
|
|
html.print("</select></td>")
|
|
|
|
html.print("<td align=center valign=top><input style='width:90;' type=text placeholder='9998-9999' title='integer 1-65535 or two integers separated by a hyphen' name=port" .. val .. "_out value='"
|
|
.. _out .. "'" .. js_port_range_pattern .. require_existing(val) .. "></td>")
|
|
html.print("<td align=center valign=top><select name=port" .. val .. "_ip>")
|
|
if val == "_add" then
|
|
html.print("<option value=''>- IP Address -</option>")
|
|
end
|
|
local lanlimit = nixio.bit.lshift(1, 32 - netmask_to_cidr(lanmask)) - 2
|
|
for i = 1,lanlimit
|
|
do
|
|
local selip = decimal_to_ip(lannet_d + i)
|
|
local ipname = dhcphosts[selip]
|
|
if not ipname then
|
|
ipname = selip
|
|
end
|
|
html.print("<option " .. (_ip == selip and "selected" or "") .. " value='" .. selip .. "'>" .. ipname .. "</option>")
|
|
end
|
|
html.print("</select></td>")
|
|
|
|
html.print("<td align=left valign=top><input style='width:90;' type='number' min='1' max='65535' placeholder='9998' title='integer 1-65535' name=port"
|
|
.. val .. "_in value='" .. _in .. "'" .. require_existing(val) .. "></td>")
|
|
html.print("<td><nobr> <input type=submit name=")
|
|
|
|
if val == "_add" then
|
|
html.print("port_add value=Add title='Add this as a port forwarding rule'")
|
|
else
|
|
html.print("port" .. val .. "_del value='Del ' title='Remove this rule'")
|
|
end
|
|
html.print("></nobr></td></tr>")
|
|
|
|
-- display any errors
|
|
while #port_err > 0 and port_err[1]:match("^" .. val .. " ")
|
|
do
|
|
local err = port_err[1]:gsub("^%S+ ", "")
|
|
html.print("<tr><th colspan=7>" .. err .. "</th></tr>")
|
|
table.remove(port_err, 1)
|
|
end
|
|
|
|
html.print("<tr><td colspan=6 height=5></td></tr>")
|
|
end
|
|
|
|
-- dmz server for nat mode
|
|
if dmz_mode == 0 then
|
|
html.print("<tr><td colspan=7 height=10></td></tr>")
|
|
html.print("<tr><td colspan=4 align=right>DMZ Server </td>")
|
|
html.print("<td colspan=3><select name=dmz_ip onChange='form.submit()' ")
|
|
html.print("title='Send all other inbound traffic to this host'>")
|
|
html.print("<option value=''>None</option>")
|
|
local lanlimit = nixio.bit.lshift(1, 32 - netmask_to_cidr(lanmask)) - 2
|
|
for i = 1,lanlimit
|
|
do
|
|
local selip = decimal_to_ip(lannet_d + i)
|
|
if selip ~= lanip then
|
|
local ipname = dhcphosts[selip]
|
|
if not ipname then
|
|
ipname = selip
|
|
end
|
|
html.print("<option " .. (parms.dmz_ip == selip and "selected" or "") .. " value='" .. selip .. "'>" .. ipname .. "</option>")
|
|
end
|
|
end
|
|
html.print("</select></td>")
|
|
|
|
print_errors(dmz_err)
|
|
end
|
|
html.print("</table>")
|
|
end
|
|
|
|
local function print_services()
|
|
local activesvc = nil
|
|
if nixio.fs.stat("/var/etc/olsrd.conf") then
|
|
activesvc = {}
|
|
for line in io.lines("/var/etc/olsrd.conf")
|
|
do
|
|
local plparam = line:match([[^%s*(PlParam "service" ".*")%s*$]])
|
|
if plparam then
|
|
activesvc[plparam] = true
|
|
end
|
|
end
|
|
end
|
|
html.print("<table cellpadding=0 cellspacing=0><tr><th colspan=4>Advertised Services</th></tr>")
|
|
if not (dmz_mode ~= 0 or parms.port_num ~= 0 or parms.dmz_ip) then
|
|
if dmz_mode ~= 0 then
|
|
html.print("<tr><td> </td></tr><tr><td height=10></td></tr>")
|
|
else
|
|
html.print("<tr><td> <br><br></td></tr>")
|
|
end
|
|
html.print("<tr><td colspan=4 align=center>none</td></tr>")
|
|
html.print("</table>")
|
|
return;
|
|
end
|
|
|
|
if dmz_mode ~= 0 then
|
|
html.print("<tr><td height=5></td></tr>")
|
|
html.print("<tr><td>Name</td><td>Link</td><td>URL</td><td></td></tr>")
|
|
html.print("<tr><td height=5></td></tr>")
|
|
else
|
|
html.print("<tr><td>Name</td><td>Link</td><td>URL</td><td><br><br></td></tr>")
|
|
end
|
|
|
|
local list = make_addable_list(parms.serv_num)
|
|
|
|
local vars = { "_name", "_link", "_proto", "_host", "_port", "_suffix" }
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _, var in ipairs(vars)
|
|
do
|
|
_G[var] = parms["serv" .. val .. var]
|
|
end
|
|
if dmz_mode == 0 then
|
|
_host = node
|
|
parms["serv" .. val .. "_host"] = node
|
|
end
|
|
|
|
print_new_entry_vsep(val, list, 4)
|
|
html.print("<tr>")
|
|
html.print("<td><input type=text style='width:120;' placeholder='Service Name' name=serv" .. val .. "_name value='" .. _name .. "' title='what to call this service'></td>")
|
|
|
|
html.print("<td><nobr><input type=checkbox name=serv" .. val .. "_link value=1")
|
|
if val ~= "_add" then
|
|
html.print(" onChange='form.submit()'")
|
|
end
|
|
if _link == "1" then
|
|
html.print(" checked")
|
|
end
|
|
html.print(" title='create a clickable link for this service'>")
|
|
html.print("<input type=text style='width:40;' name=serv" .. val .. "_proto value='" .. _proto .. "' title='URL Protocol'")
|
|
if val ~= "_add" and _link ~= "1" then
|
|
html.print(" disabled")
|
|
end
|
|
html.print("></nobr></td>")
|
|
|
|
if dmz_mode ~= 0 then
|
|
html.print("<td><nobr><b>:</b>//<select name=serv" .. val .. "_host")
|
|
if val ~= "_add" and _link ~= "1" then
|
|
html.print(" disabled")
|
|
end
|
|
html.print(">")
|
|
html.print("<option " .. (node == _host and "selected" or "") .. " value='" .. node .. "'>" .. node .. "</option>")
|
|
for i = 1,parms.alias_num
|
|
do
|
|
html.print("<option " .. (parms["alias" .. i .. "_host"] == _host and "selected" or "") .. " value='" .. parms["alias" .. i .. "_host"] .. "'>" .. parms["alias" .. i .. "_host"] .. "</option>")
|
|
end
|
|
for i = 1,parms.dhcp_num
|
|
do
|
|
if parms["dhcp" .. i .. "_noprop"] ~= "#NOPROP" then
|
|
html.print("<option " .. (parms["dhcp" .. i .. "_host"] == _host and "selected" or "") .. " value='" .. parms["dhcp" .. i .. "_host"] .. "'>" .. parms["dhcp" .. i .. "_host"] .. "</option>")
|
|
end
|
|
end
|
|
html.print("</select>")
|
|
else
|
|
html.print("<td style='width:99%'><nobr><b>:</b>//<small>" .. _host .. "</small>")
|
|
end
|
|
|
|
html.print("<b>:</b><input type=text style='width:40;' name=serv" .. val .. "_port value='" .. _port .. "' title='port number'")
|
|
if val ~= "_add" and _link ~= "1" then
|
|
html.print(" disabled")
|
|
end
|
|
html.print("> / <input type=text name=serv" .. val .. "_suffix value='" .. _suffix .. "' ")
|
|
html.print("title='leave blank unless the URL needs a more specific path'")
|
|
if val ~= "_add" and _link ~= "1" then
|
|
html.print(" disabled")
|
|
end
|
|
html.print("></nobr></td>")
|
|
|
|
html.print("<td><nobr> <input type=submit name=")
|
|
if val == "_add" then
|
|
html.print("serv_add value=Add title='Add this as a service'>")
|
|
else
|
|
html.print("serv" .. val .. "_del value='Del ' title='Remove this service'>")
|
|
end
|
|
local svc = string.format([[PlParam "service" "%s://%s:%s/%s|tcp|%s"]], (_link == "1" and _proto or "http"), _host, (_link == "1" and _port or "0"), _suffix, _name)
|
|
if val ~= "_add" and activesvc and not activesvc[svc] then
|
|
html.print("<img style='vertical-align:middle' src='/alert-circle-outline.png' title='Service is not being advertised'>")
|
|
end
|
|
html.print("</nobr></td></tr>")
|
|
|
|
-- display any errors
|
|
while #serv_err > 0 and serv_err[1]:match("^" .. val .. " ")
|
|
do
|
|
html.print("<tr><th colspan=4>" .. serv_err[1]:gsub("^%S+ ", "") .. "</th></tr>")
|
|
table.remove(serv_err, 1)
|
|
end
|
|
|
|
if _link ~= "1" and val ~= "_add" then
|
|
hide("<input type=hidden name=serv" .. val .. "_proto value='" .. _proto .. "'>")
|
|
hide("<input type=hidden name=serv" .. val .. "_host value='" .. _host .. "'>")
|
|
hide("<input type=hidden name=serv" .. val .. "_port value='" .. _port .. "'>")
|
|
hide("<input type=hidden name=serv" .. val .. "_suffix value='" .. _suffix .. "'>")
|
|
end
|
|
|
|
html.print("<tr><td colspan=4 height=4></td></tr>")
|
|
end
|
|
html.print("</table>")
|
|
end
|
|
|
|
local function print_aliases()
|
|
html.print("<table cellpadding=0 cellspacing=0><tr><th colspan=4>DNS Aliases</th></tr>")
|
|
html.print("<tr><td colspan=3 height=5></td></tr>")
|
|
html.print("<tr><td align=center>Alias Name</td><td></td><td align=center>IP Address</td></tr>")
|
|
html.print("<tr><td colspan=3 height=5></td></tr>")
|
|
|
|
local list = make_addable_list(parms.alias_num)
|
|
|
|
for _, val in ipairs(list)
|
|
do
|
|
local host = parms["alias" .. val .. "_host"]
|
|
local ip = parms["alias" .. val .. "_ip"]
|
|
print_new_entry_vsep(val, list , 4)
|
|
html.print("<tr><td align=center><input type=text placeholder='alias-name' title='alphanumerics and embedded hyphens' name=alias" .. val .. "_host value='" .. host .. "' size=20" .. js_alias_pattern .. require_existing(val) .. "></td>")
|
|
html.print("<td> </td>")
|
|
html.print("<td align=center><select name=alias" .. val .. "_ip>")
|
|
if val == "_add" then
|
|
html.print("<option value=''>- IP Address -</option>")
|
|
end
|
|
for i = dhcp_start,dhcp_end
|
|
do
|
|
local selip = decimal_to_ip(lannet_d + i - lannet_d % 256)
|
|
if selip ~= lanip then
|
|
local ipname = dhcphosts[selip]
|
|
if not ipname then
|
|
ipname = selip
|
|
end
|
|
html.print("<option " .. (ip == selip and "selected" or "") .. " value='" .. selip .. "'>" .. ipname .. "</option>")
|
|
end
|
|
end
|
|
html.print("</select></td>")
|
|
html.print("<td><nobr> <input type=submit name=")
|
|
if val == "_add" then
|
|
html.print("alias_add value=Add title='Add Alias'")
|
|
else
|
|
html.print("alias" .. val .. "_del value='Del ' title='Remove Alias'")
|
|
end
|
|
html.print("></nobr></td></tr>")
|
|
end
|
|
for _, e in ipairs(alias_err)
|
|
do
|
|
html.print("<tr><th colspan=4>" .. e:gsub("^%S+ ", "") .. "</th></tr>")
|
|
end
|
|
html.print("</table>")
|
|
end
|
|
|
|
local function print_dhcp_cond_selector(row, selected_key, allow_unset, patname)
|
|
local field_name = "dhcptag" .. row .. "_cond"
|
|
html.print("<td align=center style='padding:0 4px'><select name='" .. field_name
|
|
.. "' title='Type of identifying information sent by the client' "
|
|
.. "onchange='setMatcherValidator(\"" .. patname .. "\",this.value);'>")
|
|
|
|
if allow_unset then
|
|
html.print("<option value=''>-Parameter-</option>")
|
|
end
|
|
for _, condition in ipairs(dhcp_tag_conds)
|
|
do
|
|
html.print("<option " .. (condition.key == selected_key and "selected " or "")
|
|
.. "value=\"" .. condition.key .. "\">" .. condition.name .. "</option>")
|
|
end
|
|
html.print("</select></td>")
|
|
end
|
|
|
|
local function dhcp_tag_used(tag_name)
|
|
if tag_name == "" then
|
|
return false
|
|
end
|
|
for i = 1,parms.dhcpoptions_num
|
|
do
|
|
if tag_name == parms["dhcpopt" .. i .. "_tag"] then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function print_dhcp_tags()
|
|
html.print("<table cellpadding=0 cellspacing=0><tr><th colspan=4>Tags for Advanced DHCP Options</th></tr>")
|
|
print_heading_vsep()
|
|
html.print("<tr><td align=center>Set a Tag Named</td><td align=center>When Client's</td><td align=center style='width:99%'>Matches</td>")
|
|
|
|
local list = make_addable_list(parms.dhcptags_num)
|
|
|
|
for _, val in ipairs(list)
|
|
do
|
|
local prefix = "dhcptag" .. val .. "_"
|
|
local name = parms[prefix .. "name"]
|
|
local cond_key = parms[prefix .. "cond"]
|
|
local matcher = parms[prefix .. "pat"]
|
|
|
|
print_new_entry_vsep(val, list, 4)
|
|
html.print("<tr><td><input style='width:110px' type='text' placeholder='tagname' name='" .. prefix .. "name' value='" .. name
|
|
.. "' pattern='^[a-zA-Z0-9]+$' title='Alphanumeric string identifying the tag to assign'"
|
|
.. require_existing(val) .. "></td>")
|
|
local patname = prefix .. "pat"
|
|
print_dhcp_cond_selector(val, cond_key, val == "_add", patname)
|
|
local t = {"<td><input style='width:99%' name='",
|
|
patname,
|
|
"' value='",
|
|
matcher,
|
|
"' id='",
|
|
patname,}
|
|
if val ~= "_add" then
|
|
local cond = dhcp_tag_validators[cond_key]
|
|
table.insert(t, "' required title='")
|
|
table.insert(t, (cond.hint:gsub("\\\\", "\\")))
|
|
table.insert(t, "' placeholder='")
|
|
table.insert(t, (cond.jsPlaceholder:gsub("\\\\", "\\")))
|
|
if cond.jsPattern ~= nil then
|
|
table.insert(t, "' pattern='")
|
|
table.insert(t, (cond.jsPattern:gsub("\\\\", "\\")))
|
|
end
|
|
end
|
|
table.insert(t, "'></td>")
|
|
html.print(table.concat(t, ''))
|
|
html.print("<td><nobr> <input type=submit name=")
|
|
if val == "_add" then
|
|
html.print("dhcptag_add value=Add title='Add DHCP Tag'")
|
|
else
|
|
html.print(prefix .. "del value='Del ' title=")
|
|
if dhcp_tag_used(name) then
|
|
html.print("'Cannot remove; tag is in use by an option.' disabled='disabled'")
|
|
else
|
|
html.print("'Remove DHCP Tag'")
|
|
end
|
|
end
|
|
html.print("></nobr></td></tr>")
|
|
end
|
|
|
|
print_errors(dhcptag_err)
|
|
|
|
html.print("</table>")
|
|
end
|
|
|
|
local function get_dhcp_tag_names()
|
|
local tag_hash = {}
|
|
local names = {}
|
|
|
|
for val = 1,parms.dhcptags_num
|
|
do
|
|
local name = parms["dhcptag" .. val .. "_name"]
|
|
if name and not tag_hash[name] then
|
|
tag_hash[name] = 0
|
|
table.insert(names, name)
|
|
end
|
|
end
|
|
table.sort(names)
|
|
|
|
return names
|
|
end
|
|
|
|
local function print_dhcp_tag_selector(row, tag_name, all_tags)
|
|
local prefix = "dhcpopt" .. row .. "_"
|
|
local field_name = prefix .. "tag"
|
|
html.print("<td><select name='" .. field_name ..
|
|
"' onchange='allowForceOption(\"" .. prefix ..
|
|
"force\", this.value);' title='Only send this option to clients with this tag'>")
|
|
html.print("<option value=''>[any]</option>")
|
|
for _, name in ipairs(all_tags)
|
|
do
|
|
local sel = name == tag_name and "selected " or ""
|
|
html.print("<option " .. sel .. "value=\"" .. name .. "\">" .. name .. "</option>")
|
|
end
|
|
html.print("</select></td>")
|
|
end
|
|
|
|
local function print_dhcp_option_selector(known_options, row, opt_num)
|
|
local field_name = "dhcpopt" .. row .. "_num"
|
|
-- custom-value option adapted from https://jsfiddle.net/6nq7w/4/
|
|
html.print("<td align=center style='padding:0 4px'><select name=\"" .. field_name .. '"')
|
|
html.print([[ onchange="if(this.options[this.selectedIndex].value=='[numeric]'){
|
|
toggleField(this,this.nextSibling);
|
|
this.selectedIndex='0';
|
|
}">
|
|
<option value="">- DHCP Option Number -</option>
|
|
<option value="[numeric]">[other option number]</option>]])
|
|
local found = false
|
|
for _, option in ipairs(known_options)
|
|
do
|
|
local sel = ""
|
|
local nnum = tonumber(opt_num)
|
|
if option.num == nnum then
|
|
found = true
|
|
sel = "selected "
|
|
elseif nnum and not found and nnum < option.num then -- not a known option
|
|
found = true
|
|
html.print("<option selected value=" .. nnum .. ">" .. nnum .. "</option>")
|
|
end
|
|
html.print("<option " .. sel .. "value=" .. option.num .. ">" .. option.num .. " (" .. option.name .. ")</option>")
|
|
end
|
|
-- do not insert any whitespace between the following two HTML tags
|
|
html.print("</select><input name=\"" .. field_name .. '"')
|
|
html.print([[ type="number" min=1 max=254 style="display:none;" disabled="disabled"
|
|
onblur="if(this.value==''){toggleField(this,this.previousSibling);}"></td>]])
|
|
end
|
|
|
|
-- load all known DHCP option names
|
|
local function load_known_options()
|
|
local known_options = {}
|
|
|
|
local optlist = io.popen("/usr/sbin/dnsmasq --help dhcp")
|
|
if optlist then
|
|
for line in optlist:lines()
|
|
do
|
|
local num, name = line:match("%s*(%d+)%s+(%S+)")
|
|
if name then
|
|
table.insert(known_options, {num = tonumber(num), name = name})
|
|
end
|
|
end
|
|
optlist:close()
|
|
end
|
|
return known_options
|
|
end
|
|
|
|
local function print_dhcp_options()
|
|
html.print("<table cellpadding=0 cellspacing=0><tr><th colspan=5>Advanced DHCP Options</th></tr>")
|
|
print_heading_vsep()
|
|
html.print("<tr><td align=center>For Tag</td><td align=center>Always</td><td align=center>Send DHCP Option</td><td align=center style='width:99%'>With Value</td></tr>")
|
|
|
|
local known_options = load_known_options()
|
|
local all_tags = get_dhcp_tag_names()
|
|
|
|
local list = make_addable_list(parms.dhcpoptions_num)
|
|
for _, val in ipairs(list)
|
|
do
|
|
local prefix = "dhcpopt" .. val .. "_"
|
|
local forceAttr = prefix .. "force"
|
|
local tag = parms[prefix .. "tag"] or ""
|
|
local force = tag == "" and "onrequest" or parms[forceAttr] -- force is unsupported for untagged options
|
|
local opt_num = tonumber(parms[prefix .. "num"])
|
|
local opt_val = parms[prefix .. "val"] or ""
|
|
|
|
print_new_entry_vsep(val, list, 5)
|
|
html.print("<tr>")
|
|
print_dhcp_tag_selector(val, tag, all_tags)
|
|
html.print("<td align=center><input type='checkbox' name='" .. forceAttr .. "' id='" .. forceAttr .. "' value='force' "
|
|
.. (force == "force" and "checked " or "") .. (tag == "" and "disabled " or "")
|
|
.. "title='" .. (tag == "" and FORCE_UNSUPPORTED_HINT or FORCE_SUPPORTED_HINT) .."'/></td>")
|
|
print_dhcp_option_selector(known_options, val, opt_num)
|
|
html.print("<td><input type='text' style='width:99%' name='" .. prefix .. "val' value='" .. opt_val .. "'></td>")
|
|
html.print("<td><nobr> <input type=submit name=")
|
|
if val == "_add" then
|
|
html.print("dhcpopt_add value=Add title='Add DHCP Option'")
|
|
else
|
|
html.print(prefix .. "del value='Del ' title='Remove DHCP Option'")
|
|
end
|
|
html.print("></nobr></td></tr>")
|
|
end
|
|
|
|
print_errors(dhcpopt_err)
|
|
|
|
html.print("</table>")
|
|
end
|
|
|
|
html.print("<tr><td align=center>")
|
|
html.print("<table width=100%><tr><td align=center valign=top>")
|
|
if dmz_mode ~= 0 then
|
|
print_reservations()
|
|
else
|
|
print_forwarding()
|
|
end
|
|
html.print("</td>")
|
|
html.print("<td> </td>")
|
|
html.print("<td align=center valign=top>")
|
|
print_services()
|
|
html.print("</td>")
|
|
html.print("</tr>")
|
|
html.print("<tr><td colspan=3><hr></td></tr>")
|
|
html.print("<tr><td valign=top>")
|
|
print_dhcp_tags()
|
|
html.print("</td><td> </td><td valign=top>")
|
|
print_dhcp_options()
|
|
html.print("</td></tr>")
|
|
html.print("<tr><td colspan=3><hr></td></tr>")
|
|
html.print("<tr><td>")
|
|
if dmz_mode ~= 0 then
|
|
print_forwarding()
|
|
else
|
|
print_reservations()
|
|
end
|
|
html.print("</td>")
|
|
html.print("<td> </td>")
|
|
html.print("<td align=center valign=top>")
|
|
print_aliases()
|
|
html.print("</td></tr>")
|
|
html.print("</table></td></tr>")
|
|
html.print("<tr><td>")
|
|
html.footer()
|
|
html.print(" </td></tr></table>")
|
|
hide("<input type=hidden name=port_num value=" .. parms.port_num .. ">")
|
|
hide("<input type=hidden name=dhcp_num value=" .. parms.dhcp_num .. ">")
|
|
hide("<input type=hidden name=dhcptags_num value=" .. parms.dhcptags_num .. ">")
|
|
hide("<input type=hidden name=dhcpoptions_num value=" .. parms.dhcpoptions_num .. ">")
|
|
hide("<input type=hidden name=serv_num value=" .. parms.serv_num .. ">")
|
|
hide("<input type=hidden name=alias_num value=" .. parms.alias_num .. ">")
|
|
|
|
for _, h in ipairs(hidden)
|
|
do
|
|
html.print(h)
|
|
end
|
|
html.print("</form></center></body></html>")
|
|
http_footer()
|