firewall: various enhancements
[openwrt.git] / package / network / config / firewall / files / lib / core_interface.sh
1 # Copyright (C) 2009-2013 OpenWrt.org
2
3 fw__uci_state_add() {
4         local var="$1"
5         local item="$2"
6
7         local val="$(uci_get_state firewall core $var)"
8         local e1; for e1 in $item; do
9                 local e2; for e2 in $val; do
10                         [ "$e1" = "$e2" ] && e1=""
11                 done
12                 val="${val:+$val${e1:+ }}$e1"
13         done
14
15         uci_toggle_state firewall core $var "$val"
16 }
17
18 fw__uci_state_del() {
19         local var="$1"
20         local item="$2"
21
22         local rest=""
23         local val="$(uci_get_state firewall core $var)"
24         local e1; for e1 in $val; do
25                 local e2; for e2 in $item; do
26                         [ "$e1" = "$e2" ] && e1=""
27                 done
28                 rest="${rest:+$rest${e1:+ }}$e1"
29         done
30
31         uci_toggle_state firewall core $var "$rest"
32 }
33
34 fw_do_interface_rules() {
35     local action=$1
36     local zone=$2
37     local chain=zone_${zone}
38     local ifname=$3
39     local subnet=$4
40     local extra_src="$5"
41     local extra_dest="$6"
42
43     local idev odev inet onet mode
44     fw_get_family_mode mode x $zone i
45
46     fw_get_negation idev '-i' "$ifname"
47     fw_get_negation odev '-o' "$ifname"
48
49     case "$mode/$subnet" in
50         # Zone supports v6 only or dual, need v6
51         G6/*:*|i/*:*)
52             fw_get_negation inet '-s' "$subnet"
53             fw_get_negation onet '-d' "$subnet"
54             mode=6
55         ;;
56
57         # Zone supports v4 only or dual, need v4
58         G4/*.*.*.*|i/*.*.*.*)
59             fw_get_negation inet '-s' "$subnet"
60             fw_get_negation onet '-d' "$subnet"
61             mode=4
62         ;;
63
64         # Need v6 while zone is v4
65         */*:*) fw_log info "zone $zone does not support IPv6 address family, skipping"; return ;;
66
67         # Need v4 while zone is v6
68         */*.*) fw_log info "zone $zone does not support IPv4 address family, skipping"; return ;;
69
70         # Strip prefix
71         *) mode="${mode#G}" ;;
72     esac
73
74     lock /var/run/firewall-interface.lock
75
76     fw $action $mode f ${chain}_dest_ACCEPT ACCEPT $ { $odev $onet $extra_dest }
77     fw $action $mode f ${chain}_src_ACCEPT  ACCEPT $ { $idev $inet $extra_src  }
78     fw $action $mode f ${chain}_dest_DROP   DROP   $ { $odev $onet $extra_dest }
79     fw $action $mode f ${chain}_src_DROP    DROP   $ { $idev $inet $extra_src  }
80     fw $action $mode f ${chain}_dest_REJECT reject $ { $odev $onet $extra_dest }
81     fw $action $mode f ${chain}_src_REJECT  reject $ { $idev $inet $extra_src  }
82
83     [ "$(uci_get_state firewall core "${zone}_tcpmss")" == 1 ] && \
84         fw $action $mode m ${chain}_MSSFIX TCPMSS $ \
85             { $odev -p tcp --tcp-flags SYN,RST SYN --clamp-mss-to-pmtu $onet $extra_dest }
86
87     fw $action $mode f delegate_input   ${chain}_input   $ { $idev $inet $extra_src  }
88     fw $action $mode f delegate_forward ${chain}_forward $ { $idev $inet $extra_src  }
89     fw $action $mode f delegate_output  ${chain}_output  $ { $odev $onet $extra_dest }
90
91     fw $action $mode n PREROUTING ${chain}_prerouting $ { $idev $inet $extra_src  }
92     fw $action $mode r PREROUTING ${chain}_notrack    $ { $idev $inet $extra_src  }
93     fw $action $mode n POSTROUTING ${chain}_nat       $ { $odev $onet $extra_dest }
94
95     # Flush conntrack table
96     echo f >/proc/net/nf_conntrack 2>/dev/null
97
98     lock -u /var/run/firewall-interface.lock
99 }
100
101 fw_configure_interface() {
102         local iface=$1
103         local action=$2
104         local ifname=$3
105         local aliasnet=$4
106
107         [ "$action" == "add" ] && {
108                 local status=$(uci_get_state network "$iface" up 0)
109                 [ "$status" == 1 ] || [ -n "$aliasnet" ] || return 0
110         }
111
112         [ -n "$ifname" ] || {
113                 ifname=$(uci_get_state network "$iface" ifname)
114                 ifname="${ifname%%:*}"
115                 [ -z "$ifname" ] && return 0
116         }
117
118         [ "$ifname" == "lo" ] && return 0
119
120         fw_callback pre interface
121
122         local old_zones old_ifname old_subnets
123         config_get old_zones core "${iface}_zone"
124         [ -n "$old_zones" ] && {
125                 config_get old_ifname core "${iface}_ifname"
126                 config_get old_subnets core "${iface}_subnets"
127
128                 local z
129                 for z in $old_zones; do
130                         local n
131                         for n in ${old_subnets:-""}; do
132                                 fw_log info "removing $iface ($old_ifname${n:+ alias $n}) from zone $z"
133                                 fw_do_interface_rules del $z $old_ifname $n
134                         done
135
136                         [ -n "$old_subnets" ] || {
137                                 fw__uci_state_del "${z}_networks" "$iface"
138                                 env -i ACTION=remove ZONE="$z" INTERFACE="$iface" DEVICE="$ifname" /sbin/hotplug-call firewall
139                         }
140                 done
141
142                 local old_aliases
143                 config_get old_aliases core "${iface}_aliases"
144
145                 local a
146                 for a in $old_aliases; do
147                         fw_configure_interface "$a" del "$old_ifname"
148                 done
149
150                 uci_revert_state firewall core "${iface}_zone"
151                 uci_revert_state firewall core "${iface}_ifname"
152                 uci_revert_state firewall core "${iface}_subnets"
153                 uci_revert_state firewall core "${iface}_aliases"
154         }
155
156         [ "$action" == del ] && return
157
158         [ -z "$aliasnet" ] && {
159                 local aliases
160                 config_get aliases "$iface" aliases
161
162                 local a
163                 for a in $aliases; do
164                         local ipaddr netmask ip6addr
165                         config_get ipaddr "$a" ipaddr
166                         config_get netmask "$a" netmask
167                         config_get ip6addr "$a" ip6addr
168
169                         [ -n "$ipaddr" ] && fw_configure_interface "$a" add "" "$ipaddr${netmask:+/$netmask}"
170                         [ -n "$ip6addr" ] && fw_configure_interface "$a" add "" "$ip6addr"
171                 done
172
173                 fw_sysctl_interface $ifname
174                 fw_callback post interface
175
176                 uci_toggle_state firewall core "${iface}_aliases" "$aliases"
177         } || {
178                 local subnets=
179                 config_get subnets core "${iface}_subnets"
180                 append subnets "$aliasnet"
181
182                 config_set core "${iface}_subnets" "$subnets"
183                 uci_toggle_state firewall core "${iface}_subnets" "$subnets"
184         }
185
186         local new_zones=
187         load_zone() {
188                 fw_config_get_zone "$1"
189                 list_contains zone_network "$iface" || return
190
191                 fw_log info "adding $iface ($ifname${aliasnet:+ alias $aliasnet}) to zone $zone_name"
192                 fw_do_interface_rules add ${zone_name} "$ifname" "$aliasnet"
193                 append new_zones $zone_name
194
195                 [ -n "$aliasnet" ] || {
196                         fw__uci_state_add "${zone_name}_networks" "${zone_network}"
197                         env -i ACTION=add ZONE="$zone_name" INTERFACE="$iface" DEVICE="$ifname" /sbin/hotplug-call firewall
198                 }
199         }
200         config_foreach load_zone zone
201
202         uci_toggle_state firewall core "${iface}_zone" "$new_zones"
203         uci_toggle_state firewall core "${iface}_ifname" "$ifname"
204 }
205
206 fw_sysctl_interface() {
207         local ifname=$1
208         {
209                 sysctl -w net.ipv4.conf.${ifname}.accept_redirects=$FW_ACCEPT_REDIRECTS
210                 sysctl -w net.ipv6.conf.${ifname}.accept_redirects=$FW_ACCEPT_REDIRECTS
211                 sysctl -w net.ipv4.conf.${ifname}.accept_source_route=$FW_ACCEPT_SRC_ROUTE
212                 sysctl -w net.ipv6.conf.${ifname}.accept_source_route=$FW_ACCEPT_SRC_ROUTE
213         } >/dev/null 2>/dev/null
214 }