]> git.ipfire.org Git - people/stevee/network.git/blob - src/functions/functions.iptables
Remove the function keyword which is a bashism
[people/stevee/network.git] / src / functions / functions.iptables
1 #!/bin/bash
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2012-2013 IPFire Network Development Team #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 IPTABLES_TABLES="filter mangle nat"
23
24 iptables() {
25 local protocol="${1}"
26 assert isset protocol
27 shift
28
29 # Rules go to the filter table by default
30 local table="filter"
31
32 # Argument list
33 local args
34
35 # Cached arguments
36 local src dst
37
38 # Parsing arguments
39 while [ $# -gt 0 ]; do
40 case "${1}" in
41 # Filter to which table this rule should go.
42 -t)
43 table="${2}"
44 shift 2
45
46 assert isoneof table ${IPTABLES_TABLES}
47 ;;
48
49 # Automatically convert ICMP to ICMPv6 for IPv6
50 --protocol|-p)
51 local proto="${2}"
52
53 if [ "${protocol}" = "ipv6" -a "${proto}" = "icmp" ]; then
54 proto="icmpv6"
55 fi
56
57 list_append args "${1} ${proto}"
58 shift 2
59 ;;
60 *)
61 list_append args "${1}"
62
63 # Save some values for further processing.
64 case "${1}" in
65 -s)
66 src="${2}"
67 ;;
68 -d)
69 dst="${2}"
70 ;;
71 esac
72 shift
73 ;;
74 esac
75 done
76
77 assert isset action
78
79 # Check if given IP addresses or networks match the protocol version.
80 local src_proto
81 if isset src; then
82 src_proto="$(ip_detect_protocol ${src})"
83
84 assert [ "${protocol}" = "${src_proto}" ]
85 fi
86
87 local dst_proto
88 if isset dst; then
89 dst_proto="$(ip_detect_protocol ${dst})"
90
91 assert [ "${protocol}" = "${dst_proto}" ]
92 fi
93
94 # Check if the directory where we put our rules in is set and
95 # exists.
96 assert isset IPTABLES_TMPDIR
97 local rulesfile="${IPTABLES_TMPDIR}/${protocol}-${table}"
98
99 print "${args}" >> "${rulesfile}"
100 assert_check_retval $?
101 }
102
103 iptables_chain_create() {
104 local protocol="${1}"
105 assert isset protocol
106 shift
107
108 local chain
109 local table="filter"
110 local policy="-"
111
112 while [ $# -gt 0 ]; do
113 case "${1}" in
114 -t)
115 table="${2}"
116 shift
117 ;;
118 --policy=*)
119 policy="$(cli_get_val ${1})"
120 ;;
121 -*)
122 log WARNING "Unrecognized argument: ${1}"
123 ;;
124 *)
125 chain=${1}
126 ;;
127 esac
128 shift
129 done
130
131 assert isset chain
132 assert isset table
133 assert isoneof policy ACCEPT DROP "-"
134
135 iptables "${protocol}" -t "${table}" ":${chain} ${policy} [0:0]"
136 }
137
138 # Calls the binary iptables command.
139 _iptables() {
140 local protocol="${1}"
141 assert isset protocol
142 shift
143
144 local cmd
145 case "${protocol}" in
146 ipv6)
147 cmd="ip6tables"
148 ;;
149 ipv4)
150 cmd="iptables"
151 ;;
152 esac
153 assert isset cmd
154 cmd="$(which ${cmd})"
155
156 cmd "${cmd}" "$@"
157 return $?
158 }
159
160 iptables_status() {
161 local protocol="${1}"
162 assert isset protocol
163
164 local table
165 for table in ${IPTABLES_TABLES}; do
166 print "${protocol} - ${table}:"
167 _iptables "${protocol}" -t "${table}" -L -n -v
168 print
169 done
170
171 return ${EXIT_OK}
172 }
173
174 iptables_rulesfile() {
175 local proto=${1}
176 proto=${proto/ipv/}
177
178 local chain=${2}
179 [ -z "${chain}" ] && chain="ruleset"
180
181 print "${IPTABLES_TMPDIR}/${chain}${proto}"
182 }
183
184 iptables_init() {
185 local protocol="${1}"
186 assert isset protocol
187
188 local policy="${2}"
189 assert isset policy
190
191 # Create filter table and initialize chains.
192 iptables "${protocol}" "* filter"
193 iptables_chain_create "${protocol}" -t filter INPUT --policy="${policy}"
194 iptables_chain_create "${protocol}" -t filter OUTPUT --policy="${policy}"
195 iptables_chain_create "${protocol}" -t filter FORWARD --policy="${policy}"
196
197 # Create mangle table and initialize chains.
198 iptables "${protocol}" -t mangle "* mangle"
199 iptables_chain_create "${protocol}" -t mangle PREROUTING --policy="ACCEPT"
200 iptables_chain_create "${protocol}" -t mangle INPUT --policy="ACCEPT"
201 iptables_chain_create "${protocol}" -t mangle OUTPUT --policy="ACCEPT"
202 iptables_chain_create "${protocol}" -t mangle FORWARD --policy="ACCEPT"
203 iptables_chain_create "${protocol}" -t mangle POSTROUTING --policy="ACCEPT"
204
205 # Create NAT table and initialize chains.
206 iptables "${protocol}" -t nat "* nat"
207 iptables_chain_create "${protocol}" -t nat PREROUTING --policy="ACCEPT"
208 iptables_chain_create "${protocol}" -t nat OUTPUT --policy="ACCEPT"
209 iptables_chain_create "${protocol}" -t nat POSTROUTING --policy="ACCEPT"
210 }
211
212 # Load the created ruleset into the kernel.
213 iptables_commit () {
214 local protocol="${1}"
215 assert isset protocol
216 shift
217
218 local testmode="false"
219
220 while [ $# -gt 0 ]; do
221 case "${1}" in
222 --test)
223 testmode="true"
224 ;;
225 *)
226 log WARNING "Unrecognized argument: ${1}"
227 ;;
228 esac
229 shift
230 done
231
232 # Concat all rules into one big file.
233 local rulesfile="${IPTABLES_TMPDIR}/ruleset"
234 _iptables_commit_cat_rulesfile "${protocol}" "${rulesfile}"
235
236 # Run the following loop twice:
237 # 1st: Check if the ruleset can be loaded
238 # 2nd: If not in test mode, actually load the ruleset into the kernel
239 local load_cmd="--test"
240 local ret=0
241
242 local i
243 for i in 0 1; do
244 _iptables_commit_load_rulesfile "${protocol}" "${rulesfile}" "${load_cmd}"
245 ret=$?
246
247 case "${i},${ret}" in
248 0,${EXIT_OK})
249 iptables_dump "${protocol}" "${rulesfile}" --log-facility="DEBUG"
250 log DEBUG "Ruleset load check succeeded (${protocol})"
251 ;;
252
253 # Loading rules has failed (test)
254 0,*)
255 iptables_dump "${protocol}" "${rulesfile}" --log-facility="CRITICAL"
256 log CRITICAL "Ruleset load check failed (${protocol} - ${ret})"
257 return ${ret}
258 ;;
259
260 1,${EXIT_OK})
261 log DEBUG "Ruleset successfully loaded (${protocol})"
262 return ${EXIT_OK}
263 ;;
264
265 1,*)
266 log CRITICAL "Ruleset loading failed (${protocol})"
267 return ${ret}
268 ;;
269 esac
270
271 # Skip the second loop iteration, if we are running in test mode.
272 enabled testmode && break
273
274 load_cmd=""
275 done
276
277 return ${EXIT_OK}
278 }
279
280 _iptables_commit_cat_rulesfile() {
281 local protocol="${1}"
282 assert isset protocol
283
284 local rulesfile="${2}"
285 assert isset rulesfile
286
287 local table
288 local file
289 for table in ${IPTABLES_TABLES}; do
290 file="${IPTABLES_TMPDIR}/${protocol}-${table}"
291
292 fread "${file}"
293
294 # Add the COMMIT statement for every table.
295 print "COMMIT"
296 done > "${rulesfile}"
297
298 assert [ -s "${rulesfile}" ]
299 }
300
301 _iptables_commit_load_rulesfile() {
302 local protocol="${1}"
303 assert isset protocol
304
305 local rulesfile="${2}"
306 assert isset rulesfile
307 shift 2
308
309 local testmode="false"
310 while [ $# -gt 0 ]; do
311 case "${1}" in
312 --test)
313 testmode="true"
314 ;;
315 esac
316 shift
317 done
318
319 local iptables_cmd
320 case "${protocol}" in
321 ipv6)
322 iptables_cmd="ip6tables-restore"
323 ;;
324 ipv4)
325 iptables_cmd="iptables-restore"
326 ;;
327 esac
328 assert isset iptables_cmd
329
330 if enabled testmode; then
331 list_append iptables_cmd "--test"
332 fi
333
334 # Save when importing the rules has started.
335 local time_started="$(timestamp)"
336
337 cmd "${iptables_cmd}" < "${rulesfile}"
338 local ret=$?
339
340 case "${ret}" in
341 ${EXIT_OK})
342 local time_finished="$(timestamp)"
343 time_finished="$(( ${time_finished} - ${time_started} ))"
344
345 enabled testmode && return ${EXIT_OK}
346
347 log INFO "Successfully loaded new firewall ruleset for ${protocol} in ${time_finished}s!"
348 ;;
349 *)
350 if ! enabled testmode; then
351 log CRITICAL "Error loading firewall ruleset for ${protocol}!"
352 fi
353 ;;
354 esac
355
356 return ${ret}
357 }
358
359 iptables_dump() {
360 local protocol="${1}"
361 assert isset protocol
362
363 local rulesfile="${2}"
364 assert isset rulesfile
365 shift 2
366
367 local log_facility="INFO"
368
369 while [ $# -gt 0 ]; do
370 case "${1}" in
371 --log-facility=*)
372 log_facility="$(cli_get_val ${1})"
373 ;;
374 *)
375 log WARNING "Unrecognized argument: ${1}"
376 ;;
377 esac
378 shift
379 done
380
381 # Say what we are going to do:
382 log "${log_facility}" "Firewall ruleset for ${protocol}:"
383
384 local counter="0"
385 local line
386 while read -r line; do
387 counter="$(( ${counter} + 1 ))"
388
389 printf -v line "%4d | %s" "${counter}" "${line}"
390 log "${log_facility}" "${line}"
391 done < "${rulesfile}"
392 }
393
394 iptables_LOG() {
395 local prefix="${1}"
396 local ret
397
398 # Automatically append a colon and whitespace.
399 case "${prefix}" in
400 # Everything is fine.
401 "*: ") ;;
402
403 # Ends with colon, add whitespace only.
404 "*:")
405 prefix="${prefix} "
406 ;;
407
408 # Append both.
409 *)
410 prefix="${prefix}: "
411 ;;
412 esac
413
414 case "${FIREWALL_LOG_METHOD}" in
415 nflog)
416 ret="NFLOG --nflog-threshold ${FIREWALL_NFLOG_THRESHOLD}"
417 isset prefix && ret="${ret} --nflog-prefix \"$prefix\""
418 ;;
419 syslog)
420 ret="LOG"
421 isset prefix && ret="${ret} --log-prefix \"$prefix\""
422 ;;
423 esac
424
425 print "${ret}"
426 }
427
428 iptables_protocol() {
429 local PROTO
430 PROTO=$1
431 for proto in tcp udp esp ah; do
432 if [ "$PROTO" = "$proto" ]; then
433 echo "-p $PROTO"
434 break
435 fi
436 done
437 }
438
439 IPTABLES_PORT=0
440 IPTABLES_MULTIPORT=1
441 IPTABLES_PORTRANGE=2
442
443 _iptables_port_range() {
444 grep -q ":" <<< $@
445 }
446
447 _iptables_port_multiport() {
448 grep -q "," <<< $@
449 }
450
451 _iptables_port() {
452 if _iptables_port_range "$@"; then
453 echo $IPTABLES_PORTRANGE
454 elif _iptables_port_multiport "$@"; then
455 echo $IPTABLES_MULTIPORT
456 else
457 echo $IPTABLES_PORT
458 fi
459 }
460
461 iptables_source_port() {
462 [ -z "$@" ] && return
463 local type
464 type=$(_iptables_port $@)
465 if [ "$type" = "$IPTABLES_MULTIPORT" ]; then
466 echo "-m multiport --source-ports $@"
467 else
468 echo "--sport $@"
469 fi
470 }
471
472 iptables_destination_port() {
473 [ -z "$@" ] && return
474 local type
475 type=$(_iptables_port $@)
476 if [ "$type" = "$IPTABLES_MULTIPORT" ]; then
477 echo "-m multiport --destination-ports $@"
478 else
479 echo "--dport $@"
480 fi
481 }