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