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