summaryrefslogtreecommitdiffstats
path: root/package/firewall/files/lib/core_init.sh
blob: 035647998ef9cec48af43b583b05473c5a3b03a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# Copyright (C) 2009-2011 OpenWrt.org
# Copyright (C) 2008 John Crispin <blogic@openwrt.org>

FW_INITIALIZED=

FW_ZONES=
FW_ZONES4=
FW_ZONES6=
FW_CONNTRACK_ZONES=
FW_NOTRACK_DISABLED=

FW_DEFAULTS_APPLIED=
FW_ADD_CUSTOM_CHAINS=
FW_ACCEPT_REDIRECTS=
FW_ACCEPT_SRC_ROUTE=

FW_DEFAULT_INPUT_POLICY=REJECT
FW_DEFAULT_OUTPUT_POLICY=REJECT
FW_DEFAULT_FORWARD_POLICY=REJECT

FW_DISABLE_IPV4=0
FW_DISABLE_IPV6=0


fw_load_defaults() {
	fw_config_get_section "$1" defaults { \
		string input $FW_DEFAULT_INPUT_POLICY \
		string output $FW_DEFAULT_OUTPUT_POLICY \
		string forward $FW_DEFAULT_FORWARD_POLICY \
		boolean drop_invalid 0 \
		boolean syn_flood 0 \
		boolean synflood_protect 0 \
		string synflood_rate 25 \
		string synflood_burst 50 \
		boolean tcp_syncookies 1 \
		boolean tcp_ecn 0 \
		boolean tcp_westwood 0 \
		boolean tcp_window_scaling 1 \
		boolean accept_redirects 0 \
		boolean accept_source_route 0 \
		boolean custom_chains 1 \
		boolean disable_ipv6 0 \
	} || return
	[ -n "$FW_DEFAULTS_APPLIED" ] && {
		fw_log error "duplicate defaults section detected, skipping"
		return 1
	}
	FW_DEFAULTS_APPLIED=1

	FW_DEFAULT_INPUT_POLICY=$defaults_input
	FW_DEFAULT_OUTPUT_POLICY=$defaults_output
	FW_DEFAULT_FORWARD_POLICY=$defaults_forward

	FW_ADD_CUSTOM_CHAINS=$defaults_custom_chains

	FW_ACCEPT_REDIRECTS=$defaults_accept_redirects
	FW_ACCEPT_SRC_ROUTE=$defaults_accept_source_route

	FW_DISABLE_IPV6=$defaults_disable_ipv6

	fw_callback pre defaults

	# Seems like there are only one sysctl for both IP versions.
	for s in syncookies ecn westwood window_scaling; do
		eval "sysctl -e -w net.ipv4.tcp_${s}=\$defaults_tcp_${s}" >/dev/null
	done
	fw_sysctl_interface all

	fw add i f INPUT   ACCEPT { -m conntrack --ctstate RELATED,ESTABLISHED }
	fw add i f OUTPUT  ACCEPT { -m conntrack --ctstate RELATED,ESTABLISHED }
	fw add i f FORWARD ACCEPT { -m conntrack --ctstate RELATED,ESTABLISHED }

	[ $defaults_drop_invalid == 1 ] && {
		fw add i f INPUT   DROP { -m conntrack --ctstate INVALID }
		fw add i f OUTPUT  DROP { -m conntrack --ctstate INVALID }
		fw add i f FORWARD DROP { -m conntrack --ctstate INVALID }
		FW_NOTRACK_DISABLED=1
	}

	fw add i f INPUT  ACCEPT { -i lo }
	fw add i f OUTPUT ACCEPT { -o lo }

	# Compatibility to old 'syn_flood' parameter
	[ $defaults_syn_flood == 1 ] && \
		defaults_synflood_protect=1

	[ "${defaults_synflood_rate%/*}" == "$defaults_synflood_rate" ] && \
		defaults_synflood_rate="$defaults_synflood_rate/second"

	[ $defaults_synflood_protect == 1 ] && {
		echo "Loading synflood protection"
		fw_callback pre synflood
		fw add i f syn_flood
		fw add i f syn_flood RETURN { \
			-p tcp --syn \
			-m limit --limit "${defaults_synflood_rate}" --limit-burst "${defaults_synflood_burst}" \
		}
		fw add i f syn_flood DROP
		fw add i f INPUT syn_flood { -p tcp --syn }
		fw_callback post synflood
	}

	[ $defaults_custom_chains == 1 ] && {
		echo "Adding custom chains"
		fw add i f input_rule
		fw add i f output_rule
		fw add i f forwarding_rule
		fw add i n prerouting_rule
		fw add i n postrouting_rule

		fw add i f INPUT       input_rule
		fw add i f OUTPUT      output_rule
		fw add i f FORWARD     forwarding_rule
		fw add i n PREROUTING  prerouting_rule
		fw add i n POSTROUTING postrouting_rule
	}

	fw add i f input
	fw add i f output
	fw add i f forward

	fw add i f INPUT   input
	fw add i f OUTPUT  output
	fw add i f FORWARD forward

	fw add i f reject
	fw add i f reject REJECT { --reject-with tcp-reset -p tcp }
	fw add i f reject REJECT { --reject-with port-unreach }

	fw_set_filter_policy

	fw_callback post defaults
}


fw_config_get_zone() {
	[ "${zone_NAME}" != "$1" ] || return
	fw_config_get_section "$1" zone { \
		string name "$1" \
		string network "" \
		string input "$FW_DEFAULT_INPUT_POLICY" \
		string output "$FW_DEFAULT_OUTPUT_POLICY" \
		string forward "$FW_DEFAULT_FORWARD_POLICY" \
		boolean masq 0 \
		string masq_src "" \
		string masq_dest "" \
		boolean conntrack 0 \
		boolean mtu_fix 0 \
		boolean custom_chains "$FW_ADD_CUSTOM_CHAINS" \
		boolean log 0 \
		string log_limit 10 \
		string family "" \
	} || return
	[ -n "$zone_name" ] || zone_name=$zone_NAME
	[ -n "$zone_network" ] || zone_network=$zone_name
}

fw_load_zone() {
	fw_config_get_zone "$1"

	list_contains FW_ZONES $zone_name && {
		fw_log error "zone ${zone_name}: duplicated zone, skipping"
		return 0
	}
	append FW_ZONES $zone_name

	fw_callback pre zone

	[ $zone_conntrack = 1 -o $zone_masq = 1 ] && \
		append FW_CONNTRACK_ZONES "$zone_name"

	local mode
	case "$zone_family" in
		*4)
			mode=4
			append FW_ZONES4 $zone_name
			uci_set_state firewall core ${zone_name}_ipv4 1
		;;
		*6)
			mode=6
			append FW_ZONES6 $zone_name
			uci_set_state firewall core ${zone_name}_ipv6 1
		;;
		*)
			mode=i
			append FW_ZONES4 $zone_name
			append FW_ZONES6 $zone_name
			uci_set_state firewall core ${zone_name}_ipv4 1
			uci_set_state firewall core ${zone_name}_ipv6 1
		;;
	esac

	local chain=zone_${zone_name}

	fw add $mode f ${chain}_ACCEPT
	fw add $mode f ${chain}_DROP
	fw add $mode f ${chain}_REJECT

	# TODO: Rename to ${chain}_input
	fw add $mode f ${chain}
	fw add $mode f ${chain} ${chain}_${zone_input} $

	fw add $mode f ${chain}_forward
	fw add $mode f ${chain}_forward ${chain}_${zone_forward} $

	# TODO: add ${chain}_output
	fw add $mode f output ${chain}_${zone_output} $

	# TODO: Rename to ${chain}_MASQUERADE
	fw add $mode n ${chain}_nat
	fw add $mode n ${chain}_prerouting

	fw add $mode r ${chain}_notrack

	[ $zone_mtu_fix == 1 ] && {
		fw add $mode m ${chain}_MSSFIX
		fw add $mode m FORWARD ${chain}_MSSFIX ^
		uci_set_state firewall core ${zone_name}_tcpmss 1
	}

	[ $zone_custom_chains == 1 ] && {
		[ $FW_ADD_CUSTOM_CHAINS == 1 ] || \
			fw_die "zone ${zone_name}: custom_chains globally disabled"

		fw add $mode f input_${zone_name}
		fw add $mode f ${chain} input_${zone_name} ^

		fw add $mode f forwarding_${zone_name}
		fw add $mode f ${chain}_forward forwarding_${zone_name} ^

		fw add $mode n prerouting_${zone_name}
		fw add $mode n ${chain}_prerouting prerouting_${zone_name} ^
	}

	[ "$zone_log" == 1 ] && {
		[ "${zone_log_limit%/*}" == "$zone_log_limit" ] && \
			zone_log_limit="$zone_log_limit/minute"

		local t
		for t in REJECT DROP; do
			fw add $mode f ${chain}_${t} LOG ^ \
				{ -m limit --limit $zone_log_limit --log-prefix "$t($zone_name): " }
		done

		[ $zone_mtu_fix == 1 ] && \
			fw add $mode m ${chain}_MSSFIX LOG ^ \
				{ -m limit --limit $zone_log_limit --log-prefix "MSSFIX($zone_name): " }
	}

	# NB: if MASQUERADING for IPv6 becomes available we'll need a family check here
	if [ "$zone_masq" == 1 ]; then
		local msrc mdst
		for msrc in ${zone_masq_src:-0.0.0.0/0}; do
			case "$msrc" in
				*.*) fw_get_negation msrc '-s' "$msrc" ;;
				*)   fw_get_subnet4 msrc '-s' "$msrc" || break ;;
			esac

			for mdst in ${zone_masq_dest:-0.0.0.0/0}; do
				case "$mdst" in
					*.*) fw_get_negation mdst '-d' "$mdst" ;;
					*)   fw_get_subnet4 mdst '-d' "$mdst" || break ;;
				esac

				fw add $mode n ${chain}_nat MASQUERADE $ { $msrc $mdst }
			done
		done
	fi

	fw_callback post zone
}

fw_load_notrack_zone() {
	fw_config_get_zone "$1"
	list_contains FW_CONNTRACK_ZONES "${zone_name}" && return

	fw_callback pre notrack

	fw add i r zone_${zone_name}_notrack NOTRACK $

	fw_callback post notrack
}


fw_load_include() {
	local name="$1"

	local path
	config_get path ${name} path

	[ -e $path ] && (
		config() {
			fw_log error "You cannot use UCI in firewall includes!" >&2
			exit 1
		}
		. $path 
	)
}


fw_clear() {
	local policy=$1

	fw_set_filter_policy $policy

	local tab
	for tab in f n r; do
		fw del i $tab
	done
}

fw_set_filter_policy() {
	local policy=$1

	local chn tgt
	for chn in INPUT OUTPUT FORWARD; do
		eval "tgt=\${policy:-\${FW_DEFAULT_${chn}_POLICY}}"
		[ $tgt == "REJECT" ] && tgt=reject
		[ $tgt == "ACCEPT" -o $tgt == "DROP" ] || {
			fw add i f $chn $tgt $
			tgt=DROP
		}
		fw policy i f $chn $tgt
	done
}


fw_callback() {
	local pp=$1
	local hk=$2

	local libs lib
	eval "libs=\$FW_CB_${pp}_${hk}"
	[ -n "$libs" ] || return
	for lib in $libs; do
		${lib}_${pp}_${hk}_cb
	done
}