luci/modules/luci-mod-admin-full/luasrc/view/admin_status/index.htm
Jo-Philipp Wich 02a93bca50 luci-mod-admin-full: map DHCPv6 DUIDs to MACs
Map DUIDs to their corresponding MAC addresses in order to correlate them with
IPv4 information. This is useful to e.g. identify IPv6 hosts which do not send
a name.

Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
2016-01-25 17:54:44 +01:00

762 lines
22 KiB
HTML

<%#
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
Licensed to the public under the Apache License 2.0.
-%>
<%
local fs = require "nixio.fs"
local util = require "luci.util"
local stat = require "luci.tools.status"
local ver = require "luci.version"
local has_ipv6 = fs.access("/proc/net/ipv6_route")
local has_dhcp = fs.access("/etc/config/dhcp")
local has_wifi = ((fs.stat("/etc/config/wireless", "size") or 0) > 0)
local sysinfo = luci.util.ubus("system", "info") or { }
local boardinfo = luci.util.ubus("system", "board") or { }
local unameinfo = nixio.uname() or { }
local meminfo = sysinfo.memory or {
total = 0,
free = 0,
buffered = 0,
shared = 0
}
local swapinfo = sysinfo.swap or {
total = 0,
free = 0
}
local has_dsl = fs.access("/etc/init.d/dsl_control")
if luci.http.formvalue("status") == "1" then
local ntm = require "luci.model.network".init()
local wan = ntm:get_wannet()
local wan6 = ntm:get_wan6net()
local conn_count = tonumber((
luci.sys.exec("wc -l /proc/net/nf_conntrack") or
luci.sys.exec("wc -l /proc/net/ip_conntrack") or
""):match("%d+")) or 0
local conn_max = tonumber((
luci.sys.exec("sysctl net.nf_conntrack_max") or
luci.sys.exec("sysctl net.ipv4.netfilter.ip_conntrack_max") or
""):match("%d+")) or 4096
local rv = {
uptime = sysinfo.uptime or 0,
localtime = os.date(),
loadavg = sysinfo.load or { 0, 0, 0 },
memory = meminfo,
swap = swapinfo,
connmax = conn_max,
conncount = conn_count,
leases = stat.dhcp_leases(),
leases6 = stat.dhcp6_leases(),
wifinets = stat.wifi_networks()
}
if wan then
rv.wan = {
ipaddr = wan:ipaddr(),
gwaddr = wan:gwaddr(),
netmask = wan:netmask(),
dns = wan:dnsaddrs(),
expires = wan:expires(),
uptime = wan:uptime(),
proto = wan:proto(),
ifname = wan:ifname(),
link = wan:adminlink()
}
end
if wan6 then
rv.wan6 = {
ip6addr = wan6:ip6addr(),
gw6addr = wan6:gw6addr(),
dns = wan6:dns6addrs(),
uptime = wan6:uptime(),
ifname = wan6:ifname(),
link = wan6:adminlink()
}
end
if has_dsl then
local dsl_stat = luci.sys.exec("/etc/init.d/dsl_control lucistat")
local dsl_func = loadstring(dsl_stat)
if dsl_func then
rv.dsl = dsl_func()
end
end
luci.http.prepare_content("application/json")
luci.http.write_json(rv)
return
elseif luci.http.formvalue("hosts") == "1" then
luci.http.prepare_content("application/json")
luci.http.write_json(luci.sys.net.host_hints())
return
end
-%>
<%+header%>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<script type="text/javascript">//<![CDATA[
function progressbar(v, m)
{
var vn = parseInt(v) || 0;
var mn = parseInt(m) || 100;
var pc = Math.floor((100 / mn) * vn);
return String.format(
'<div style="width:200px; position:relative; border:1px solid #999999">' +
'<div style="background-color:#CCCCCC; width:%d%%; height:15px">' +
'<div style="position:absolute; left:0; top:0; text-align:center; width:100%%; color:#000000">' +
'<small>%s / %s (%d%%)</small>' +
'</div>' +
'</div>' +
'</div>', pc, v, m, pc
);
}
function wifirate(bss, rx) {
var p = rx ? 'rx_' : 'tx_',
s = '%.1f <%:Mbit/s%>, %d<%:MHz%>'
.format(bss[p+'rate'] / 1000, bss[p+'mhz']),
ht = bss[p+'ht'], vht = bss[p+'vht'],
mhz = bss[p+'mhz'], nss = bss[p+'nss'],
mcs = bss[p+'mcs'], sgi = bss[p+'short_gi'];
if (ht || vht) {
if (vht) s += ', VHT-MCS %d'.format(mcs);
if (nss) s += ', VHT-NSS %d'.format(nss);
if (ht) s += ', MCS %s'.format(mcs);
if (sgi) s += ', <%:Short GI%>';
}
return s;
}
function duid2mac(duid) {
// DUID-LLT / Ethernet
if (duid.length === 28 && duid.substr(0, 8) === '00010001')
return duid.substr(16).replace(/(..)(?=..)/g, '$1:').toUpperCase();
// DUID-LL / Ethernet
if (duid.length === 24 && duid.substr(0, 8) === '00030001')
return duid.substr(8).replace(/(..)(?=..)/g, '$1:').toUpperCase();
return null;
}
var npoll = 1;
var hosts = <%=luci.http.write_json(luci.sys.net.host_hints())%>;
function updateHosts() {
XHR.get('<%=REQUEST_URI%>', { hosts: 1 }, function(x, data) {
hosts = data;
});
}
XHR.poll(5, '<%=REQUEST_URI%>', { status: 1 },
function(x, info)
{
if (!(npoll++ % 5))
updateHosts();
var si = document.getElementById('wan4_i');
var ss = document.getElementById('wan4_s');
var ifc = info.wan;
if (ifc && ifc.ifname && ifc.proto != 'none')
{
var s = String.format(
'<strong><%:Type%>: </strong>%s<br />' +
'<strong><%:Address%>: </strong>%s<br />' +
'<strong><%:Netmask%>: </strong>%s<br />' +
'<strong><%:Gateway%>: </strong>%s<br />',
ifc.proto,
(ifc.ipaddr) ? ifc.ipaddr : '0.0.0.0',
(ifc.netmask && ifc.netmask != ifc.ipaddr) ? ifc.netmask : '255.255.255.255',
(ifc.gwaddr) ? ifc.gwaddr : '0.0.0.0'
);
for (var i = 0; i < ifc.dns.length; i++)
{
s += String.format(
'<strong><%:DNS%> %d: </strong>%s<br />',
i + 1, ifc.dns[i]
);
}
if (ifc.expires > -1)
{
s += String.format(
'<strong><%:Expires%>: </strong>%t<br />',
ifc.expires
);
}
if (ifc.uptime > 0)
{
s += String.format(
'<strong><%:Connected%>: </strong>%t<br />',
ifc.uptime
);
}
ss.innerHTML = String.format('<small>%s</small>', s);
si.innerHTML = String.format(
'<img src="<%=resource%>/icons/ethernet.png" />' +
'<br /><small><a href="%s">%s</a></small>',
ifc.link, ifc.ifname
);
}
else
{
si.innerHTML = '<img src="<%=resource%>/icons/ethernet_disabled.png" /><br /><small>?</small>';
ss.innerHTML = '<em><%:Not connected%></em>';
}
<% if has_ipv6 then %>
var si6 = document.getElementById('wan6_i');
var ss6 = document.getElementById('wan6_s');
var ifc6 = info.wan6;
if (ifc6 && ifc6.ifname && ifc6.proto != 'none')
{
var s = String.format(
'<strong><%:Address%>: </strong>%s<br />' +
'<strong><%:Gateway%>: </strong>%s<br />',
(ifc6.ip6addr) ? ifc6.ip6addr : '::',
(ifc6.gw6addr) ? ifc6.gw6addr : '::'
);
for (var i = 0; i < ifc6.dns.length; i++)
{
s += String.format(
'<strong><%:DNS%> %d: </strong>%s<br />',
i + 1, ifc6.dns[i]
);
}
if (ifc6.uptime > 0)
{
s += String.format(
'<strong><%:Connected%>: </strong>%t<br />',
ifc6.uptime
);
}
ss6.innerHTML = String.format('<small>%s</small>', s);
si6.innerHTML = String.format(
'<img src="<%=resource%>/icons/ethernet.png" />' +
'<br /><small><a href="%s">%s</a></small>',
ifc6.link, ifc6.ifname
);
}
else
{
si6.innerHTML = '<img src="<%=resource%>/icons/ethernet_disabled.png" /><br /><small>?</small>';
ss6.innerHTML = '<em><%:Not connected%></em>';
}
<% end %>
<% if has_dsl then %>
var dsl_i = document.getElementById('dsl_i');
var dsl_s = document.getElementById('dsl_s');
var s = String.format(
'<strong><%:Status%>: </strong>%s<br />' +
'<strong><%:Line State%>: </strong>%s [0x%x]<br />' +
'<strong><%:Line Speed%>: </strong>%s/s / %s/s<br />' +
'<strong><%:Line Attenuation%>: </strong>%s dB / %s dB<br />' +
'<strong><%:Noise Margin%>: </strong>%s dB / %s dB<br />',
info.dsl.line_state, info.dsl.line_state_detail,
info.dsl.line_state_num,
info.dsl.data_rate_down_s, info.dsl.data_rate_up_s,
info.dsl.line_attenuation_down, info.dsl.line_attenuation_up,
info.dsl.noise_margin_down, info.dsl.noise_margin_up
);
dsl_s.innerHTML = String.format('<small>%s</small>', s);
dsl_i.innerHTML = String.format(
'<img src="<%=resource%>/icons/ethernet.png" />' +
'<br /><small>ADSL</small>'
);
<% end %>
<% if has_dhcp then %>
var ls = document.getElementById('lease_status_table');
if (ls)
{
/* clear all rows */
while( ls.rows.length > 1 )
ls.rows[0].parentNode.deleteRow(1);
for( var i = 0; i < info.leases.length; i++ )
{
var timestr;
if (info.leases[i].expires <= 0)
timestr = '<em><%:expired%></em>';
else
timestr = String.format('%t', info.leases[i].expires);
var tr = ls.rows[0].parentNode.insertRow(-1);
tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
tr.insertCell(-1).innerHTML = info.leases[i].hostname ? info.leases[i].hostname : '?';
tr.insertCell(-1).innerHTML = info.leases[i].ipaddr;
tr.insertCell(-1).innerHTML = info.leases[i].macaddr;
tr.insertCell(-1).innerHTML = timestr;
}
if( ls.rows.length == 1 )
{
var tr = ls.rows[0].parentNode.insertRow(-1);
tr.className = 'cbi-section-table-row';
var td = tr.insertCell(-1);
td.colSpan = 4;
td.innerHTML = '<em><br /><%:There are no active leases.%></em>';
}
}
var ls6 = document.getElementById('lease6_status_table');
if (ls6 && info.leases6)
{
ls6.parentNode.style.display = 'block';
/* clear all rows */
while( ls6.rows.length > 1 )
ls6.rows[0].parentNode.deleteRow(1);
for( var i = 0; i < info.leases6.length; i++ )
{
var timestr;
if (info.leases6[i].expires <= 0)
timestr = '<em><%:expired%></em>';
else
timestr = String.format('%t', info.leases6[i].expires);
var tr = ls6.rows[0].parentNode.insertRow(-1);
tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
var host = hosts[duid2mac(info.leases6[i].duid)];
if (host)
tr.insertCell(-1).innerHTML = String.format(
'<div style="max-width:200px;overflow:hidden;text-overflow:ellipsis">%s</div>',
((host.name && (host.ipv4 || host.ipv6))
? '%h (%s)'.format(host.name, host.ipv4 || host.ipv6)
: '%h'.format(host.name || host.ipv4 || host.ipv6)).nobr()
);
else
tr.insertCell(-1).innerHTML = info.leases6[i].hostname ? info.leases6[i].hostname : '?';
tr.insertCell(-1).innerHTML = info.leases6[i].ip6addr;
tr.insertCell(-1).innerHTML = info.leases6[i].duid;
tr.insertCell(-1).innerHTML = timestr;
}
if( ls6.rows.length == 1 )
{
var tr = ls6.rows[0].parentNode.insertRow(-1);
tr.className = 'cbi-section-table-row';
var td = tr.insertCell(-1);
td.colSpan = 4;
td.innerHTML = '<em><br /><%:There are no active leases.%></em>';
}
}
<% end %>
<% if has_wifi then %>
var assoclist = [ ];
var ws = document.getElementById('wifi_status_table');
if (ws)
{
var wsbody = ws.rows[0].parentNode;
while (ws.rows.length > 0)
wsbody.deleteRow(0);
for (var didx = 0; didx < info.wifinets.length; didx++)
{
var dev = info.wifinets[didx];
var tr = wsbody.insertRow(-1);
var td;
td = tr.insertCell(-1);
td.width = "33%";
td.innerHTML = dev.name;
td.style.verticalAlign = "top";
td = tr.insertCell(-1);
var s = '';
for (var nidx = 0; nidx < dev.networks.length; nidx++)
{
var net = dev.networks[nidx];
var is_assoc = (net.bssid != '00:00:00:00:00:00' && net.channel && !net.disabled);
var icon;
if (!is_assoc)
icon = "<%=resource%>/icons/signal-none.png";
else if (net.quality == 0)
icon = "<%=resource%>/icons/signal-0.png";
else if (net.quality < 25)
icon = "<%=resource%>/icons/signal-0-25.png";
else if (net.quality < 50)
icon = "<%=resource%>/icons/signal-25-50.png";
else if (net.quality < 75)
icon = "<%=resource%>/icons/signal-50-75.png";
else
icon = "<%=resource%>/icons/signal-75-100.png";
s += String.format(
'<table><tr><td style="text-align:center; width:32px; padding:3px">' +
'<img src="%s" title="<%:Signal%>: %d dBm / <%:Noise%>: %d dBm" />' +
'<br /><small>%d%%</small>' +
'</td><td style="text-align:left; padding:3px"><small>' +
'<strong><%:SSID%>:</strong> <a href="%s">%h</a><br />' +
'<strong><%:Mode%>:</strong> %s<br />' +
'<strong><%:Channel%>:</strong> %d (%.3f <%:GHz%>)<br />' +
'<strong><%:Bitrate%>:</strong> %s <%:Mbit/s%><br />',
icon, net.signal, net.noise,
net.quality,
net.link, net.ssid,
net.mode,
net.channel, net.frequency,
net.bitrate || '?'
);
if (is_assoc)
{
s += String.format(
'<strong><%:BSSID%>:</strong> %s<br />' +
'<strong><%:Encryption%>:</strong> %s',
net.bssid,
net.encryption
);
}
else
{
s += '<em><%:Wireless is disabled or not associated%></em>';
}
s += '</small></td></tr></table>';
for (var bssid in net.assoclist)
{
var bss = net.assoclist[bssid];
bss.bssid = bssid;
bss.link = net.link;
bss.name = net.name;
bss.ifname = net.ifname;
bss.radio = dev.name;
assoclist.push(bss);
}
}
if (!s)
s = '<em><%:No information available%></em>';
td.innerHTML = s;
}
}
var ac = document.getElementById('wifi_assoc_table');
if (ac)
{
/* clear all rows */
while( ac.rows.length > 1 )
ac.rows[0].parentNode.deleteRow(1);
assoclist.sort(function(a, b) {
return (a.name == b.name)
? (a.bssid < b.bssid)
: (a.name > b.name )
;
});
for( var i = 0; i < assoclist.length; i++ )
{
var tr = ac.rows[0].parentNode.insertRow(-1);
tr.className = 'cbi-section-table-row cbi-rowstyle-' + (1 + (i % 2));
var icon;
var q = (-1 * (assoclist[i].noise - assoclist[i].signal)) / 5;
if (q < 1)
icon = "<%=resource%>/icons/signal-0.png";
else if (q < 2)
icon = "<%=resource%>/icons/signal-0-25.png";
else if (q < 3)
icon = "<%=resource%>/icons/signal-25-50.png";
else if (q < 4)
icon = "<%=resource%>/icons/signal-50-75.png";
else
icon = "<%=resource%>/icons/signal-75-100.png";
tr.insertCell(-1).innerHTML = String.format(
'<span class="ifacebadge" title="%q"><img src="<%=resource%>/icons/wifi.png" /> %h</span>',
assoclist[i].radio, assoclist[i].ifname
);
tr.insertCell(-1).innerHTML = String.format(
'<a href="%s">%s</a>',
assoclist[i].link,
'%h'.format(assoclist[i].name).nobr()
);
tr.insertCell(-1).innerHTML = assoclist[i].bssid;
var host = hosts[assoclist[i].bssid];
if (host)
tr.insertCell(-1).innerHTML = String.format(
'<div style="max-width:200px;overflow:hidden;text-overflow:ellipsis">%s</div>',
((host.name && (host.ipv4 || host.ipv6))
? '%h (%s)'.format(host.name, host.ipv4 || host.ipv6)
: '%h'.format(host.name || host.ipv4 || host.ipv6)).nobr()
);
else
tr.insertCell(-1).innerHTML = '?';
tr.insertCell(-1).innerHTML = String.format(
'<span class="ifacebadge" title="<%:Signal%>: %d <%:dBm%> / <%:Noise%>: %d <%:dBm%> / <%:SNR%>: %d"><img src="%s" /> %d / %d <%:dBm%></span>',
assoclist[i].signal, assoclist[i].noise, assoclist[i].signal - assoclist[i].noise,
icon,
assoclist[i].signal, assoclist[i].noise
);
tr.insertCell(-1).innerHTML = wifirate(assoclist[i], true).nobr() + '<br />' + wifirate(assoclist[i], false).nobr();
}
if (ac.rows.length == 1)
{
var tr = ac.rows[0].parentNode.insertRow(-1);
tr.className = 'cbi-section-table-row';
var td = tr.insertCell(-1);
td.colSpan = 7;
td.innerHTML = '<br /><em><%:No information available%></em>';
}
}
<% end %>
var e;
if (e = document.getElementById('localtime'))
e.innerHTML = info.localtime;
if (e = document.getElementById('uptime'))
e.innerHTML = String.format('%t', info.uptime);
if (e = document.getElementById('loadavg'))
e.innerHTML = String.format(
'%.02f, %.02f, %.02f',
info.loadavg[0] / 65535.0,
info.loadavg[1] / 65535.0,
info.loadavg[2] / 65535.0
);
if (e = document.getElementById('memtotal'))
e.innerHTML = progressbar(
((info.memory.free + info.memory.buffered) / 1024) + " <%:kB%>",
(info.memory.total / 1024) + " <%:kB%>"
);
if (e = document.getElementById('memfree'))
e.innerHTML = progressbar(
(info.memory.free / 1024) + " <%:kB%>",
(info.memory.total / 1024) + " <%:kB%>"
);
if (e = document.getElementById('membuff'))
e.innerHTML = progressbar(
(info.memory.buffered / 1024) + " <%:kB%>",
(info.memory.total / 1024) + " <%:kB%>"
);
if (e = document.getElementById('swaptotal'))
e.innerHTML = progressbar(
(info.swap.free / 1024) + " <%:kB%>",
(info.swap.total / 1024) + " <%:kB%>"
);
if (e = document.getElementById('swapfree'))
e.innerHTML = progressbar(
(info.swap.free / 1024) + " <%:kB%>",
(info.swap.total / 1024) + " <%:kB%>"
);
if (e = document.getElementById('conns'))
e.innerHTML = progressbar(info.conncount, info.connmax);
}
);
//]]></script>
<h2 name="content"><%:Status%></h2>
<fieldset class="cbi-section">
<legend><%:System%></legend>
<table width="100%" cellspacing="10">
<tr><td width="33%"><%:Hostname%></td><td><%=luci.sys.hostname() or "?"%></td></tr>
<tr><td width="33%"><%:Model%></td><td><%=pcdata(boardinfo.model or boardinfo.system or "?")%></td></tr>
<tr><td width="33%"><%:Firmware Version%></td><td>
<%=pcdata(ver.distname)%> <%=pcdata(ver.distversion)%> /
<%=pcdata(ver.luciname)%> (<%=pcdata(ver.luciversion)%>)
</td></tr>
<tr><td width="33%"><%:Kernel Version%></td><td><%=unameinfo.release or "?"%></td></tr>
<tr><td width="33%"><%:Local Time%></td><td id="localtime">-</td></tr>
<tr><td width="33%"><%:Uptime%></td><td id="uptime">-</td></tr>
<tr><td width="33%"><%:Load Average%></td><td id="loadavg">-</td></tr>
</table>
</fieldset>
<fieldset class="cbi-section">
<legend><%:Memory%></legend>
<table width="100%" cellspacing="10">
<tr><td width="33%"><%:Total Available%></td><td id="memtotal">-</td></tr>
<tr><td width="33%"><%:Free%></td><td id="memfree">-</td></tr>
<tr><td width="33%"><%:Buffered%></td><td id="membuff">-</td></tr>
</table>
</fieldset>
<% if swapinfo.total > 0 then %>
<fieldset class="cbi-section">
<legend><%:Swap%></legend>
<table width="100%" cellspacing="10">
<tr><td width="33%"><%:Total Available%></td><td id="swaptotal">-</td></tr>
<tr><td width="33%"><%:Free%></td><td id="swapfree">-</td></tr>
</table>
</fieldset>
<% end %>
<fieldset class="cbi-section">
<legend><%:Network%></legend>
<table width="100%" cellspacing="10">
<tr><td width="33%" style="vertical-align:top"><%:IPv4 WAN Status%></td><td>
<table><tr>
<td id="wan4_i" style="width:16px; text-align:center; padding:3px"><img src="<%=resource%>/icons/ethernet_disabled.png" /><br /><small>?</small></td>
<td id="wan4_s" style="vertical-align:middle; padding: 3px"><em><%:Collecting data...%></em></td>
</tr></table>
</td></tr>
<% if has_ipv6 then %>
<tr><td width="33%" style="vertical-align:top"><%:IPv6 WAN Status%></td><td>
<table><tr>
<td id="wan6_i" style="width:16px; text-align:center; padding:3px"><img src="<%=resource%>/icons/ethernet_disabled.png" /><br /><small>?</small></td>
<td id="wan6_s" style="vertical-align:middle; padding: 3px"><em><%:Collecting data...%></em></td>
</tr></table>
</td></tr>
<% end %>
<tr><td width="33%"><%:Active Connections%></td><td id="conns">-</td></tr>
</table>
</fieldset>
<% if has_dhcp then %>
<fieldset class="cbi-section">
<legend><%:DHCP Leases%></legend>
<table class="cbi-section-table" id="lease_status_table">
<tr class="cbi-section-table-titles">
<th class="cbi-section-table-cell"><%:Hostname%></th>
<th class="cbi-section-table-cell"><%:IPv4-Address%></th>
<th class="cbi-section-table-cell"><%:MAC-Address%></th>
<th class="cbi-section-table-cell"><%:Leasetime remaining%></th>
</tr>
<tr class="cbi-section-table-row">
<td colspan="4"><em><br /><%:Collecting data...%></em></td>
</tr>
</table>
</fieldset>
<fieldset class="cbi-section" style="display:none">
<legend><%:DHCPv6 Leases%></legend>
<table class="cbi-section-table" id="lease6_status_table">
<tr class="cbi-section-table-titles">
<th class="cbi-section-table-cell"><%:Host%></th>
<th class="cbi-section-table-cell"><%:IPv6-Address%></th>
<th class="cbi-section-table-cell"><%:DUID%></th>
<th class="cbi-section-table-cell"><%:Leasetime remaining%></th>
</tr>
<tr class="cbi-section-table-row">
<td colspan="4"><em><br /><%:Collecting data...%></em></td>
</tr>
</table>
</fieldset>
<% end %>
<% if has_dsl then %>
<fieldset class="cbi-section">
<legend><%:ADSL%></legend>
<table width="100%" cellspacing="10">
<tr><td width="33%" style="vertical-align:top"><%:ADSL Status%></td><td>
<table><tr>
<td id="dsl_i" style="width:16px; text-align:center; padding:3px"><img src="<%=resource%>/icons/ethernet_disabled.png" /><br /><small>?</small></td>
<td id="dsl_s" style="vertical-align:middle; padding: 3px"><em><%:Collecting data...%></em></td>
</tr></table>
</td></tr>
</table>
</fieldset>
<% end %>
<% if has_wifi then %>
<fieldset class="cbi-section">
<legend><%:Wireless%></legend>
<table id="wifi_status_table" width="100%" cellspacing="10">
<tr><td><em><%:Collecting data...%></em></td></tr>
</table>
</fieldset>
<fieldset class="cbi-section">
<legend><%:Associated Stations%></legend>
<table class="cbi-section-table valign-middle" id="wifi_assoc_table">
<tr class="cbi-section-table-titles">
<th class="cbi-section-table-cell">&#160;</th>
<th class="cbi-section-table-cell"><%:Network%></th>
<th class="cbi-section-table-cell"><%:MAC-Address%></th>
<th class="cbi-section-table-cell"><%:Host%></th>
<th class="cbi-section-table-cell"><%:Signal%> / <%:Noise%></th>
<th class="cbi-section-table-cell"><%:RX Rate%> / <%:TX Rate%></th>
</tr>
<tr class="cbi-section-table-row">
<td colspan="6"><em><br /><%:Collecting data...%></em></td>
</tr>
</table>
</fieldset>
<% end %>
<%-
local incdir = util.libpath() .. "/view/admin_status/index/"
if fs.access(incdir) then
local inc
for inc in fs.dir(incdir) do
if inc:match("%.htm$") then
include("admin_status/index/" .. inc:gsub("%.htm$", ""))
end
end
end
-%>
<%+footer%>