Moar Makefile fixes
[project/luci.git] / applications / luci-splash / root / usr / sbin / luci-splash
1 #!/usr/bin/lua
2
3 require("luci.util")
4 require("luci.model.uci")
5 require("luci.sys.iptparser")
6
7 -- Init state session
8 local uci = luci.model.uci.cursor_state()
9 local ipt = luci.sys.iptparser.IptParser()
10
11
12 function main(argv)
13         local cmd = argv[1]
14         local arg = argv[2]
15         
16         if cmd == "status" and arg then
17                 if islisted("whitelist", arg) then
18                         print("whitelisted")
19                 elseif islisted("blacklist", arg) then
20                         print("blacklisted")
21                 else            
22                         local lease = haslease(arg)
23                         if lease and lease.kicked then
24                                 print("kicked")
25                         elseif lease then
26                                 print("lease")
27                         else
28                                 print("unknown")
29                         end
30                 end
31                 os.exit(0)
32         elseif cmd == "add" and arg then
33                 if not haslease(arg) then
34                         add_lease(arg)
35                 else
36                         print("already leased!")
37                         os.exit(2)
38                 end
39                 os.exit(0)
40         elseif cmd == "remove" and arg then
41                 remove_lease(arg)
42                 os.exit(0)              
43         elseif cmd == "sync" then
44                 sync()
45                 os.exit(0)
46         else
47                 print("Usage: " .. argv[0] .. " <status|add|remove|sync> [MAC]")
48                 os.exit(1)      
49         end
50 end
51
52 -- Add a lease to state and invoke add_rule
53 function add_lease(mac)
54         uci:section("luci_splash", "lease", nil, {
55                 mac = mac,
56                 start = os.time()
57         })
58         add_rule(mac)
59         
60         uci:save("luci_splash")
61 end
62
63
64 -- Remove a lease from state and invoke remove_rule
65 function remove_lease(mac)
66         mac = mac:lower()
67         remove_rule(mac)
68
69         uci:delete_all("luci_splash", "lease",
70                 function(s) return ( s.mac:lower() == mac ) end)
71                 
72         uci:save("luci_splash")
73 end
74
75
76 -- Add an iptables rule
77 function add_rule(mac)
78         os.execute("iptables -I luci_splash_counter -m mac --mac-source '"..mac.."'")
79         return os.execute("iptables -t nat -I luci_splash_leases -m mac --mac-source '"..mac.."' -j RETURN")
80 end
81
82
83 -- Remove an iptables rule
84 function remove_rule(mac)
85         for _, r in ipairs(ipt:find({table="filter", chain="luci_splash_counter"})) do
86                 if r.options and #r.options >= 2 and r.options[1] == "MAC" and
87                    r.options[2]:lower() == mac:lower()
88                 then
89                         os.execute("iptables -D luci_splash_counter -m mac --mac-source %q -j %s"
90                                 %{ mac, r.target })
91                 end
92         end
93
94         for _, r in ipairs(ipt:find({table="nat", chain="luci_splash_leases"})) do
95                 if r.options and #r.options >= 2 and r.options[1] == "MAC" and
96                    r.options[2]:lower() == mac:lower()
97                 then
98                         os.execute("iptables -t nat -D luci_splash_leases -m mac --mac-source %q -j %s"
99                                 %{ mac, r.target })
100                 end
101         end
102
103         ipt:resync()
104 end
105
106
107 -- Check whether a MAC-Address is listed in the lease state list
108 function haslease(mac)
109         mac = mac:lower()
110         local lease = nil
111
112         uci:foreach("luci_splash", "lease",
113                 function (section)
114                         if section.mac:lower() == mac then
115                                 lease = section
116                         end
117                 end)
118
119         return lease
120 end
121
122
123 -- Check whether a MAC-Address is in given list
124 function islisted(what, mac)
125         mac = mac:lower()
126
127         uci:foreach("luci_splash", what,
128                 function (section)
129                         if section.mac:lower() == mac then
130                                 stat = true
131                                 return
132                         end
133                 end)
134
135         return false
136 end
137
138
139 -- Returns a list of MAC-Addresses for which a rule is existing
140 function listrules()
141         local macs = { }
142         for i, r in ipairs(ipt:find({table="nat", chain="luci_splash_leases"})) do
143                 if r.options and #r.options >= 2 and r.options[1] == "MAC" then
144                         macs[r.options[2]:lower()] = true
145                 end
146         end
147         return luci.util.keys(macs)
148 end
149
150
151 -- Synchronise leases, remove abandoned rules
152 function sync()
153         local written = {}
154         local time = os.time()
155         
156         -- Current leases in state files
157         local leases = uci:get_all("luci_splash")
158         
159         -- Convert leasetime to seconds
160         local leasetime = tonumber(uci:get("luci_splash", "general", "leasetime")) * 3600
161         
162         -- Clean state file
163         uci:load("luci_splash")
164         uci:revert("luci_splash")
165         
166         
167         -- For all leases
168         for k, v in pairs(leases) do
169                 if v[".type"] == "lease" then
170                         if os.difftime(time, tonumber(v.start)) > leasetime then
171                                 -- Remove expired
172                                 remove_rule(v.mac)
173                         else
174                                 -- Rewrite state
175                                 uci:section("luci_splash", "lease", nil, {              
176                                         mac    = v.mac,
177                                         start  = v.start,
178                                         kicked = v.kicked
179                                 })
180                                 written[v.mac:lower()] = 1
181                         end
182                 elseif v[".type"] == "whitelist" or v[".type"] == "blacklist" then
183                         written[v.mac:lower()] = 1
184                 end
185         end
186         
187         
188         -- Delete rules without state
189         for i, r in ipairs(listrules()) do
190                 if #r > 0 and not written[r:lower()] then
191                         remove_rule(r)
192                 end
193         end
194         
195         uci:save("luci_splash")
196 end
197
198 main(arg)