]>
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}) | |
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}" | |
98146c00 MT |
154 | } |
155 | ||
156 | function iptables_init() { | |
157 | local policy=${1} | |
afb7d704 | 158 | assert isset policy |
98146c00 | 159 | |
afb7d704 | 160 | # Create filter table and initialize chains. |
98146c00 | 161 | iptables "* filter" |
afb7d704 MT |
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} | |
98146c00 | 165 | |
afb7d704 | 166 | # Create mangle table initialize chains. |
98146c00 | 167 | iptables -t mangle "* mangle" |
afb7d704 MT |
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 | |
98146c00 | 173 | |
afb7d704 MT |
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 | |
98146c00 MT |
179 | } |
180 | ||
afb7d704 MT |
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}" | |
98146c00 | 186 | |
afb7d704 | 187 | local rulesfile |
98146c00 | 188 | |
afb7d704 MT |
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() { | |
98146c00 MT |
244 | iptables -t filter "COMMIT" |
245 | iptables -t mangle "COMMIT" | |
98146c00 | 246 | |
afb7d704 MT |
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} | |
98146c00 | 255 | |
afb7d704 MT |
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} | |
98146c00 | 299 | |
afb7d704 MT |
300 | local rulesfile |
301 | local counter | |
98146c00 | 302 | local line |
98146c00 | 303 | |
afb7d704 MT |
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 | |
98146c00 MT |
319 | } |
320 | ||
321 | function iptables_chain_create() { | |
afb7d704 MT |
322 | local chain |
323 | local table="filter" | |
324 | local policy="-" | |
325 | local proto | |
98146c00 | 326 | local args |
afb7d704 MT |
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 "-" | |
98146c00 | 349 | |
afb7d704 | 350 | iptables ${proto} -t ${table} ":${chain} ${policy} [0:0]" |
98146c00 MT |
351 | } |
352 | ||
353 | function iptables_LOG() { | |
354 | local prefix=${1} | |
afb7d704 | 355 | local ret |
98146c00 | 356 | |
afb7d704 MT |
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}" | |
98146c00 MT |
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 | } |