#!/usr/bin/lua require("luci.http") require("luci.util") require("luci.model.uci") -- Init state session luci.model.uci.load_state("luci_splash") local uci = luci.model.uci function main(argv) local cmd = argv[1] local arg = argv[2] if cmd == "status" then if not arg then os.exit(1) end if iswhitelisted(arg) then print("whitelisted") os.exit(0) end if haslease(arg) then print("lease") os.exit(0) end print("unknown") os.exit(0) elseif cmd == "add" then if not arg then os.exit(1) end if not haslease(arg) then add_lease(arg) else print("already leased!") os.exit(2) end os.exit(0) elseif cmd == "remove" then if not arg then os.exit(1) end remove_lease(arg) os.exit(0) elseif cmd == "sync" then sync() os.exit(0) else print("Usage: " .. argv[0] .. " [MAC]") os.exit(1) end end -- Add a lease to state and invoke add_rule function add_lease(mac) uci.section("luci_splash", "lease", nil, { mac = mac, start = os.time() }) add_rule(mac) uci.save_state("luci_splash") end -- Remove a lease from state and invoke remove_rule function remove_lease(mac) mac = mac:lower() local del = {} uci.foreach("luci_splash", "lease", function (section) if section.mac:lower() == mac then table.insert(del, section[".name"]) end end) for i,j in ipairs(del) do remove_rule(j) uci.delete("luci_splash", j) end uci.save_state("luci_splash") end -- Add an iptables rule function add_rule(mac) return os.execute("iptables -t nat -I luci_splash_leases -m mac --mac-source '"..mac.."' -j RETURN") end -- Remove an iptables rule function remove_rule(mac) return os.execute("iptables -t nat -D luci_splash_leases -m mac --mac-source '"..mac.."' -j RETURN") end -- Check whether a MAC-Address is listed in the lease state list function haslease(mac) mac = mac:lower() local stat = false uci.foreach("luci_splash", "lease", function (section) if section.mac:lower() == mac then stat = true return end end) return stat end -- Check whether a MAC-Address is whitelisted function iswhitelisted(mac) mac = mac:lower() uci.foreach("luci_splash", "whitelist", function (section) if section.mac:lower() == mac then stat = true return end end) return false end -- Returns a list of MAC-Addresses for which a rule is existing function listrules() local cmd = "iptables -t nat -L luci_splash_leases | grep RETURN |" cmd = cmd .. "egrep -io [0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+" return luci.util.split(luci.util.exec(cmd)) end -- Synchronise leases, remove abandoned rules function sync() local written = {} local time = os.time() -- Current leases in state files local leases = uci.get_all("luci_splash") -- Convert leasetime to seconds local leasetime = tonumber(uci.get("luci_splash", "general", "leasetime")) * 3600 -- Clean state file uci.load_state("luci_splash") uci.revert("luci_splash") -- For all leases for k, v in pairs(leases) do if v[".type"] == "lease" then if os.difftime(time, tonumber(v.start)) > leasetime then -- Remove expired remove_rule(v.mac) else -- Rewrite state uci.section("luci_splash", "lease", nil, { mac = v.mac, start = v.start }) written[v.mac:lower()] = 1 end end end -- Delete rules without state for i, r in ipairs(listrules()) do if #r > 0 and not written[r:lower()] then remove_rule(r) end end uci.save_state("luci_splash") end main(arg)