applications/luci-splash: Fix clients upload and download limiting
[project/luci.git] / applications / luci-splash / root / etc / init.d / luci_splash
1 #!/bin/sh /etc/rc.common
2
3 START=70
4 EXTRA_COMMANDS=clear_leases
5 LIMIT_DOWN=0
6 LIMIT_DOWN_BURST=0
7 LIMIT_UP=0
8 TC=/usr/sbin/tc
9 IPT=/usr/sbin/iptables
10 IPT6=/usr/sbin/ip6tables
11
12 IPT_REPLAY=/var/run/luci_splash.iptlog
13 LOCK=/var/run/luci_splash.lock
14 [ -x $IPT6 ] && [ -f /proc/net/ipv6_route ] && HAS_IPV6=1
15
16 silent() {
17         "$@" 2>/dev/null
18 }
19
20 ipt_log() {
21         $IPT -I "$@"
22         echo $IPT -D "$@" >> $IPT_REPLAY
23 }
24
25 ipt6_log() {
26         [ "$HAS_IPV6" = 1 ] || return
27         $IPT6 -I "$@"
28         echo $IPT6 -D "$@" >> $IPT_REPLAY
29 }
30
31
32 iface_add() {
33         local cfg="$1"
34
35         config_get zone "$cfg" zone
36         [ -n "$zone" ] || return 0
37
38         config_get net "$cfg" network
39         [ -n "$net" ] || return 0
40
41         config_get ifname "$net" ifname
42         [ -n "$ifname" ] || return 0
43
44         config_get ipaddr "$net" ipaddr
45         [ -n "$ipaddr" ] || return 0
46
47         config_get netmask "$net" netmask
48         [ -n "$netmask" ] || return 0
49
50         config_get ip6addr "$net" ip6addr
51
52         config_get type "$net" type
53
54         parentiface="$(uci -q get network.${net}.ifname)"
55
56         [ -n "$parentiface" ] && [ ! "$type" = "bridge" ] && {
57                 parentiface=${parentiface#@}
58                 config_get parentproto   "$parentiface" proto
59                 config_get parentipaddr  "$parentiface" ipaddr
60                 config_get parentnetmask "$parentiface" netmask
61         }
62
63         eval "$(ipcalc.sh $ipaddr $netmask)"
64
65         logger -s -p info -t splash "Add $NETWORK/$PREFIX ($ifname) to splashed networks."
66
67         ### Add interface specific chain entry rules
68         ipt_log "prerouting_${zone}_rule" -i "${ifname%:*}" -s "$NETWORK/$PREFIX" -j luci_splash_prerouting -t nat
69         ipt_log "forwarding_${zone}_rule"    -i "${ifname%:*}" -s "$NETWORK/$PREFIX" -j luci_splash_forwarding -t filter
70
71         if [ "$HAS_IPV6" = 1 ] && [ -n "$ip6addr" ]; then
72                 ipt6_log "forwarding_${zone}_rule"    -i "${ifname%:*}" -s "$ip6addr" -j luci_splash_forwarding -t filter
73         fi
74
75         ### Allow traffic to the same subnet
76         $IPT -t nat    -I luci_splash_prerouting -d "$ipaddr/${netmask:-32}" -j RETURN
77         $IPT -t filter -I luci_splash_forwarding -d "$ipaddr/${netmask:-32}" -j RETURN
78
79         ### Allow traffic to the mesh subnet
80         [ "$parentproto" = "static" -a -n "$parentipaddr" ] && {
81                 $IPT -t nat    -I luci_splash_prerouting -d "$parentipaddr/${parentnetmask:-32}" -j RETURN
82                 $IPT -t filter -I luci_splash_forwarding -d "$parentipaddr/${parentnetmask:-32}" -j RETURN
83         }
84
85         qos_iface_add "$ifname" "$NETWORK" "$PREFIX"
86 }
87
88 iface_del() {
89         config_get zone "$1" zone
90         [ -n "$zone" ] || return 0
91
92         config_get net "$1" network
93         [ -n "$net" ] || return 0
94
95         config_get ifname "$net" ifname
96         [ -n "$ifname" ] || return 0
97
98         # Clear interface specific rules
99         [ -s $IPT_REPLAY ] && {
100                 logger -s -p info -t splash "Remove $ifname from splashed networks."
101                 grep -- "-i ${ifname%:*}" $IPT_REPLAY | while read ln; do silent $ln; done
102                 sed -ie "/-i ${ifname%:*}/d" $IPT_REPLAY
103         }
104
105         qos_iface_del "$ifname"
106 }
107
108 mac_add() {
109         config_get mac "$1" mac
110         append MACS "$mac"
111 }
112
113 whitelist_add() {
114         config_get mac "$1" mac
115         iface=$2
116         $TC filter add dev "$iface" parent ffff: protocol ip prio 1 u32 match ether src $mac police pass
117         $TC filter add dev "$iface" parent 1:0 protocol ip prio 1 u32 match ether dst classid 1:1
118 }
119
120
121 subnet_add() {
122         local cfg="$1"
123
124         config_get ipaddr  "$cfg" ipaddr
125         config_get netmask "$cfg" netmask
126
127         [ -n "$ipaddr" ] && {
128                 $IPT -t nat    -I luci_splash_prerouting -d "$ipaddr/${netmask:-32}" -j RETURN
129                 $IPT -t filter -I luci_splash_forwarding -d "$ipaddr/${netmask:-32}" -j RETURN
130         }
131 }
132
133 qos_iface_add() {
134         local iface="$1"
135         local network="$2"
136         local prefix="$3"
137
138         # 77   -> download root qdisc
139         # ffff -> upload root qdisc
140
141         silent $TC qdisc del dev "$iface" root handle 1:
142         silent $TC class del dev "$iface" parent 1: classid 1:ffff
143         silent $TC class del dev "$iface" parent 1: classid 1:1
144         silent $TC filter del dev "$iface" parent ffff: protocol ip prio 1 u32
145         silent $TC filter del dev "$iface" parent ffff: protocol ip prio 2 u32
146         silent $TC filter del dev "$iface" parent ffff: protocol ip prio 3 u32
147
148         if [ "$LIMIT_UP" -gt 0 -a "$LIMIT_DOWN" -gt 0 ]; then
149                 # Setup qdiscs
150                 $TC qdisc add dev "$iface" root handle 1: htb default 1
151                 silent $TC qdisc add dev "$iface" ingress
152
153                 # Default class - all clients which are not otherwise handled are put in that class
154                 # and share that bandwidth.
155                 $TC class add dev "$iface" parent 1: classid 1:ffff htb rate ${LIMIT_DOWN}kbit
156
157                 # default class and class for whitelisted clients = unlimited
158                 $TC class add dev "$iface" parent 1: classid 1:1 htb rate 100mbit
159
160                 # All traffic to the dhcp subnet is put into the limited class
161                 $TC filter add dev "$iface" parent 1:0 protocol ip prio 3 u32 match ip dst $network/$prefix classid 1:ffff
162                 $TC qdisc add dev "$iface" parent 1:ffff sfq perturb 10
163                 $TC filter add dev "$iface" parent ffff: protocol ip prio 3 u32 match ip src $network/$prefix police rate ${LIMIT_UP}kbit mtu 6k burst 6k drop
164
165                 # classify packets by their iptables MARK set in luci_splash_mark_in (mangle table)
166                 # every client gets his own class and so his own bandwidth limit
167                 $TC filter add dev "$iface" parent 1:0 protocol ip prio 2 fw
168
169                 config_foreach whitelist_add whitelist $iface
170         fi
171 }
172
173 qos_iface_del() {
174         local iface="$1"
175         silent $TC qdisc del dev "$iface" root handle 77:
176 }
177
178 boot() {
179         ### Setup splash-relay
180         uci get uhttpd.splash 2>/dev/null || {
181 uci batch <<EOF
182         set uhttpd.splash=uhttpd
183         set uhttpd.splash.home="/www/cgi-bin/splash/"
184         set uhttpd.splash.interpreter=".sh=/bin/ash"
185         set uhttpd.splash.listen_http="8082"
186         set uhttpd.splash.index_page="splash.sh"
187         set uhttpd.splash.error_page="/splash.sh"
188
189         commit uhttpd
190 EOF
191         }
192
193         ### We are started by the firewall include
194         exit 0
195 }
196
197 start() {
198         lock $LOCK
199         logger -s -p info -t splash "Starting luci-splash"
200         include /lib/network
201         . /lib/functions/network.sh
202         scan_interfaces
203         config_load luci_splash
204
205         ### Find QoS limits
206         config_get LIMIT_UP general limit_up
207         config_get LIMIT_DOWN general limit_down
208         config_get LIMIT_DOWN_BURST general limit_down_burst
209
210         LIMIT_UP="$((8*${LIMIT_UP:-0}))"
211         LIMIT_DOWN="$((8*${LIMIT_DOWN:-0}))"
212         LIMIT_DOWN_BURST="${LIMIT_DOWN_BURST:+$((8*$LIMIT_DOWN_BURST))}"
213         LIMIT_DOWN_BURST="${LIMIT_DOWN_BURST:-$(($LIMIT_DOWN / 5 * 6))}"
214
215         ### Load required modules
216         [ "$LIMIT_UP" -gt 0 -a "$LIMIT_DOWN" -gt 0 ] && {
217                 silent insmod act_police
218                 silent insmod cls_fw
219                 silent insmod cls_u32
220                 silent insmod sch_htb
221                 silent insmod sch_sfq
222                 silent insmod sch_ingress
223         }
224
225         ### Create subchains
226         $IPT -t nat    -N luci_splash_prerouting
227         $IPT -t nat    -N luci_splash_leases
228         $IPT -t filter -N luci_splash_forwarding
229         $IPT -t filter -N luci_splash_filter
230
231         if [ "$HAS_IPV6" = 1 ]; then
232                 $IPT6 -t filter -N luci_splash_forwarding
233                 $IPT6 -t filter -N luci_splash_filter
234         fi
235
236         ### Clear iptables replay log
237         [ -s $IPT_REPLAY ] && . $IPT_REPLAY
238         echo -n > $IPT_REPLAY
239
240         ### Add interface independant prerouting rules
241         $IPT -t nat -A luci_splash_prerouting -j luci_splash_leases
242         $IPT -t nat -A luci_splash_leases -p udp --dport 53 -j REDIRECT --to-ports 53
243         $IPT -t nat -A luci_splash_leases -p tcp --dport 80 -j REDIRECT --to-ports 8082
244
245         ### Add interface independant forwarding rules
246         $IPT -t filter -A luci_splash_forwarding -j luci_splash_filter
247         $IPT -t filter -A luci_splash_filter -p tcp -j REJECT --reject-with tcp-reset
248         $IPT -t filter -A luci_splash_filter -j REJECT --reject-with icmp-net-prohibited
249
250         if [ "$HAS_IPV6" = 1 ]; then
251                 $IPT6 -t filter -A luci_splash_forwarding -j luci_splash_filter
252                 $IPT6 -t filter -A luci_splash_filter -p tcp -j REJECT --reject-with tcp-reset
253                 $IPT6 -t filter -A luci_splash_filter -j REJECT --reject-with adm-prohibited
254         fi
255
256         ### Add QoS chain
257         $IPT -t mangle -N luci_splash_mark_out
258         $IPT -t mangle -N luci_splash_mark_in
259         $IPT -t mangle -I PREROUTING  -j luci_splash_mark_out
260         $IPT -t mangle -I POSTROUTING -j luci_splash_mark_in
261
262         if [ "$HAS_IPV6" = 1 ]; then
263                 $IPT6 -t mangle -N luci_splash_mark_out
264                 $IPT6 -t mangle -N luci_splash_mark_in
265                 $IPT6 -t mangle -I PREROUTING  -j luci_splash_mark_out
266                 $IPT6 -t mangle -I POSTROUTING -j luci_splash_mark_in
267         fi
268
269         ### Build the main and portal rule
270         config_foreach iface_add iface
271         config_foreach subnet_add subnet
272
273         ### Find active mac addresses
274         MACS=""
275
276         
277         config_foreach mac_add blacklist
278         config_foreach mac_add whitelist
279
280         #config_load luci_splash_leases
281         config_foreach mac_add lease
282
283         ### Add crontab entry
284         test -f /etc/crontabs/root || touch /etc/crontabs/root
285         grep -q luci-splash /etc/crontabs/root || {
286                 echo '*/5 * * * *       /usr/sbin/luci-splash sync' >> /etc/crontabs/root
287         }
288
289         lock -u $LOCK
290
291         ### Populate iptables
292         [ -n "$MACS" ] && luci-splash add-rules $MACS
293 }
294
295 stop() {
296         lock $LOCK
297
298         include /lib/network
299         scan_interfaces
300         config_load luci_splash
301
302         ### Clear interface rules
303         config_foreach iface_del iface
304
305         silent $IPT -t mangle -D PREROUTING  -j luci_splash_mark_out
306         silent $IPT -t mangle -D POSTROUTING -j luci_splash_mark_in
307
308         if [ "$HAS_IPV6" = 1 ]; then
309                 silent $IPT6 -t mangle -D PREROUTING  -j luci_splash_mark_out
310                 silent $IPT6 -t mangle -D POSTROUTING -j luci_splash_mark_in
311         fi
312
313         ### Clear subchains
314         silent $IPT -t nat    -F luci_splash_prerouting
315         silent $IPT -t nat    -F luci_splash_leases
316         silent $IPT -t filter -F luci_splash_forwarding
317         silent $IPT -t filter -F luci_splash_filter
318         silent $IPT -t mangle -F luci_splash_mark_out
319         silent $IPT -t mangle -F luci_splash_mark_in
320
321         if [ "$HAS_IPV6" = 1 ]; then
322                 $IPT6 -t filter -F luci_splash_forwarding
323                 $IPT6 -t filter -F luci_splash_filter
324                 $IPT6 -t mangle -F luci_splash_mark_out
325                 $IPT6 -t mangle -F luci_splash_mark_in
326         fi
327
328         ### Delete subchains
329         silent $IPT -t nat    -X luci_splash_prerouting
330         silent $IPT -t nat    -X luci_splash_leases
331         silent $IPT -t filter -X luci_splash_forwarding
332         silent $IPT -t filter -X luci_splash_filter
333         silent $IPT -t mangle -X luci_splash_mark_out
334         silent $IPT -t mangle -X luci_splash_mark_in
335         if [ "$HAS_IPV6" = 1 ]; then
336                 $IPT6 -t filter -X luci_splash_forwarding
337                 $IPT6 -t filter -X luci_splash_filter
338                 $IPT6 -t mangle -X luci_splash_mark_out
339                 $IPT6 -t mangle -X luci_splash_mark_in
340         fi
341         sed -ie '/\/usr\/sbin\/luci-splash sync/d' /var/spool/cron/crontabs/root
342
343         lock -u $LOCK
344 }
345
346 clear_leases() {
347         ### Find active mac addresses
348         MACS=""
349         config_foreach mac_add lease
350
351         ### Clear leases
352         [ -n "$MACS" ] && luci-splash remove $MACS
353 }
354