luci-splash 22 KB

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