mirror of https://github.com/aredn/aredn.git
First draft of advanced DHCP option specification on Ports tab. (#1073)
* First draft of advanced DHCP option specification on Ports tab. Allows the node administrator to specify additional DHCP options that will be supplied to LAN clients in specific circumstances. This change adds two tables to the Ports configuration tab. The "Tags for Advanced DHCP Options" table allows the administrator to specify DHCP tags that will be assigned to clients that identify themselves with specific values for properties such as Vendor Class or MAC address. The "Advanced DHCP Options" table allows the administrator to specify arbitrary DHCP options to send to any client, or only to clients with a specific tag. Option numbers can be entered directly or chosen from a list of well-known options. Option values are manually entered by the administrator. In-browser validation is implemented for all input fields with easily recognizable content such as host names, MAC addresses, and port and option numbers. Placeholders are also supplied for input fields, such as MAC addresses with wildcard matching, that might otherwise be difficult to describe. Issues with the current version: - Sending DHCP options not requested by the client is implemented using the dhcp_option_force UCI configuration option, but does not currently work. - Tagging by client host name is supported by dnsmasq, but not yet by UCI. - DHCP option values must be entered manually by the administrator, but are not currently validated. * Better validation, placeholders, and hints for existing input fields. * Remove junk accidentally inserted in comment. * Preserve Advanced DHCP options across updates.
This commit is contained in:
parent
8340f18116
commit
4e35b2f0c6
|
@ -3,6 +3,10 @@
|
||||||
/etc/config.mesh/_setup
|
/etc/config.mesh/_setup
|
||||||
/etc/config.mesh/_setup.dhcp.dmz
|
/etc/config.mesh/_setup.dhcp.dmz
|
||||||
/etc/config.mesh/_setup.dhcp.nat
|
/etc/config.mesh/_setup.dhcp.nat
|
||||||
|
/etc/config.mesh/_setup.dhcptags.dmz
|
||||||
|
/etc/config.mesh/_setup.dhcptags.nat
|
||||||
|
/etc/config.mesh/_setup.dhcpoptions.dmz
|
||||||
|
/etc/config.mesh/_setup.dhcpoptions.nat
|
||||||
/etc/config.mesh/_setup.ports.dmz
|
/etc/config.mesh/_setup.ports.dmz
|
||||||
/etc/config.mesh/_setup.ports.nat
|
/etc/config.mesh/_setup.ports.nat
|
||||||
/etc/config.mesh/_setup.services.dmz
|
/etc/config.mesh/_setup.services.dmz
|
||||||
|
|
|
@ -252,16 +252,22 @@ end
|
||||||
-- select ports and dhcp files based on mode
|
-- select ports and dhcp files based on mode
|
||||||
local portfile = "/etc/config.mesh/_setup.ports"
|
local portfile = "/etc/config.mesh/_setup.ports"
|
||||||
local dhcpfile = "/etc/config.mesh/_setup.dhcp"
|
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 aliasfile = "/etc/config.mesh/aliases"
|
||||||
local servfile = "/etc/config.mesh/_setup.services"
|
local servfile = "/etc/config.mesh/_setup.services"
|
||||||
if is_null(cfg.dmz_mode) then
|
if is_null(cfg.dmz_mode) then
|
||||||
portfile = portfile .. ".nat"
|
portfile = portfile .. ".nat"
|
||||||
dhcpfile = dhcpfile .. ".nat"
|
dhcpfile = dhcpfile .. ".nat"
|
||||||
|
dhcptagsfile = dhcptagsfile .. ".nat"
|
||||||
|
dhcpoptionsfile = dhcpoptionsfile .. ".nat"
|
||||||
aliasfile = aliasfile .. ".nat"
|
aliasfile = aliasfile .. ".nat"
|
||||||
servfile = servfile .. ".nat"
|
servfile = servfile .. ".nat"
|
||||||
else
|
else
|
||||||
portfile = portfile .. ".dmz"
|
portfile = portfile .. ".dmz"
|
||||||
dhcpfile = dhcpfile .. ".dmz"
|
dhcpfile = dhcpfile .. ".dmz"
|
||||||
|
dhcptagsfile = dhcptagsfile .. ".dmz"
|
||||||
|
dhcpoptionsfile = dhcpoptionsfile .. ".dmz"
|
||||||
aliasfile = aliasfile .. ".dmz"
|
aliasfile = aliasfile .. ".dmz"
|
||||||
servfile = servfile .. ".dmz"
|
servfile = servfile .. ".dmz"
|
||||||
end
|
end
|
||||||
|
@ -632,21 +638,89 @@ if add_masq then
|
||||||
end
|
end
|
||||||
|
|
||||||
-- setup node lan dhcp
|
-- setup node lan dhcp
|
||||||
if nc:get("aredn", "@wan[0]", "lan_dhcp_route") == "1" or nc:get("aredn", "@wan[0]", "lan_dhcp_defaultroute") == "1" then
|
function load_dhcp_tags(dhcptagsfile)
|
||||||
-- Provide stateless routes and default route
|
local dhcp_tags = {}
|
||||||
nc:set("dhcp", "@dhcp[0]", "dhcp_option", {
|
|
||||||
"121,10.0.0.0/8," .. cfg.lan_ip .. ",0.0.0.0/0," .. cfg.lan_ip,
|
for line in io.lines(dhcptagsfile)
|
||||||
"249,10.0.0.0/8," .. cfg.lan_ip .. ",0.0.0.0/0," .. cfg.lan_ip
|
do
|
||||||
})
|
if not (line:match("^%s*#") or line:match("^%s*$")) then
|
||||||
else
|
local name, condition, pattern = line:match("(%S+)%s+(%S+)%s+(.*)")
|
||||||
-- Provide stateless routes to the mesh, and a blank default route (option 3 has no values) to
|
if pattern then
|
||||||
-- surpress default route being sent
|
local cond_table = dhcp_tags[condition]
|
||||||
nc:set("dhcp", "@dhcp[0]", "dhcp_option", {
|
if not cond_table then
|
||||||
"121,10.0.0.0/8," .. cfg.lan_ip,
|
cond_table = {}
|
||||||
"249,10.0.0.0/8," .. cfg.lan_ip,
|
dhcp_tags[condition] = cond_table
|
||||||
"3"
|
end
|
||||||
})
|
table.insert(cond_table, {name = name, pattern = pattern})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return dhcp_tags
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function load_dhcp_options(dhcpoptionsfile)
|
||||||
|
local dhcp_options = {}
|
||||||
|
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
|
||||||
|
table.insert(dhcp_options, {
|
||||||
|
tag = tag,
|
||||||
|
force = force == "force",
|
||||||
|
num = opt_num,
|
||||||
|
val = opt_val})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return dhcp_options
|
||||||
|
end
|
||||||
|
|
||||||
|
if nixio.fs.access(dhcptagsfile) then
|
||||||
|
for condition, cond_table in pairs(load_dhcp_tags(dhcptagsfile))
|
||||||
|
do
|
||||||
|
for i, props in ipairs(cond_table)
|
||||||
|
do
|
||||||
|
nc:add("dhcp", condition)
|
||||||
|
nc:set("dhcp", string.format("@%s[%d]", condition, i-1), "networkid", props.name)
|
||||||
|
nc:set("dhcp", string.format("@%s[%d]", condition, i-1), condition, props.pattern)
|
||||||
|
end
|
||||||
|
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
|
||||||
|
if nixio.fs.access(dhcpoptionsfile) then
|
||||||
|
local forced_advanced_options = {}
|
||||||
|
for _, option in ipairs(load_dhcp_options(dhcpoptionsfile))
|
||||||
|
do
|
||||||
|
local parts = {}
|
||||||
|
if option.tag ~= "" then
|
||||||
|
table.insert(parts, "tag:" .. option.tag)
|
||||||
|
end
|
||||||
|
table.insert(parts, option.num)
|
||||||
|
table.insert(parts, option.val)
|
||||||
|
table.insert(option.force and forced_advanced_options or dhcp_option_list, table.concat(parts, ","))
|
||||||
|
end
|
||||||
|
if #forced_advanced_options > 0 then
|
||||||
|
nc:set("dhcp", "@dhcp[0]", "dhcp_option_force", forced_advanced_options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #dhcp_option_list > 0 then
|
||||||
|
nc:set("dhcp", "@dhcp[0]", "dhcp_option", dhcp_option_list)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
nc:commit("dhcp")
|
nc:commit("dhcp")
|
||||||
|
|
||||||
-- generate the wireless config file
|
-- generate the wireless config file
|
||||||
|
|
|
@ -97,6 +97,67 @@ function validate_service_suffix(suffix)
|
||||||
end
|
end
|
||||||
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 host names with optional trailing wildcard
|
||||||
|
local function validate_host_matcher(matcher)
|
||||||
|
return matcher:match("^%w[%w.-]*%*?$")
|
||||||
|
end
|
||||||
|
|
||||||
|
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 = "name",
|
||||||
|
name = "Host Name",
|
||||||
|
validator = validate_host_matcher,
|
||||||
|
hint = "alphanumerics, hyphens, and dots, with an optional trailing asterisk",
|
||||||
|
jsPattern = [[^[a-zA-Z0-9][a-zA-Z0-9.\\-]*\\*?$]],
|
||||||
|
jsPlaceholder = "client.host-name*",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
function html_safe(s)
|
||||||
|
return s:gsub("&", "&"):gsub(">", ">"):gsub("<", "<")
|
||||||
|
end
|
||||||
|
|
||||||
local serv_err = {}
|
local serv_err = {}
|
||||||
function serverr(msg)
|
function serverr(msg)
|
||||||
serv_err[#serv_err + 1] = msg:gsub(">", ">"):gsub("<", "<")
|
serv_err[#serv_err + 1] = msg:gsub(">", ">"):gsub("<", "<")
|
||||||
|
@ -114,6 +175,14 @@ local dhcp_err = {}
|
||||||
function dhcperr(msg)
|
function dhcperr(msg)
|
||||||
dhcp_err[#dhcp_err + 1] = msg
|
dhcp_err[#dhcp_err + 1] = msg
|
||||||
end
|
end
|
||||||
|
local dhcptag_err = {}
|
||||||
|
function dhcptagerr(msg)
|
||||||
|
dhcptag_err[#dhcptag_err + 1] = msg
|
||||||
|
end
|
||||||
|
local dhcpopt_err = {}
|
||||||
|
function dhcpopterr(msg)
|
||||||
|
dhcpopt_err[#dhcpopt_err + 1] = msg
|
||||||
|
end
|
||||||
local alias_err = {}
|
local alias_err = {}
|
||||||
function aliaserr(msg)
|
function aliaserr(msg)
|
||||||
alias_err[#alias_err + 1] = msg
|
alias_err[#alias_err + 1] = msg
|
||||||
|
@ -169,6 +238,8 @@ nixio.fs.mkdir(tmpdir)
|
||||||
local fsuffix = dmz_mode == 0 and ".nat" or ".dmz"
|
local fsuffix = dmz_mode == 0 and ".nat" or ".dmz"
|
||||||
local portfile = "/etc/config.mesh/_setup.ports" .. fsuffix
|
local portfile = "/etc/config.mesh/_setup.ports" .. fsuffix
|
||||||
local dhcpfile = "/etc/config.mesh/_setup.dhcp" .. 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 servfile = "/etc/config.mesh/_setup.services" .. fsuffix
|
||||||
local aliasfile = "/etc/config.mesh/aliases" .. fsuffix
|
local aliasfile = "/etc/config.mesh/aliases" .. fsuffix
|
||||||
|
|
||||||
|
@ -226,6 +297,45 @@ if parms.button_reset or not parms.reload then
|
||||||
end
|
end
|
||||||
parms.dhcp_num = i
|
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
|
-- services
|
||||||
i = 0
|
i = 0
|
||||||
if nixio.fs.stat(servfile) then
|
if nixio.fs.stat(servfile) then
|
||||||
|
@ -270,7 +380,7 @@ if parms.button_reset or not parms.reload then
|
||||||
end
|
end
|
||||||
parms.alias_num = i
|
parms.alias_num = i
|
||||||
|
|
||||||
-- sanatize the 'add' values
|
-- sanitize the 'add' values
|
||||||
parms.port_add_intf = dmz_mode ~= 0 and "wan" or "wifi"
|
parms.port_add_intf = dmz_mode ~= 0 and "wan" or "wifi"
|
||||||
parms.port_add_type = "tcp"
|
parms.port_add_type = "tcp"
|
||||||
if not parms.dmz_ip then
|
if not parms.dmz_ip then
|
||||||
|
@ -283,6 +393,12 @@ if parms.button_reset or not parms.reload then
|
||||||
parms.dhcp_add_ip = ""
|
parms.dhcp_add_ip = ""
|
||||||
parms.dhcp_add_mac = ""
|
parms.dhcp_add_mac = ""
|
||||||
parms.dhcp_add_noprop = ""
|
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_name = ""
|
||||||
parms.serv_add_proto = ""
|
parms.serv_add_proto = ""
|
||||||
parms.serv_add_host = ""
|
parms.serv_add_host = ""
|
||||||
|
@ -298,12 +414,17 @@ local dhcp_end = dhcp_start + dhcp_limit - 1
|
||||||
|
|
||||||
-- load and validate the ports
|
-- load and validate the ports
|
||||||
|
|
||||||
local list = {}
|
function make_addable_list(max_row)
|
||||||
for i = 1,parms.port_num
|
local list = {}
|
||||||
do
|
for i = 1,max_row
|
||||||
list[#list + 1] = i
|
do
|
||||||
|
list[#list + 1] = i
|
||||||
|
end
|
||||||
|
list[#list + 1] = "_add"
|
||||||
|
return list
|
||||||
end
|
end
|
||||||
list[#list + 1] = "_add"
|
|
||||||
|
local list = make_addable_list(parms.port_num)
|
||||||
local port_num = 0
|
local port_num = 0
|
||||||
local usedports = {}
|
local usedports = {}
|
||||||
|
|
||||||
|
@ -415,12 +536,7 @@ parms.port_num = port_num
|
||||||
|
|
||||||
-- load and validate the dhcp reservations
|
-- load and validate the dhcp reservations
|
||||||
|
|
||||||
local list = {}
|
local list = make_addable_list(parms.dhcp_num)
|
||||||
for i = 1,parms.dhcp_num
|
|
||||||
do
|
|
||||||
list[#list + 1] = i
|
|
||||||
end
|
|
||||||
list[#list + 1] = "_add"
|
|
||||||
local dhcp_num = 0
|
local dhcp_num = 0
|
||||||
|
|
||||||
for _, val in ipairs(list)
|
for _, val in ipairs(list)
|
||||||
|
@ -501,7 +617,7 @@ do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if mac:match("[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]") then
|
if mac:match(mac_pattern) then
|
||||||
if macs[mac] then
|
if macs[mac] then
|
||||||
dhcperr(val .. " MAC " .. mac .. " is already in use")
|
dhcperr(val .. " MAC " .. mac .. " is already in use")
|
||||||
end
|
end
|
||||||
|
@ -613,13 +729,162 @@ if f then
|
||||||
f:close()
|
f:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- aliases
|
function write_temp_records(filename, group_name, record_count, fields)
|
||||||
local list = {}
|
local f = io.open(tmpdir .. "/" .. filename, "w")
|
||||||
for i = 1,parms.alias_num
|
if f then
|
||||||
do
|
for i = 1,record_count
|
||||||
list[#list + 1] = i
|
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
|
end
|
||||||
list[#list + 1] = "_add"
|
|
||||||
|
-- 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([[%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))
|
||||||
|
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
|
local alias_num = 0
|
||||||
|
|
||||||
for _, val in ipairs(list)
|
for _, val in ipairs(list)
|
||||||
|
@ -677,23 +942,11 @@ do
|
||||||
end
|
end
|
||||||
|
|
||||||
-- write to temp file
|
-- write to temp file
|
||||||
local f = io.open(tmpdir .. "/aliases", "w")
|
write_temp_records("aliases", "alias", alias_num, {"ip", "host"})
|
||||||
if f then
|
|
||||||
for i = 1,alias_num
|
|
||||||
do
|
|
||||||
f:write(parms["alias" .. i .. "_ip"] .. " " .. parms["alias" .. i .. "_host"] .. "\n")
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
parms.alias_num = alias_num
|
parms.alias_num = alias_num
|
||||||
|
|
||||||
-- load and validate services
|
-- load and validate services
|
||||||
local list = {}
|
local list = make_addable_list(parms.serv_num)
|
||||||
for i = 1,parms.serv_num
|
|
||||||
do
|
|
||||||
list[#list + 1] = i
|
|
||||||
end
|
|
||||||
list[#list + 1] = "_add"
|
|
||||||
local serv_num = 0
|
local serv_num = 0
|
||||||
hosts[""] = true
|
hosts[""] = true
|
||||||
hosts[node] = true
|
hosts[node] = true
|
||||||
|
@ -806,9 +1059,11 @@ end
|
||||||
parms.serv_num = serv_num
|
parms.serv_num = serv_num
|
||||||
|
|
||||||
-- save configuration
|
-- save configuration
|
||||||
if parms.button_save and not (#port_err > 0 or #dhcp_err > 0 or #dmz_err > 0 or #serv_err > 0 or #alias_err > 0) then
|
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 .. "/ports", portfile)
|
||||||
filecopy(tmpdir .. "/dhcp", dhcpfile)
|
filecopy(tmpdir .. "/dhcp", dhcpfile)
|
||||||
|
filecopy(tmpdir .. "/dhcptags", dhcptagsfile)
|
||||||
|
filecopy(tmpdir .. "/dhcpoptions", dhcpoptionsfile)
|
||||||
filecopy(tmpdir .. "/services", servfile)
|
filecopy(tmpdir .. "/services", servfile)
|
||||||
filecopy(tmpdir .. "/aliases", aliasfile)
|
filecopy(tmpdir .. "/aliases", aliasfile)
|
||||||
|
|
||||||
|
@ -824,7 +1079,70 @@ end
|
||||||
-- generate the page
|
-- generate the page
|
||||||
|
|
||||||
http_header()
|
http_header()
|
||||||
html.header(node .. " setup", true)
|
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) {
|
||||||
|
console.log(`setOrRemoveAttribute(${elem.name},${attrName},${value})`)
|
||||||
|
if (value == null)
|
||||||
|
elem.removeAttribute(attrName);
|
||||||
|
else
|
||||||
|
elem.setAttribute(attrName, value)
|
||||||
|
}
|
||||||
|
function setMatcherValidator(inputId, matcherType) {
|
||||||
|
console.log(`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.print("<body><center>")
|
||||||
html.alert_banner()
|
html.alert_banner()
|
||||||
html.print("<form method=post action=/cgi-bin/ports enctype='multipart/form-data'>")
|
html.print("<form method=post action=/cgi-bin/ports enctype='multipart/form-data'>")
|
||||||
|
@ -839,12 +1157,12 @@ html.print([[<tr><td align=center>
|
||||||
<input type=submit name=button_reset value='Reset Values' title='Revert to the last saved settings'>
|
<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();'>
|
<input type=button name=button_refresh value='Refresh' title='Refresh this page' onclick='window.location.reload();'>
|
||||||
<tr><td> </td></tr>]])
|
<tr><td> </td></tr>]])
|
||||||
hide("<input type=hidden name=reload value=1></td></tr>")
|
hide("<input type=hidden name=reload value=1>")
|
||||||
|
|
||||||
-- messages
|
-- messages
|
||||||
|
|
||||||
if parms.button_save then
|
if parms.button_save then
|
||||||
if #port_err > 0 or #dhcp_err > 0 or #dmz_err > 0 or #serv_err > 0 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>")
|
html.print("<tr><td align=center><b>Configuration NOT saved!</b></td></tr>")
|
||||||
elseif #errors > 0 then
|
elseif #errors > 0 then
|
||||||
html.print("<tr><td align=center><b>Configuration saved, however:<br>")
|
html.print("<tr><td align=center><b>Configuration saved, however:<br>")
|
||||||
|
@ -861,9 +1179,35 @@ end
|
||||||
|
|
||||||
-- everything else
|
-- 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_port_range_pattern = [[ pattern='^\s*\d{1,5}(?:\s*-\s*\d{1,5})?\s*$' ]]
|
||||||
|
|
||||||
|
function print_heading_vsep()
|
||||||
|
html.print("<tr><td colspan=4 height=5></td></tr>")
|
||||||
|
end
|
||||||
|
|
||||||
|
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"
|
||||||
|
function require_existing(val)
|
||||||
|
return val == "_add" and "" or " required "
|
||||||
|
end
|
||||||
|
|
||||||
|
function print_errors(error_list)
|
||||||
|
for _, e in ipairs(error_list)
|
||||||
|
do
|
||||||
|
html.print("<tr><th colspan=8>" .. e .. "</th></tr>")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function print_reservations()
|
function print_reservations()
|
||||||
html.print("<table cellpadding=0 cellspacing=0><tr><th colspan=4>DHCP Address Reservations</th></tr>")
|
html.print("<table cellpadding=0 cellspacing=0><tr><th colspan=4>DHCP Address Reservations</th></tr>")
|
||||||
html.print("<tr><td colspan=4 height=5></td></tr>")
|
print_heading_vsep()
|
||||||
html.print("<tr><td align=center>Hostname</td><td align=center>IP Address</td><td align=center>MAC Address</td>")
|
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
|
if dmz_mode ~= 0 then
|
||||||
|
@ -871,14 +1215,9 @@ function print_reservations()
|
||||||
else
|
else
|
||||||
html.print("<td></td><td></td></tr>")
|
html.print("<td></td><td></td></tr>")
|
||||||
end
|
end
|
||||||
html.print("<tr><td colspan=4 height=5></td></tr>")
|
print_heading_vsep()
|
||||||
|
|
||||||
local list = {}
|
local list = make_addable_list(parms.dhcp_num)
|
||||||
for i = 1,parms.dhcp_num
|
|
||||||
do
|
|
||||||
list[#list + 1] = i
|
|
||||||
end
|
|
||||||
list[#list + 1] = "_add"
|
|
||||||
|
|
||||||
local mac_list = {}
|
local mac_list = {}
|
||||||
for _, val in ipairs(list)
|
for _, val in ipairs(list)
|
||||||
|
@ -889,13 +1228,11 @@ function print_reservations()
|
||||||
local noprop = parms["dhcp" .. val .. "_noprop"]
|
local noprop = parms["dhcp" .. val .. "_noprop"]
|
||||||
mac_list[mac] = true
|
mac_list[mac] = true
|
||||||
|
|
||||||
if val == "_add" and #list > 1 then
|
print_new_entry_vsep(val, list, 5)
|
||||||
html.print("<tr><td colspan=4 height=10></td></tr>")
|
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>")
|
||||||
end
|
|
||||||
html.print("<tr><td><input style='width=180;' type=text name=dhcp" .. val .. "_host value='" .. host .. "'></td>")
|
|
||||||
html.print("<td align=center style='padding:0 4px'><select style='width:130px' name=dhcp" .. val .. "_ip>")
|
html.print("<td align=center style='padding:0 4px'><select style='width:130px' name=dhcp" .. val .. "_ip>")
|
||||||
if val == "_add" then
|
if val == "_add" then
|
||||||
html.print("<option value=''>- IP Address -</option>\n")
|
html.print("<option value=''>- IP Address -</option>\n")
|
||||||
end
|
end
|
||||||
for i = dhcp_start,dhcp_end
|
for i = dhcp_start,dhcp_end
|
||||||
do
|
do
|
||||||
|
@ -909,13 +1246,10 @@ function print_reservations()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
html.print("</select></td>")
|
html.print("</select></td>")
|
||||||
html.print("<td><input style='width:120;' type=text name=dhcp" .. val .. "_mac value='" .. mac .. "'></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
|
if dmz_mode ~= 0 then
|
||||||
if noprop == "#NOPROP" then
|
html.print("<td align=center><input type=checkbox id=dhcp" .. val .. "_noprop name=dhcp" .. val .. "_noprop value='#NOPROP'"
|
||||||
html.print("<td align=center><input type=checkbox id=dhcp" .. val .. "_noprop name=dhcp" .. val .. "_noprop value='#NOPROP' checked></td>")
|
.. (noprop == "#NOPROP" and " checked" or "") .. "></td>")
|
||||||
else
|
|
||||||
html.print("<td align=center><input type=checkbox id=dhcp" .. val .. "_noprop name=dhcp" .. val .. "_noprop value='#NOPROP'></td>")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
html.print("<td></td>")
|
html.print("<td></td>")
|
||||||
end
|
end
|
||||||
|
@ -973,12 +1307,7 @@ function print_forwarding()
|
||||||
html.print("<td align=center>LAN Port</td><td> </td></tr>")
|
html.print("<td align=center>LAN Port</td><td> </td></tr>")
|
||||||
html.print("<tr><td colspan=6 height=5></td></tr>")
|
html.print("<tr><td colspan=6 height=5></td></tr>")
|
||||||
|
|
||||||
local list = {}
|
local list = make_addable_list(parms.port_num)
|
||||||
for i = 1,parms.port_num
|
|
||||||
do
|
|
||||||
list[#list + 1] = i
|
|
||||||
end
|
|
||||||
list[#list + 1] = "_add"
|
|
||||||
local vars = { "_intf", "_type", "_out", "_ip", "_in", "_enable", "_adv", "_link", "_proto", "_suffix", "_name" }
|
local vars = { "_intf", "_type", "_out", "_ip", "_in", "_enable", "_adv", "_link", "_proto", "_suffix", "_name" }
|
||||||
for _, val in ipairs(list)
|
for _, val in ipairs(list)
|
||||||
do
|
do
|
||||||
|
@ -987,9 +1316,7 @@ function print_forwarding()
|
||||||
_G[var] = parms["port" .. val .. var]
|
_G[var] = parms["port" .. val .. var]
|
||||||
end
|
end
|
||||||
|
|
||||||
if val == "_add" and #list > 1 then
|
print_new_entry_vsep(val, list, 6)
|
||||||
html.print("<tr><td colspan=6 height=10></td></tr>")
|
|
||||||
end
|
|
||||||
|
|
||||||
html.print("<tr>")
|
html.print("<tr>")
|
||||||
hide("<input style='width:90;' type=hidden name=port" .. val .. "_enable value=1>")
|
hide("<input style='width:90;' type=hidden name=port" .. val .. "_enable value=1>")
|
||||||
|
@ -1011,7 +1338,8 @@ function print_forwarding()
|
||||||
html.print("<option " .. (_type == "both" and "selected" or "") .. " value='both'>Both</option>")
|
html.print("<option " .. (_type == "both" and "selected" or "") .. " value='both'>Both</option>")
|
||||||
html.print("</select></td>")
|
html.print("</select></td>")
|
||||||
|
|
||||||
html.print("<td align=center valign=top><input style='width:90;' type=text name=port" .. val .. "_out value='" .. _out .. "'></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>")
|
html.print("<td align=center valign=top><select name=port" .. val .. "_ip>")
|
||||||
if val == "_add" then
|
if val == "_add" then
|
||||||
html.print("<option value=''>- IP Address -</option>")
|
html.print("<option value=''>- IP Address -</option>")
|
||||||
|
@ -1028,7 +1356,8 @@ function print_forwarding()
|
||||||
end
|
end
|
||||||
html.print("</select></td>")
|
html.print("</select></td>")
|
||||||
|
|
||||||
html.print("<td align=left valign=top><input style='width:90;' type=text name=port" .. val .. "_in value='" .. _in .. "'></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=")
|
html.print("<td><nobr> <input type=submit name=")
|
||||||
|
|
||||||
if val == "_add" then
|
if val == "_add" then
|
||||||
|
@ -1070,10 +1399,7 @@ function print_forwarding()
|
||||||
end
|
end
|
||||||
html.print("</select></td>")
|
html.print("</select></td>")
|
||||||
|
|
||||||
for _, e in ipairs(dmz_err)
|
print_errors(dmz_err)
|
||||||
do
|
|
||||||
html.print("<tr><th colspan=8>" .. e .. "</th></tr>")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
html.print("</table>")
|
html.print("</table>")
|
||||||
end
|
end
|
||||||
|
@ -1110,12 +1436,7 @@ function print_services()
|
||||||
html.print("<tr><td>Name</td><td>Link</td><td>URL</td><td><br><br></td></tr>")
|
html.print("<tr><td>Name</td><td>Link</td><td>URL</td><td><br><br></td></tr>")
|
||||||
end
|
end
|
||||||
|
|
||||||
local list = {}
|
local list = make_addable_list(parms.serv_num)
|
||||||
for i = 1,parms.serv_num
|
|
||||||
do
|
|
||||||
list[#list + 1] = i
|
|
||||||
end
|
|
||||||
list[#list + 1] = "_add"
|
|
||||||
|
|
||||||
local vars = { "_name", "_link", "_proto", "_host", "_port", "_suffix" }
|
local vars = { "_name", "_link", "_proto", "_host", "_port", "_suffix" }
|
||||||
for _, val in ipairs(list)
|
for _, val in ipairs(list)
|
||||||
|
@ -1129,11 +1450,9 @@ function print_services()
|
||||||
parms["serv" .. val .. "_host"] = node
|
parms["serv" .. val .. "_host"] = node
|
||||||
end
|
end
|
||||||
|
|
||||||
if val == "_add" and #list > 1 then
|
print_new_entry_vsep(val, list, 4)
|
||||||
html.print("<tr><td colspan=4 height=10></td></tr>")
|
|
||||||
end
|
|
||||||
html.print("<tr>")
|
html.print("<tr>")
|
||||||
html.print("<td><input type=text style='width:120;' name=serv" .. val .. "_name value='" .. _name .. "' title='what to call this service'></td>")
|
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")
|
html.print("<td><nobr><input type=checkbox name=serv" .. val .. "_link value=1")
|
||||||
if val ~= "_add" then
|
if val ~= "_add" then
|
||||||
|
@ -1168,14 +1487,14 @@ function print_services()
|
||||||
end
|
end
|
||||||
html.print("</select>")
|
html.print("</select>")
|
||||||
else
|
else
|
||||||
html.print("<td><nobr><b>:</b>//<small>" .. _host .. "</small>")
|
html.print("<td style='width:99%'><nobr><b>:</b>//<small>" .. _host .. "</small>")
|
||||||
end
|
end
|
||||||
|
|
||||||
html.print("<b>:</b><input type=text style='width:40;' name=serv" .. val .. "_port value='" .. _port .. "' title='port number'")
|
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
|
if val ~= "_add" and _link ~= "1" then
|
||||||
html.print(" disabled")
|
html.print(" disabled")
|
||||||
end
|
end
|
||||||
html.print("> / <input type=text style='width:80;' name=serv" .. val .. "_suffix value='" .. _suffix .. "' ")
|
html.print("> / <input type=text name=serv" .. val .. "_suffix value='" .. _suffix .. "' ")
|
||||||
html.print("title='leave blank unless the URL needs a more specific path'")
|
html.print("title='leave blank unless the URL needs a more specific path'")
|
||||||
if val ~= "_add" and _link ~= "1" then
|
if val ~= "_add" and _link ~= "1" then
|
||||||
html.print(" disabled")
|
html.print(" disabled")
|
||||||
|
@ -1219,21 +1538,14 @@ function print_aliases()
|
||||||
html.print("<tr><td align=center>Alias Name</td><td></td><td align=center>IP Address</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>")
|
html.print("<tr><td colspan=3 height=5></td></tr>")
|
||||||
|
|
||||||
local list = {}
|
local list = make_addable_list(parms.alias_num)
|
||||||
for i = 1,parms.alias_num
|
|
||||||
do
|
|
||||||
list[#list + 1] = i
|
|
||||||
end
|
|
||||||
list[#list + 1] = "_add"
|
|
||||||
|
|
||||||
for _, val in ipairs(list)
|
for _, val in ipairs(list)
|
||||||
do
|
do
|
||||||
local host = parms["alias" .. val .. "_host"]
|
local host = parms["alias" .. val .. "_host"]
|
||||||
local ip = parms["alias" .. val .. "_ip"]
|
local ip = parms["alias" .. val .. "_ip"]
|
||||||
if val == "_add" and #list > 1 then
|
print_new_entry_vsep(val, list , 4)
|
||||||
html.print("<tr><td colspan=3 height=10></td></tr>\n")
|
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_host_pattern .. require_existing(val) .. "></td>")
|
||||||
end
|
|
||||||
html.print("<tr><td align=center><input type=text name=alias" .. val .. "_host value='" .. host .. "' size=20></td>")
|
|
||||||
html.print("<td> </td>")
|
html.print("<td> </td>")
|
||||||
html.print("<td align=center><select name=alias" .. val .. "_ip>")
|
html.print("<td align=center><select name=alias" .. val .. "_ip>")
|
||||||
if val == "_add" then
|
if val == "_add" then
|
||||||
|
@ -1266,6 +1578,197 @@ function print_aliases()
|
||||||
html.print("</table>")
|
html.print("</table>")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
table.insert(t, "' placeholder='")
|
||||||
|
table.insert(t, cond.jsPlaceholder)
|
||||||
|
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
|
||||||
|
|
||||||
|
function print_dhcp_tag_selector(row, tag_name)
|
||||||
|
local field_name = "dhcpopt" .. row .. "_tag"
|
||||||
|
html.print("<td><select name='" .. field_name .. "' title='Only send this option to clients with this tag'>")
|
||||||
|
|
||||||
|
html.print("<option value=''>[any]</option>")
|
||||||
|
for val = 1,parms.dhcptags_num
|
||||||
|
do
|
||||||
|
local name = parms["dhcptag" .. val .. "_name"] or ""
|
||||||
|
local sel = ""
|
||||||
|
if name ~= "" and name == tag_name then
|
||||||
|
sel = "selected "
|
||||||
|
end
|
||||||
|
html.print("<option " .. sel .. "value=\"" .. name .. "\">" .. name .. "</option>")
|
||||||
|
end
|
||||||
|
html.print("</select></td>")
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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 list = make_addable_list(parms.dhcpoptions_num)
|
||||||
|
for _, val in ipairs(list)
|
||||||
|
do
|
||||||
|
local prefix = "dhcpopt" .. val .. "_"
|
||||||
|
local tag = parms[prefix .. "tag"] or ""
|
||||||
|
local force = parms[prefix .. "force"]
|
||||||
|
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)
|
||||||
|
html.print("<td align=center><input type='checkbox' name='" .. prefix .. "force' value='force' "
|
||||||
|
.. (force == "force" and "checked " or "")
|
||||||
|
.. "title='Send option even when not requested by client'/></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("<tr><td align=center>")
|
||||||
html.print("<table width=100%><tr><td align=center valign=top>")
|
html.print("<table width=100%><tr><td align=center valign=top>")
|
||||||
if dmz_mode ~= 0 then
|
if dmz_mode ~= 0 then
|
||||||
|
@ -1280,7 +1783,13 @@ print_services()
|
||||||
html.print("</td>")
|
html.print("</td>")
|
||||||
html.print("</tr>")
|
html.print("</tr>")
|
||||||
html.print("<tr><td colspan=3><hr></td></tr>")
|
html.print("<tr><td colspan=3><hr></td></tr>")
|
||||||
html.print("<tr><td align=center valign=top>")
|
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
|
if dmz_mode ~= 0 then
|
||||||
print_forwarding()
|
print_forwarding()
|
||||||
else
|
else
|
||||||
|
@ -1291,10 +1800,14 @@ html.print("<td> </td>")
|
||||||
html.print("<td align=center valign=top>")
|
html.print("<td align=center valign=top>")
|
||||||
print_aliases()
|
print_aliases()
|
||||||
html.print("</td></tr>")
|
html.print("</td></tr>")
|
||||||
html.print("</table>")
|
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=port_num value=" .. parms.port_num .. ">")
|
||||||
hide("<input type=hidden name=dhcp_num value=" .. parms.dhcp_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=serv_num value=" .. parms.serv_num .. ">")
|
||||||
hide("<input type=hidden name=alias_num value=" .. parms.alias_num .. ">")
|
hide("<input type=hidden name=alias_num value=" .. parms.alias_num .. ">")
|
||||||
|
|
||||||
|
@ -1302,10 +1815,5 @@ for _, h in ipairs(hidden)
|
||||||
do
|
do
|
||||||
html.print(h)
|
html.print(h)
|
||||||
end
|
end
|
||||||
|
html.print("</form></center></body></html>")
|
||||||
html.print("<tr><td>")
|
|
||||||
html.footer()
|
|
||||||
html.print(" </td></tr>")
|
|
||||||
html.print("</form></center></table>")
|
|
||||||
html.print("</body></html>")
|
|
||||||
http_footer()
|
http_footer()
|
||||||
|
|
Loading…
Reference in New Issue