]> git.ipfire.org Git - people/stevee/network.git/blob - functions.iptables
ipv{6,4}: Simplify some functions and introduce new ones.
[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 local file
127
128 case "${proto}" in
129 ipv6)
130 file="/proc/net/ip6_tables_names"
131 ;;
132 ipv4)
133 file="/proc/net/ip_tables_names"
134 ;;
135 *)
136 return ${EXIT_ERROR}
137 ;;
138 esac
139
140 assert [ -r "${file}" ]
141
142 print "$(<${file})"
143 return ${EXIT_OK}
144 }
145
146 function iptables_rulesfile() {
147 local proto=${1}
148 proto=${proto/ipv/}
149
150 local chain=${2}
151 [ -z "${chain}" ] && chain="ruleset"
152
153 print "${IPTABLES_TMPDIR}/${chain}${proto}"
154 }
155
156 function iptables_init() {
157 local policy=${1}
158 assert isset policy
159
160 # Create filter table and initialize chains.
161 iptables "* filter"
162 iptables_chain_create -t filter INPUT --policy=${policy}
163 iptables_chain_create -t filter OUTPUT --policy=${policy}
164 iptables_chain_create -t filter FORWARD --policy=${policy}
165
166 # Create mangle table initialize chains.
167 iptables -t mangle "* mangle"
168 iptables_chain_create -t mangle PREROUTING --policy=ACCEPT
169 iptables_chain_create -t mangle INPUT --policy=ACCEPT
170 iptables_chain_create -t mangle OUTPUT --policy=ACCEPT
171 iptables_chain_create -t mangle FORWARD --policy=ACCEPT
172 iptables_chain_create -t mangle POSTROUTING --policy=ACCEPT
173
174 # Add NAT table for IPv4.
175 iptables -4 -t nat "* nat"
176 iptables_chain_create -4 -t nat PREROUTING --policy=ACCEPT
177 iptables_chain_create -4 -t nat OUTPUT --policy=ACCEPT
178 iptables_chain_create -4 -t nat POSTROUTING --policy=ACCEPT
179 }
180
181 # Load the created ruleset into the kernel.
182 function iptables_load() {
183 # If first argument is present and true, we
184 # run in test mode.
185 local test="${1}"
186
187 local rulesfile
188
189 # First, commit all tables.
190 _iptables_commit
191
192 # Concat the table rulesets into one big file.
193 local proto
194 for proto in 6 4; do
195 rulesfile=$(iptables_rulesfile ipv${proto})
196
197 local table
198 local tablefile
199 for table in $(iptables_tables ipv${proto}); do
200 tablefile=$(iptables_rulesfile ipv${proto} ${table})
201 print "$(<${tablefile})"
202 done > ${rulesfile}
203 done
204
205 local error="false"
206 local ret
207
208 # First check if everything is correctly formatted.
209 for proto in 6 4; do
210 rulesfile=$(iptables_rulesfile ipv${proto})
211
212 _iptables_load ipv${proto} ${rulesfile} true
213 if [ $? -ne ${EXIT_OK} ]; then
214 log CRITICAL "Ruleset load check failed for IPv${proto}"
215 error="true"
216 fi
217 done
218
219 # Check if there has been an error in the load check.
220 if enabled error; then
221 iptables_dump CRITICAL
222
223 log CRITICAL "New firewall rules could not be loaded."
224 return ${EXIT_ERROR}
225 fi
226
227 # Dump the data, we are going to load.
228 iptables_dump
229
230 # If we are running in test mode, we are done here.
231 enabled test && return ${EXIT_OK}
232
233 # If we got until here, everything is fine to load the ruleset.
234 for proto in 6 4; do
235 rulesfile=$(iptables_rulesfile ipv${proto})
236
237 _iptables_load ipv${proto} ${rulesfile}
238 done
239 return ${EXIT_OK}
240 }
241
242 # Commit all tables.
243 function _iptables_commit() {
244 iptables -t filter "COMMIT"
245 iptables -t mangle "COMMIT"
246
247 # Commit NAT chain for IPv4.
248 iptables -4 -t nat "COMMIT"
249 }
250
251 function _iptables_load() {
252 local proto=${1}
253 local file=${2}
254 local testmode=${3}
255
256 local command
257 case "${proto}" in
258 ipv6)
259 command="ip6tables-restore"
260 ;;
261 ipv4)
262 command="iptables-restore"
263 ;;
264 esac
265 assert isset command
266
267 if enabled testmode; then
268 command="${command} --test"
269 fi
270
271 local time_started=$(date -u "+%s")
272
273 cmd ${command} < ${file}
274 local ret=$?
275
276 case "${ret}" in
277 ${EXIT_OK})
278 local time_finished=$(date -u "+%s")
279 time_finished=$(( ${time_finished} - ${time_started} ))
280 log INFO \
281 "Successfully loaded new firewall ruleset for IPv${proto/ipv/} in ${time_finished}s!"
282 ;;
283 *)
284 if ! enabled testmode; then
285 log CRITICAL "Error loading firewall ruleset for IPv${proto/ipv/}!"
286 fi
287 ;;
288 esac
289
290 return ${ret}
291 }
292
293 function iptables_dump() {
294 local log_facility=${1-DEBUG}
295
296 # Here is nothing to do, if we are not running in
297 # debug mode.
298 enabled DEBUG || return ${EXIT_OK}
299
300 local rulesfile
301 local counter
302 local line
303
304 local proto
305 for proto in 6 4; do
306 rulesfile=$(iptables_rulesfile ipv${proto})
307 [ -e "${rulesfile}" ] || continue
308
309 log ${log_facility} "Firewall ruleset for IPv${proto}:"
310
311 counter=1
312 while read line; do
313 line=$(print "%4d | %s" "${counter}" "${line}")
314 log ${log_facility} "${line}"
315
316 counter=$(( $counter + 1 ))
317 done < ${rulesfile}
318 done
319 }
320
321 function iptables_chain_create() {
322 local chain
323 local table="filter"
324 local policy="-"
325 local proto
326 local args
327 while [ $# -gt 0 ]; do
328 case "${1}" in
329 -6|-4)
330 proto=${1}
331 ;;
332 -t)
333 table=${2}
334 shift
335 ;;
336 --policy=*)
337 policy=$(cli_get_val ${1})
338 ;;
339 *)
340 chain=${1}
341 ;;
342 esac
343 shift
344 done
345
346 assert isset chain
347 assert isset table
348 assert isoneof policy ACCEPT DROP "-"
349
350 iptables ${proto} -t ${table} ":${chain} ${policy} [0:0]"
351 }
352
353 function iptables_LOG() {
354 local prefix=${1}
355 local ret
356
357 case "${FIREWALL_LOG_METHOD}" in
358 nflog)
359 ret="NFLOG --nflog-threshold ${FIREWALL_NFLOG_THRESHOLD}"
360 isset prefix && ret="${ret} --nflog-prefix \"$prefix\""
361 ;;
362 syslog)
363 ret="LOG"
364 isset prefix && ret="${ret} --log-prefix \"$prefix\""
365 ;;
366 esac
367
368 print "${ret}"
369 }
370
371 function iptables_protocol() {
372 local PROTO
373 PROTO=$1
374 for proto in tcp udp esp ah; do
375 if [ "$PROTO" = "$proto" ]; then
376 echo "-p $PROTO"
377 break
378 fi
379 done
380 }
381
382 IPTABLES_PORT=0
383 IPTABLES_MULTIPORT=1
384 IPTABLES_PORTRANGE=2
385
386 function _iptables_port_range() {
387 grep -q ":" <<< $@
388 }
389
390 function _iptables_port_multiport() {
391 grep -q "," <<< $@
392 }
393
394 function _iptables_port() {
395 if _iptables_port_range "$@"; then
396 echo $IPTABLES_PORTRANGE
397 elif _iptables_port_multiport "$@"; then
398 echo $IPTABLES_MULTIPORT
399 else
400 echo $IPTABLES_PORT
401 fi
402 }
403
404 function iptables_source_port() {
405 [ -z "$@" ] && return
406 local type
407 type=$(_iptables_port $@)
408 if [ "$type" = "$IPTABLES_MULTIPORT" ]; then
409 echo "-m multiport --source-ports $@"
410 else
411 echo "--sport $@"
412 fi
413 }
414
415 function iptables_destination_port() {
416 [ -z "$@" ] && return
417 local type
418 type=$(_iptables_port $@)
419 if [ "$type" = "$IPTABLES_MULTIPORT" ]; then
420 echo "-m multiport --destination-ports $@"
421 else
422 echo "--dport $@"
423 fi
424 }