]> git.ipfire.org Git - people/stevee/network.git/blob - functions.iptables
Make dumping all firewall rules a bit faster.
[people/stevee/network.git] / functions.iptables
1 #!/bin/bash
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2012 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 function iptables() {
23 local arg
24 local args
25 local table="filter"
26 local src dst
27
28 # Default is both protocols.
29 local proto="6 4"
30
31 # Check if the directory where we put our rules in is set and
32 # exists.
33 assert isset IPTABLES_TMPDIR
34 assert [ -d "${IPTABLES_TMPDIR}" ]
35
36 # Parsing arguments
37 while [ $# -gt 0 ]; do
38 case "${1}" in
39 # Select IPv4 protocol.
40 -4)
41 proto="4"
42 shift
43 ;;
44 # Select IPv6 protocol.
45 -6)
46 proto="6"
47 shift
48 ;;
49 -t)
50 table=${2}
51 shift 2
52 ;;
53 -A)
54 args="${args} -A ${2^^}"
55 shift 2
56 ;;
57 *)
58 args="${args} ${1}"
59
60 # Save some values for further processing.
61 case "${1}" in
62 -s)
63 src="${2}"
64 ;;
65 -d)
66 dst="${2}"
67 ;;
68 esac
69 shift
70 ;;
71 esac
72 done
73
74 # Check that the nat table is not used for IPv6.
75 if isoneof 6 ${proto}; then
76 assert [ "${table}" != "nat" ]
77 fi
78
79 # Detect the version of the IP protocol.
80 local src_proto
81 isset src && src_proto=$(ip_detect_protocol ${src})
82
83 local dst_proto
84 isset dst && dst_proto=$(ip_detect_protocol ${dst})
85
86 # Check that the source and destinations are not
87 # using different versions of the IP protocol.
88 if isset src_proto && isset dst_proto; then
89 assert [ "${src_proto}" = "${dst_proto}" ]
90 fi
91
92 local rulesfile
93 local p
94 for p in ${proto}; do
95 case "${p}" in
96 6)
97 listmatch ipv4 ${src_proto} ${dst_proto} \
98 && continue
99 ;;
100 4)
101 listmatch ipv6 ${src_proto} ${dst_proto} \
102 && continue
103 ;;
104 esac
105
106 rulesfile=$(iptables_rulesfile ipv${p} ${table})
107 print "${args:1:${#args}}" >> ${rulesfile}
108 done
109 }
110
111 # Calls the binary iptables command.
112 function _iptables() {
113 local iptables_cmd=$(which iptables)
114 assert isset iptables_cmd
115
116 ${iptables_cmd} $@
117 }
118
119 function iptables_status() {
120 _iptables -L -n -v
121 }
122
123 # Returns which tables exist for the given protocol.
124 function iptables_tables() {
125 local proto=${1}
126 assert isset proto
127
128 case "${proto}" in
129 ipv6)
130 print "filter mangle"
131 ;;
132 ipv4)
133 print "filter mangle nat"
134 ;;
135 *)
136 return ${EXIT_ERROR}
137 ;;
138 esac
139
140 return ${EXIT_OK}
141 }
142
143 function iptables_rulesfile() {
144 local proto=${1}
145 proto=${proto/ipv/}
146
147 local chain=${2}
148 [ -z "${chain}" ] && chain="ruleset"
149
150 print "${IPTABLES_TMPDIR}/${chain}${proto}"
151 }
152
153 function iptables_init() {
154 local policy=${1}
155 assert isset policy
156
157 # Create filter table and initialize chains.
158 iptables "* filter"
159 iptables_chain_create -t filter INPUT --policy=${policy}
160 iptables_chain_create -t filter OUTPUT --policy=${policy}
161 iptables_chain_create -t filter FORWARD --policy=${policy}
162
163 # Create mangle table initialize chains.
164 iptables -t mangle "* mangle"
165 iptables_chain_create -t mangle PREROUTING --policy=ACCEPT
166 iptables_chain_create -t mangle INPUT --policy=ACCEPT
167 iptables_chain_create -t mangle OUTPUT --policy=ACCEPT
168 iptables_chain_create -t mangle FORWARD --policy=ACCEPT
169 iptables_chain_create -t mangle POSTROUTING --policy=ACCEPT
170
171 # Add NAT table for IPv4.
172 iptables -4 -t nat "* nat"
173 iptables_chain_create -4 -t nat PREROUTING --policy=ACCEPT
174 iptables_chain_create -4 -t nat OUTPUT --policy=ACCEPT
175 iptables_chain_create -4 -t nat POSTROUTING --policy=ACCEPT
176 }
177
178 # Load the created ruleset into the kernel.
179 function iptables_load() {
180 # If first argument is present and true, we
181 # run in test mode.
182 local test="${1}"
183
184 local rulesfile
185
186 # Concat the table rulesets into one big file.
187 local proto
188 for proto in 6 4; do
189 rulesfile=$(iptables_rulesfile ipv${proto})
190 assert isset rulesfile
191
192 local table
193 local tablefile
194 for table in $(iptables_tables ipv${proto}); do
195 tablefile=$(iptables_rulesfile ipv${proto} ${table})
196
197 fread ${tablefile}
198
199 # Add the COMMIT statement for every table.
200 if [ -s "${tablefile}" ]; then
201 print "COMMIT"
202 fi
203 done > ${rulesfile}
204
205 assert [ -s "${rulesfile}" ]
206 done
207
208 local error="false"
209 local ret
210
211 # First check if everything is correctly formatted.
212 for proto in 6 4; do
213 rulesfile=$(iptables_rulesfile ipv${proto})
214
215 _iptables_load ipv${proto} ${rulesfile} true
216 if [ $? -ne ${EXIT_OK} ]; then
217 log CRITICAL "Ruleset load check failed for IPv${proto}"
218 error="true"
219 fi
220 done
221
222 # Check if there has been an error in the load check.
223 if enabled error; then
224 iptables_dump CRITICAL
225
226 log CRITICAL "New firewall rules could not be loaded."
227 return ${EXIT_ERROR}
228 fi
229
230 # Dump the data, we are going to load.
231 iptables_dump
232
233 # If we are running in test mode, we are done here.
234 enabled test && return ${EXIT_OK}
235
236 # If we got until here, everything is fine to load the ruleset.
237 for proto in 6 4; do
238 rulesfile=$(iptables_rulesfile ipv${proto})
239
240 _iptables_load ipv${proto} ${rulesfile}
241 done
242 return ${EXIT_OK}
243 }
244
245 function _iptables_load() {
246 local proto=${1}
247 local file=${2}
248 local testmode=${3}
249
250 local command
251 case "${proto}" in
252 ipv6)
253 command="ip6tables-restore"
254 ;;
255 ipv4)
256 command="iptables-restore"
257 ;;
258 esac
259 assert isset command
260
261 if enabled testmode; then
262 command="${command} --test"
263 fi
264
265 local time_started=$(date -u "+%s")
266
267 cmd ${command} < ${file}
268 local ret=$?
269
270 case "${ret}" in
271 ${EXIT_OK})
272 local time_finished=$(date -u "+%s")
273 time_finished=$(( ${time_finished} - ${time_started} ))
274 log INFO \
275 "Successfully loaded new firewall ruleset for IPv${proto/ipv/} in ${time_finished}s!"
276 ;;
277 *)
278 if ! enabled testmode; then
279 log CRITICAL "Error loading firewall ruleset for IPv${proto/ipv/}!"
280 fi
281 ;;
282 esac
283
284 return ${ret}
285 }
286
287 function iptables_dump() {
288 local log_facility=${1-DEBUG}
289
290 # Here is nothing to do, if we are not running in
291 # debug mode.
292 enabled DEBUG || return ${EXIT_OK}
293
294 local rulesfile
295 local counter
296 local line
297
298 local proto
299 for proto in 6 4; do
300 rulesfile=$(iptables_rulesfile ipv${proto})
301 [ -e "${rulesfile}" ] || continue
302
303 log ${log_facility} "Firewall ruleset for IPv${proto}:"
304
305 counter=1
306 while read -r line; do
307 printf -v line "%4d | %s" "${counter}" "${line}"
308 log ${log_facility} "${line}"
309
310 counter=$(( $counter + 1 ))
311 done < ${rulesfile}
312 done
313 }
314
315 function iptables_chain_create() {
316 local chain
317 local table="filter"
318 local policy="-"
319 local proto
320 local args
321 while [ $# -gt 0 ]; do
322 case "${1}" in
323 -6|-4)
324 proto=${1}
325 ;;
326 -t)
327 table=${2}
328 shift
329 ;;
330 --policy=*)
331 policy=$(cli_get_val ${1})
332 ;;
333 *)
334 chain=${1}
335 ;;
336 esac
337 shift
338 done
339
340 assert isset chain
341 assert isset table
342 assert isoneof policy ACCEPT DROP "-"
343
344 iptables ${proto} -t ${table} ":${chain} ${policy} [0:0]"
345 }
346
347 function iptables_LOG() {
348 local prefix=${1}
349 local ret
350
351 case "${FIREWALL_LOG_METHOD}" in
352 nflog)
353 ret="NFLOG --nflog-threshold ${FIREWALL_NFLOG_THRESHOLD}"
354 isset prefix && ret="${ret} --nflog-prefix \"$prefix\""
355 ;;
356 syslog)
357 ret="LOG"
358 isset prefix && ret="${ret} --log-prefix \"$prefix\""
359 ;;
360 esac
361
362 print "${ret}"
363 }
364
365 function iptables_protocol() {
366 local PROTO
367 PROTO=$1
368 for proto in tcp udp esp ah; do
369 if [ "$PROTO" = "$proto" ]; then
370 echo "-p $PROTO"
371 break
372 fi
373 done
374 }
375
376 IPTABLES_PORT=0
377 IPTABLES_MULTIPORT=1
378 IPTABLES_PORTRANGE=2
379
380 function _iptables_port_range() {
381 grep -q ":" <<< $@
382 }
383
384 function _iptables_port_multiport() {
385 grep -q "," <<< $@
386 }
387
388 function _iptables_port() {
389 if _iptables_port_range "$@"; then
390 echo $IPTABLES_PORTRANGE
391 elif _iptables_port_multiport "$@"; then
392 echo $IPTABLES_MULTIPORT
393 else
394 echo $IPTABLES_PORT
395 fi
396 }
397
398 function iptables_source_port() {
399 [ -z "$@" ] && return
400 local type
401 type=$(_iptables_port $@)
402 if [ "$type" = "$IPTABLES_MULTIPORT" ]; then
403 echo "-m multiport --source-ports $@"
404 else
405 echo "--sport $@"
406 fi
407 }
408
409 function iptables_destination_port() {
410 [ -z "$@" ] && return
411 local type
412 type=$(_iptables_port $@)
413 if [ "$type" = "$IPTABLES_MULTIPORT" ]; then
414 echo "-m multiport --destination-ports $@"
415 else
416 echo "--dport $@"
417 fi
418 }