675e6cb97a3e0daa14c10db433ba9bf318a9d8e6
[project/luci.git] / applications / luci-splash / root / usr / sbin / luci-splash
1 #!/usr/bin/lua
2
3 utl = require "luci.util"
4 sys = require "luci.sys"
5
6 require("luci.model.uci")
7 require("luci.sys.iptparser")
8
9 -- Init state session
10 local uci = luci.model.uci.cursor_state()
11 local ipt = luci.sys.iptparser.IptParser()
12 local net = sys.net
13 local fs = require "luci.fs"
14 local ip = require "luci.ip"
15
16 local debug = true
17
18 local has_ipv6 = fs.access("/proc/net/ipv6_route") and fs.access("/usr/sbin/ip6tables")
19
20 function lock()
21 os.execute("lock /var/run/luci_splash.lock")
22 end
23
24 function unlock()
25 os.execute("lock -u /var/run/luci_splash.lock")
26 end
27
28 function exec(cmd)
29 if debug then
30 local ret = sys.exec(cmd)
31 print('+ ' .. cmd)
32 if ret and ret ~= "" then
33 print(ret)
34 end
35 else
36 local ret = sys.exec(cmd .. " &> /dev/null")
37 end
38 end
39
40 function get_id(ip)
41 local o3, o4 = ip:match("[0-9]+%.[0-9]+%.([0-9]+)%.([0-9]+)")
42 if o3 and 04 then
43 return string.format("%02X%s", tonumber(o3), "") .. string.format("%02X%s", tonumber(o4), "")
44 else
45 return false
46 end
47 end
48
49 function update_stats(leased, whitelisted, whitelisttotal, blacklisted, blacklisttotal)
50 local leases = uci:get_all("luci_splash_leases", "stats")
51 uci:delete("luci_splash_leases", "stats")
52 uci:section("luci_splash_leases", "stats", "stats", {
53 leases = leased or (leases and leases.leases) or 0,
54 whitelisttotal = whitelisttotal or (leased and leases.whitelisttotal) or 0,
55 whitelistonline = whitelisted or (leases and leases.whitelistonline) or 0,
56 blacklisttotal = blacklisttotal or (leases and leases.blacklisttotal) or 0,
57 blacklistonline = blacklisted or (leases and leases.blacklistonline) or 0,
58 })
59 uci:save("luci_splash_leases")
60 end
61
62 function get_device_for_ip(ipaddr)
63 local dev
64 uci:foreach("network", "interface", function(s)
65 if s.ipaddr and s.netmask then
66 local network = ip.IPv4(s.ipaddr, s.netmask)
67 if network:contains(ip.IPv4(ipaddr)) then
68 -- this should be rewritten to luci functions if possible
69 dev = utl.trim(sys.exec(". /lib/functions/network.sh; network_get_device IFNAME '" .. s['.name'] .. "'; echo $IFNAME"))
70 end
71 end
72 end)
73 return dev
74 end
75
76 function get_physdev(interface)
77 local dev
78 dev = utl.trim(sys.exec(". /lib/functions/network.sh; network_get_device IFNAME '" .. interface .. "'; echo $IFNAME"))
79 return dev
80 end
81
82
83
84 function get_filter_handle(parent, direction, device, mac)
85 local input = utl.split(sys.exec('/usr/sbin/tc filter show dev ' .. device .. ' parent ' .. parent) or {})
86 local tbl = {}
87 local handle
88 for k, v in pairs(input) do
89 handle = v:match('filter protocol ip pref %d+ u32 fh (%d*:%d*:%d*) order') or v:match('filter protocol all pref %d+ u32 fh (%d*:%d*:%d*) order')
90 if handle then
91 local mac, mac1, mac2, mac3, mac4, mac5, mac6
92 if direction == 'src' then
93 mac1, mac2, mac3, mac4 = input[k+1]:match('match ([%a%d][%a%d])([%a%d][%a%d])([%a%d][%a%d])([%a%d][%a%d])/ffffffff')
94 mac5, mac6 = input[k+2]:match('match ([%a%d][%a%d])([%a%d][%a%d])0000/ffff0000')
95 else
96 mac1, mac2 = input[k+1]:match('match 0000([%a%d][%a%d])([%a%d][%a%d])/0000ffff')
97 mac3, mac4, mac5, mac6 = input[k+2]:match('match ([%a%d][%a%d])([%a%d][%a%d])([%a%d][%a%d])([%a%d][%a%d])/ffffffff')
98 end
99 if mac1 and mac2 and mac3 and mac4 and mac5 and mac6 then
100 mac = "%s:%s:%s:%s:%s:%s" % { mac1, mac2, mac3, mac4, mac5, mac6 }
101 tbl[mac] = handle
102 end
103 end
104 end
105 if tbl[mac] then
106 handle = tbl[mac]
107 end
108 return handle
109 end
110
111 function macvalid(mac)
112 if mac and mac:match(
113 "^[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:" ..
114 "[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]:" ..
115 "[a-fA-F0-9][a-fA-F0-9]:[a-fA-F0-9][a-fA-F0-9]$"
116 ) then
117 return true
118 end
119
120 return false
121 end
122
123 function ipvalid(ipaddr)
124 if ipaddr then
125 return ip.IPv4(ipaddr) and true or false
126 end
127
128 return false
129 end
130
131 function main(argv)
132 local cmd = table.remove(argv, 1)
133 local arg = argv[1]
134
135 limit_up = (tonumber(uci:get("luci_splash", "general", "limit_up")) or 0) * 8
136 limit_down = (tonumber(uci:get("luci_splash", "general", "limit_down")) or 0) * 8
137
138 if ( cmd == "lease" or cmd == "add-rules" or cmd == "remove" or
139 cmd == "whitelist" or cmd == "blacklist" or cmd == "status" ) and #argv > 0
140 then
141 if not (macvalid(arg) or ipvalid(arg)) then
142 print("Invalid argument. The second argument must " ..
143 "be a valid IPv4 or Mac Address.")
144 os.exit(1)
145 end
146
147 lock()
148
149 local arp_cache = net.arptable()
150 local leased_macs = get_known_macs("lease")
151 local blacklist_macs = get_known_macs("blacklist")
152 local whitelist_macs = get_known_macs("whitelist")
153
154 for i, adr in ipairs(argv) do
155 local mac = nil
156 if adr:find(":") then
157 mac = adr:lower()
158 else
159 for _, e in ipairs(arp_cache) do
160 if e["IP address"] == adr then
161 mac = e["HW address"]:lower()
162 break
163 end
164 end
165 end
166
167 if mac and cmd == "add-rules" then
168 if leased_macs[mac] then
169 add_lease(mac, arp_cache, true)
170 elseif blacklist_macs[mac] then
171 add_blacklist_rule(mac)
172 elseif whitelist_macs[mac] then
173 add_whitelist_rule(mac)
174 end
175 elseif mac and cmd == "status" then
176 print(leased_macs[mac] and "lease"
177 or whitelist_macs[mac] and "whitelist"
178 or blacklist_macs[mac] and "blacklist"
179 or "new")
180 elseif mac and ( cmd == "whitelist" or cmd == "blacklist" or cmd == "lease" ) then
181 if cmd ~= "lease" and leased_macs[mac] then
182 print("Removing %s from leases" % mac)
183 remove_lease(mac)
184 leased_macs[mac] = nil
185 end
186
187 if cmd ~= "whitelist" and whitelist_macs[mac] then
188 if cmd == "lease" then
189 print('%s is whitelisted. Remove it before you can lease it.' % mac)
190 else
191 print("Removing %s from whitelist" % mac)
192 remove_whitelist(mac)
193 whitelist_macs[mac] = nil
194 end
195 end
196
197 if cmd == "whitelist" and leased_macs[mac] then
198 print("Removing %s from leases" % mac)
199 remove_lease(mac)
200 leased_macs[mac] = nil
201 end
202
203 if cmd ~= "blacklist" and blacklist_macs[mac] then
204 print("Removing %s from blacklist" % mac)
205 remove_blacklist(mac)
206 blacklist_macs[mac] = nil
207 end
208
209 if cmd == "lease" and not leased_macs[mac] then
210 if not whitelist_macs[mac] then
211 print("Adding %s to leases" % mac)
212 add_lease(mac)
213 leased_macs[mac] = true
214 end
215 elseif cmd == "whitelist" and not whitelist_macs[mac] then
216 print("Adding %s to whitelist" % mac)
217 add_whitelist(mac)
218 whitelist_macs[mac] = true
219 elseif cmd == "blacklist" and not blacklist_macs[mac] then
220 print("Adding %s to blacklist" % mac)
221 add_blacklist(mac)
222 blacklist_macs[mac] = true
223 else
224 print("The mac %s is already %sed" %{ mac, cmd })
225 end
226 elseif mac and cmd == "remove" then
227 if leased_macs[mac] then
228 print("Removing %s from leases" % mac)
229 remove_lease(mac)
230 leased_macs[mac] = nil
231 elseif whitelist_macs[mac] then
232 print("Removing %s from whitelist" % mac)
233 remove_whitelist(mac)
234 whitelist_macs[mac] = nil
235 elseif blacklist_macs[mac] then
236 print("Removing %s from blacklist" % mac)
237 remove_blacklist(mac)
238 blacklist_macs[mac] = nil
239 else
240 print("The mac %s is not known" % mac)
241 end
242 else
243 print("Can not find mac for ip %s" % argv[i])
244 end
245 end
246
247 unlock()
248 os.exit(0)
249 elseif cmd == "sync" then
250 sync()
251 os.exit(0)
252 elseif cmd == "list" then
253 list()
254 os.exit(0)
255 else
256 print("Usage:")
257 print("\n luci-splash list\n List connected, black- and whitelisted clients")
258 print("\n luci-splash sync\n Synchronize firewall rules and clear expired leases")
259 print("\n luci-splash lease <MAC-or-IP>\n Create a lease for the given address")
260 print("\n luci-splash blacklist <MAC-or-IP>\n Add given address to blacklist")
261 print("\n luci-splash whitelist <MAC-or-IP>\n Add given address to whitelist")
262 print("\n luci-splash remove <MAC-or-IP>\n Remove given address from the lease-, black- or whitelist")
263 print("")
264
265 os.exit(1)
266 end
267 end
268
269 -- Get current arp cache
270 function get_arpcache()
271 local arpcache = { }
272 for _, entry in ipairs(net.arptable()) do
273 arpcache[entry["HW address"]:lower()] = { entry["Device"]:lower(), entry["IP address"]:lower() }
274 end
275 return arpcache
276 end
277
278 -- Get a list of known mac addresses
279 function get_known_macs(list)
280 local leased_macs = { }
281
282 if not list or list == "lease" then
283 uci:foreach("luci_splash_leases", "lease",
284 function(s) leased_macs[s.mac:lower()] = true end)
285 end
286
287 if not list or list == "whitelist" then
288 uci:foreach("luci_splash", "whitelist",
289 function(s) leased_macs[s.mac:lower()] = true end)
290 end
291
292 if not list or list == "blacklist" then
293 uci:foreach("luci_splash", "blacklist",
294 function(s) leased_macs[s.mac:lower()] = true end)
295 end
296
297 return leased_macs
298 end
299
300
301 -- Helper to delete iptables rules
302 function ipt_delete_all(args, comp, off)
303 off = off or { }
304 for i, r in ipairs(ipt:find(args)) do
305 if comp == nil or comp(r) then
306 off[r.table] = off[r.table] or { }
307 off[r.table][r.chain] = off[r.table][r.chain] or 0
308
309 exec("iptables -t %q -D %q %d 2>/dev/null"
310 %{ r.table, r.chain, r.index - off[r.table][r.chain] })
311
312 off[r.table][r.chain] = off[r.table][r.chain] + 1
313 end
314 end
315 end
316
317 function ipt6_delete_all(args, comp, off)
318 off = off or { }
319 for i, r in ipairs(ipt:find(args)) do
320 if comp == nil or comp(r) then
321 off[r.table] = off[r.table] or { }
322 off[r.table][r.chain] = off[r.table][r.chain] or 0
323
324 exec("ip6tables -t %q -D %q %d 2>/dev/null"
325 %{ r.table, r.chain, r.index - off[r.table][r.chain] })
326
327 off[r.table][r.chain] = off[r.table][r.chain] + 1
328 end
329 end
330 end
331
332
333 -- Convert mac to uci-compatible section name
334 function convert_mac_to_secname(mac)
335 return string.gsub(mac, ":", "")
336 end
337
338 -- Add a lease to state and invoke add_rule
339 function add_lease(mac, arp, no_uci)
340 mac = mac:lower()
341
342 -- Get current ip address
343 local ipaddr
344 for _, entry in ipairs(arp or net.arptable()) do
345 if entry["HW address"]:lower() == mac then
346 ipaddr = entry["IP address"]
347 break
348 end
349 end
350
351 -- Add lease if there is an ip addr
352 if ipaddr then
353 local device = get_device_for_ip(ipaddr)
354 if not no_uci then
355 local leased = uci:get("luci_splash_leases", "stats", "leases")
356 if type(tonumber(leased)) == "number" then
357 update_stats(leased + 1, nil, nil, nil, nil)
358 end
359
360 uci:section("luci_splash_leases", "lease", convert_mac_to_secname(mac), {
361 mac = mac,
362 ipaddr = ipaddr,
363 device = device,
364 limit_up = limit_up,
365 limit_down = limit_down,
366 start = os.time()
367 })
368 uci:save("luci_splash_leases")
369 end
370 add_lease_rule(mac, ipaddr, device)
371 else
372 print("Found no active IP for %s, lease not added" % mac)
373 end
374 end
375
376
377 -- Remove a lease from state and invoke remove_rule
378 function remove_lease(mac)
379 mac = mac:lower()
380
381 uci:delete_all("luci_splash_leases", "lease",
382 function(s)
383 if s.mac:lower() == mac then
384 remove_lease_rule(mac, s.ipaddr, s.device, tonumber(s.limit_up), tonumber(s.limit_down))
385 local leased = uci:get("luci_splash_leases", "stats", "leases")
386 if type(tonumber(leased)) == "number" and tonumber(leased) > 0 then
387 update_stats(leased - 1, nil, nil, nil, nil)
388 end
389 return true
390 end
391 return false
392 end)
393
394 uci:save("luci_splash_leases")
395 end
396
397
398 -- Add a whitelist entry
399 function add_whitelist(mac)
400 uci:section("luci_splash", "whitelist", convert_mac_to_secname(mac), { mac = mac })
401 uci:save("luci_splash")
402 uci:commit("luci_splash")
403 add_whitelist_rule(mac)
404 end
405
406
407 -- Add a blacklist entry
408 function add_blacklist(mac)
409 uci:section("luci_splash", "blacklist", convert_mac_to_secname(mac), { mac = mac })
410 uci:save("luci_splash")
411 uci:commit("luci_splash")
412 add_blacklist_rule(mac)
413 end
414
415
416 -- Remove a whitelist entry
417 function remove_whitelist(mac)
418 mac = mac:lower()
419 uci:delete_all("luci_splash", "whitelist",
420 function(s) return not s.mac or s.mac:lower() == mac end)
421 uci:save("luci_splash")
422 uci:commit("luci_splash")
423 remove_lease_rule(mac)
424 remove_whitelist_tc(mac)
425 end
426
427 function remove_whitelist_tc(mac)
428 uci:foreach("luci_splash", "iface", function(s)
429 local device = get_physdev(s['.name'])
430 if device and device ~= "" then
431 if debug then
432 print("Removing whitelist filters for %s interface %s." % {mac, device})
433 end
434 local handle = get_filter_handle('ffff:', 'src', device, mac)
435 if handle then
436 exec('tc filter del dev "%s" parent ffff: protocol ip prio 1 handle %s u32' % { device, handle })
437 else
438 print('Warning! Could not get a handle for %s parent :ffff on interface %s' % { mac, device })
439 end
440 local handle = get_filter_handle('1:', 'dest', device, mac)
441 if handle then
442 exec('tc filter del dev "%s" parent 1:0 protocol ip prio 1 handle %s u32' % { device, handle })
443 else
444 print('Warning! Could not get a handle for %s parent 1:0 on interface %s' % { mac, device })
445 end
446 end
447 end)
448 end
449
450 -- Remove a blacklist entry
451 function remove_blacklist(mac)
452 mac = mac:lower()
453 uci:delete_all("luci_splash", "blacklist",
454 function(s) return not s.mac or s.mac:lower() == mac end)
455 uci:save("luci_splash")
456 uci:commit("luci_splash")
457 remove_lease_rule(mac)
458 end
459
460
461 -- Add an iptables rule
462 function add_lease_rule(mac, ipaddr, device)
463 local id
464 if ipaddr then
465 id = get_id(ipaddr)
466 end
467
468 exec("iptables -t mangle -I luci_splash_mark_out -m mac --mac-source %q -j RETURN" % mac)
469
470 -- Mark incoming packets to a splashed host
471 -- for ipv4 - by iptables and destination
472 if id and device then
473 exec("iptables -t mangle -I luci_splash_mark_in -d %q -j MARK --set-mark 0x1%s -m comment --comment %s" % {ipaddr, id, mac:upper()})
474 end
475
476 --for ipv6: need to use the mac here
477 if has_ipv6 then
478 exec("ip6tables -t mangle -I luci_splash_mark_out -m mac --mac-source %q -j MARK --set-mark 79" % mac)
479 if id and device and tonumber(limit_down) then
480 exec("tc filter add dev %s parent 1:0 protocol ipv6 prio 1 u32 match ether dst %s classid 1:%s" % {device, mac:lower(), id})
481 end
482 end
483
484
485 if device and tonumber(limit_up) > 0 then
486 exec('tc filter add dev "%s" parent ffff: protocol all prio 2 u32 match ether src %s police rate %skbit mtu 6k burst 6k drop' % {device, mac, limit_up})
487 end
488
489 if id and device and tonumber(limit_down) > 0 then
490 exec("tc class add dev %s parent 1: classid 1:0x%s htb rate %skbit" % { device, id, limit_down })
491 exec("tc qdisc add dev %s parent 1:%s sfq perturb 10" % { device, id })
492 end
493
494 exec("iptables -t filter -I luci_splash_filter -m mac --mac-source %q -j RETURN" % mac)
495 exec("iptables -t nat -I luci_splash_leases -m mac --mac-source %q -j RETURN" % mac)
496 if has_ipv6 then
497 exec("ip6tables -t filter -I luci_splash_filter -m mac --mac-source %q -j RETURN" % mac)
498 end
499 end
500
501
502 -- Remove lease, black- or whitelist rules
503 function remove_lease_rule(mac, ipaddr, device, limit_up, limit_down)
504 local id
505 if ipaddr then
506 id = get_id(ipaddr)
507 end
508
509 ipt:resync()
510 ipt_delete_all({table="mangle", chain="luci_splash_mark_in", options={"/*", mac:upper()}})
511 ipt_delete_all({table="mangle", chain="luci_splash_mark_out", options={"MAC", mac:upper()}})
512 ipt_delete_all({table="filter", chain="luci_splash_filter", options={"MAC", mac:upper()}})
513 ipt_delete_all({table="nat", chain="luci_splash_leases", options={"MAC", mac:upper()}})
514 if has_ipv6 then
515 ipt6_delete_all({table="mangle", chain="luci_splash_mark_out", options={"MAC", mac:upper()}})
516 ipt6_delete_all({table="filter", chain="luci_splash_filter", options={"MAC", mac:upper()}})
517 end
518
519 if device and tonumber(limit_up) > 0 then
520 local handle = get_filter_handle('ffff:', 'src', device, mac)
521 if handle then
522 exec('tc filter del dev "%s" parent ffff: protocol all prio 2 handle %s u32 police rate %skbit mtu 6k burst 6k drop' % {device, handle, limit_up})
523 else
524 print('Warning! Could not get a handle for %s parent :ffff on interface %s' % { mac, device })
525 end
526 end
527
528 -- remove clients class
529 if device and id then
530 exec('tc class del dev "%s" classid 1:%s' % {device, id})
531 exec('tc filter del dev "%s" parent 1:0 prio 1' % device) -- ipv6 rule
532 --exec('tc qdisc del dev "%s" parent 1:%s sfq perturb 10' % { device, id })
533 end
534
535 end
536
537
538 -- Add whitelist rules
539 function add_whitelist_rule(mac)
540 exec("iptables -t filter -I luci_splash_filter -m mac --mac-source %q -j RETURN" % mac)
541 exec("iptables -t nat -I luci_splash_leases -m mac --mac-source %q -j RETURN" % mac)
542 if has_ipv6 then
543 exec("ip6tables -t filter -I luci_splash_filter -m mac --mac-source %q -j RETURN" % mac)
544 end
545 uci:foreach("luci_splash", "iface", function(s)
546 local device = get_physdev(s['.name'])
547 if device and device ~= "" then
548 exec('tc filter add dev "%s" parent ffff: protocol ip prio 1 u32 match ether src %s police pass' % { device, mac })
549 exec('tc filter add dev "%s" parent 1:0 protocol ip prio 1 u32 match ether dst %s classid 1:1' % { device, mac })
550 end
551 end)
552 end
553
554
555 -- Add blacklist rules
556 function add_blacklist_rule(mac)
557 exec("iptables -t filter -I luci_splash_filter -m mac --mac-source %q -j DROP" % mac)
558 if has_ipv6 then
559 exec("ip6tables -t filter -I luci_splash_filter -m mac --mac-source %q -j DROP" % mac)
560 end
561 end
562
563
564 -- Synchronise leases, remove abandoned rules
565 function sync()
566 lock()
567
568 local time = os.time()
569
570 -- Current leases in state files
571 local leases = uci:get_all("luci_splash_leases")
572
573 -- Convert leasetime to seconds
574 local leasetime = tonumber(uci:get("luci_splash", "general", "leasetime")) * 3600
575
576 -- Clean state file
577 uci:load("luci_splash_leases")
578 uci:revert("luci_splash_leases")
579
580
581 -- Get the mac addresses of current leases
582 local macs = get_known_macs()
583 local arpcache = get_arpcache()
584
585 local blackwhitelist = uci:get_all("luci_splash")
586 local whitelist_total = 0
587 local whitelist_online = 0
588 local blacklist_total = 0
589 local blacklist_online = 0
590
591
592 -- For all leases
593 local leasecount = 0
594 local leases_online = 0
595 for k, v in pairs(leases) do
596 if v[".type"] == "lease" then
597 if os.difftime(time, tonumber(v.start)) > leasetime then
598 -- Remove expired
599 remove_lease_rule(v.mac, v.ipaddr, v.device, tonumber(v.limit_up), tonumber(v.limit_down))
600 else
601 leasecount = leasecount + 1
602
603 -- only count leases_online for connected clients
604 if arpcache[v.mac] then
605 leases_online = leases_online + 1
606 end
607
608 -- Rewrite state
609 uci:section("luci_splash_leases", "lease", convert_mac_to_secname(v.mac), {
610 mac = v.mac,
611 ipaddr = v.ipaddr,
612 device = v.device,
613 limit_up = limit_up,
614 limit_down = limit_down,
615 start = v.start
616 })
617 end
618 end
619 end
620
621 -- Whitelist, Blacklist
622 for _, s in utl.spairs(blackwhitelist,
623 function(a,b) return blackwhitelist[a][".type"] > blackwhitelist[b][".type"] end
624 ) do
625 if (s[".type"] == "whitelist") then
626 whitelist_total = whitelist_total + 1
627 if s.mac then
628 local mac = s.mac:lower()
629 if arpcache[mac] then
630 whitelist_online = whitelist_online + 1
631 end
632 end
633 end
634 if (s[".type"] == "blacklist") then
635 blacklist_total = blacklist_total + 1
636 if s.mac then
637 local mac = s.mac:lower()
638 if arpcache[mac] then
639 blacklist_online = blacklist_online + 1
640 end
641 end
642 end
643 end
644
645 -- ToDo:
646 -- include a new field "leases_online" in stats to differ between active clients and leases:
647 -- update_stats(leasecount, leases_online, whitelist_online, whitelist_total, blacklist_online, blacklist_total) later:
648 update_stats(leases_online, whitelist_online, whitelist_total, blacklist_online, blacklist_total)
649
650 uci:save("luci_splash_leases")
651
652 ipt:resync()
653
654 ipt_delete_all({table="filter", chain="luci_splash_filter", options={"MAC"}},
655 function(r) return not macs[r.options[2]:lower()] end)
656 ipt_delete_all({table="nat", chain="luci_splash_leases", options={"MAC"}},
657 function(r) return not macs[r.options[2]:lower()] end)
658 ipt_delete_all({table="mangle", chain="luci_splash_mark_out", options={"MAC", "MARK", "set"}},
659 function(r) return not macs[r.options[2]:lower()] end)
660 ipt_delete_all({table="mangle", chain="luci_splash_mark_in", options={"/*", "MARK", "set"}},
661 function(r) return not macs[r.options[2]:lower()] end)
662
663
664 if has_ipv6 then
665 ipt6_delete_all({table="filter", chain="luci_splash_filter", options={"MAC"}},
666 function(r) return not macs[r.options[2]:lower()] end)
667 ipt6_delete_all({table="mangle", chain="luci_splash_mark_out", options={"MAC", "MARK", "set"}},
668 function(r) return not macs[r.options[2]:lower()] end)
669 end
670
671 unlock()
672 end
673
674 -- Show client info
675 function list()
676 local arpcache = get_arpcache()
677 -- Find traffic usage
678 local function traffic(lease)
679 local traffic_in = 0
680 local traffic_out = 0
681
682 local rin = ipt:find({table="mangle", chain="luci_splash_mark_in", destination=lease.ipaddr})
683 local rout = ipt:find({table="mangle", chain="luci_splash_mark_out", options={"MAC", lease.mac:upper()}})
684
685 if rin and #rin > 0 then traffic_in = math.floor( rin[1].bytes / 1024) end
686 if rout and #rout > 0 then traffic_out = math.floor(rout[1].bytes / 1024) end
687
688 return traffic_in, traffic_out
689 end
690
691 -- Print listings
692 local leases = uci:get_all("luci_splash_leases")
693 local blackwhitelist = uci:get_all("luci_splash")
694
695 print(string.format(
696 "%-17s %-15s %-9s %-4s %-7s %20s",
697 "MAC", "IP", "State", "Dur.", "Intf.", "Traffic down/up"
698 ))
699
700 -- Leases
701 for _, s in pairs(leases) do
702 if s[".type"] == "lease" and s.mac then
703 local ti, to = traffic(s)
704 local mac = s.mac:lower()
705 local arp = arpcache[mac]
706 print(string.format(
707 "%-17s %-15s %-9s %3dm %-7s %7dKB %7dKB",
708 mac, s.ipaddr, "leased",
709 math.floor(( os.time() - tonumber(s.start) ) / 60),
710 arp and arp[1] or "?", ti, to
711 ))
712 end
713 end
714
715 -- Whitelist, Blacklist
716 for _, s in utl.spairs(blackwhitelist,
717 function(a,b) return blackwhitelist[a][".type"] > blackwhitelist[b][".type"] end
718 ) do
719 if (s[".type"] == "whitelist" or s[".type"] == "blacklist") and s.mac then
720 local mac = s.mac:lower()
721 local arp = arpcache[mac]
722 print(string.format(
723 "%-17s %-15s %-9s %4s %-7s %9s %9s",
724 mac, arp and arp[2] or "?", s[".type"],
725 "- ", arp and arp[1] or "?", "-", "-"
726 ))
727 end
728 end
729 end
730
731 main(arg)