]>
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} | |
ab2eb246 | 126 | assert isset proto |
afb7d704 MT |
127 | |
128 | case "${proto}" in | |
129 | ipv6) | |
ab2eb246 | 130 | print "filter mangle" |
afb7d704 MT |
131 | ;; |
132 | ipv4) | |
ab2eb246 | 133 | print "filter mangle nat" |
afb7d704 MT |
134 | ;; |
135 | *) | |
136 | return ${EXIT_ERROR} | |
137 | ;; | |
138 | esac | |
139 | ||
afb7d704 MT |
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}" | |
98146c00 MT |
151 | } |
152 | ||
153 | function iptables_init() { | |
154 | local policy=${1} | |
afb7d704 | 155 | assert isset policy |
98146c00 | 156 | |
afb7d704 | 157 | # Create filter table and initialize chains. |
98146c00 | 158 | iptables "* filter" |
afb7d704 MT |
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} | |
98146c00 | 162 | |
afb7d704 | 163 | # Create mangle table initialize chains. |
98146c00 | 164 | iptables -t mangle "* mangle" |
afb7d704 MT |
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 | |
98146c00 | 170 | |
afb7d704 MT |
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 | |
98146c00 MT |
176 | } |
177 | ||
afb7d704 MT |
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}" | |
98146c00 | 183 | |
afb7d704 | 184 | local rulesfile |
98146c00 | 185 | |
afb7d704 MT |
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}) | |
16ac9775 | 190 | assert isset rulesfile |
afb7d704 MT |
191 | |
192 | local table | |
193 | local tablefile | |
194 | for table in $(iptables_tables ipv${proto}); do | |
195 | tablefile=$(iptables_rulesfile ipv${proto} ${table}) | |
16ac9775 MT |
196 | |
197 | fread ${tablefile} | |
198 | ||
199 | # Add the COMMIT statement for every table. | |
200 | if [ -s "${tablefile}" ]; then | |
201 | print "COMMIT" | |
202 | fi | |
afb7d704 | 203 | done > ${rulesfile} |
16ac9775 MT |
204 | |
205 | assert [ -s "${rulesfile}" ] | |
afb7d704 MT |
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 | ||
afb7d704 MT |
245 | function _iptables_load() { |
246 | local proto=${1} | |
247 | local file=${2} | |
248 | local testmode=${3} | |
98146c00 | 249 | |
afb7d704 MT |
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} | |
98146c00 | 293 | |
afb7d704 MT |
294 | local rulesfile |
295 | local counter | |
98146c00 | 296 | local line |
98146c00 | 297 | |
afb7d704 MT |
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 | |
ed26fecf MT |
306 | while read -r line; do |
307 | printf -v line "%4d | %s" "${counter}" "${line}" | |
afb7d704 MT |
308 | log ${log_facility} "${line}" |
309 | ||
310 | counter=$(( $counter + 1 )) | |
311 | done < ${rulesfile} | |
312 | done | |
98146c00 MT |
313 | } |
314 | ||
315 | function iptables_chain_create() { | |
afb7d704 MT |
316 | local chain |
317 | local table="filter" | |
318 | local policy="-" | |
319 | local proto | |
98146c00 | 320 | local args |
afb7d704 MT |
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 "-" | |
98146c00 | 343 | |
afb7d704 | 344 | iptables ${proto} -t ${table} ":${chain} ${policy} [0:0]" |
98146c00 MT |
345 | } |
346 | ||
347 | function iptables_LOG() { | |
348 | local prefix=${1} | |
afb7d704 | 349 | local ret |
98146c00 | 350 | |
afb7d704 MT |
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}" | |
98146c00 MT |
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 | } |