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