2 Copyright
2008-
2009 Steven Barth
<steven@midlink.org
>
3 Copyright
2008-
2018 Jo-Philipp Wich
<jo@mein.io
>
4 Licensed to the public under the Apache License
2.0.
8 local fs = require
"nixio.fs"
9 local has_ip6tables = fs.access(
"/usr/sbin/ip6tables")
13 mode = luci.dispatcher.context.requestpath
14 mode = tonumber(mode[#mode] ~=
"iptables" and mode[#mode]) or
4
20 <style type=
"text/css">
21 span.jump, .cbi-tooltip-container {
22 border-bottom:
1px dotted blue;
34 .references .cbi-tooltip {
36 top:
1.5em !important;
44 <script type=
"text/javascript">//<![CDATA[
45 var table_names = [ 'Filter', 'NAT', 'Mangle', 'Raw' ];
47 function create_table_section(table)
49 var idiv = document.getElementById('iptables'),
50 tdiv = idiv.querySelector('[
data-table=
"%s"]'.format(table)),
51 title = '<%:Table%
>: %s'.format(table);
54 tdiv = E('div', { 'data-table': table }, [
59 if (idiv.firstElementChild.nodeName.toLowerCase() === 'p')
60 idiv.removeChild(idiv.firstElementChild);
62 var added = false, thisIdx = table_names.indexOf(table);
64 idiv.querySelectorAll('[data-table]').forEach(function(child) {
65 var childIdx = table_names.indexOf(child.getAttribute('data-table'));
67 if (added === false && childIdx
> thisIdx) {
68 idiv.insertBefore(tdiv, child);
74 idiv.appendChild(tdiv);
77 return tdiv.lastElementChild;
80 function create_chain_section(table, chain, policy, packets, bytes, references)
82 var tdiv = create_table_section(table),
83 cdiv = tdiv.querySelector('[
data-chain=
"%s"]'.format(chain)),
87 title = '<%:Chain%
> <em>%s
</em> <span>(<%:Policy%
>:
<em>%s
</em>, %d <%:Packets%
>, %
.2mB <%:Traffic%
>)
</span>'.format(chain, policy, packets, bytes);
89 title = '<%:Chain%
> <em>%s
</em> <span class=
"references">(%d <%:References%
>)
</span>'.format(chain, references);
92 cdiv = E('div', { 'data-chain': chain }, [
93 E('h4', { 'id': 'rule_%s_%s'.format(table.toLowerCase(), chain) }, title),
94 E('div', { 'class': 'table' }, [
95 E('div', { 'class': 'tr table-titles' }, [
96 E('div', { 'class': 'th center' }, '<%:Pkts.%
>'),
97 E('div', { 'class': 'th center' }, '<%:Traffic%
>'),
98 E('div', { 'class': 'th' }, '<%:Target%
>'),
99 E('div', { 'class': 'th' }, '<%:Prot.%
>'),
100 E('div', { 'class': 'th' }, '<%:In%
>'),
101 E('div', { 'class': 'th' }, '<%:Out%
>'),
102 E('div', { 'class': 'th' }, '<%:Source%
>'),
103 E('div', { 'class': 'th' }, '<%:Destination%
>'),
104 E('div', { 'class': 'th' }, '<%:Options%
>'),
105 E('div', { 'class': 'th' }, '<%:Comment%
>')
110 tdiv.appendChild(cdiv);
113 cdiv.firstElementChild.innerHTML = title;
116 return cdiv.lastElementChild;
119 function update_chain_section(chaintable, rows)
124 cbi_update_table(chaintable, rows, '<%:No rules in this chain.%
>');
126 if (rows.length ===
0 &&
127 document.querySelector('form
> [
data-hide-empty=
"true"]'))
128 chaintable.parentNode.style.display = 'none';
130 chaintable.parentNode.style.display = '';
132 chaintable.parentNode.setAttribute('data-empty', rows.length ===
0);
135 function hide_empty(btn)
137 var hide = (btn.getAttribute('data-hide-empty') === 'false');
139 btn.setAttribute('data-hide-empty', hide);
140 btn.value = hide ? '<%:Show empty chains%
>' : '<%:Hide empty chains%
>';
143 document.querySelectorAll('[data-chain][
data-empty=
"true"]')
144 .forEach(function(chaintable) {
145 chaintable.style.display = hide ? 'none' : '';
149 function jump_target(ev)
151 var link = ev.target,
152 table = findParent(link, '[data-table]').getAttribute('data-table'),
153 chain = link.textContent,
154 num = +link.getAttribute('data-num'),
155 elem = document.getElementById('rule_%s_%s'.format(table.toLowerCase(), chain));
158 (document.documentElement || document.body.parentNode || document.body).scrollTop = elem.offsetTop -
40;
159 elem.classList.remove('flash');
160 void elem.offsetWidth;
161 elem.classList.add('flash');
164 var rule = elem.nextElementSibling.childNodes[num];
166 rule.classList.remove('flash');
167 void rule.offsetWidth;
168 rule.classList.add('flash');
174 function parse_output(table, s)
176 var current_chain = null;
177 var current_rules = [];
178 var seen_chains = {};
180 var re = /([^\n]*)\n/g;
183 while ((m = re.exec(s)) != null) {
184 if (m[
1].match(/^Chain (.+) \(policy (\w+) (\d+) packets, (\d+) bytes\)$/)) {
185 var chain = RegExp.$
1,
187 packets = +RegExp.$
3,
190 update_chain_section(current_chain, current_rules);
192 seen_chains[chain] = true;
193 current_chain = create_chain_section(table, chain, policy, packets, bytes);
196 else if (m[
1].match(/^Chain (.+) \((\d+) references\)$/)) {
197 var chain = RegExp.$
1,
198 references = +RegExp.$
2;
200 update_chain_section(current_chain, current_rules);
202 seen_chains[chain] = true;
203 current_chain = create_chain_section(table, chain, null, null, null, references);
206 else if (m[
1].match(/^num /)) {
209 else if ((m2 = m[
1].match(/^(\d+) +(\d+) +(\d+) +(.*?) +(\S+) +(\S*) +(\S+) +(\S+) +([a-f0-
9:.]+\/\d+) +([a-f0-
9:.]+\/\d+) +(.+)$/)) !== null) {
219 options = m2[
11] || '-',
222 options = options.trim().replace(/(?:^| )\/\* (.+) \*\//,
224 comment = m2.replace(/^!fw3(: |$)/, '').trim() || '-';
229 '%
.2m'.format(pkts).nobr(),
230 '%
.2mB'.format(bytes).nobr(),
231 target ? '
<span class=
"target">%s
</span>'.format(target) : '-',
233 (indev !== '*') ? '
<span class=
"ifacebadge">%s
</span>'.format(indev) : '*',
234 (outdev !== '*') ? '
<span class=
"ifacebadge">%s
</span>'.format(outdev) : '*',
242 chain_refs[target] = chain_refs[target] || [];
243 chain_refs[target].push([ current_chain, num ]);
248 update_chain_section(current_chain, current_rules);
250 document.querySelectorAll('[
data-table=
"%s"] [data-chain]'.format(table))
251 .forEach(function(cdiv) {
252 if (!seen_chains[cdiv.getAttribute('data-chain')]) {
253 cdiv.parentNode.removeChild(cdiv);
257 cdiv.querySelectorAll('.target').forEach(function(tspan) {
258 if (seen_chains[tspan.textContent]) {
259 tspan.classList.add('jump');
260 tspan.addEventListener('click', jump_target);
264 cdiv.querySelectorAll('.references').forEach(function(rspan) {
265 var refs = chain_refs[cdiv.getAttribute('data-chain')];
266 if (refs && refs.length) {
267 rspan.classList.add('cbi-tooltip-container');
268 rspan.appendChild(E('small', { 'class': 'cbi-tooltip ifacebadge', 'style': 'top:
1em; left:auto' }, [ E('ul') ]));
270 refs.forEach(function(ref) {
271 var chain = ref[
0].parentNode.getAttribute('data-chain'),
274 rspan.lastElementChild.lastElementChild.appendChild(E('li', {}, [
279 'onclick': 'jump_target(event)'
281 ', <%:Rule%
> #%d'.format(num)
289 table_names.forEach(function(table) {
290 XHR.poll(
5, '<%=url(
"admin/status/iptables_dump", tostring(mode))%
>/' + table.toLowerCase(), null,
292 parse_output(table, xhr.responseText);
297 <h2 name=
"content"><%:Firewall Status%
></h2>
299 <% if has_ip6tables then %
>
300 <ul class=
"cbi-tabmenu">
301 <li class=
"cbi-tab<%= mode ~= 4 and "-disabled
" %>"><a href=
"<%=url("admin/status/iptables/
4")%>"><%:IPv4 Firewall%
></a></li>
302 <li class=
"cbi-tab<%= mode ~= 6 and "-disabled
" %>"><a href=
"<%=url("admin/status/iptables/
6")%>"><%:IPv6 Firewall%
></a></li>
306 <div style=
"position: relative">
307 <form method=
"post" action=
"<%=url("admin/status/iptables_action
")%>" style=
"position: absolute; right: 0">
308 <input type=
"hidden" name=
"token" value=
"<%=token%>" />
309 <input type=
"hidden" name=
"family" value=
"<%=mode%>" />
310 <input type=
"button" class=
"cbi-button" data-hide-empty=
"false" value=
"<%:Hide empty chains%>" onclick=
"hide_empty(this)" />
311 <input type=
"submit" class=
"cbi-button" name=
"zero" value=
"<%:Reset Counters%>" />
312 <input type=
"submit" class=
"cbi-button" name=
"restart" value=
"<%:Restart Firewall%>" />
317 <p><em><%:Collecting data...%
></em></p>