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