msgstr ""
msgid ""
-"Size of the download queue to handle downloads & list processing in parallel "
+"Size of the download queue to handle downloads & list processing in parallel "
"(default '4').<br />"
msgstr ""
"処理エラーまたはドメイン カウントが0以下の場合、メールを送信します。<br />"
msgid ""
-"Size of the download queue to handle downloads & list processing in parallel "
+"Size of the download queue to handle downloads & list processing in parallel "
"(default '4').<br />"
msgstr ""
"ダウンロードの制御とリストの処理を同時並行的に行うダウンロード キューのサイズ"
msgstr ""
msgid ""
-"Size of the download queue to handle downloads & list processing in parallel "
+"Size of the download queue to handle downloads & list processing in parallel "
"(default '4').<br />"
msgstr ""
"≤ 0.<br />"
msgid ""
-"Size of the download queue to handle downloads & list processing in parallel "
+"Size of the download queue to handle downloads & list processing in parallel "
"(default '4').<br />"
msgstr ""
"Значение очереди загрузки для выполнения параллельных загрузок (по умолчанию "
msgstr ""
msgid ""
-"Size of the download queue to handle downloads & list processing in parallel "
+"Size of the download queue to handle downloads & list processing in parallel "
"(default '4').<br />"
msgstr ""
msgstr ""
msgid ""
-"Size of the download queue to handle downloads & list processing in parallel "
+"Size of the download queue to handle downloads & list processing in parallel "
"(default '4').<br />"
msgstr ""
msgstr ""
msgid ""
-"Size of the download queue to handle downloads & list processing in parallel "
+"Size of the download queue to handle downloads & list processing in parallel "
"(default '4').<br />"
msgstr ""
msgstr ""
msgid ""
-"Size of the download queue to handle downloads & list processing in parallel "
+"Size of the download queue to handle downloads & list processing in parallel "
"(default '4').<br />"
msgstr ""
rm -rf /tmp/luci-indexcache /tmp/luci-modulecache/
/etc/init.d/uhttpd restart
+/etc/init.d/rpcd reload
return 0
}
}
} else {
- error_box("<b>Ubus call faild:</b></br>Request: " + request_json + "</br>Response: " + JSON.stringify(response))
+ error_box("<b>Ubus call faild:</b><br />Request: " + request_json + "<br />Response: " + JSON.stringify(response))
}
ubus_closed++;
}
if(request_json.upgrades != undefined) {
info_output += "<h3>Package upgrades available</h3>"
for (upgrade in request_json.upgrades) {
- info_output += "<b>" + upgrade + "</b>: " + request_json.upgrades[upgrade][1] + " to " + request_json.upgrades[upgrade][0] + "</br>"
+ info_output += "<b>" + upgrade + "</b>: " + request_json.upgrades[upgrade][1] + " to " + request_json.upgrades[upgrade][0] + "<br />"
}
}
data.packages = request_json.packages
var filename_split = data.sysupgrade_url.split("/")
data.filename = filename_split[filename_split.length - 1]
- info_output = "Firmware created</br><b>" + data.filename + "</b>"
+ info_output = 'Firmware created: <a href="' + data.sysupgrade_url + '"><b>' + data.filename + '</b></a>'
if(data.advanced_mode == 1) {
- info_output += '</br><a target="_blank" href="' + data.sysupgrade_url + '.log">Build log</a>'
+ info_output += '<br /><a target="_blank" href="' + data.sysupgrade_url + '.log">Build log</a>'
}
info_box(info_output);
error_box("No firmware created due to image size. Try again with less packages selected.")
} else if (request.status === 422) {
- error_box("Unknown package in request")
-
+ var package_missing = response.getResponseHeader("X-Unknown-Package");
+ error_box("Unknown package in request: <b>" + package_missing + "</b>")
} else if (request.status === 500) {
request_json = JSON.parse(request_text)
- error_box_content = "<b>Internal server error</b></br>"
+ error_box_content = "<b>Internal server error</b><br />"
error_box_content += request_json.error
if(request_json.log != undefined) {
data.log_url = request_json.log
local trackingNumber = uci:get("mwan3", iface, "track_ip")
overview[iface]["tracking"] = 0
- if #trackingNumber > 0 then
+ if trackingNumber and #trackingNumber > 0 then
overview[iface]["tracking"] = #trackingNumber
overview[iface]["reliability"] = false
local reliabilityNumber = tonumber(uci:get("mwan3", iface, "reliability"))
for ( var iface in status.interfaces)
{
var state = '';
- var css = '';
switch (status.interfaces[iface].status)
{
case 'online':
state = '<%:Online (tracking active)%>';
- css = 'wanon';
break;
case 'notMonitored':
state = '<%:Online (tracking off)%>';
- css = 'wanon';
break;
case 'offline':
state = '<%:Offline%>';
- css = 'wanoff';
break;
default:
state = '<%:Disabled%>';
- css = 'wanoff';
break;
}
statusview += String.format(
- '<span class="%s"><strong>%s</strong><br />%s</span>',
- css,
- iface,
+ '<div><strong>Interface: </strong>%s</div>',
+ iface
+ );
+ statusview += String.format(
+ '<div><strong>Status: </strong>%s</div></br></br>',
state
);
}
//]]></script>
<fieldset id="interface_field" class="cbi-section">
- <legend><%:MWAN Interface Live Status%></legend>
- <div id="mwan_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div>
+ <legend><%:MWAN Interfaces%></legend>
+ <div id="mwan_status_text">
+ <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%>
+ </div>
</fieldset>
-
-<style type="text/css">
- .container { /* container for entire page. fixes bootstrap theme's ridiculously small page width */
- max-width: 1044px;
- }
- #mwan_status_text {
- display: table;
- font-size: 14px;
- margin: auto;
- max-width: 1044px;
- min-width: 246px;
- width: 100%;
- }
- .wanon {
- background-color: rgb(144, 240, 144);
- }
- .wanoff {
- background-color: rgb(240, 144, 144);
- }
- .wanon, .wanoff {
- border-radius: 60px;
- box-shadow: 0px 2px 5px -3px;
- float: left;
- margin: 8px 3px 0px 3px;
- min-height: 30px;
- min-width: 235px;
- padding: 5px 10px 8px 10px;
- text-align: center;
- }
-</style>
</ul>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
-<script type="text/javascript">//<![CDATA[
- XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "status", "mwan", "interface_status")%>', null,
- function(x, status)
- {
- var legend = document.getElementById('diag-rc-legend');
- var statusDiv = document.getElementById('diag-rc-output');
- legend.style.display = 'none';
- if (status.interfaces)
- {
- var statusview = '';
- for ( var iface in status.interfaces)
- {
- var state = '';
- var css = '';
- switch (status.interfaces[iface].status)
- {
- case 'online':
- state = '<%:Online (tracking active)%>';
- css = 'wanon';
- break;
- case 'notMonitored':
- state = '<%:Online (tracking off)%>';
- css = 'wanon';
- break;
- case 'offline':
- state = '<%:Offline%>';
- css = 'wanoff';
- break;
- default:
- state = '<%:Disabled%>';
- css = 'wanoff';
- break;
- }
- statusview += String.format(
- '<span class="%s"><strong>%s</strong><br />%s</span>',
- css,
- iface,
- state
- );
- }
- statusDiv.innerHTML = statusview;
- }
- else
- {
- statusDiv.innerHTML = '<strong><%:No MWAN interfaces found%></strong>';
- }
- }
- );
-//]]></script>
-<div class="cbi-map">
- <h2 name="content"><%:MWAN Status - Interface%></h2>
- <%if not require("luci.sys").init.enabled("mwan3") then%>
- <div><strong><%:INFO: MWAN not running%></strong></div>
- <%end%>
- <fieldset class="cbi-section">
- <legend id="diag-rc-legend"><%:Collecting data...%></legend>
- <span id="diag-rc-output">
- <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align: middle;" />
- </span>
- </fieldset>
-</div>
-
-<style type="text/css">
- #mwan_status_text {
- display: table;
- font-size: 14px;
- margin: auto;
- max-width: 1044px;
- min-width: 246px;
- width: 100%;
- }
- .wanon {
- background-color: rgb(144, 240, 144);
- }
- .wanoff {
- background-color: rgb(240, 144, 144);
- }
- .wanon, .wanoff {
- border-radius: 60px;
- box-shadow: 0px 2px 5px -3px;
- float: left;
- margin: 8px 3px 0px 3px;
- min-height: 30px;
- min-width: 235px;
- padding: 5px 10px 8px 10px;
- text-align: center;
- }
- #mwan_statuslog_text {
- padding: 20px;
- text-align: left;
- }
-</style>
+<%+mwan/overview_status_interface%>
<%+footer%>
local compr = s:taboption("general", Flag, "compression", translate("Enable compression"),
translate("Enable compression"))
-compr.default = "1"
+compr.default = "0"
local udp = s:taboption("general", Flag, "udp", translate("Enable UDP"),
translate("Enable UDP channel support; this must be enabled unless you know what you are doing"))
local function local_mac_lookup(ipaddr)
- local _, ifa, dev
-
- ipaddr = tostring(ipaddr)
-
- if not ifaddr_table then
- ifaddr_table = nixio.getifaddrs()
- end
-
- -- ipaddr -> ifname
- for _, ifa in ipairs(ifaddr_table) do
- if ifa.addr == ipaddr then
- dev = ifa.name
- break
- end
- end
-
- -- ifname -> macaddr
- for _, ifa in ipairs(ifaddr_table) do
- if ifa.name == dev and ifa.family == "packet" then
- return ifa.addr
- end
+ local _, rt
+ for _, rt in ipairs(luci.ip.routes({ type = 1, src = ipaddr })) do
+ local link = rt.dev and luci.ip.link(rt.dev)
+ local mac = link and luci.ip.checkmac(link.mac)
+ if mac then return mac end
end
end
local function remote_mac_lookup(ipaddr)
local _, n
-
- if not neigh_table then
- neigh_table = luci.ip.neighbors()
- end
-
- for _, n in ipairs(neigh_table) do
- if n.mac and n.dest and n.dest:equal(ipaddr) then
- return n.mac
- end
+ for _, n in ipairs(luci.ip.neighbors({ dest = ipaddr })) do
+ local mac = luci.ip.checkmac(n.mac)
+ if mac then return mac end
end
end
for _, val in ipairs(assoclist) do
if val.network == interface and val.list then
+ local assocmac, assot
for assocmac, assot in pairs(val.list) do
- assocmac = string.lower(assocmac or "")
- if rmac == assocmac then
+ if rmac == luci.ip.checkmac(assocmac) then
signal = tonumber(assot.signal)
noise = tonumber(assot.noise)
snr = (noise*-1) - (signal*-1)
local uci = luci.model.uci.cursor()
local util = require "luci.util"
+local ipc = require "luci.ip"
function index()
entry({"admin", "services", "splash"}, cbi("splash/splash"), _("Client-Splash"), 90)
end
function ip_to_mac(ip)
- local ipc = require "luci.ip"
local i, n
-
- for i, n in ipairs(ipc.neighbors()) do
- if n.mac and n.dest and n.dest:equal(ip) then
- return n.mac
- end
+ for i, n in ipairs(ipc.neighbors({ dest = ip })) do
+ local mac = ipc.checkmac(n.mac)
+ if mac then return mac end
end
end
function action_dispatch()
local uci = luci.model.uci.cursor_state()
- local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR")) or ""
+ local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR"))
local access = false
- uci:foreach("luci_splash", "lease", function(s)
- if s.mac and s.mac:lower() == mac then access = true end
- end)
+ if mac then
+ uci:foreach("luci_splash", "lease", function(s)
+ if ipc.checkmac(s.mac) == mac then
+ access = true
+ return false
+ end
+ end)
- uci:foreach("luci_splash", "whitelist", function(s)
- if s.mac and s.mac:lower() == mac then access = true end
- end)
+ uci:foreach("luci_splash", "whitelist", function(s)
+ if ipc.checkmac(s.mac) == mac then
+ access = true
+ return false
+ end
+ end)
+ end
- if #mac > 0 and access then
+ if access then
luci.http.redirect(luci.dispatcher.build_url())
else
luci.http.redirect(luci.dispatcher.build_url("splash", "splash"))
function blacklist()
leased_macs = { }
- uci:foreach("luci_splash", "blacklist",
- function(s) leased_macs[s.mac:lower()] = true
+ uci:foreach("luci_splash", "blacklist", function(s)
+ local m = ipc.checkmac(s.mac)
+ if m then leased_macs[m] = true end
end)
return leased_macs
end
function action_activate()
local ipc = require "luci.ip"
- local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR") or "127.0.0.1") or ""
+ local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR") or "127.0.0.1")
local uci_state = require "luci.model.uci".cursor_state()
local blacklisted = false
if mac and luci.http.formvalue("accept") then
- uci:foreach("luci_splash", "blacklist",
- function(s) if s.mac and s.mac:lower() == mac then blacklisted = true end
+ uci:foreach("luci_splash", "blacklist", function(s)
+ if ipc.checkmac(s.mac) == mac then
+ blacklisted = true
+ return false
+ end
end)
+
if blacklisted then
luci.http.redirect(luci.dispatcher.build_url("splash" ,"blocked"))
else
+ local id = tostring(mac):gsub(':', ''):lower()
local redirect_url = uci:get("luci_splash", "general", "redirect_url")
if not redirect_url then
- redirect_url = uci_state:get("luci_splash_locations", mac:gsub(':', ''):lower(), "location")
+ redirect_url = uci_state:get("luci_splash_locations", id, "location")
end
if not redirect_url then
redirect_url = luci.model.uci.cursor():get("freifunk", "community", "homepage") or 'http://www.freifunk.net'
end
- remove_redirect(mac:gsub(':', ''):lower())
- os.execute("luci-splash lease "..mac.." >/dev/null 2>&1")
+ remove_redirect(id)
+ os.execute("luci-splash lease "..tostring(mac).." >/dev/null 2>&1")
luci.http.redirect(redirect_url)
end
else
remove = { }
}
+ local key, _
for key, _ in pairs(macs) do
local policy = luci.http.formvalue("policy.%s" % key)
local mac = luci.http.protocol.urldecode(key)
luci.template.render("admin_status/splash", { is_admin = false })
end
-function remove_redirect(mac)
- local mac = mac:lower()
- mac = mac:gsub(":", "")
+function remove_redirect(id)
local uci = require "luci.model.uci".cursor_state()
local redirects = uci:get_all("luci_splash_locations")
--uci:load("luci_splash_locations")
uci:revert("luci_splash_locations")
+
-- For all redirects
+ local k, v
for k, v in pairs(redirects) do
if v[".type"] == "redirect" then
- if v[".name"] ~= mac then
+ if v[".name"] ~= id then
-- Rewrite state
uci:section("luci_splash_locations", "redirect", v[".name"], {
location = v.location
end
end
end
+
uci:save("luci_splash_redirects")
end
<%-
local utl = require "luci.util"
+local sys = require "luci.sys"
+local ipc = require "luci.ip"
local ipt = require "luci.sys.iptparser".IptParser()
local uci = require "luci.model.uci".cursor_state()
local wat = require "luci.tools.webadmin"
local clients = { }
local leasetime = tonumber(uci:get("luci_splash", "general", "leasetime") or 1) * 60 * 60
-local leasefile = "/tmp/dhcp.leases"
-
-uci:foreach("dhcp", "dnsmasq",
- function(s)
- if s.leasefile then leasefile = s.leasefile end
- end)
-
uci:foreach("luci_splash_leases", "lease",
function(s)
- if s.start and s.mac then
- clients[s.mac:lower()] = {
+ local m = ipc.checkmac(s.mac)
+ if m and s.start then
+ clients[m] = {
start = tonumber(s.start),
limit = ( tonumber(s.start) + leasetime ),
- mac = s.mac:upper(),
+ mac = m,
ipaddr = s.ipaddr,
policy = "normal",
packets = 0,
for _, r in ipairs(ipt:find({table="nat", chain="luci_splash_leases"})) do
if r.options and #r.options >= 2 and r.options[1] == "MAC" then
- if not clients[r.options[2]:lower()] then
- clients[r.options[2]:lower()] = {
+ local m = ipc.checkmac(r.options[2])
+ if m and not clients[m] then
+ clients[m] = {
start = 0,
limit = 0,
- mac = r.options[2]:upper(),
+ mac = m,
policy = ( r.target == "RETURN" ) and "whitelist" or "blacklist",
packets = 0,
bytes = 0
if client.ipaddr then
local rin = ipt:find({table="mangle", chain="luci_splash_mark_in", destination=client.ipaddr})
- local rout = ipt:find({table="mangle", chain="luci_splash_mark_out", options={"MAC", client.mac:upper()}})
+ local rout = ipt:find({table="mangle", chain="luci_splash_mark_out", options={"MAC", client.mac}})
if rin and #rin > 0 then
client.bytes_in = rin[1].bytes
uci:foreach("luci_splash", "whitelist",
function(s)
- if s.mac and clients[s.mac:lower()] then
- clients[s.mac:lower()].policy="whitelist"
+ local m = ipc.checkmac(s.mac)
+ if m and clients[m] then
+ clients[m].policy="whitelist"
end
end)
uci:foreach("luci_splash", "blacklist",
function(s)
- if s.mac and clients[s.mac:lower()] then
- clients[s.mac:lower()].policy=(s.kicked and "kicked" or "blacklist")
+ local m = ipc.checkmac(s.mac)
+ if m and clients[m] then
+ clients[m].policy=(s.kicked and "kicked" or "blacklist")
end
end)
-if fs.access(leasefile) then
- for l in io.lines(leasefile) do
- local time, mac, ip, name = l:match("^(%d+) (%S+) (%S+) (%S+)")
- if time and mac and ip then
- local c = clients[mac:lower()]
- if c then
- c.ip = ip
- c.hostname = ( name ~= "*" ) and name or nil
- end
- end
+sys.net.host_hints(function(mac, v4, v6, name)
+ local c = mac and clients[mac]
+ if c then
+ c.ip = c.ip or v4
+ c.hostname = c.hostname or name
end
-end
-
-for i, n in ipairs(ipc.neighbors({ family = 4 })) do
- if n.mac and n.dest then
- local c = clients[n.mac]
- if c and not c.ip then
- c.ip = n.dest:string()
- end
- end
-end
+end)
local function showmac(mac)
if not is_admin then
splash.hostname, splash.ip, splash.mac, splash.timeleft, splash.trafficin, splash.trafficout);
<% if is_admin then %>
- s += String.format('<select name="policy.%s" style="width:200px">', splash.mac.toLowerCase());
+ s += String.format('<select name="policy.%s" style="width:200px">', splash.mac);
if (splash.policy == 'whitelist') {
s += '<option value="whitelist" selected="selected"><%:whitelisted%></option>'
} else {
s += String.format(
'</select>' +
'<input type="submit" class="cbi-button cbi-button-save" name="save.%s" value="<%:Save%>" />',
- splash.mac.toLowerCase());
+ splash.mac);
<% else %>
s += String.format('%s', splash.policy);
<% end %>
module("luci.statistics.rrdtool.definitions.apcups",package.seeall)
-function rrdargs( graph, plugin, plugin_instance, dtype )
+function rrdargs( graph, plugin, plugin_instance )
+
+ local lu = require("luci.util")
+ local rv = { }
+
+ -- Types and instances supported by APC UPS
+ -- e.g. ups_types -> { 'timeleft', 'charge', 'percent', 'voltage' }
+ -- e.g. ups_inst['voltage'] -> { 'input', 'battery' }
+
+ local ups_types = graph.tree:data_types( plugin, plugin_instance )
+
+ local ups_inst = {}
+ for _, t in ipairs(ups_types) do
+ ups_inst[t] = graph.tree:data_instances( plugin, plugin_instance, t )
+ end
+
+
+ -- Check if hash table or array is empty or nil-filled
+
+ local function empty( t )
+ for _, v in pairs(t) do
+ if type(v) then return false end
+ end
+ return true
+ end
+
+
+ -- Append graph definition but only types/instances which are
+ -- supported and available to the plugin and UPS.
+
+ local function add_supported( t, defs )
+ local def_inst = defs['data']['instances']
+
+ if type(def_inst) == "table" then
+ for k, v in pairs( def_inst ) do
+ if lu.contains( ups_types, k) then
+ for j = #v, 1, -1 do
+ if not lu.contains( ups_inst[k], v[j] ) then
+ table.remove( v, j )
+ end
+ end
+ if #v == 0 then
+ def_inst[k] = nil -- can't assign v: immutable
+ end
+ else
+ def_inst[k] = nil -- can't assign v: immutable
+ end
+ end
+ if empty(def_inst) then return end
+ end
+ table.insert( t, defs )
+ end
+
+
+ -- Graph definitions for APC UPS measurements MUST use only 'instances':
+ -- e.g. instances = { voltage = { "input", "output" } }
local voltagesdc = {
title = "%H: Voltages on APC UPS - Battery",
vlabel = "Volts DC",
- alt_autoscale = true,
+ alt_autoscale = true,
number_format = "%5.1lfV",
data = {
instances = {
voltage = { "battery" }
},
-
- options = {
+ options = {
voltage = { title = "Battery voltage", noarea=true }
}
}
}
-
- local voltages = {
+ add_supported( rv, voltagesdc )
+
+ local voltagesac = {
title = "%H: Voltages on APC UPS - AC",
vlabel = "Volts AC",
alt_autoscale = true,
instances = {
voltage = { "input", "output" }
},
-
options = {
voltage_output = { color = "00e000", title = "Output voltage", noarea=true, overlay=true },
voltage_input = { color = "ffb000", title = "Input voltage", noarea=true, overlay=true }
}
}
}
+ add_supported( rv, voltagesac )
local percentload = {
title = "%H: Load on APC UPS ",
y_max = "100",
number_format = "%5.1lf%%",
data = {
- sources = {
- percent_load = { "value" }
- },
instances = {
- percent = "load"
+ percent = { "load" }
},
options = {
percent_load = { color = "00ff00", title = "Load level" }
}
}
}
+ add_supported( rv, percentload )
local charge_percent = {
title = "%H: Battery charge on APC UPS ",
y_max = "100",
number_format = "%5.1lf%%",
data = {
- types = { "charge" },
+ instances = {
+ charge = { "" }
+ },
options = {
charge = { color = "00ff0b", title = "Charge level" }
}
}
}
+ add_supported( rv, charge_percent )
local temperature = {
title = "%H: Battery temperature on APC UPS ",
vlabel = "\176C",
number_format = "%5.1lf\176C",
data = {
- types = { "temperature" },
+ instances = {
+ temperature = { "" }
+ },
options = {
temperature = { color = "ffb000", title = "Battery temperature" } }
}
}
+ add_supported( rv, temperature )
local timeleft = {
title = "%H: Time left on APC UPS ",
vlabel = "Minutes",
number_format = "%.1lfm",
data = {
- sources = {
- timeleft = { "value" }
+ instances = {
+ timeleft = { "" }
},
options = {
timeleft = { color = "0000ff", title = "Time left" }
}
}
}
+ add_supported( rv, timeleft )
local frequency = {
title = "%H: Incoming line frequency on APC UPS ",
vlabel = "Hz",
number_format = "%5.0lfhz",
data = {
- sources = {
- frequency_input = { "value" }
- },
instances = {
- frequency = "frequency"
+ frequency = { "input" }
},
options = {
- frequency_frequency = { color = "000fff", title = "Line frequency" }
+ frequency_input = { color = "000fff", title = "Line frequency" }
}
}
}
+ add_supported( rv, frequency )
- return { voltages, voltagesdc, percentload, charge_percent, temperature, timeleft, frequency }
+ return rv
end
-# Copyright 2017 Dirk Brenken (dev@brenken.org)
+# Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
# This is free software, licensed under the Apache License, Version 2.0
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for Travelmate
-LUCI_DEPENDS:=+travelmate +luci-lib-jsonc
+LUCI_DEPENDS:=+travelmate +luci-lib-jsonc +qrencode
LUCI_PKGARCH:=all
include ../../luci.mk
module("luci.controller.travelmate", package.seeall)
-local fs = require("nixio.fs")
local util = require("luci.util")
local i18n = require("luci.i18n")
local templ = require("luci.template")
entry({"admin", "services", "travelmate"}, firstchild(), _("Travelmate"), 40).dependent = false
entry({"admin", "services", "travelmate", "tab_from_cbi"}, cbi("travelmate/overview_tab", {hideresetbtn=true, hidesavebtn=true}), _("Overview"), 10).leaf = true
entry({"admin", "services", "travelmate", "stations"}, template("travelmate/stations"), _("Wireless Stations"), 20).leaf = true
- entry({"admin", "services", "travelmate", "logfile"}, call("logread"), _("View Logfile"), 30).leaf = true
+ entry({"admin", "services", "travelmate", "apqr"}, template("travelmate/ap_qr"), _("AP QR-Codes"), 30).leaf = true
+ entry({"admin", "services", "travelmate", "logfile"}, call("logread"), _("View Logfile"), 40).leaf = true
entry({"admin", "services", "travelmate", "advanced"}, firstchild(), _("Advanced"), 100)
entry({"admin", "services", "travelmate", "advanced", "configuration"}, cbi("travelmate/configuration_tab"), _("Edit Travelmate Configuration"), 110).leaf = true
entry({"admin", "services", "travelmate", "advanced", "cfg_wireless"}, cbi("travelmate/cfg_wireless_tab"), _("Edit Wireless Configuration"), 120).leaf = true
--- /dev/null
+<%#
+Copyright 2018 Dirk Brenken (dev@brenken.org)
+This is free software, licensed under the Apache License, Version 2.0
+-%>
+
+<%+header%>
+
+<div class="cbi-map">
+ <div class="cbi-map-descr">
+ <%=translate("Here you'll find the QR codes from all of your configured Access Points. It allows you to connect your Android or iOS devices to your router's WiFi using the QR code shown below.")%>
+ </div>
+<%-
+ local write = io.write
+ local uci = require("luci.model.uci").cursor()
+
+ uci:foreach("wireless", "wifi-iface", function(s)
+ local device = s.device or ""
+ local mode = s.mode or ""
+ local ssid = s.ssid or ""
+ local enc = s.encryption or ""
+ local key = s.key or ""
+ local hidden = s.hidden or "false"
+ local disabled = s.disabled or ""
+ local wep_slots = {s.key1 or "", s.key2 or "", s.key3 or "", s.key4 or ""}
+
+ if device and mode == "ap" and disabled ~= "1" then
+ if string.match(enc, '^psk') then
+ enc = "WPA"
+ elseif string.match(enc, '^wep') then
+ enc = "WEP"
+ if tonumber(key) then
+ key = wep_slots[tonumber(key)]
+ end
+ elseif enc == "none" then
+ enc = "nopass"
+ key = "nokey"
+ else
+ enc = ""
+ end
+ if hidden == "1" then
+ hidden = "true"
+ end
+ if ssid and enc and key then
+ local e_ssid = string.gsub(ssid,"[\"\\';:, ]",[[\\\%1]])
+ local e_key = string.gsub(key,"[\"\\';:, ]",[[\\\%1]])
+ local qrcode = ""
+ if nixio.fs.access("/usr/bin/qrencode") then
+ qrcode = luci.sys.exec("/usr/bin/qrencode --inline --8bit --type=SVG --output=- 'WIFI:S:\"'" .. e_ssid .. "'\";T:'" .. enc .. "';P:\"'" .. e_key .. "'\";H:'" .. hidden .. "';'")
+ end
+-%>
+ <fieldset class="cbi-section">
+ <legend>AP on <%=device%> with SSID "<%=ssid%>"</legend>
+ <h3 name="content"><%=qrcode%></h3>
+ </fieldset>
+<%-
+ end
+ end
+ end)
+%>
+</div>
+
+<%+footer%>
<%+header%>
<div class="cbi-map">
-<h2 name="content"><%:Wireless Stations%></h2>
<div class="cbi-map-descr">
<%=translatef("Provides an overview of all configured uplinks for the travelmate interface (%s). You can edit, delete or re-order existing uplinks or scan for a new one. The currently used uplink is emphasized in blue.", trmiface)%>
</div>
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 2.0.5\n"
+"X-Generator: Poedit 2.0.6\n"
"Last-Translator: INAGAKI Hiroshi <musashino.open@gmail.com>\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"Language: ja\n"
+msgid "AP QR-Codes"
+msgstr "AP QR-コード"
+
msgid "Actions"
msgstr "操作"
msgid "Back to overview"
msgstr "概要へ戻る"
+msgid "Captive Portal Detection"
+msgstr "キャプティブポータル検知"
+
+msgid ""
+"Check the internet availability, log captive portal redirections and keep "
+"the uplink connection 'alive'."
+msgstr ""
+"インターネットの利用可否を確認し、キャプティブポータル リダイレクトを記録して"
+"アップリンク接続を 'alive' として保持します。"
+
msgid "Cipher"
msgstr "暗号化方式"
msgstr ""
"'trigger' モード時に、手動でアップリンクの再スキャンと再接続を行います。"
-msgid "How long should travelmate wait for a successful wlan interface reload."
+msgid ""
+"Here you'll find the QR codes from all of your configured Access Points. It "
+"allows you to connect your Android or iOS devices to your router's WiFi "
+"using the QR code shown below."
msgstr ""
-"無線LAN インターフェースのリロードが成功するまでの、Travelmate の待機時間で"
-"す。"
+"ここには、構成済みの全アクセスポイントの QR コードを表示しています。以下の "
+"QR コードを使用して、 Android または iOS デバイスをルータの WiFi に接続するこ"
+"とができます。"
-msgid "How many times should travelmate try to connect to an Uplink."
-msgstr "Travelmate がアップリンクへの接続を試行する回数です。"
+msgid ""
+"How long should travelmate wait for a successful wlan uplink connection."
+msgstr "Travelmate が無線アップリンクへの接続成功を待つ時間です。"
msgid "Identity"
msgstr "ID"
msgid "Manual Rescan"
msgstr "手動再スキャン"
+msgid ""
+"Minimum signal quality threshold as percent for conditional uplink (dis-) "
+"connections."
+msgstr ""
+"条件付きアップリンク接続(または切断)のための、シグナル品質閾値の下限(%)で"
+"す。"
+
msgid "Move down"
msgstr "下へ"
msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'."
msgstr "Travelmate が指定された無線に接続するよう制御します。(例: 'radio0')"
+msgid "Retry limit to connect to an uplink."
+msgstr "アップリンクへの接続を試行する回数です。"
+
msgid "Runtime Information"
msgstr "実行情報"
msgid "Scan"
msgstr "スキャン:"
+msgid "Signal Quality Threshold"
+msgstr "シグナル品質閾値"
+
msgid "Signal strength"
msgstr "信号強度"
msgid "Timeout in seconds between retries in 'automatic' mode."
msgstr "'automatic' モード時に接続を確認または再試行する間隔(秒)です。"
-msgid "To disable this feature set it to '0' which means unlimited retries."
-msgstr ""
-"この機能を無効にして接続の再試行を無制限にする場合、 '0' を設定します。"
-
msgid "Travelmate"
msgstr "Travelmate"
msgid "Travelmate Logfile"
msgstr "Travelmate ログファイル"
-msgid "Travelmate Status"
-msgstr "Travelmate ステータス"
+msgid "Travelmate Status (Quality)"
+msgstr "Travelmate ステータス(品質)"
msgid "Travelmate Version"
msgstr "Travelmate バージョン"
-msgid "Trigger delay"
-msgstr "トリガー遅延"
+msgid "Trigger Delay"
+msgstr "トリガ遅延"
msgid "Unknown"
msgstr "不明"
"ファイアウォールの wan ゾーンに追加します。このステップは、一度だけ実行される"
"必要があります。"
-msgid "connected"
-msgstr "接続済"
-
-msgid "error"
-msgstr "エラー"
-
msgid "hidden"
msgstr "(不明)"
msgid "n/a"
msgstr "利用不可"
-
-msgid "not connected"
-msgstr "未接続"
-
-msgid "running"
-msgstr "実行中"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"Language: pt_BR\n"
+msgid "AP QR-Codes"
+msgstr ""
+
msgid "Actions"
msgstr ""
msgid "Back to overview"
msgstr ""
+msgid "Captive Portal Detection"
+msgstr ""
+
+msgid ""
+"Check the internet availability, log captive portal redirections and keep "
+"the uplink connection 'alive'."
+msgstr ""
+
msgid "Cipher"
msgstr ""
msgid "Force a manual uplink rescan / reconnect in 'trigger' mode."
msgstr ""
-msgid "How long should travelmate wait for a successful wlan interface reload."
+msgid ""
+"Here you'll find the QR codes from all of your configured Access Points. It "
+"allows you to connect your Android or iOS devices to your router's WiFi "
+"using the QR code shown below."
msgstr ""
-msgid "How many times should travelmate try to connect to an Uplink."
+msgid ""
+"How long should travelmate wait for a successful wlan uplink connection."
msgstr ""
msgid "Identity"
msgid "Manual Rescan"
msgstr ""
+msgid ""
+"Minimum signal quality threshold as percent for conditional uplink (dis-) "
+"connections."
+msgstr ""
+
msgid "Move down"
msgstr ""
msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'."
msgstr ""
+msgid "Retry limit to connect to an uplink."
+msgstr ""
+
msgid "Runtime Information"
msgstr ""
msgid "Scan"
msgstr ""
+msgid "Signal Quality Threshold"
+msgstr ""
+
msgid "Signal strength"
msgstr ""
msgid "Timeout in seconds between retries in 'automatic' mode."
msgstr ""
-msgid "To disable this feature set it to '0' which means unlimited retries."
-msgstr ""
-
msgid "Travelmate"
msgstr "Travelmate"
msgid "Travelmate Logfile"
msgstr ""
-msgid "Travelmate Status"
+msgid "Travelmate Status (Quality)"
msgstr ""
msgid "Travelmate Version"
msgstr ""
-msgid "Trigger delay"
+msgid "Trigger Delay"
msgstr ""
msgid "Unknown"
"add it to the wan zone of the firewall. This step has only to be done once."
msgstr ""
-msgid "connected"
-msgstr ""
-
-msgid "error"
-msgstr ""
-
msgid "hidden"
msgstr ""
msgid "n/a"
msgstr ""
-msgid "not connected"
-msgstr ""
-
-msgid "running"
-msgstr ""
-
#~ msgid ""
#~ "Brief advice: Create a wwan interface, configure it to use dhcp and add "
#~ "it to the wan zone in firewall. Create the wifi interfaces to be used "
"Project-Info: Это технический перевод, не дословный. Главное-удобный русский "
"интерфейс, все проверялось в графическом режиме, совместим с другими apps\n"
+msgid "AP QR-Codes"
+msgstr ""
+
msgid "Actions"
msgstr "Действия"
msgid "Back to overview"
msgstr "Назад в меню"
+msgid "Captive Portal Detection"
+msgstr ""
+
+msgid ""
+"Check the internet availability, log captive portal redirections and keep "
+"the uplink connection 'alive'."
+msgstr ""
+
msgid "Cipher"
msgstr "Алгоритм шифрования"
"Принудительно выполнить повторное сканирование/повторное подключение внешних "
"сетей в режиме 'ручной'."
-msgid "How long should travelmate wait for a successful wlan interface reload."
+msgid ""
+"Here you'll find the QR codes from all of your configured Access Points. It "
+"allows you to connect your Android or iOS devices to your router's WiFi "
+"using the QR code shown below."
msgstr ""
-"Временная задержка необходима TravelMate для полной перезагрузки wlan "
-"интерфейса."
-msgid "How many times should travelmate try to connect to an Uplink."
-msgstr "Сколько раз TravelMate должен пытаться подключиться к сети. "
+msgid ""
+"How long should travelmate wait for a successful wlan uplink connection."
+msgstr ""
msgid "Identity"
msgstr "Идентификация EAP"
msgid "Manual Rescan"
msgstr "Поиск сети вручную"
+msgid ""
+"Minimum signal quality threshold as percent for conditional uplink (dis-) "
+"connections."
+msgstr ""
+
msgid "Move down"
msgstr "Переместить вниз"
msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'."
msgstr "Выделить TravelMate-у конкретное Wi-Fi устройство, например 'radio0'."
+msgid "Retry limit to connect to an uplink."
+msgstr ""
+
msgid "Runtime Information"
msgstr "Информация о состоянии"
msgid "Scan"
msgstr "Поиск"
+msgid "Signal Quality Threshold"
+msgstr ""
+
msgid "Signal strength"
msgstr "Мощность сигнала"
"Время ожидания в секундах между повторными попытками соединения в режиме "
"'автоматически'."
-msgid "To disable this feature set it to '0' which means unlimited retries."
-msgstr ""
-"<br />Чтобы отключить эту функцию, установите значение '0', что означает "
-"неограниченное количество попыток."
-
msgid "Travelmate"
msgstr "TravelMate"
msgid "Travelmate Logfile"
msgstr "Системный журнал TravelMate"
-msgid "Travelmate Status"
-msgstr "Состояние Travelmate"
+msgid "Travelmate Status (Quality)"
+msgstr ""
msgid "Travelmate Version"
msgstr "Версия TravelMate"
-msgid "Trigger delay"
-msgstr "Задержка запуска"
+msgid "Trigger Delay"
+msgstr ""
msgid "Unknown"
msgstr "Неизвестно"
"add it to the wan zone of the firewall. This step has only to be done once."
msgstr "добавить в wan зону межсетевого экрана. Можно сделать только один раз."
-msgid "connected"
-msgstr "подключен"
-
-msgid "error"
-msgstr "ошибка"
-
msgid "hidden"
msgstr "скрытый"
msgid "n/a"
msgstr "нет данных"
-msgid "not connected"
-msgstr "не подключено"
+#~ msgid ""
+#~ "How long should travelmate wait for a successful wlan interface reload."
+#~ msgstr ""
+#~ "Временная задержка необходима TravelMate для полной перезагрузки wlan "
+#~ "интерфейса."
+
+#~ msgid "How many times should travelmate try to connect to an Uplink."
+#~ msgstr "Сколько раз TravelMate должен пытаться подключиться к сети. "
+
+#~ msgid "To disable this feature set it to '0' which means unlimited retries."
+#~ msgstr ""
+#~ "<br />Чтобы отключить эту функцию, установите значение '0', что означает "
+#~ "неограниченное количество попыток."
+
+#~ msgid "Travelmate Status"
+#~ msgstr "Состояние Travelmate"
+
+#~ msgid "Trigger delay"
+#~ msgstr "Задержка запуска"
+
+#~ msgid "connected"
+#~ msgstr "подключен"
+
+#~ msgid "error"
+#~ msgstr "ошибка"
+
+#~ msgid "not connected"
+#~ msgstr "не подключено"
-msgid "running"
-msgstr "работает"
+#~ msgid "running"
+#~ msgstr "работает"
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
+msgid "AP QR-Codes"
+msgstr ""
+
msgid "Actions"
msgstr ""
msgid "Back to overview"
msgstr ""
+msgid "Captive Portal Detection"
+msgstr ""
+
+msgid ""
+"Check the internet availability, log captive portal redirections and keep "
+"the uplink connection 'alive'."
+msgstr ""
+
msgid "Cipher"
msgstr ""
msgid "Force a manual uplink rescan / reconnect in 'trigger' mode."
msgstr ""
-msgid "How long should travelmate wait for a successful wlan interface reload."
+msgid ""
+"Here you'll find the QR codes from all of your configured Access Points. It "
+"allows you to connect your Android or iOS devices to your router's WiFi "
+"using the QR code shown below."
msgstr ""
-msgid "How many times should travelmate try to connect to an Uplink."
+msgid ""
+"How long should travelmate wait for a successful wlan uplink connection."
msgstr ""
msgid "Identity"
msgid "Manual Rescan"
msgstr ""
+msgid ""
+"Minimum signal quality threshold as percent for conditional uplink (dis-) "
+"connections."
+msgstr ""
+
msgid "Move down"
msgstr ""
msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'."
msgstr ""
+msgid "Retry limit to connect to an uplink."
+msgstr ""
+
msgid "Runtime Information"
msgstr ""
msgid "Scan"
msgstr ""
+msgid "Signal Quality Threshold"
+msgstr ""
+
msgid "Signal strength"
msgstr ""
msgid "Timeout in seconds between retries in 'automatic' mode."
msgstr ""
-msgid "To disable this feature set it to '0' which means unlimited retries."
-msgstr ""
-
msgid "Travelmate"
msgstr ""
msgid "Travelmate Logfile"
msgstr ""
-msgid "Travelmate Status"
+msgid "Travelmate Status (Quality)"
msgstr ""
msgid "Travelmate Version"
msgstr ""
-msgid "Trigger delay"
+msgid "Trigger Delay"
msgstr ""
msgid "Unknown"
"add it to the wan zone of the firewall. This step has only to be done once."
msgstr ""
-msgid "connected"
-msgstr ""
-
-msgid "error"
-msgstr ""
-
msgid "hidden"
msgstr ""
msgid "n/a"
msgstr ""
-
-msgid "not connected"
-msgstr ""
-
-msgid "running"
-msgstr ""
-- Licensed to the public under the Apache License 2.0.
local sys = require "luci.sys"
+local ipc = require "luci.ip"
local fs = require "nixio.fs"
m = SimpleForm("wol", translate("Wake on LAN"),
function host.write(self, s, val)
local host = luci.http.formvalue("cbid.wol.1.mac")
- if host and #host > 0 and host:match("^[a-fA-F0-9:]+$") then
+ local mac = ipc.checkmac(host)
+ if mac then
local cmd
local util = luci.http.formvalue("cbid.wol.1.binary") or (
has_ewk and "/usr/bin/etherwake" or "/usr/bin/wol"
local broadcast = luci.http.formvalue("cbid.wol.1.broadcast")
cmd = "%s -D%s %s %q" %{
util, (iface ~= "" and " -i %q" % iface or ""),
- (broadcast == "1" and " -b" or ""), host
+ (broadcast == "1" and " -b" or ""), mac
}
else
- cmd = "%s -v %q" %{ util, host }
+ cmd = "%s -v %q" %{ util, mac }
end
local msg = "<p><strong>%s</strong><br /><br /><code>%s<br /><br />" %{
Decode a mime encoded http message body with multipart/form-data
Content-Type. Stores all extracted data associated with its parameter name
-in the params table within the given message object. Multiple parameter
+in the params table within the given message object. Multiple parameter
values are stored as tables, ordinary ones as strings.
If an optional file callback function is given then it is feeded with the
file contents chunk by chunk and only the extracted file name is stored
Decode an urlencoded http message body with application/x-www-urlencoded
Content-Type. Stores all extracted data associated with its parameter name
-in the params table within the given message object. Multiple parameter
+in the params table within the given message object. Multiple parameter
values are stored as tables, ordinary ones as strings.
<td class="summary">
Checks whether the CIDR instance is an IPv6 mapped IPv4 address
+ </td>
+ </tr>
+
+ <tr>
+ <td class="name" nowrap><a href="#cidr.ismac">cidr:ismac</a> ()</td>
+ <td class="summary">
+
+Checks whether the CIDR instance is an ethernet MAC address range
+ </td>
+ </tr>
+
+ <tr>
+ <td class="name" nowrap><a href="#cidr.ismaclocal">cidr:ismaclocal</a> ()</td>
+ <td class="summary">
+
+Checks whether the CIDR instance is a locally administered (LAA) MAC address
+ </td>
+ </tr>
+
+ <tr>
+ <td class="name" nowrap><a href="#cidr.ismacmcast">cidr:ismacmcast</a> ()</td>
+ <td class="summary">
+
+Checks whether the CIDR instance is a multicast MAC address
</td>
</tr>
Derive mapped IPv4 address of CIDR instance.</td>
</tr>
+ <tr>
+ <td class="name" nowrap><a href="#cidr.tomac">cidr:tomac</a> ()</td>
+ <td class="summary">
+
+Derive MAC address of IPv6 link local CIDR instance.</td>
+ </tr>
+
+ <tr>
+ <td class="name" nowrap><a href="#cidr.tolinklocal">cidr:tolinklocal</a> ()</td>
+ <td class="summary">
+
+Derive IPv6 link local address from MAC address CIDR instance.</td>
+ </tr>
+
<tr>
<td class="name" nowrap><a href="#cidr.contains">cidr:contains</a> (addr)</td>
<td class="summary">
cidr:is6
</a>
+ <li><a href="#cidr.ismac">
+ cidr:ismac
+ </a>
+
</ul>
</dd>
cidr:is4
</a>
+ <li><a href="#cidr.ismac">
+ cidr:ismac
+ </a>
+
</ul>
</dd>
+<dt><a name="cidr.ismac"></a><strong>cidr:ismac</strong> ()</dt>
+<dd>
+
+
+Checks whether the CIDR instance is an ethernet MAC address range
+
+
+
+
+
+
+
+
+<h3>Return value:</h3>
+<code>true</code> if the CIDR is a MAC address range, else <code>false</code>
+
+
+
+<h3>See also:</h3>
+<ul>
+
+ <li><a href="#cidr.is4">
+ cidr:is4
+ </a>
+
+ <li><a href="#cidr.is6">
+ cidr:is6
+ </a>
+
+</ul>
+
+</dd>
+
+
+
+
+<dt><a name="cidr.ismaclocal"></a><strong>cidr:ismaclocal</strong> ()</dt>
+<dd>
+
+
+Checks whether the CIDR instance is a locally administered (LAA) MAC address
+
+
+
+
+
+
+<h3>Usage:</h3>
+<pre>local mac = luci.ip.new("02:C0:FF:EE:00:01")
+if mac:ismaclocal() then
+ print("Is an LAA MAC address")
+end</pre>
+
+
+
+<h3>Return value:</h3>
+<code>true</code> if the MAC address sets the locally administered bit.
+
+
+
+</dd>
+
+
+
+
+<dt><a name="cidr.ismacmcast"></a><strong>cidr:ismacmcast</strong> ()</dt>
+<dd>
+
+
+Checks whether the CIDR instance is a multicast MAC address
+
+
+
+
+
+
+<h3>Usage:</h3>
+<pre>local mac = luci.ip.new("01:00:5E:7F:00:10")
+if addr:ismacmcast() then
+ print("Is a multicast MAC address")
+end</pre>
+
+
+
+<h3>Return value:</h3>
+<code>true</code> if the MAC address sets the multicast bit.
+
+
+
+</dd>
+
+
+
+
<dt><a name="cidr.lower"></a><strong>cidr:lower</strong> (addr)</dt>
<dd>
Checks whether this CIDR instance is lower than the given argument.
The comparisation follows these rules:
-<ul><li>An IPv4 address is always lower than an IPv6 address</li>
+<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses
+are considered lower than MAC addresses</li>
<li>Prefix sizes are ignored</li></ul>
print(addr:lower(addr)) -- false
print(addr:lower("10.10.10.10/24")) -- false
print(addr:lower(luci.ip.new("::1"))) -- true
-print(addr:lower(luci.ip.new("192.168.200.1"))) -- true</pre>
+print(addr:lower(luci.ip.new("192.168.200.1"))) -- true
+print(addr:lower(luci.ip.new("00:14:22:01:23:45"))) -- true</pre>
Checks whether this CIDR instance is higher than the given argument.
The comparisation follows these rules:
-<ul><li>An IPv4 address is always lower than an IPv6 address</li>
+<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses
+are considered lower than MAC addresses</li>
<li>Prefix sizes are ignored</li></ul>
print(addr:higher(addr)) -- false
print(addr:higher("10.10.10.10/24")) -- true
print(addr:higher(luci.ip.new("::1"))) -- false
-print(addr:higher(luci.ip.new("192.168.200.1"))) -- false</pre>
+print(addr:higher(luci.ip.new("192.168.200.1"))) -- false
+print(addr:higher(luci.ip.new("00:14:22:01:23:45"))) -- false</pre>
local addr6 = luci.ip.new("::1")
print(addr6:equal("0:0:0:0:0:0:0:1/64")) -- true
-print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false</pre>
+print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false
+
+local mac = luci.ip.new("00:14:22:01:23:45")
+print(mac:equal("0:14:22:1:23:45")) -- true
+print(mac:equal(luci.ip.new("01:23:45:67:89:AB")) -- false</pre>
<li>
mask: Either a number containing the number of bits (<code>0..32</code>
- for IPv4, <code>0..128</code> for IPv6) or a string containing a valid
- netmask (optional)
+ for IPv4, <code>0..128</code> for IPv6 or <code>0..48</code> for MAC addresses) or a string
+ containing a valid netmask (optional)
</li>
</ul>
<li>
mask: Either a number containing the number of bits (<code>0..32</code>
- for IPv4, <code>0..128</code> for IPv6) or a string containing a valid
- netmask (optional)
+ for IPv4, <code>0..128</code> for IPv6 or <code>0..48</code> for MAC addresses) or a string
+ containing a valid netmask (optional)
</li>
</ul>
Derive host address of CIDR instance.
This function essentially constructs a copy of this CIDR with the prefix size
-set to <code>32</code> for IPv4 and <code>128</code> for IPv6.
+set to <code>32</code> for IPv4, <code>128</code> for IPv6 or <code>48</code> for MAC addresses.
<li>
mask: Either a number containing the number of bits (<code>0..32</code>
- for IPv4, <code>0..128</code> for IPv6) or a string containing a valid
- netmask (optional)
+ for IPv4, <code>0..128</code> for IPv6 or <code>0..48</code> for MAC addresses) or a string
+ containing a valid netmask (optional)
</li>
</ul>
Constructs a CIDR instance representing the broadcast address of this instance.
The used prefix size can be overridden by the optional mask parameter.
-This function has no effect on IPv6 instances, it will return nothing in this
-case.
+This function has no effect on IPv6 or MAC address instances, it will return
+nothing in this case.
<ul>
<li>
- mask: Either a number containing the number of bits (<code>0..32</code>
- for IPv4, <code>0..128</code> for IPv6) or a string containing a valid
- netmask (optional)
+ mask: Either a number containing the number of bits (<code>0..32</code> for IPv4) or
+ a string containing a valid netmask (optional)
</li>
</ul>
Constructs a CIDR instance representing the IPv4 address of the IPv6 mapped
IPv4 address in this instance.
-This function has no effect on IPv4 instances or IPv6 instances which are not a
-mapped address, it will return nothing in this case.
+This function has no effect on IPv4 instances, MAC address instances or IPv6
+instances which are not a mapped address, it will return nothing in this case.
+<dt><a name="cidr.tomac"></a><strong>cidr:tomac</strong> ()</dt>
+<dd>
+
+
+Derive MAC address of IPv6 link local CIDR instance.
+
+Constructs a CIDR instance representing the MAC address contained in the IPv6
+link local address of this instance.
+
+This function has no effect on IPv4 instances, MAC address instances or IPv6
+instances which are not a link local address, it will return nothing in this
+case.
+
+
+
+
+
+
+<h3>Usage:</h3>
+<pre>local addr = luci.ip.new("fe80::6666:b3ff:fe47:e1b9")
+print(addr:tomac()) -- "64:66:B3:47:E1:B9"</pre>
+
+
+
+<h3>Return value:</h3>
+Return a new CIDR instance representing the MAC address if this
+ instance is an IPv6 link local address, else return nothing.
+
+
+
+</dd>
+
+
+
+
+<dt><a name="cidr.tolinklocal"></a><strong>cidr:tolinklocal</strong> ()</dt>
+<dd>
+
+
+Derive IPv6 link local address from MAC address CIDR instance.
+
+Constructs a CIDR instance representing the IPv6 link local address of the
+MAC address represented by this instance.
+
+This function has no effect on IPv4 instances or IPv6 instances, it will return
+nothing in this case.
+
+
+
+
+
+
+<h3>Usage:</h3>
+<pre>local mac = luci.ip.new("64:66:B3:47:E1:B9")
+print(mac:tolinklocal()) -- "fe80::6666:b3ff:fe47:e1b9"</pre>
+
+
+
+<h3>Return value:</h3>
+Return a new CIDR instance representing the IPv6 link local address.
+
+
+
+</dd>
+
+
+
+
<dt><a name="cidr.contains"></a><strong>cidr:contains</strong> (addr)</dt>
<dd>
local range6 = luci.ip.new("fe80::/10")
print(range6:contains("fe80::221:63f:fe75:aa17/64")) -- true
-print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false</pre>
+print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false
+
+local intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24")
+print(intel_macs:contains("C0:B6:F9:A3:C:11")) -- true
+print(intel_macs:contains("64:66:B3:47:E1:B9")) -- false</pre>
<h3>Usage:</h3>
<pre>local addr = luci.ip.new("192.168.1.1/24")
-print(addr:add(250)) -- "192.168.1.251/24"
-print(addr:add("0.0.99.0")) -- "192.168.100.1/24"
+print(addr:add(250)) -- "192.168.1.251/24"
+print(addr:add("0.0.99.0")) -- "192.168.100.1/24"
-addr:add(256, true) -- true
-print(addr) -- "192.168.2.1/24
+addr:add(256, true) -- true
+print(addr) -- "192.168.2.1/24
-addr:add("255.0.0.0", true) -- false (overflow)
-print(addr) -- "255.255.255.255/24
+addr:add("255.0.0.0", true) -- false (overflow)
+print(addr) -- "255.255.255.255/24
local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64")
-print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64"
-print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64"
+print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64"
+print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64"
+
+addr6:add(256, true) -- true
+print(addr6) -- "fe80::221:63f:fe75:ab17/64
+
+addr6:add("ffff::", true) -- false (overflow)
+print(addr6) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"
+
+local mac = luci.ip.new("00:14:22:01:23:45")
+print(mac:add(256)) -- "00:14:22:01:24:45"
+print(mac:add("0:0:0:0:FF:0") -- "00:14:22:02:22:45"
-addr:add(256, true) -- true
-print(addr) -- "fe80::221:63f:fe75:ab17/64
+mac:add(256, true) -- true
+print(mac) -- "00:14:22:01:24:45"
-addr:add("ffff::", true) -- false (overflow)
-print(addr) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"</pre>
+mac:add("FF:FF:0:0:0:0", true) -- false (overflow)
+print(mac) -- "FF:FF:FF:FF:FF:FF"</pre>
<h3>Return value:</h3>
<ul>
- <li>When adding inplace: Return <code>true</code> if the addition succeeded
+ <li>When adding inplace: Return <code>true</code> if the addition succeded
or <code>false</code> when the addition overflowed.</li>
<li>When deriving new CIDR: Return new instance representing the value of
this instance plus the added amount or the highest possible address if
<dd>
-Subtract given amount from CIDR instance. If the result would under, the lowest
+Subtract given amount from CIDR instance. If the result would under, the lowest
possible address is returned.
print(addr) -- "fe80::221:63f:fe75:a917/64"
addr:sub("ffff::", true) -- false (underflow)
-print(addr) -- "::/64"</pre>
+print(addr) -- "::/64"
+
+local mac = luci.ip.new("00:14:22:01:23:45")
+print(mac:sub(256)) -- "00:14:22:01:22:45"
+print(mac:sub("0:0:0:0:FF:0") -- "00:14:22:00:24:45"
+
+mac:sub(256, true) -- true
+print(mac) -- "00:14:22:01:22:45"
+
+mac:sub("FF:FF:0:0:0:0", true) -- false (overflow)
+print(mac) -- "00:00:00:00:00:00"</pre>
<h3>Return value:</h3>
<ul>
- <li>When subtracting inplace: Return <code>true</code> if the subtraction
- succeeded or <code>false</code> when the subtraction underflowed.</li>
+ <li>When subtracting inplace: Return <code>true</code> if the subtraction
+ succeeded or <code>false</code> when the subtraction underflowed.</li>
<li>When deriving new CIDR: Return new instance representing the value of
- this instance minus the subtracted amount or the lowest address if
+ this instance minus the subtracted amount or the lowest address if
the subtraction underflowed.</li></ul>
print(addr:minhost()) -- "192.168.123.1"
local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
-print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"</pre>
+print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"
+
+local mac = luci.ip.new("00:14:22:01:22:45/32")
+print(mac:minhost()) -- "00:14:22:01:00:01"</pre>
print(addr:maxhost()) -- "192.168.123.254" (.255 is broadcast)
local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
-print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"</pre>
+print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"
+
+local mac = luci.ip.new("00:14:22:01:22:45/32")
+print(mac:maxhost()) -- "00:14:22:01:FF:FF"</pre>
Convert CIDR instance into string representation.
-If the prefix size of instance is less than 32 for IPv4 or 128 for IPv6, the
-address is returned in the form "address/prefix" otherwise just "address".
+If the prefix size of instance is less than 32 for IPv4, 128 for IPv6 or 48 for
+MACs, the address is returned in the form "address/prefix" otherwise just
+"address".
It is usually not required to call this function directly as CIDR objects
define it as __tostring function in the associated metatable.
Construct a new IPv6 luci.ip.cidr instance.</td>
</tr>
+ <tr>
+ <td class="name" nowrap><a href="#MAC">MAC</a> (address, netmask)</td>
+ <td class="summary">
+
+Construct a new MAC luci.ip.cidr instance.</td>
+ </tr>
+
+ <tr>
+ <td class="name" nowrap><a href="#checkip4">checkip4</a> (address)</td>
+ <td class="summary">
+
+Verify an IPv4 address.</td>
+ </tr>
+
+ <tr>
+ <td class="name" nowrap><a href="#checkip6">checkip6</a> (address)</td>
+ <td class="summary">
+
+Verify an IPv6 address.</td>
+ </tr>
+
+ <tr>
+ <td class="name" nowrap><a href="#checkmac">checkmac</a> (address)</td>
+ <td class="summary">
+
+Verify an ethernet MAC address.</td>
+ </tr>
+
<tr>
<td class="name" nowrap><a href="#route">route</a> (address)</td>
<td class="summary">
IPv6
</a>
+ <li><a href="#MAC">
+ MAC
+ </a>
+
</ul>
</dd>
IPv6
</a>
+ <li><a href="#MAC">
+ MAC
+ </a>
+
</ul>
</dd>
IPv4
</a>
+ <li><a href="#MAC">
+ MAC
+ </a>
+
+</ul>
+
+</dd>
+
+
+
+
+<dt><a name="MAC"></a><strong>MAC</strong> (address, netmask)</dt>
+<dd>
+
+
+Construct a new MAC luci.ip.cidr instance.
+Throws an error if the given string does not represent a valid ethernet MAC
+address or if the given optional mask is of a different family.
+
+
+<h3>Parameters</h3>
+<ul>
+
+ <li>
+ address: String containing a valid ethernet MAC address, optionally with
+prefix size (CIDR notation) or mask separated by slash.
+ </li>
+
+ <li>
+ netmask: String containing a valid MAC address mask or number
+containing a prefix size between <code>0</code> and <code>48</code> bit.
+Overrides mask embedded in the first argument if specified. (optional)
+ </li>
+
+</ul>
+
+
+
+
+<h3>Usage:</h3>
+<pre>intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24")
+intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/FF:FF:FF:0:0:0")
+intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00", "FF:FF:FF:0:0:0")
+intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24", 48) -- override mask</pre>
+
+
+
+<h3>Return value:</h3>
+A <code>luci.ip.cidr</code> object representing the given MAC address range.
+
+
+
+<h3>See also:</h3>
+<ul>
+
+ <li><a href="#IPv4">
+ IPv4
+ </a>
+
+ <li><a href="#IPv6">
+ IPv6
+ </a>
+
+</ul>
+
+</dd>
+
+
+
+
+<dt><a name="checkip4"></a><strong>checkip4</strong> (address)</dt>
+<dd>
+
+
+Verify an IPv4 address.
+
+Checks whether given argument is a preexisting luci.ip.cidr IPv4 address
+instance or a string literal convertible to an IPv4 address and returns a
+plain Lua string containing the canonical representation of the address.
+
+If the argument is not a valid address, returns nothing. This function is
+intended to aid in safely verifying address literals without having to deal
+with exceptions.
+
+
+<h3>Parameters</h3>
+<ul>
+
+ <li>
+ address: String containing a valid IPv4 address or existing
+luci.ip.cidr IPv4 instance.
+ </li>
+
+</ul>
+
+
+
+
+<h3>Usage:</h3>
+<pre>ipv4 = luci.ip.checkip4(luci.ip.new("127.0.0.1")) -- "127.0.0.1"
+ipv4 = luci.ip.checkip4("127.0.0.1") -- "127.0.0.1"
+ipv4 = luci.ip.checkip4("nonesense") -- nothing
+ipv4 = luci.ip.checkip4(123) -- nothing
+ipv4 = luci.ip.checkip4(nil) -- nothing
+ipv4 = luci.ip.checkip4() -- nothing</pre>
+
+
+
+<h3>Return value:</h3>
+A string representing the given IPv4 address.
+
+
+
+<h3>See also:</h3>
+<ul>
+
+ <li><a href="#checkip6">
+ checkip6
+ </a>
+
+ <li><a href="#checkmac">
+ checkmac
+ </a>
+
+</ul>
+
+</dd>
+
+
+
+
+<dt><a name="checkip6"></a><strong>checkip6</strong> (address)</dt>
+<dd>
+
+
+Verify an IPv6 address.
+
+Checks whether given argument is a preexisting luci.ip.cidr IPv6 address
+instance or a string literal convertible to an IPv6 address and returns a
+plain Lua string containing the canonical representation of the address.
+
+If the argument is not a valid address, returns nothing. This function is
+intended to aid in safely verifying address literals without having to deal
+with exceptions.
+
+
+<h3>Parameters</h3>
+<ul>
+
+ <li>
+ address: String containing a valid IPv6 address or existing
+luci.ip.cidr IPv6 instance.
+ </li>
+
+</ul>
+
+
+
+
+<h3>Usage:</h3>
+<pre>ipv6 = luci.ip.checkip6(luci.ip.new("0:0:0:0:0:0:0:1")) -- "::1"
+ipv6 = luci.ip.checkip6("0:0:0:0:0:0:0:1") -- "::1"
+ipv6 = luci.ip.checkip6("nonesense") -- nothing
+ipv6 = luci.ip.checkip6(123) -- nothing
+ipv6 = luci.ip.checkip6(nil) -- nothing
+ipv6 = luci.ip.checkip6() -- nothing</pre>
+
+
+
+<h3>Return value:</h3>
+A string representing the given IPv6 address.
+
+
+
+<h3>See also:</h3>
+<ul>
+
+ <li><a href="#checkip4">
+ checkip4
+ </a>
+
+ <li><a href="#checkmac">
+ checkmac
+ </a>
+
+</ul>
+
+</dd>
+
+
+
+
+<dt><a name="checkmac"></a><strong>checkmac</strong> (address)</dt>
+<dd>
+
+
+Verify an ethernet MAC address.
+
+Checks whether given argument is a preexisting luci.ip.cidr MAC address
+instance or a string literal convertible to an ethernet MAC and returns a
+plain Lua string containing the canonical representation of the address.
+
+If the argument is not a valid address, returns nothing. This function is
+intended to aid in safely verifying address literals without having to deal
+with exceptions.
+
+
+<h3>Parameters</h3>
+<ul>
+
+ <li>
+ address: String containing a valid MAC address or existing luci.ip.cidr
+MAC address instance.
+ </li>
+
+</ul>
+
+
+
+
+<h3>Usage:</h3>
+<pre>mac = luci.ip.checkmac(luci.ip.new("00-11-22-cc-dd-ee")) -- "00:11:22:CC:DD:EE"
+mac = luci.ip.checkmac("00:11:22:cc:dd:ee") -- "00:11:22:CC:DD:EE"
+mac = luci.ip.checkmac("nonesense") -- nothing
+mac = luci.ip.checkmac(123) -- nothing
+mac = luci.ip.checkmac(nil) -- nothing
+mac = luci.ip.checkmac() -- nothing</pre>
+
+
+
+<h3>Return value:</h3>
+A string representing the given MAC address.
+
+
+
+<h3>See also:</h3>
+<ul>
+
+ <li><a href="#checkip4">
+ checkip4
+ </a>
+
+ <li><a href="#checkip6">
+ checkip6
+ </a>
+
</ul>
</dd>
</tr>
<tr>
<td><code>mac</code></td>
- <td>String containing the associated MAC address</td>
+ <td>MAC address <code>luci.ip.cidr</code> instance</td>
</tr>
<tr>
<td><code>router</code></td>
</tr>
<tr>
<td><code>mac</code></td>
- <td>String containing the link local address of the device in
- dotted hex notation</td>
+ <td>MAC address <code>luci.ip.cidr</code> instance representing the device ethernet
+ address</td>
</tr>
</table>
<dd>
-Count the occurrences of given substring in given string.
+Count the occurrences of given substring in given string.
<br />In general all functions are namend and behave like their POSIX API
counterparts - where applicable - applying the following rules:
<ul>
- <li>Functions should be named like the underlying POSIX API function omitting
+ <li>Functions should be named like the underlying POSIX API function omitting
prefixes or suffixes - especially when placed in an object-context (
lockf -> File:lock, fsync -> File:sync, dup2 -> dup, ...)</li>
<li>If you are unclear about the behaviour of a function you should consult
<li>If the name is significantly different from the POSIX-function, the
underlying function(s) are stated in the documentation.</li>
<li>Parameters should reflect those of the C-API, buffer length arguments and
- by-reference parameters should be omitted for pratical purposes.</li>
+ by-reference parameters should be omitted for practical purposes.</li>
<li>If a C function accepts a bitfield as parameter, it should be translated
into lower case string flags representing the flags if the bitfield is the
- last parameter and also omitting prefixes or suffixes. (e.g. waitpid
+ last parameter and also omitting prefixes or suffixes. (e.g. waitpid
(pid, &s, WNOHANG | WUNTRACED) -> waitpid(pid, "nohang", "untraced"),
getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) ->
Socket:getopt("socket", "reuseaddr"), etc.) </li>
<li>The blocksize given is only advisory and to be seen as an upper limit,
if an underlying read returns less bytes the chunk is nevertheless returned.
- <li>If the limit parameter is omitted, the iterator returns data
+ <li>If the limit parameter is omitted, the iterator returns data
until an end-of-file, end-of-stream, connection shutdown or similar happens.
<li>The iterator will not buffer so it is safe to mix with calls to read.
<li>This function uses the blocksource function of the source descriptor
and the sink function of the target descriptor.
- <li>If the limit parameter is omitted, data is copied
+ <li>If the limit parameter is omitted, data is copied
until an end-of-file, end-of-stream, connection shutdown or similar happens.
<li>If the descriptor is non-blocking the function may fail with EAGAIN.
blocksource function of the source descriptor and the sink function
of the target descriptor as a fallback mechanism.
- <li>If the limit parameter is omitted, data is copied
+ <li>If the limit parameter is omitted, data is copied
until an end-of-file, end-of-stream, connection shutdown or similar happens.
<li>If the descriptor is non-blocking the function may fail with EAGAIN.
you can pass "true" to the iterator which will flush the buffer
and return the bufferd data.
- <li>If the limit parameter is omitted, this function uses the nixio
+ <li>If the limit parameter is omitted, this function uses the nixio
buffersize (8192B by default).
<li>If the descriptor is non-blocking the iterator may fail with EAGAIN.
<li>This function uses the low-level read function of the descriptor.
- <li>If the length parameter is omitted, this function returns all data
+ <li>If the length parameter is omitted, this function returns all data
that can be read before an end-of-file, end-of-stream, connection shutdown
or similar happens.
/*
-Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
+Copyright 2015-2018 Jo-Philipp Wich <jo@mein.io>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
#define RTA_INT(x) (*(int *)RTA_DATA(x))
#define RTA_U32(x) (*(uint32_t *)RTA_DATA(x))
+#define AF_BITS(f) \
+ ((f) == AF_INET ? 32 : \
+ ((f) == AF_INET6 ? 128 : \
+ ((f) == AF_PACKET ? 48 : 0)))
+
+#define AF_BYTES(f) \
+ ((f) == AF_INET ? 4 : \
+ ((f) == AF_INET6 ? 16 : \
+ ((f) == AF_PACKET ? 6 : 0)))
+
static int hz = 0;
static struct nl_sock *sock = NULL;
union {
struct in_addr v4;
struct in6_addr v6;
+ struct ether_addr mac;
+ uint8_t u8[16];
} addr;
- int len;
- int bits;
- int family;
- bool exact;
+ uint16_t family;
+ int16_t bits;
} cidr_t;
struct dump_filter {
cidr_t src;
cidr_t dst;
struct ether_addr mac;
+ bool from_exact;
+ bool dst_exact;
};
struct dump_state {
return NULL;
}
-static bool parse_mask(int family, const char *mask, int *bits)
+static bool parse_mac(const char *mac, struct ether_addr *ea)
+{
+ unsigned long int n;
+ char *e, sep = 0;
+ int i;
+
+ for (i = 0; i < 6; i++)
+ {
+ if (i > 0)
+ {
+ if (sep == 0 && (mac[0] == ':' || mac[0] == '-'))
+ sep = mac[0];
+
+ if (sep == 0 || mac[0] != sep)
+ return false;
+
+ mac++;
+ }
+
+ n = strtoul(mac, &e, 16);
+
+ if (n > 0xFF)
+ return false;
+
+ mac += (e - mac);
+ ea->ether_addr_octet[i] = n;
+ }
+
+ if (mac[0] != 0)
+ return false;
+
+ return true;
+}
+
+static bool parse_mask(int family, const char *mask, int16_t *bits)
{
char *e;
- struct in_addr m;
- struct in6_addr m6;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ struct ether_addr mac;
+ uint8_t u8[16];
+ } m;
- if (family == AF_INET && inet_pton(AF_INET, mask, &m))
+ if (family == AF_INET && inet_pton(AF_INET, mask, &m.v4))
{
- for (*bits = 0, m.s_addr = ntohl(m.s_addr);
- *bits < 32 && (m.s_addr << *bits) & 0x80000000;
+ for (*bits = 0, m.v4.s_addr = ntohl(m.v4.s_addr);
+ *bits < AF_BITS(AF_INET) && (m.v4.s_addr << *bits) & 0x80000000;
++*bits);
}
- else if (family == AF_INET6 && inet_pton(AF_INET6, mask, &m6))
+ else if ((family == AF_INET6 && inet_pton(AF_INET6, mask, &m.v6)) ||
+ (family == AF_PACKET && parse_mac(mask, &m.mac)))
{
for (*bits = 0;
- *bits < 128 && (m6.s6_addr[*bits / 8] << (*bits % 8)) & 128;
+ *bits < AF_BITS(family) && (m.u8[*bits / 8] << (*bits % 8)) & 128;
++*bits);
}
else
{
*bits = strtoul(mask, &e, 10);
- if (e == mask || *e != 0 || *bits > ((family == AF_INET) ? 32 : 128))
+ if (e == mask || *e != 0 || *bits > AF_BITS(family))
return false;
}
static bool parse_cidr(const char *dest, cidr_t *pp)
{
char *p, buf[INET6_ADDRSTRLEN * 2 + 2];
- uint8_t bitlen = 0;
strncpy(buf, dest, sizeof(buf) - 1);
*p++ = 0;
if (inet_pton(AF_INET, buf, &pp->addr.v4))
- {
- bitlen = 32;
pp->family = AF_INET;
- pp->len = sizeof(struct in_addr);
- }
else if (inet_pton(AF_INET6, buf, &pp->addr.v6))
- {
- bitlen = 128;
pp->family = AF_INET6;
- pp->len = sizeof(struct in6_addr);
- }
+ else if (parse_mac(buf, &pp->addr.mac))
+ pp->family = AF_PACKET;
else
return false;
}
else
{
- pp->bits = bitlen;
+ pp->bits = AF_BITS(pp->family);
}
return true;
}
+static int format_cidr(lua_State *L, cidr_t *p)
+{
+ char buf[INET6_ADDRSTRLEN];
+
+ if (p->family == AF_PACKET)
+ {
+ snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X",
+ p->addr.mac.ether_addr_octet[0],
+ p->addr.mac.ether_addr_octet[1],
+ p->addr.mac.ether_addr_octet[2],
+ p->addr.mac.ether_addr_octet[3],
+ p->addr.mac.ether_addr_octet[4],
+ p->addr.mac.ether_addr_octet[5]);
+
+ if (p->bits < AF_BITS(AF_PACKET))
+ lua_pushfstring(L, "%s/%d", buf, p->bits);
+ else
+ lua_pushstring(L, buf);
+ }
+ else
+ {
+ if (p->bits < AF_BITS(p->family))
+ lua_pushfstring(L, "%s/%d",
+ inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)),
+ p->bits);
+ else
+ lua_pushstring(L,
+ inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)));
+ }
+
+ return 1;
+}
+
static int L_getint(lua_State *L, int index, const char *name)
{
int rv = 0;
if (family == AF_INET)
{
p->family = AF_INET;
- p->bits = (bits < 0) ? 32 : bits;
- p->len = sizeof(p->addr.v4);
+ p->bits = (bits < 0) ? AF_BITS(AF_INET) : bits;
p->addr.v4 = *(struct in_addr *)addr;
}
- else
+ else if (family == AF_INET6)
{
p->family = AF_INET6;
- p->bits = (bits < 0) ? 128 : bits;
- p->len = sizeof(p->addr.v6);
+ p->bits = (bits < 0) ? AF_BITS(AF_INET6) : bits;
p->addr.v6 = *(struct in6_addr *)addr;
}
+ else
+ {
+ p->family = AF_PACKET;
+ p->bits = (bits < 0) ? AF_BITS(AF_PACKET) : bits;
+ p->addr.mac = *(struct ether_addr *)addr;
+ }
luaL_getmetatable(L, LUCI_IP_CIDR);
lua_setmetatable(L, -2);
static int L_checkbits(lua_State *L, int index, cidr_t *p)
{
+ int16_t s16;
int bits;
if (lua_gettop(L) < index || lua_isnil(L, index))
{
bits = lua_tointeger(L, index);
- if (bits < 0 || bits > ((p->family == AF_INET) ? 32 : 128))
+ if (bits < 0 || bits > AF_BITS(p->family))
return luaL_error(L, "Invalid prefix size");
}
else if (lua_type(L, index) == LUA_TSTRING)
{
- if (!parse_mask(p->family, lua_tostring(L, index), &bits))
+ if (!parse_mask(p->family, lua_tostring(L, index), &s16))
return luaL_error(L, "Invalid netmask format");
+
+ bits = s16;
}
else
{
if (family == AF_INET6)
{
cidr.family = AF_INET6;
- cidr.bits = 128;
- cidr.len = sizeof(cidr.addr.v6);
cidr.addr.v6.s6_addr[12] = n;
cidr.addr.v6.s6_addr[13] = (n >> 8);
cidr.addr.v6.s6_addr[14] = (n >> 16);
cidr.addr.v6.s6_addr[15] = (n >> 24);
}
- else
+ else if (family == AF_INET)
{
cidr.family = AF_INET;
- cidr.bits = 32;
- cidr.len = sizeof(cidr.addr.v4);
cidr.addr.v4.s_addr = n;
}
+ else
+ {
+ cidr.family = AF_PACKET;
+ cidr.addr.mac.ether_addr_octet[2] = n;
+ cidr.addr.mac.ether_addr_octet[3] = (n >> 8);
+ cidr.addr.mac.ether_addr_octet[4] = (n >> 16);
+ cidr.addr.mac.ether_addr_octet[5] = (n >> 24);
+ }
+
+ cidr.bits = AF_BITS(cidr.family);
}
else
{
return _cidr_new(L, 1, AF_INET6, true);
}
+static int cidr_mac(lua_State *L)
+{
+ return _cidr_new(L, 1, AF_PACKET, true);
+}
+
+static int cidr_check(lua_State *L, int family)
+{
+ cidr_t cidr = { }, *cidrp;
+ const char *addr;
+
+ if (lua_type(L, 1) == LUA_TSTRING)
+ {
+ addr = lua_tostring(L, 1);
+
+ if (addr && parse_cidr(addr, &cidr) && cidr.family == family)
+ return format_cidr(L, &cidr);
+ }
+ else
+ {
+ cidrp = lua_touserdata(L, 1);
+
+ if (cidrp == NULL)
+ return 0;
+
+ if (!lua_getmetatable(L, 1))
+ return 0;
+
+ lua_getfield(L, LUA_REGISTRYINDEX, LUCI_IP_CIDR);
+
+ if (!lua_rawequal(L, -1, -2))
+ cidrp = NULL;
+
+ lua_pop(L, 2);
+
+ if (cidrp != NULL && cidrp->family == family)
+ return format_cidr(L, cidrp);
+ }
+
+ return 0;
+}
+
+static int cidr_checkip4(lua_State *L)
+{
+ return cidr_check(L, AF_INET);
+}
+
+static int cidr_checkip6(lua_State *L)
+{
+ return cidr_check(L, AF_INET6);
+}
+
+static int cidr_checkmac(lua_State *L)
+{
+ return cidr_check(L, AF_PACKET);
+}
+
static int cidr_is4(lua_State *L)
{
cidr_t *p = L_checkcidr(L, 1, NULL);
return 1;
}
+static int cidr_ismac(lua_State *L)
+{
+ cidr_t *p = L_checkcidr(L, 1, NULL);
+
+ lua_pushboolean(L, p->family == AF_PACKET);
+ return 1;
+}
+
+static int cidr_ismacmcast(lua_State *L)
+{
+ cidr_t *p = L_checkcidr(L, 1, NULL);
+
+ lua_pushboolean(L, (p->family == AF_PACKET &&
+ (p->addr.mac.ether_addr_octet[0] & 0x1)));
+
+ return 1;
+}
+
+static int cidr_ismaclocal(lua_State *L)
+{
+ cidr_t *p = L_checkcidr(L, 1, NULL);
+
+ lua_pushboolean(L, (p->family == AF_PACKET &&
+ (p->addr.mac.ether_addr_octet[0] & 0x2)));
+
+ return 1;
+}
+
static int _cidr_cmp(lua_State *L)
{
cidr_t *a = L_checkcidr(L, 1, NULL);
if (a->family != b->family)
return (a->family - b->family);
- return memcmp(&a->addr.v6, &b->addr.v6, a->len);
+ return memcmp(&a->addr.v6, &b->addr.v6, AF_BYTES(a->family));
}
static int cidr_lower(lua_State *L)
if (bits <= 0)
{
- memset(&p->addr.v6, inv * 0xFF, p->len);
+ memset(&p->addr.u8, inv * 0xFF, AF_BYTES(p->family));
}
- else if (p->family == AF_INET && bits <= 32)
+ else if (p->family == AF_INET && bits <= AF_BITS(AF_INET))
{
if (inv)
- p->addr.v4.s_addr |= ntohl((1 << (32 - bits)) - 1);
+ p->addr.v4.s_addr |= ntohl((1 << (AF_BITS(AF_INET) - bits)) - 1);
else
- p->addr.v4.s_addr &= ntohl(~((1 << (32 - bits)) - 1));
+ p->addr.v4.s_addr &= ntohl(~((1 << (AF_BITS(AF_INET) - bits)) - 1));
}
- else if (p->family == AF_INET6 && bits <= 128)
+ else if (bits <= AF_BITS(p->family))
{
- for (i = 0; i < sizeof(p->addr.v6.s6_addr); i++)
+ for (i = 0; i < AF_BYTES(p->family); i++)
{
b = (bits > 8) ? 8 : bits;
if (inv)
- p->addr.v6.s6_addr[i] |= ~((uint8_t)(0xFF << (8 - b)));
+ p->addr.u8[i] |= ~((uint8_t)(0xFF << (8 - b)));
else
- p->addr.v6.s6_addr[i] &= (uint8_t)(0xFF << (8 - b));
+ p->addr.u8[i] &= (uint8_t)(0xFF << (8 - b));
bits -= b;
}
}
return 0;
*p2 = *p1;
- p2->bits = (p1->family == AF_INET) ? 32 : 128;
+ p2->bits = AF_BITS(p1->family);
_apply_mask(p2, bits, false);
luaL_getmetatable(L, LUCI_IP_CIDR);
return 0;
*p2 = *p1;
- p2->bits = (p1->family == AF_INET) ? 32 : 128;
+ p2->bits = AF_BITS(p1->family);
luaL_getmetatable(L, LUCI_IP_CIDR);
lua_setmetatable(L, -2);
if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
return 0;
- p2->bits = (p1->family == AF_INET) ? 32 : 128;
+ p2->bits = AF_BITS(p1->family);
p2->family = p1->family;
memset(&p2->addr.v6.s6_addr, 0xFF, sizeof(p2->addr.v6.s6_addr));
cidr_t *p2;
int bits = L_checkbits(L, 2, p1);
- if (p1->family == AF_INET6)
+ if (p1->family != AF_INET)
return 0;
if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
return 0;
*p2 = *p1;
- p2->bits = (p1->family == AF_INET) ? 32 : 128;
+ p2->bits = AF_BITS(AF_INET);
_apply_mask(p2, bits, true);
luaL_getmetatable(L, LUCI_IP_CIDR);
return 0;
p2->family = AF_INET;
- p2->bits = (p1->bits > 32) ? 32 : p1->bits;
+ p2->bits = (p1->bits > AF_BITS(AF_INET)) ? AF_BITS(AF_INET) : p1->bits;
memcpy(&p2->addr.v4, p1->addr.v6.s6_addr + 12, sizeof(p2->addr.v4));
luaL_getmetatable(L, LUCI_IP_CIDR);
return 1;
}
+static int cidr_tolinklocal(lua_State *L)
+{
+ cidr_t *p1 = L_checkcidr(L, 1, NULL);
+ cidr_t *p2;
+ int i;
+
+ if (p1->family != AF_PACKET)
+ return 0;
+
+ if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
+ return 0;
+
+ p2->family = AF_INET6;
+ p2->bits = AF_BITS(AF_INET6);
+ p2->addr.u8[0] = 0xFE;
+ p2->addr.u8[1] = 0x80;
+ p2->addr.u8[8] = p1->addr.u8[0] ^ 0x02;
+ p2->addr.u8[9] = p1->addr.u8[1];
+ p2->addr.u8[10] = p1->addr.u8[2];
+ p2->addr.u8[11] = 0xFF;
+ p2->addr.u8[12] = 0xFE;
+ p2->addr.u8[13] = p1->addr.u8[3];
+ p2->addr.u8[14] = p1->addr.u8[4];
+ p2->addr.u8[15] = p1->addr.u8[5];
+
+ luaL_getmetatable(L, LUCI_IP_CIDR);
+ lua_setmetatable(L, -2);
+ return 1;
+}
+
+static int cidr_tomac(lua_State *L)
+{
+ cidr_t *p1 = L_checkcidr(L, 1, NULL);
+ cidr_t *p2;
+ int i;
+
+ if (p1->family != AF_INET6 ||
+ p1->addr.u8[0] != 0xFE ||
+ p1->addr.u8[1] != 0x80 ||
+ p1->addr.u8[2] != 0x00 ||
+ p1->addr.u8[3] != 0x00 ||
+ p1->addr.u8[4] != 0x00 ||
+ p1->addr.u8[5] != 0x00 ||
+ p1->addr.u8[6] != 0x00 ||
+ p1->addr.u8[7] != 0x00 ||
+ p1->addr.u8[11] != 0xFF ||
+ p1->addr.u8[12] != 0xFE)
+ return 0;
+
+ if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
+ return 0;
+
+ p2->family = AF_PACKET;
+ p2->bits = AF_BITS(AF_PACKET);
+ p2->addr.u8[0] = p1->addr.u8[8] ^ 0x02;
+ p2->addr.u8[1] = p1->addr.u8[9];
+ p2->addr.u8[2] = p1->addr.u8[10];
+ p2->addr.u8[3] = p1->addr.u8[13];
+ p2->addr.u8[4] = p1->addr.u8[14];
+ p2->addr.u8[5] = p1->addr.u8[15];
+
+ luaL_getmetatable(L, LUCI_IP_CIDR);
+ lua_setmetatable(L, -2);
+ return 1;
+}
+
static int cidr_contains(lua_State *L)
{
cidr_t *p1 = L_checkcidr(L, 1, NULL);
_apply_mask(&a, p1->bits, false);
_apply_mask(&b, p1->bits, false);
- rv = !memcmp(&a.addr.v6, &b.addr.v6, a.len);
+ rv = !memcmp(&a.addr.v6, &b.addr.v6, AF_BYTES(a.family));
}
lua_pushboolean(L, rv);
return 1;
}
-#define S6_BYTE(a, i) \
- (a)->addr.v6.s6_addr[sizeof((a)->addr.v6.s6_addr) - (i) - 1]
+#define BYTE(a, i) \
+ (a)->addr.u8[AF_BYTES((a)->family) - (i) - 1]
static int _cidr_add_sub(lua_State *L, bool add)
{
if (p1->family == p2->family)
{
- if (p1->family == AF_INET6)
+ if (p1->family == AF_INET)
+ {
+ a = ntohl(p1->addr.v4.s_addr);
+ b = ntohl(p2->addr.v4.s_addr);
+
+ /* would over/underflow */
+ if ((add && (UINT_MAX - a) < b) || (!add && a < b))
+ {
+ r.addr.v4.s_addr = add * 0xFFFFFFFF;
+ ok = false;
+ }
+ else
+ {
+ r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b);
+ }
+ }
+ else
{
- for (i = 0, carry = 0; i < sizeof(r.addr.v6.s6_addr); i++)
+ for (i = 0, carry = 0; i < AF_BYTES(p1->family); i++)
{
if (add)
{
- S6_BYTE(&r, i) = S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry;
- carry = (S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry) / 256;
+ BYTE(&r, i) = BYTE(p1, i) + BYTE(p2, i) + carry;
+ carry = (BYTE(p1, i) + BYTE(p2, i) + carry) / 256;
}
else
{
- S6_BYTE(&r, i) = (S6_BYTE(p1, i) - S6_BYTE(p2, i) - carry);
- carry = (S6_BYTE(p1, i) < (S6_BYTE(p2, i) + carry));
+ BYTE(&r, i) = (BYTE(p1, i) - BYTE(p2, i) - carry);
+ carry = (BYTE(p1, i) < (BYTE(p2, i) + carry));
}
}
/* would over/underflow */
if (carry)
{
- memset(&r.addr.v6, add * 0xFF, sizeof(r.addr.v6));
+ memset(&r.addr.u8, add * 0xFF, AF_BYTES(r.family));
ok = false;
}
}
- else
- {
- a = ntohl(p1->addr.v4.s_addr);
- b = ntohl(p2->addr.v4.s_addr);
-
- /* would over/underflow */
- if ((add && (UINT_MAX - a) < b) || (!add && a < b))
- {
- r.addr.v4.s_addr = add * 0xFFFFFFFF;
- ok = false;
- }
- else
- {
- r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b);
- }
- }
}
else
{
_apply_mask(&r, r.bits, false);
- if (r.family == AF_INET6 && r.bits < 128)
+ if (r.family == AF_INET && r.bits < AF_BITS(AF_INET))
+ {
+ r.bits = AF_BITS(AF_INET);
+ r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) + 1);
+ }
+ else if (r.bits < AF_BITS(r.family))
{
- r.bits = 128;
+ r.bits = AF_BITS(r.family);
- for (i = 0, carry = 1; i < sizeof(r.addr.v6.s6_addr); i++)
+ for (i = 0, carry = 1; i < AF_BYTES(r.family); i++)
{
- rest = (S6_BYTE(&r, i) + carry) > 255;
- S6_BYTE(&r, i) += carry;
+ rest = (BYTE(&r, i) + carry) > 255;
+ BYTE(&r, i) += carry;
carry = rest;
}
}
- else if (r.family == AF_INET && r.bits < 32)
- {
- r.bits = 32;
- r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) + 1);
- }
if (!(p = lua_newuserdata(L, sizeof(*p))))
return 0;
_apply_mask(&r, r.bits, true);
- if (r.family == AF_INET && r.bits < 32)
+ if (r.family == AF_INET && r.bits < AF_BITS(AF_INET))
{
- r.bits = 32;
+ r.bits = AF_BITS(AF_INET);
r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) - 1);
}
- else if (r.family == AF_INET6)
+ else
{
- r.bits = 128;
+ r.bits = AF_BITS(r.family);
}
if (!(p = lua_newuserdata(L, sizeof(*p))))
static int cidr_tostring (lua_State *L)
{
- char buf[INET6_ADDRSTRLEN];
cidr_t *p = L_checkcidr(L, 1, NULL);
-
- if ((p->family == AF_INET && p->bits < 32) ||
- (p->family == AF_INET6 && p->bits < 128))
- {
- lua_pushfstring(L, "%s/%d",
- inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)),
- p->bits);
- }
- else
- {
- lua_pushstring(L, inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)));
- }
-
- return 1;
+ return format_cidr(L, p);
}
/*
* route functions
*/
-static bool diff_prefix(int family, void *addr, int bits, cidr_t *p)
+static bool diff_prefix(int family, void *addr, int bits, bool exact, cidr_t *p)
{
- uint8_t i, b, r;
+ uint8_t i, b, r, *a;
uint32_t m;
if (!p->family)
if (!addr || p->family != family || p->bits > bits)
return true;
- if (family == AF_INET6)
+ if (family == AF_INET)
{
- for (i = 0, r = p->bits; i < sizeof(struct in6_addr); i++)
+ m = p->bits ? htonl(~((1 << (AF_BITS(AF_INET) - p->bits)) - 1)) : 0;
+
+ if ((((struct in_addr *)addr)->s_addr & m) != (p->addr.v4.s_addr & m))
+ return true;
+ }
+ else
+ {
+ for (i = 0, a = addr, r = p->bits; i < AF_BYTES(p->family); i++)
{
b = r ? (0xFF << (8 - ((r > 8) ? 8 : r))) : 0;
- if ((((struct in6_addr *)addr)->s6_addr[i] & b) !=
- (p->addr.v6.s6_addr[i] & b))
+ if ((a[i] & b) != (p->addr.u8[i] & b))
return true;
r -= ((r > 8) ? 8 : r);
}
}
- else
- {
- m = p->bits ? htonl(~((1 << (32 - p->bits)) - 1)) : 0;
- if ((((struct in_addr *)addr)->s_addr & m) != (p->addr.v4.s_addr & m))
- return true;
- }
-
- return (p->exact && p->bits != bits);
+ return (exact && p->bits != bits);
}
static int cb_dump_route(struct nl_msg *msg, void *arg)
dst = tb[RTA_DST] ? RTA_DATA(tb[RTA_DST]) : &def;
gw = tb[RTA_GATEWAY] ? RTA_DATA(tb[RTA_GATEWAY]) : NULL;
- bitlen = (rt->rtm_family == AF_INET6) ? 128 : 32;
+ bitlen = AF_BITS(rt->rtm_family);
if ((f->type && rt->rtm_type != f->type) ||
(f->family && rt->rtm_family != f->family) ||
(f->iif && iif != f->iif) ||
(f->oif && oif != f->oif) ||
(f->table && table != f->table) ||
- diff_prefix(rt->rtm_family, from, rt->rtm_src_len, &f->from) ||
- diff_prefix(rt->rtm_family, dst, rt->rtm_dst_len, &f->dst) ||
- diff_prefix(rt->rtm_family, gw, bitlen, &f->gw) ||
- diff_prefix(rt->rtm_family, src, bitlen, &f->src))
+ diff_prefix(rt->rtm_family, from, rt->rtm_src_len,
+ f->from_exact, &f->from) ||
+ diff_prefix(rt->rtm_family, dst, rt->rtm_dst_len,
+ f->dst_exact, &f->dst) ||
+ diff_prefix(rt->rtm_family, gw, bitlen,
+ false, &f->gw) ||
+ diff_prefix(rt->rtm_family, src, bitlen,
+ false, &f->src))
goto out;
if (s->callback)
nlmsg_append(msg, &rtm, sizeof(rtm), 0);
if (filter->get)
- nla_put(msg, RTA_DST, filter->dst.len, &filter->dst.addr.v6);
+ nla_put(msg, RTA_DST, AF_BYTES(filter->dst.family),
+ &filter->dst.addr.v6);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_route, &s);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &s);
filter.dst = p;
if ((s = L_getstr(L, 1, "from_exact")) != NULL && parse_cidr(s, &p))
- filter.from = p, filter.from.exact = true;
+ filter.from = p, filter.from_exact = true;
if ((s = L_getstr(L, 1, "dest_exact")) != NULL && parse_cidr(s, &p))
- filter.dst = p, filter.dst.exact = true;
+ filter.dst = p, filter.dst_exact = true;
}
return _route_dump(L, &filter);
mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL;
- bitlen = (nd->ndm_family == AF_INET) ? 32 : 128;
+ bitlen = AF_BITS(nd->ndm_family);
if ((f->family && nd->ndm_family != f->family) ||
(f->iif && nd->ndm_ifindex != f->iif) ||
(f->type && !(f->type & nd->ndm_state)) ||
- diff_prefix(nd->ndm_family, dst, bitlen, &f->dst) ||
+ diff_prefix(nd->ndm_family, dst, bitlen, false, &f->dst) ||
diff_macaddr(mac, &f->mac))
goto out;
L_setaddr(s->L, "dest", nd->ndm_family, dst, -1);
if (mac)
- {
- snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
- mac->ether_addr_octet[0], mac->ether_addr_octet[1],
- mac->ether_addr_octet[2], mac->ether_addr_octet[3],
- mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
-
- lua_pushstring(s->L, buf);
- lua_setfield(s->L, -2, "mac");
- }
+ L_setaddr(s->L, "mac", AF_PACKET, mac, -1);
s->index++;
static int cb_dump_link(struct nl_msg *msg, void *arg)
{
- char *p, *addr, buf[48];
+ char buf[48];
struct dump_state *s = arg;
struct nlmsghdr *hdr = nlmsg_hdr(msg);
struct ifinfomsg *ifm = NLMSG_DATA(hdr);
if (tb[IFLA_MASTER])
L_setdev(s->L, "master", tb[IFLA_MASTER]);
- if (tb[IFLA_ADDRESS])
- {
- len = nla_len(tb[IFLA_ADDRESS]);
- addr = nla_get_string(tb[IFLA_ADDRESS]);
-
- if ((len * 3) <= sizeof(buf))
- {
- for (p = buf, i = 0; i < len; i++)
- p += sprintf(p, "%s%02x", (i ? ":" : ""), (uint8_t)*addr++);
-
- L_setstr(s->L, "mac", buf);
- }
- }
+ if (tb[IFLA_ADDRESS] && nla_len(tb[IFLA_ADDRESS]) == AF_BYTES(AF_PACKET))
+ L_setaddr(s->L, "mac", AF_PACKET, nla_get_string(tb[IFLA_ADDRESS]), -1);
s->pending = 0;
return NL_SKIP;
{ "new", cidr_new },
{ "IPv4", cidr_ipv4 },
{ "IPv6", cidr_ipv6 },
+ { "MAC", cidr_mac },
+
+ { "checkip4", cidr_checkip4 },
+ { "checkip6", cidr_checkip6 },
+ { "checkmac", cidr_checkmac },
{ "route", route_get },
{ "routes", route_dump },
{ "neighbors", neighbor_dump },
- { "link", link_get },
+ { "link", link_get },
{ }
};
{ "is6", cidr_is6 },
{ "is6linklocal", cidr_is6linklocal },
{ "is6mapped4", cidr_is6mapped4 },
+ { "ismac", cidr_ismac },
+ { "ismaclocal", cidr_ismaclocal },
+ { "ismacmcast", cidr_ismacmcast },
{ "lower", cidr_lower },
{ "higher", cidr_higher },
{ "equal", cidr_equal },
{ "mask", cidr_mask },
{ "broadcast", cidr_broadcast },
{ "mapped4", cidr_mapped4 },
- { "contains", cidr_contains },
+ { "tomac", cidr_tomac },
+ { "tolinklocal", cidr_tolinklocal },
+ { "contains", cidr_contains },
{ "add", cidr_add },
{ "sub", cidr_sub },
{ "minhost", cidr_minhost },
addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask`
@see IPv4
@see IPv6
+@see MAC
]]
---[[
addr = luci.ip.IPv4("10.24.0.1", "255.255.255.0") -- separate netmask
addr = luci.ip.IPv4("10.24.0.1/24", 16) -- override netmask`
@see IPv6
+@see MAC
]]
---[[
addr6 = luci.ip.IPv6("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::")
addr6 = luci.ip.IPv6("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask`
@see IPv4
+@see MAC
]]
---[[
-Determine the route leading to the given destination.
+Construct a new MAC luci.ip.cidr instance.
+Throws an error if the given string does not represent a valid ethernet MAC
+address or if the given optional mask is of a different family.
@class function
@sort 4
+@name MAC
+@param address String containing a valid ethernet MAC address, optionally with
+prefix size (CIDR notation) or mask separated by slash.
+@param netmask String containing a valid MAC address mask or number
+containing a prefix size between `0` and `48` bit.
+Overrides mask embedded in the first argument if specified. (optional)
+@return A `luci.ip.cidr` object representing the given MAC address range.
+@usage `intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24")
+intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/FF:FF:FF:0:0:0")
+intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00", "FF:FF:FF:0:0:0")
+intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24", 48) -- override mask`
+@see IPv4
+@see IPv6
+]]
+
+---[[
+Verify an IPv4 address.
+
+Checks whether given argument is a preexisting luci.ip.cidr IPv4 address
+instance or a string literal convertible to an IPv4 address and returns a
+plain Lua string containing the canonical representation of the address.
+
+If the argument is not a valid address, returns nothing. This function is
+intended to aid in safely verifying address literals without having to deal
+with exceptions.
+@class function
+@sort 5
+@name checkip4
+@param address String containing a valid IPv4 address or existing
+luci.ip.cidr IPv4 instance.
+@return A string representing the given IPv4 address.
+@usage `ipv4 = luci.ip.checkip4(luci.ip.new("127.0.0.1")) -- "127.0.0.1"
+ipv4 = luci.ip.checkip4("127.0.0.1") -- "127.0.0.1"
+ipv4 = luci.ip.checkip4("nonesense") -- nothing
+ipv4 = luci.ip.checkip4(123) -- nothing
+ipv4 = luci.ip.checkip4(nil) -- nothing
+ipv4 = luci.ip.checkip4() -- nothing`
+@see checkip6
+@see checkmac
+]]
+
+---[[
+Verify an IPv6 address.
+
+Checks whether given argument is a preexisting luci.ip.cidr IPv6 address
+instance or a string literal convertible to an IPv6 address and returns a
+plain Lua string containing the canonical representation of the address.
+
+If the argument is not a valid address, returns nothing. This function is
+intended to aid in safely verifying address literals without having to deal
+with exceptions.
+@class function
+@sort 6
+@name checkip6
+@param address String containing a valid IPv6 address or existing
+luci.ip.cidr IPv6 instance.
+@return A string representing the given IPv6 address.
+@usage `ipv6 = luci.ip.checkip6(luci.ip.new("0:0:0:0:0:0:0:1")) -- "::1"
+ipv6 = luci.ip.checkip6("0:0:0:0:0:0:0:1") -- "::1"
+ipv6 = luci.ip.checkip6("nonesense") -- nothing
+ipv6 = luci.ip.checkip6(123) -- nothing
+ipv6 = luci.ip.checkip6(nil) -- nothing
+ipv6 = luci.ip.checkip6() -- nothing`
+@see checkip4
+@see checkmac
+]]
+
+---[[
+Verify an ethernet MAC address.
+
+Checks whether given argument is a preexisting luci.ip.cidr MAC address
+instance or a string literal convertible to an ethernet MAC and returns a
+plain Lua string containing the canonical representation of the address.
+
+If the argument is not a valid address, returns nothing. This function is
+intended to aid in safely verifying address literals without having to deal
+with exceptions.
+@class function
+@sort 7
+@name checkmac
+@param address String containing a valid MAC address or existing luci.ip.cidr
+MAC address instance.
+@return A string representing the given MAC address.
+@usage `mac = luci.ip.checkmac(luci.ip.new("00-11-22-cc-dd-ee")) -- "00:11:22:CC:DD:EE"
+mac = luci.ip.checkmac("00:11:22:cc:dd:ee") -- "00:11:22:CC:DD:EE"
+mac = luci.ip.checkmac("nonesense") -- nothing
+mac = luci.ip.checkmac(123) -- nothing
+mac = luci.ip.checkmac(nil) -- nothing
+mac = luci.ip.checkmac() -- nothing`
+@see checkip4
+@see checkip6
+]]
+
+---[[
+Determine the route leading to the given destination.
+@class function
+@sort 8
@name route
@param address A `luci.ip.cidr` instance or a string containing
a valid IPv4 or IPv6 range as specified by `luci.ip.new()`.
---[[
Fetch all routes, optionally matching the given criteria.
@class function
-@sort 5
+@sort 9
@name routes
@param filter <p>Table containing one or more of the possible filter
critera described below (optional)</p><table>
---[[
Fetches entries from the IPv4 ARP and IPv6 neighbour kernel table
@class function
-@sort 6
+@sort 10
@name neighbors
@param filter <p>Table containing one or more of the possible filter
critera described below (optional)</p><table>
</tr>
<tr>
<td>`mac`</td>
- <td>String containing the associated MAC address</td>
+ <td>MAC address `luci.ip.cidr` instance</td>
</tr>
<tr>
<td>`router`</td>
---[[
Fetch basic device information
@class function
-@sort 7
+@sort 11
@name link
@param device String containing the network device to query
@return If the given interface is found, a table containing the fields
</tr>
<tr>
<td>`mac`</td>
- <td>String containing the link local address of the device in
- dotted hex notation</td>
+ <td>MAC address `luci.ip.cidr` instance representing the device ethernet
+ address</td>
</tr>
</table>
@usage <ul>
@sort 1
@name cidr.is4
@see cidr.is6
+@see cidr.ismac
@return `true` if the CIDR is an IPv4 range, else `false`
]]
@sort 4
@name cidr.is6
@see cidr.is4
+@see cidr.ismac
@return `true` if the CIDR is an IPv6 range, else `false`
]]
end`
]]
+---[[
+Checks whether the CIDR instance is an ethernet MAC address range
+
+@class function
+@sort 7
+@name cidr.ismac
+@see cidr.is4
+@see cidr.is6
+@return `true` if the CIDR is a MAC address range, else `false`
+]]
+
+---[[
+Checks whether the CIDR instance is a locally administered (LAA) MAC address
+
+@class function
+@sort 8
+@name cidr.ismaclocal
+@return `true` if the MAC address sets the locally administered bit.
+@usage `local mac = luci.ip.new("02:C0:FF:EE:00:01")
+if mac:ismaclocal() then
+ print("Is an LAA MAC address")
+end`
+]]
+
+---[[
+Checks whether the CIDR instance is a multicast MAC address
+
+@class function
+@sort 9
+@name cidr.ismacmcast
+@return `true` if the MAC address sets the multicast bit.
+@usage `local mac = luci.ip.new("01:00:5E:7F:00:10")
+if addr:ismacmcast() then
+ print("Is a multicast MAC address")
+end`
+]]
+
---[[
Checks whether this CIDR instance is lower than the given argument.
The comparisation follows these rules:
-<ul><li>An IPv4 address is always lower than an IPv6 address</li>
+<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses
+are considered lower than MAC addresses</li>
<li>Prefix sizes are ignored</li></ul>
@class function
-@sort 7
+@sort 10
@name cidr.lower
@param addr A `luci.ip.cidr` instance or a string convertable by
`luci.ip.new()` to compare against.
print(addr:lower(addr)) -- false
print(addr:lower("10.10.10.10/24")) -- false
print(addr:lower(luci.ip.new("::1"))) -- true
-print(addr:lower(luci.ip.new("192.168.200.1"))) -- true`
+print(addr:lower(luci.ip.new("192.168.200.1"))) -- true
+print(addr:lower(luci.ip.new("00:14:22:01:23:45"))) -- true`
@see cidr.higher
@see cidr.equal
]]
---[[
Checks whether this CIDR instance is higher than the given argument.
The comparisation follows these rules:
-<ul><li>An IPv4 address is always lower than an IPv6 address</li>
+<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses
+are considered lower than MAC addresses</li>
<li>Prefix sizes are ignored</li></ul>
@class function
-@sort 8
+@sort 11
@name cidr.higher
@param addr A `luci.ip.cidr` instance or a string convertable by
`luci.ip.new()` to compare against.
print(addr:higher(addr)) -- false
print(addr:higher("10.10.10.10/24")) -- true
print(addr:higher(luci.ip.new("::1"))) -- false
-print(addr:higher(luci.ip.new("192.168.200.1"))) -- false`
+print(addr:higher(luci.ip.new("192.168.200.1"))) -- false
+print(addr:higher(luci.ip.new("00:14:22:01:23:45"))) -- false`
@see cidr.lower
@see cidr.equal
]]
Checks whether this CIDR instance is equal to the given argument.
@class function
-@sort 9
+@sort 12
@name cidr.equal
@param addr A `luci.ip.cidr` instance or a string convertable by
`luci.ip.new()` to compare against.
local addr6 = luci.ip.new("::1")
print(addr6:equal("0:0:0:0:0:0:0:1/64")) -- true
-print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false`
+print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false
+
+local mac = luci.ip.new("00:14:22:01:23:45")
+print(mac:equal("0:14:22:1:23:45")) -- true
+print(mac:equal(luci.ip.new("01:23:45:67:89:AB")) -- false`
@see cidr.lower
@see cidr.higher
]]
else the current prefix size is returned.
@class function
-@sort 10
+@sort 13
@name cidr.prefix
@param mask Either a number containing the number of bits (`0..32`
- for IPv4, `0..128` for IPv6) or a string containing a valid
- netmask (optional)
+ for IPv4, `0..128` for IPv6 or `0..48` for MAC addresses) or a string
+ containing a valid netmask (optional)
@return Bit count of the current prefix size
@usage `local range = luci.ip.new("192.168.1.1/255.255.255.0")
print(range:prefix()) -- 24
optional mask parameter.
@class function
-@sort 11
+@sort 14
@name cidr.network
@param mask Either a number containing the number of bits (`0..32`
- for IPv4, `0..128` for IPv6) or a string containing a valid
- netmask (optional)
+ for IPv4, `0..128` for IPv6 or `0..48` for MAC addresses) or a string
+ containing a valid netmask (optional)
@return CIDR instance representing the network address
@usage `local range = luci.ip.new("192.168.62.243/255.255.0.0")
print(range:network()) -- "192.168.0.0"
Derive host address of CIDR instance.
This function essentially constructs a copy of this CIDR with the prefix size
-set to `32` for IPv4 and `128` for IPv6.
+set to `32` for IPv4, `128` for IPv6 or `48` for MAC addresses.
@class function
-@sort 12
+@sort 15
@name cidr.host
@return CIDR instance representing the host address
@usage `local range = luci.ip.new("172.19.37.45/16")
prefix size can be overridden by the optional mask parameter.
@class function
-@sort 13
+@sort 16
@name cidr.mask
@param mask Either a number containing the number of bits (`0..32`
- for IPv4, `0..128` for IPv6) or a string containing a valid
- netmask (optional)
+ for IPv4, `0..128` for IPv6 or `0..48` for MAC addresses) or a string
+ containing a valid netmask (optional)
@return CIDR instance representing the netmask
@usage `local range = luci.ip.new("172.19.37.45/16")
print(range:mask()) -- "255.255.0.0"
Constructs a CIDR instance representing the broadcast address of this instance.
The used prefix size can be overridden by the optional mask parameter.
-This function has no effect on IPv6 instances, it will return nothing in this
-case.
+This function has no effect on IPv6 or MAC address instances, it will return
+nothing in this case.
@class function
-@sort 14
+@sort 17
@name cidr.broadcast
-@param mask Either a number containing the number of bits (`0..32`
- for IPv4, `0..128` for IPv6) or a string containing a valid
- netmask (optional)
+@param mask Either a number containing the number of bits (`0..32` for IPv4) or
+ a string containing a valid netmask (optional)
@return Return a new CIDR instance representing the broadcast address if this
instance is an IPv4 range, else return nothing.
@usage `local range = luci.ip.new("172.19.37.45/16")
Constructs a CIDR instance representing the IPv4 address of the IPv6 mapped
IPv4 address in this instance.
-This function has no effect on IPv4 instances or IPv6 instances which are not a
-mapped address, it will return nothing in this case.
+This function has no effect on IPv4 instances, MAC address instances or IPv6
+instances which are not a mapped address, it will return nothing in this case.
@class function
-@sort 15
+@sort 18
@name cidr.mapped4
@return Return a new CIDR instance representing the IPv4 address if this
instance is an IPv6 mapped IPv4 address, else return nothing.
print(addr:mapped4()) -- "172.16.19.1"`
]]
+---[[
+Derive MAC address of IPv6 link local CIDR instance.
+
+Constructs a CIDR instance representing the MAC address contained in the IPv6
+link local address of this instance.
+
+This function has no effect on IPv4 instances, MAC address instances or IPv6
+instances which are not a link local address, it will return nothing in this
+case.
+
+@class function
+@sort 19
+@name cidr.tomac
+@return Return a new CIDR instance representing the MAC address if this
+ instance is an IPv6 link local address, else return nothing.
+@usage `local addr = luci.ip.new("fe80::6666:b3ff:fe47:e1b9")
+print(addr:tomac()) -- "64:66:B3:47:E1:B9"`
+]]
+
+---[[
+Derive IPv6 link local address from MAC address CIDR instance.
+
+Constructs a CIDR instance representing the IPv6 link local address of the
+MAC address represented by this instance.
+
+This function has no effect on IPv4 instances or IPv6 instances, it will return
+nothing in this case.
+
+@class function
+@sort 20
+@name cidr.tolinklocal
+@return Return a new CIDR instance representing the IPv6 link local address.
+@usage `local mac = luci.ip.new("64:66:B3:47:E1:B9")
+print(mac:tolinklocal()) -- "fe80::6666:b3ff:fe47:e1b9"`
+]]
+
---[[
Test whether CIDR contains given range.
@class function
-@sort 16
+@sort 21
@name cidr.contains
@param addr A `luci.ip.cidr` instance or a string convertable by
`luci.ip.new()` to test.
local range6 = luci.ip.new("fe80::/10")
print(range6:contains("fe80::221:63f:fe75:aa17/64")) -- true
-print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false`
+print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false
+
+local intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24")
+print(intel_macs:contains("C0:B6:F9:A3:C:11")) -- true
+print(intel_macs:contains("64:66:B3:47:E1:B9")) -- false`
]]
---[[
address space, the result is set to the highest possible address.
@class function
-@sort 17
+@sort 22
@name cidr.add
@param amount A numeric value between 0 and 0xFFFFFFFF, a
`luci.ip.cidr` instance or a string convertable by
this instance plus the added amount or the highest possible address if
the addition overflowed the available address space.</li></ul>
@usage `local addr = luci.ip.new("192.168.1.1/24")
-print(addr:add(250)) -- "192.168.1.251/24"
-print(addr:add("0.0.99.0")) -- "192.168.100.1/24"
+print(addr:add(250)) -- "192.168.1.251/24"
+print(addr:add("0.0.99.0")) -- "192.168.100.1/24"
-addr:add(256, true) -- true
-print(addr) -- "192.168.2.1/24
+addr:add(256, true) -- true
+print(addr) -- "192.168.2.1/24
-addr:add("255.0.0.0", true) -- false (overflow)
-print(addr) -- "255.255.255.255/24
+addr:add("255.0.0.0", true) -- false (overflow)
+print(addr) -- "255.255.255.255/24
local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64")
-print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64"
-print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64"
+print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64"
+print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64"
+
+addr6:add(256, true) -- true
+print(addr6) -- "fe80::221:63f:fe75:ab17/64
+
+addr6:add("ffff::", true) -- false (overflow)
+print(addr6) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"
-addr:add(256, true) -- true
-print(addr) -- "fe80::221:63f:fe75:ab17/64
+local mac = luci.ip.new("00:14:22:01:23:45")
+print(mac:add(256)) -- "00:14:22:01:24:45"
+print(mac:add("0:0:0:0:FF:0") -- "00:14:22:02:22:45"
-addr:add("ffff::", true) -- false (overflow)
-print(addr) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"`
+mac:add(256, true) -- true
+print(mac) -- "00:14:22:01:24:45"
+
+mac:add("FF:FF:0:0:0:0", true) -- false (overflow)
+print(mac) -- "FF:FF:FF:FF:FF:FF"`
]]
---[[
-Substract given amount from CIDR instance. If the result would under, the lowest
+Subtract given amount from CIDR instance. If the result would under, the lowest
possible address is returned.
@class function
-@sort 18
+@sort 23
@name cidr.sub
@param amount A numeric value between 0 and 0xFFFFFFFF, a
`luci.ip.cidr` instance or a string convertable by
@param inplace If `true`, modify this instance instead of returning
a new derived CIDR instance.
@return <ul>
- <li>When substracting inplace: Return `true` if the substraction
- succeded or `false` when the substraction underflowed.</li>
+ <li>When subtracting inplace: Return `true` if the subtraction
+ succeeded or `false` when the subtraction underflowed.</li>
<li>When deriving new CIDR: Return new instance representing the value of
- this instance minus the substracted amount or the lowest address if
- the substraction underflowed.</li></ul>
+ this instance minus the subtracted amount or the lowest address if
+ the subtraction underflowed.</li></ul>
@usage `local addr = luci.ip.new("192.168.1.1/24")
print(addr:sub(256)) -- "192.168.0.1/24"
print(addr:sub("0.168.0.0")) -- "192.0.1.1/24"
print(addr) -- "fe80::221:63f:fe75:a917/64"
addr:sub("ffff::", true) -- false (underflow)
-print(addr) -- "::/64"`
+print(addr) -- "::/64"
+
+local mac = luci.ip.new("00:14:22:01:23:45")
+print(mac:sub(256)) -- "00:14:22:01:22:45"
+print(mac:sub("0:0:0:0:FF:0") -- "00:14:22:00:24:45"
+
+mac:sub(256, true) -- true
+print(mac) -- "00:14:22:01:22:45"
+
+mac:sub("FF:FF:0:0:0:0", true) -- false (overflow)
+print(mac) -- "00:00:00:00:00:00"`
]]
---[[
Calculate the lowest possible host address within this CIDR instance.
@class function
-@sort 19
+@sort 24
@name cidr.minhost
@return Returns a new CIDR instance representing the lowest host address
within this range.
print(addr:minhost()) -- "192.168.123.1"
local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
-print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"`
+print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"
+
+local mac = luci.ip.new("00:14:22:01:22:45/32")
+print(mac:minhost()) -- "00:14:22:01:00:01"`
]]
---[[
Calculate the highest possible host address within this CIDR instance.
@class function
-@sort 20
+@sort 25
@name cidr.maxhost
@return Returns a new CIDR instance representing the highest host address
within this range.
print(addr:maxhost()) -- "192.168.123.254" (.255 is broadcast)
local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
-print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"`
+print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"
+
+local mac = luci.ip.new("00:14:22:01:22:45/32")
+print(mac:maxhost()) -- "00:14:22:01:FF:FF"`
]]
---[[
Convert CIDR instance into string representation.
-If the prefix size of instance is less than 32 for IPv4 or 128 for IPv6, the
-address is returned in the form "address/prefix" otherwise just "address".
+If the prefix size of instance is less than 32 for IPv4, 128 for IPv6 or 48 for
+MACs, the address is returned in the form "address/prefix" otherwise just
+"address".
It is usually not required to call this function directly as CIDR objects
define it as __tostring function in the associated metatable.
@class function
-@sort 21
+@sort 26
@name cidr.string
@return Returns a string representing the range or address of this CIDR instance
]]
-- <br />In general all functions are namend and behave like their POSIX API
-- counterparts - where applicable - applying the following rules:
-- <ul>
--- <li>Functions should be named like the underlying POSIX API function ommiting
+-- <li>Functions should be named like the underlying POSIX API function omitting
-- prefixes or suffixes - especially when placed in an object-context (
-- lockf -> File:lock, fsync -> File:sync, dup2 -> dup, ...)</li>
-- <li>If you are unclear about the behaviour of a function you should consult
-- <li>If the name is significantly different from the POSIX-function, the
-- underlying function(s) are stated in the documentation.</li>
-- <li>Parameters should reflect those of the C-API, buffer length arguments and
--- by-reference parameters should be ommitted for pratical purposes.</li>
+-- by-reference parameters should be omitted for practical purposes.</li>
-- <li>If a C function accepts a bitfield as parameter, it should be translated
-- into lower case string flags representing the flags if the bitfield is the
--- last parameter and also ommiting prefixes or suffixes. (e.g. waitpid
+-- last parameter and also omitting prefixes or suffixes. (e.g. waitpid
-- (pid, &s, WNOHANG | WUNTRACED) -> waitpid(pid, "nohang", "untraced"),
-- getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) ->
-- Socket:getopt("socket", "reuseaddr"), etc.) </li>
--- Send a message on the socket.
-- This function is identical to sendto except for the missing destination
--- paramters. See the sendto description for a detailed description.
+-- parameters. See the sendto description for a detailed description.
-- @class function
-- @name Socket.send
-- @param buffer Buffer holding the data to be written.
-- "mtu" (IP, IPv6), "hdrincl" (IP), "multicast_ttl" (IP), "multicast_loop"
-- (IP, IPv6), "multicast_if" (IP, IPv6), "v6only" (IPv6), "multicast_hops"
-- (IPv6), "add_membership" (IP, IPv6), "drop_membership" (IP, IPv6)]
--- @return Value
\ No newline at end of file
+-- @return Value
-- @class function
-- @name UnifiedIO.readall
-- @usage This function uses the low-level read function of the descriptor.
--- @usage If the length parameter is ommited, this function returns all data
+-- @usage If the length parameter is omitted, this function returns all data
-- that can be read before an end-of-file, end-of-stream, connection shutdown
-- or similar happens.
-- @usage If the descriptor is non-blocking this function may fail with EAGAIN.
-- @param length Bytes to read (optional)
--- @return data that was successfully read if no error occured
+-- @return data that was successfully read if no error occurred
-- @return - reserved for error code -
-- @return - reserved for error message -
--- @return data that was successfully read even if an error occured
+-- @return data that was successfully read even if an error occurred
--- Write a block of data and wait until all data is written.
-- @class function
-- @usage This function uses the low-level write function of the descriptor.
-- @usage If the descriptor is non-blocking this function may fail with EAGAIN.
-- @param block Bytes to write
--- @return bytes that were successfully written if no error occured
+-- @return bytes that were successfully written if no error occurred
-- @return - reserved for error code -
-- @return - reserved for error message -
--- @return bytes that were successfully written even if an error occured
+-- @return bytes that were successfully written even if an error occurred
--- Create a line-based iterator.
-- Lines may end with either \n or \r\n, these control chars are not included
-- to stop reading line-based and want to use the read(all) functions instead
-- you can pass "true" to the iterator which will flush the buffer
-- and return the bufferd data.
--- @usage If the limit parameter is ommited, this function uses the nixio
+-- @usage If the limit parameter is omitted, this function uses the nixio
-- buffersize (8192B by default).
-- @usage If the descriptor is non-blocking the iterator may fail with EAGAIN.
-- @usage The iterator can be used as an LTN12 source.
-- @usage This function uses the low-level read function of the descriptor.
-- @usage The blocksize given is only advisory and to be seen as an upper limit,
-- if an underlying read returns less bytes the chunk is nevertheless returned.
--- @usage If the limit parameter is ommited, the iterator returns data
+-- @usage If the limit parameter is omitted, the iterator returns data
-- until an end-of-file, end-of-stream, connection shutdown or similar happens.
-- @usage The iterator will not buffer so it is safe to mix with calls to read.
-- @usage If the descriptor is non-blocking the iterator may fail with EAGAIN.
-- @name UnifiedIO.copy
-- @usage This function uses the blocksource function of the source descriptor
-- and the sink function of the target descriptor.
--- @usage If the limit parameter is ommited, data is copied
+-- @usage If the limit parameter is omitted, data is copied
-- until an end-of-file, end-of-stream, connection shutdown or similar happens.
-- @usage If the descriptor is non-blocking the function may fail with EAGAIN.
-- @param fdout Target Descriptor
-- @param size Bytes to copy (optional)
--- @return bytes that were successfully written if no error occured
+-- @return bytes that were successfully written if no error occurred
-- @return - reserved for error code -
-- @return - reserved for error message -
--- @return bytes that were successfully written even if an error occured
+-- @return bytes that were successfully written even if an error occurred
--- Copy data from the current descriptor to another one using kernel-space
-- copying if possible.
-- @usage This function uses the sendfile() syscall to copy the data or the
-- blocksource function of the source descriptor and the sink function
-- of the target descriptor as a fallback mechanism.
--- @usage If the limit parameter is ommited, data is copied
+-- @usage If the limit parameter is omitted, data is copied
-- until an end-of-file, end-of-stream, connection shutdown or similar happens.
-- @usage If the descriptor is non-blocking the function may fail with EAGAIN.
-- @param fdout Target Descriptor
-- @param size Bytes to copy (optional)
--- @return bytes that were successfully written if no error occured
+-- @return bytes that were successfully written if no error occurred
-- @return - reserved for error code -
-- @return - reserved for error message -
--- @return bytes that were successfully written even if an error occured
+-- @return bytes that were successfully written even if an error occurred
--- Close the descriptor.
-- @class function
-- @name UnifiedIO.close
-- @usage If the descriptor is a TLS-socket the underlying descriptor is
-- closed without touching the TLS connection.
--- @return true
\ No newline at end of file
+-- @return true
-- @name nixio.fs.rename
-- @param src Source path
-- @param dest Destination path
--- @usage It is normally not possible to rename files accross filesystems.
+-- @usage It is normally not possible to rename files across filesystems.
-- @return true
--- Remove an empty directory.
-- omit the basename even if source and destination basename are equal.
-- @param src Source path
-- @param dest Destination path
--- @return true
\ No newline at end of file
+-- @return true
-- @param flag1 First Flag ["append", "creat", "excl", "nonblock", "ndelay",
-- "sync", "trunc", "rdonly", "wronly", "rdwr"]
-- @param ... More Flags [-"-]
--- @return flag to be used as second paramter to open
+-- @return flag to be used as second parameter to open
--- Duplicate a file descriptor.
-- @class function
--- Wait for some event on a file descriptor.
-- poll() sets the revents-field of the tables provided by fds to a bitfield
--- indicating the events that occured.
+-- indicating the events that occurred.
-- @class function
-- @usage This function works in-place on the provided table and only
-- writes the revents field, you can use other fields on your demand.
--- Set or unset a environment variable.
-- @class function
-- @name nixio.setenv
--- @usage The environment variable will be unset if value is ommited.
+-- @usage The environment variable will be unset if value is omitted.
-- @param variable Variable
-- @param value Value (optional)
-- @return true
end
function macaddr(val)
- if val and val: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]+$"
- ) then
- local parts = util.split( val, ":" )
-
- for i = 1,6 do
- parts[i] = tonumber( parts[i], 16 )
- if parts[i] < 0 or parts[i] > 255 then
- return false
- end
- end
-
- return true
- end
-
- return false
+ return ip.checkmac(val) and true or false
end
function hostname(val)
Decode a mime encoded http message body with multipart/form-data
Content-Type. Stores all extracted data associated with its parameter name
-in the params table withing the given message object. Multiple parameter
+in the params table within the given message object. Multiple parameter
values are stored as tables, ordinary ones as strings.
If an optional file callback function is given then it is feeded with the
file contents chunk by chunk and only the extracted file name is stored
Decode an urlencoded http message body with application/x-www-urlencoded
Content-Type. Stores all extracted data associated with its parameter name
-in the params table withing the given message object. Multiple parameter
+in the params table within the given message object. Multiple parameter
values are stored as tables, ordinary ones as strings.
@class function
@name urldecode_message_body
if i.family == "packet" then
_interfaces[name].flags = i.flags
_interfaces[name].stats = i.data
- _interfaces[name].macaddr = i.addr
+ _interfaces[name].macaddr = ipc.checkmac(i.addr)
elseif i.family == "inet" then
_interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
elseif i.family == "inet6" then
function del_network(self, n)
local r = _uci:delete("network", n)
if r then
+ _uci:delete_all("luci", "ifstate",
+ function(s) return (s.interface == n) end)
+
_uci:delete_all("network", "alias",
function(s) return (s.interface == n) end)
if type(addrs) == "table" then
for n, addr in ipairs(addrs) do
- if type(addr["local-address"]) == "table" then
+ if type(addr["local-address"]) == "table" and
+ type(addr["local-address"].mask) == "number" and
+ type(addr["local-address"].address) == "string"
+ then
rv[#rv+1] = "%s/%d" %{
addr["local-address"].address,
addr["local-address"].mask
end
function interface.mac(self)
- local mac = self:_ubus("macaddr")
- return mac and mac:upper()
+ return ipc.checkmac(self:_ubus("macaddr"))
end
function interface.ipaddrs(self)
luci.ip.neighbors(nil, function(neigh)
if neigh.mac and neigh.family == 4 then
- _add(what, neigh.mac:upper(), neigh.dest:string(), nil, nil)
+ _add(what, neigh.mac:string(), neigh.dest:string(), nil, nil)
elseif neigh.mac and neigh.family == 6 then
- _add(what, neigh.mac:upper(), nil, neigh.dest:string(), nil)
+ _add(what, neigh.mac:string(), nil, neigh.dest:string(), nil)
end
end)
if fs.access("/etc/ethers") then
for e in io.lines("/etc/ethers") do
- mac, ip = e:match("^([a-f0-9]%S+) (%S+)")
- if mac and ip then
- _add(what, mac:upper(), ip, nil, nil)
+ mac, name = e:match("^([a-fA-F0-9:-]+)%s+(%S+)")
+ mac = luci.ip.checkmac(mac)
+ if mac and name then
+ if luci.ip.checkip4(name) then
+ _add(what, mac, name, nil, nil)
+ else
+ _add(what, mac, nil, nil, name)
+ end
end
end
end
if s.leasefile and fs.access(s.leasefile) then
for e in io.lines(s.leasefile) do
mac, ip, name = e:match("^%d+ (%S+) (%S+) (%S+)")
+ mac = luci.ip.checkmac(mac)
if mac and ip then
- _add(what, mac:upper(), ip, nil, name ~= "*" and name)
+ _add(what, mac, ip, nil, name ~= "*" and name)
end
end
end
cur:foreach("dhcp", "host",
function(s)
for mac in luci.util.imatch(s.mac) do
- _add(what, mac:upper(), s.ip, nil, s.name)
+ mac = luci.ip.checkmac(mac)
+ if mac then
+ _add(what, mac, s.ip, nil, s.name)
+ end
end
end)
module("luci.tools.status", package.seeall)
local uci = require "luci.model.uci".cursor()
+local ipc = require "luci.ip"
local function dhcp_leases_common(family)
local rv = { }
if family == 4 and not ip:match(":") then
rv[#rv+1] = {
expires = (expire ~= 0) and os.difftime(expire, os.time()),
- macaddr = mac,
+ macaddr = ipc.checkmac(mac) or "00:00:00:00:00:00",
ipaddr = ip,
hostname = (name ~= "*") and name
}
hostname = (name ~= "-") and name
}
elseif ip and iaid == "ipv4" and family == 4 then
- local mac, mac1, mac2, mac3, mac4, mac5, mac6
- if duid and type(duid) == "string" then
- mac1, mac2, mac3, mac4, mac5, mac6 = duid:match("^(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)$")
- end
- if not (mac1 and mac2 and mac3 and mac4 and mac5 and mac6) then
- mac = "FF:FF:FF:FF:FF:FF"
- else
- mac = mac1..":"..mac2..":"..mac3..":"..mac4..":"..mac5..":"..mac6
- end
rv[#rv+1] = {
expires = (expire >= 0) and os.difftime(expire, os.time()),
- macaddr = duid,
- macaddr = mac:lower(),
+ macaddr = ipc.checkmac(duid:gsub("^(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)$", "%1:%2:%3:%4:%5:%6")) or "00:00:00:00:00:00",
ipaddr = ip,
hostname = (name ~= "-") and name
}
]]
---[[
-Count the occurences of given substring in given string.
+Count the occurrences of given substring in given string.
@class function
@name cmatch
@param str String to search in
@param pattern String containing pattern to find
-@return Number of found occurences
+@return Number of found occurrences
]]
---[[
end
-%>
-<ul style="margin:0; list-style-type:none; text-align:left">
- <% if self.allowlocal then %>
- <li style="padding:0.5em">
- <input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_empty") .. attr("name", cbid) .. attr("value", "") .. ifattr(checked[""], "checked", "checked")%> />  
- <label<%=attr("for", cbid .. "_empty")%>></label>
- <label<%=attr("for", cbid .. "_empty")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
- <strong><%:Device%></strong>
- <% if self.allowany and self.allowlocal then %>(<%:input%>)<% end %>
- </label>
- </li>
- <% end %>
- <% if self.allowany then %>
- <li style="padding:0.5em">
- <input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_any") .. attr("name", cbid) .. attr("value", "*") .. ifattr(checked["*"], "checked", "checked")%> />  
- <label<%=attr("for", cbid .. "_any")%>></label>
- <label<%=attr("for", cbid .. "_any")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
- <strong><%:Any zone%></strong>
- <% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %>
- </label>
- </li>
- <% end %>
- <%
- for _, zone in utl.spairs(zones, function(a,b) return (zones[a]:name() < zones[b]:name()) end) do
- if zone:name() ~= self.exclude then
- selected = selected or (value == zone:name())
- %>
- <li style="padding:0.5em">
- <input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "." .. zone:name()) .. attr("name", cbid) .. attr("value", zone:name()) .. ifattr(checked[zone:name()], "checked", "checked")%> />  
- <label<%=attr("for", cbid .. "." .. zone:name())%>></label>
- <label<%=attr("for", cbid .. "." .. zone:name())%> style="background-color:<%=zone:get_color()%>" class="zonebadge">
- <strong><%=zone:name()%>:</strong>
- <%
- local zempty = true
- for _, net in ipairs(zone:get_networks()) do
- net = nwm:get_network(net)
- if net then
- zempty = false
- %>
- <span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
+<span>
+ <ul style="margin:0; list-style-type:none; text-align:left">
+ <% if self.allowlocal then %>
+ <li style="padding:0.5em">
+ <input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_empty") .. attr("name", cbid) .. attr("value", "") .. ifattr(checked[""], "checked", "checked")%> />  
+ <label<%=attr("for", cbid .. "_empty")%>></label>
+ <label<%=attr("for", cbid .. "_empty")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
+ <strong><%:Device%></strong>
+ <% if self.allowany and self.allowlocal then %>(<%:input%>)<% end %>
+ </label>
+ </li>
+ <% end %>
+ <% if self.allowany then %>
+ <li style="padding:0.5em">
+ <input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_any") .. attr("name", cbid) .. attr("value", "*") .. ifattr(checked["*"], "checked", "checked")%> />  
+ <label<%=attr("for", cbid .. "_any")%>></label>
+ <label<%=attr("for", cbid .. "_any")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
+ <strong><%:Any zone%></strong>
+ <% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %>
+ </label>
+ </li>
+ <% end %>
+ <%
+ for _, zone in utl.spairs(zones, function(a,b) return (zones[a]:name() < zones[b]:name()) end) do
+ if zone:name() ~= self.exclude then
+ selected = selected or (value == zone:name())
+ %>
+ <li style="padding:0.5em">
+ <input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "." .. zone:name()) .. attr("name", cbid) .. attr("value", zone:name()) .. ifattr(checked[zone:name()], "checked", "checked")%> />  
+ <label<%=attr("for", cbid .. "." .. zone:name())%>></label>
+ <label<%=attr("for", cbid .. "." .. zone:name())%> style="background-color:<%=zone:get_color()%>" class="zonebadge">
+ <strong><%=zone:name()%>:</strong>
<%
- local nempty = true
- for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
- nempty = false
- %>
- <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
- <% end %>
- <% if nempty then %><em><%:(empty)%></em><% end %>
- </span>
- <% end end %>
- <% if zempty then %><em><%:(empty)%></em><% end %>
- </label>
- </li>
- <% end end %>
+ local zempty = true
+ for _, net in ipairs(zone:get_networks()) do
+ net = nwm:get_network(net)
+ if net then
+ zempty = false
+ %>
+ <span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
+ <%
+ local nempty = true
+ for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
+ nempty = false
+ %>
+ <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
+ <% end %>
+ <% if nempty then %><em><%:(empty)%></em><% end %>
+ </span>
+ <% end end %>
+ <% if zempty then %><em><%:(empty)%></em><% end %>
+ </label>
+ </li>
+ <% end end %>
- <% if self.widget ~= "checkbox" and not self.nocreate then %>
- <li style="padding:0.5em">
- <input class="cbi-input-radio" data-update="click change" type="radio"<%=attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not selected, "checked", "checked")%> />  
- <label<%=attr("for", cbid .. "_new")%>></label>
- <div onclick="document.getElementById('<%=cbid%>_new').checked=true" class="zonebadge" style="background-color:<%=fwm.zone.get_color()%>">
- <em><%:unspecified -or- create:%> </em>
- <input type="text"<%=attr("name", cbid .. ".newzone") .. ifattr(not selected, "value", luci.http.formvalue(cbid .. ".newzone") or self.default)%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
- </div>
- </li>
- <% end %>
-</ul>
+ <% if self.widget ~= "checkbox" and not self.nocreate then %>
+ <li style="padding:0.5em">
+ <input class="cbi-input-radio" data-update="click change" type="radio"<%=attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not selected, "checked", "checked")%> />  
+ <label<%=attr("for", cbid .. "_new")%>></label>
+ <div onclick="document.getElementById('<%=cbid%>_new').checked=true" class="zonebadge" style="background-color:<%=fwm.zone.get_color()%>">
+ <em><%:unspecified -or- create:%> </em>
+ <input type="text"<%=attr("name", cbid .. ".newzone") .. ifattr(not selected, "value", luci.http.formvalue(cbid .. ".newzone") or self.default)%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
+ </div>
+ </li>
+ <% end %>
+ </ul>
+</span>
<%+cbi/valuefooter%>
type = device:type(),
ifname = device:name(),
macaddr = device:mac(),
- macaddr = device:mac(),
is_up = device:is_up(),
rx_bytes = device:rx_bytes(),
tx_bytes = device:tx_bytes(),
-- Licensed to the public under the Apache License 2.0.
local ipc = require "luci.ip"
+local sys = require "luci.sys"
local o
require "luci.util"
hostid = s:option(Value, "hostid", translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Suffix (hex)"))
-ipc.neighbors({ family = 4 }, function(n)
- if n.mac and n.dest then
- ip:value(n.dest:string())
- mac:value(n.mac, "%s (%s)" %{ n.mac, n.dest:string() })
+sys.net.host_hints(function(m, v4, v6, name)
+ if m and v4 then
+ ip:value(v4)
+ mac:value(m, "%s (%s)" %{ m, name or v4 })
end
end)
-- Licensed to the public under the Apache License 2.0.
local ipc = require "luci.ip"
+local sys = require "luci.sys"
m = Map("dhcp", translate("Hostnames"))
ip.datatype = "ipaddr"
ip.rmempty = true
-ipc.neighbors({ }, function(n)
- if n.mac and n.dest and not n.dest:is6linklocal() then
- ip:value(n.dest:string(), "%s (%s)" %{ n.dest:string(), n.mac })
+sys.net.host_hints(function(mac, v4, v6, name)
+ v6 = v6 and ipc.IPv6(v6)
+
+ if v4 or (v6 and not v6:is6linklocal()) then
+ ip:value(tostring(v4 or v6), "%s (%s)" %{ tostring(v4 or v6), name or mac })
end
end)
m = Map("network", translate("Interfaces") .. " - " .. arg[1]:upper(), translate("On this page you can configure the network interfaces. You can bridge several interfaces by ticking the \"bridge interfaces\" field and enter the names of several network interfaces separated by spaces. You can also use <abbr title=\"Virtual Local Area Network\">VLAN</abbr> notation <samp>INTERFACE.VLANNR</samp> (<abbr title=\"for example\">e.g.</abbr>: <samp>eth0.1</samp>)."))
m.redirect = luci.dispatcher.build_url("admin", "network", "network")
m:chain("wireless")
+m:chain("luci")
if has_firewall then
m:chain("firewall")
local net = nw:get_network(arg[1])
+local function set_ifstate(name, option, value)
+ local found = false
+
+ m.uci:foreach("luci", "ifstate", function (s)
+ if s.interface == name then
+ m.uci:set("luci", s[".name"], option, value)
+ found = true
+ return false
+ end
+ end)
+
+ if not found then
+ local sid = m.uci:add("luci", "ifstate")
+ m.uci:set("luci", sid, "interface", name)
+ m.uci:set("luci", sid, option, value)
+ end
+
+ m.uci:save("luci")
+end
+
+local function get_ifstate(name, option)
+ local val
+
+ m.uci:foreach("luci", "ifstate", function (s)
+ if s.interface == name then
+ val = m.uci:get("luci", s[".name"], option)
+ return false
+ end
+ end)
+
+ return val
+end
+
local function backup_ifnames(is_bridge)
- if not net:is_floating() and not m:get(net:name(), "_orig_ifname") then
+ if not net:is_floating() and not get_ifstate(net:name(), "ifname") then
local ifcs = net:get_interfaces() or { net:get_interface() }
if ifcs then
local _, ifn
local ifns = { }
for _, ifn in ipairs(ifcs) do
- ifns[#ifns+1] = ifn:name()
+ local wif = ifn:get_wifinet()
+ ifns[#ifns+1] = wif and wif:id() or ifn:name()
end
if #ifns > 0 then
- m:set(net:name(), "_orig_ifname", table.concat(ifns, " "))
- m:set(net:name(), "_orig_bridge", tostring(net:is_bridge()))
+ set_ifstate(net:name(), "ifname", table.concat(ifns, " "))
+ set_ifstate(net:name(), "bridge", tostring(net:is_bridge()))
end
end
end
elseif net:is_floating() and not proto:is_floating() then
-- if we have backup data, then re-add all orphaned interfaces
-- from it and restore the bridge choice
- local br = (m:get(net:name(), "_orig_bridge") == "true")
+ local br = (get_ifstate(net:name(), "bridge") == "true")
local ifn
local ifns = { }
- for ifn in ut.imatch(m:get(net:name(), "_orig_ifname")) do
+ for ifn in ut.imatch(get_ifstate(net:name(), "ifname")) do
ifn = nw:get_interface(ifn)
if ifn and not ifn:get_network() then
proto:add_interface(ifn)
for k, v in pairs(m:get(net:name())) do
if k:sub(1,1) ~= "." and
k ~= "type" and
- k ~= "ifname" and
- k ~= "_orig_ifname" and
- k ~= "_orig_bridge"
+ k ~= "ifname"
then
m:del(net:name(), k)
end
<tr class="cbi-section-table-row cbi-rowstyle-<%=(style and 1 or 2)%>">
<td class="cbi-value-field"><%=v.dest%></td>
<td class="cbi-value-field"><%=v.mac%></td>
- <td class="cbi-value-field"><%=v.dev%></td>
+ <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></td>
</tr>
<%
style = not style
-- Licensed to the public under the Apache License 2.0.
local uci = require "luci.model.uci".cursor()
-local ipc = require "luci.ip"
+local sys = require "luci.sys"
local wa = require "luci.tools.webadmin"
local fs = require "nixio.fs"
mac = s2:option(Value, "mac", translate("<abbr title=\"Media Access Control\">MAC</abbr>-Address"))
ip = s2:option(Value, "ip", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Address"))
-ipc.neighbors({ family = 4 }, function(n)
- if n.mac and n.dest then
- ip:value(n.dest:string())
- mac:value(n.mac, "%s (%s)" %{ n.mac, n.dest:string() })
+sys.host_hints(function(m, v4, v6, name)
+ if m and v4 then
+ ip:value(v4)
+ mac:value(m, "%s (%s)" %{ m, name or v4 })
end
end)
return m
-