]> git.ipfire.org Git - network.git/blob - functions.firewall
947367a4bbdd643d8567a824baf9fc2fd4c168b1
[network.git] / functions.firewall
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 # This function initializes all kernel parameters that need to be adjusted
23 # to run this firewall properly.
24 function firewall_kernel_init() {
25 log INFO "Configuring kernel parameters..."
26 local option
27
28 # Enable conntrack accounting
29 conntrack_set_accounting "true"
30
31 # Adjust max. amount of simultaneous connections
32 conntrack_set_max_connections "${CONNTRACK_MAX_CONNECTIONS}"
33
34 # Increase UDP connection timeout (fixes DNS)
35 conntrack_set_udp_timeout "${CONNTRACK_UDP_TIMEOUT}"
36
37 # Disable sending redirects
38 log INFO "Disabling sending redirects"
39 sysctl_set_recursively "net.ipv6.conf" "send_redirects" 0
40 sysctl_set_recursively "net.ipv4.conf" "send_redirects" 0
41
42 # Enable source route protection
43 log INFO "Enabling source route protection"
44 sysctl_set_recursively "net.ipv6.conf" "accept_source_route" 0
45 sysctl_set_recursively "net.ipv4.conf" "accept_source_route" 0
46
47 # ICMP broadcast protection (smurf amplifier protection)
48 log INFO "Enabling ICMP broadcast protection (smurf amplifier protection)"
49 sysctl_set "net.ipv4.icmp_echo_ignore_broadcasts" 1
50
51 # ICMP Dead Error Message protection
52 log INFO "Enabling ICMP dead error message protection"
53 sysctl_set "net.ipv4.icmp_ignore_bogus_error_responses" 0
54
55 # Enable packet forwarding
56 log INFO "Enabling packet forwarding"
57 sysctl_set_recursively "net.ipv6.conf" "forwarding" 1
58 sysctl_set_recursively "net.ipv4.conf" "forwarding" 1
59
60 # Setting some kernel performance options
61 log INFO "Setting some kernel performance options"
62 for option in window_scaling timestamps sack dsack fack; do
63 sysctl_set "net.ipv4.tcp_${option}" 1
64 done
65 sysctl_set "net.ipv4.tcp_low_latency" 0
66
67 # Reduce DoS ability by reducing timeouts
68 log INFO "Reducing DoS ability"
69 sysctl_set "net.ipv4.tcp_fin_timeout" 30
70 sysctl_set "net.ipv4.tcp_keepalive_time" 1800
71
72 # Set number of times to retry SYN in a new connection
73 sysctl_set "net.ipv4.tcp_syn_retries" 3
74
75 # Set number of times to retry a SYN-ACK in a half-open new connection
76 sysctl_set "net.ipv4.tcp_synack_retries" 2
77
78 # Enable a fix for RFC1337 - time-wait assassination hazards in TCP
79 sysctl_set "net.ipv4.tcp_rfc1337" 1
80
81 # SYN-flood protection
82 if enabled FIREWALL_SYN_COOKIES; then
83 log INFO "Enabling SYN-flood protection via SYN-cookies"
84 sysctl_set_bool "net.ipv4.tcp_syncookies" 1
85 else
86 log INFO "Disabling SYN-flood protection via SYN-cookies"
87 sysctl_set_bool "net.ipv4.tcp_syncookies" 0
88 fi
89
90 # rp_filter
91 if enabled FIREWALL_RP_FILTER; then
92 log INFO "Enabling anti-spoof from non-routable IP addresses"
93 sysctl_set_recursively "net.ipv4.conf" "rp_filter" 1
94 else
95 log INFO "Disabling anti-spoof from non-routable IP addresses"
96 sysctl_set_recursively "net.ipv4.conf" "rp_filter" 0
97 fi
98
99 # Log martians
100 if enabled FIREWALL_LOG_MARTIANS; then
101 log INFO "Enabling the logging of martians"
102 sysctl_set_recursively "net.ipv4.conf" "log_martians" 1
103 else
104 log INFO "Disabling the logging of martians"
105 sysctl_set_recursively "net.ipv4.conf" "log_martians" 0
106 fi
107
108 # ICMP redirect messages
109 if enabled FIREWALL_ACCEPT_ICMP_REDIRECTS; then
110 log INFO "Enabling accepting ICMP-redirect messages"
111 sysctl_set_recursively "net.ipv6.conf" "accept_redirects" 1
112 sysctl_set_recursively "net.ipv4.conf" "accept_redirects" 1
113 else
114 log INFO "Disabling accepting ICMP-redirect messages"
115 sysctl_set_recursively "net.ipv6.conf" "accept_redirects" 0
116 sysctl_set_recursively "net.ipv4.conf" "accept_redirects" 0
117 fi
118
119 # Explicit Congestion Notification
120 if enabled FIREWALL_USE_ECN; then
121 log INFO "Enabling ECN (Explicit Congestion Notification)"
122 sysctl_set "net.ipv4.tcp_ecn" 1
123 else
124 log INFO "Disabling ECN (Explicit Congestion Notification)"
125 sysctl_set "net.ipv4.tcp_ecn" 2
126 fi
127
128 # Dynamic IP address hacking
129 log INFO "Enabling kernel support for dynamic IP addresses"
130 sysctl_set "net.ipv4.ip_dynaddr" 1
131
132 if enabled FIREWALL_PMTU_DISCOVERY; then
133 log INFO "Enabling PMTU discovery"
134 sysctl_set "net.ipv4.ip_no_pmtu_disc" 0
135 else
136 log INFO "Disabling PMTU discovery"
137 sysctl_set "net.ipv4.ip_no_pmtu_disc" 1
138 fi
139
140 # TTL
141 if ipv4_ttl_valid "${FIREWALL_DEFAULT_TTL}"; then
142 log INFO "Setting default TTL to ${FIREWALL_DEFAULT_TTL}"
143 sysctl_set "net.ipv4.ip_default_ttl" "${FIREWALL_DEFAULT_TTL}"
144 else
145 log ERROR "Invalid value for default TTL '${FIREWALL_DEFAULT_TTL}'"
146 log ERROR " Must be between 10 and 255!"
147 fi
148
149 return ${EXIT_OK}
150 }
151
152 # High-level function which will create a ruleset for the current firewall
153 # configuration and load it into the kernel.
154 function firewall_start() {
155 local protocol="${1}"
156 assert isset protocol
157 shift
158
159 # Test mode.
160 local test="false"
161
162 while [ $# -gt 0 ]; do
163 case "${1}" in
164 --test)
165 test="true"
166 ;;
167 *)
168 error "Unrecognized argument: ${1}"
169 return ${EXIT_ERROR}
170 ;;
171 esac
172 shift
173 done
174
175 if enabled test; then
176 log INFO "Test mode enabled."
177 log INFO "The firewall ruleset will not be loaded."
178 fi
179
180 firewall_lock_acquire
181
182 # Initialize an empty iptables ruleset.
183 iptables_init "${protocol}" "DROP"
184
185 # Add default chains.
186 firewall_filter_rh0_headers "${protocol}"
187 firewall_filter_icmp "${protocol}"
188 firewall_tcp_state_flags "${protocol}"
189 firewall_custom_chains "${protocol}"
190 firewall_connection_tracking "${protocol}"
191 firewall_tcp_clamp_mss "${protocol}"
192
193 # Add policies for every zone.
194 firewall_localhost_create_chains "${protocol}"
195
196 local zone
197 for zone in $(zones_get_all); do
198 # Create all needed chains for the zone.
199 firewall_zone_create_chains "${protocol}" "${zone}"
200
201 # After the chains that are always available have been
202 # created, we will add a custom policy to every single
203 # zone.
204
205 policy_zone_add "${protocol}" "${zone}"
206 done
207
208 # Load the new ruleset.
209 local args
210 if enabled testmode; then
211 list_append args "--test"
212 fi
213 iptables_commit "${protocol}" ${args}
214
215 firewall_lock_release
216 }
217
218 function firewall_stop() {
219 local protocol="${1}"
220 assert isset protocol
221
222 firewall_lock_acquire
223
224 # Initialize an empty firewall ruleset
225 # with default policy ACCEPT.
226 iptables_init "${protocol}" ACCEPT
227
228 # Load it.
229 ipables_load "${protocol}"
230
231 firewall_lock_release
232 }
233
234 function firewall_show() {
235 local protocol="${1}"
236 assert isset protocol
237
238 # Shows the ruleset that is currently loaded.
239 iptables_status "${protocol}"
240
241 return ${EXIT_OK}
242 }
243
244 function firewall_panic() {
245 local protocol="${1}"
246 assert isset protocol
247 shift
248
249 local admin_hosts="$@"
250
251 firewall_lock_acquire "${protocol}"
252
253 # Drop all communications.
254 iptables_init "${protocol}" DROP
255
256 # If an admin host is provided, some administrative
257 # things will be allowed from there.
258 local admin_host
259 for admin_host in ${admin_hosts}; do
260 iptables "${protocol}" -A INPUT -s "${admin_host}" -j ACCEPT
261 iptables "${protocol}" -A OUTPUT -d "${admin_host}" -j ACCEPT
262 done
263
264 # Load it.
265 iptables_commit "${protocol}"
266
267 firewall_lock_release
268 }
269
270 function firewall_lock_acquire() {
271 lock_acquire ${RUN_DIR}/.firewall_lock
272
273 # Make sure the lock is released after the firewall
274 # script has crashed or exited early.
275 trap firewall_lock_release EXIT TERM KILL
276
277 # Create a directory where we can put our
278 # temporary data in the most secure way as possible.
279 IPTABLES_TMPDIR=$(mktemp -d)
280 }
281
282 function firewall_lock_release() {
283 if isset IPTABLES_TMPDIR; then
284 # Remove all temporary data.
285 rm -rf ${IPTABLES_TMPDIR}
286
287 # Reset the tempdir variable.
288 IPTABLES_TMPDIR=
289 fi
290
291 # Reset the trap.
292 trap true EXIT TERM KILL
293
294 lock_release ${RUN_DIR}/.firewall_lock
295 }
296
297 function firewall_custom_chains() {
298 local protocol="${1}"
299 assert isset protocol
300
301 log INFO "Creating CUSTOM* chains..."
302
303 # These chains are intened to be filled with
304 # rules by the user. They are processed at the very
305 # beginning so it is possible to overwrite everything.
306
307 iptables_chain_create "${protocol}" CUSTOMINPUT
308 iptables "${protocol}" -A INPUT -j CUSTOMINPUT
309
310 iptables_chain_create "${protocol}" CUSTOMFORWARD
311 iptables "${protocol}" -A FORWARD -j CUSTOMFORWARD
312
313 iptables_chain_create "${protocol}" CUSTOMOUTPUT
314 iptables "${protocol}" -A OUTPUT -j CUSTOMOUTPUT
315
316 iptables_chain_create "${protocol}" -t nat CUSTOMPREROUTING
317 iptables "${protocol}" -t nat -A PREROUTING -j CUSTOMPREROUTING
318
319 iptables_chain_create "${protocol}" -t nat CUSTOMPOSTROUTING
320 iptables "${protocol}" -t nat -A POSTROUTING -j CUSTOMPOSTROUTING
321
322 iptables_chain_create "${protocol}" -t nat CUSTOMOUTPUT
323 iptables "${protocol}" -t nat -A OUTPUT -j CUSTOMOUTPUT
324 }
325
326 function firewall_tcp_state_flags() {
327 local protocol="${1}"
328 assert isset protocol
329
330 log INFO "Creating TCP State Flags chain..."
331
332 iptables_chain_create "${protocol}" BADTCP_LOG
333 iptables "${protocol}" -A BADTCP_LOG -p tcp -j "$(iptables_LOG "Illegal TCP state: ")"
334 iptables "${protocol}" -A BADTCP_LOG -j DROP
335
336 iptables_chain_create "${protocol}" BADTCP
337 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags ALL NONE -j BADTCP_LOG
338 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags SYN,FIN SYN,FIN -j BADTCP_LOG
339 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags SYN,RST SYN,RST -j BADTCP_LOG
340 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags FIN,RST FIN,RST -j BADTCP_LOG
341 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags ACK,FIN FIN -j BADTCP_LOG
342 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags ACK,PSH PSH -j BADTCP_LOG
343 iptables "${protocol}" -A BADTCP -p tcp --tcp-flags ACK,URG URG -j BADTCP_LOG
344
345 iptables "${protocol}" -A INPUT -p tcp -j BADTCP
346 iptables "${protocol}" -A OUTPUT -p tcp -j BADTCP
347 iptables "${protocol}" -A FORWARD -p tcp -j BADTCP
348 }
349
350 function firewall_tcp_clamp_mss() {
351 # Do nothing if this has been disabled.
352 enabled FIREWALL_CLAMP_PATH_MTU || return ${EXIT_OK}
353
354 local protocol="${1}"
355 assert isset protocol
356
357 log DEBUG "Adding rules to clamp MSS to path MTU..."
358
359 iptables "${protocol}" -t mangle -A FORWARD \
360 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
361 }
362
363 function firewall_connection_tracking() {
364 local protocol="${1}"
365 assert isset protocol
366
367 log INFO "Creating Connection Tracking chain..."
368
369 iptables_chain_create "${protocol}" CONNTRACK
370 iptables "${protocol}" -A CONNTRACK -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
371 iptables "${protocol}" -A CONNTRACK -m conntrack --ctstate INVALID -j "$(iptables_LOG "INVALID packet: ")"
372 iptables "${protocol}" -A CONNTRACK -m conntrack --ctstate INVALID -j DROP
373
374 iptables "${protocol}" -A INPUT -j CONNTRACK
375 iptables "${protocol}" -A OUTPUT -j CONNTRACK
376 iptables "${protocol}" -A FORWARD -j CONNTRACK
377 }
378
379 function firewall_localhost_create_chains() {
380 local protocol="${1}"
381 assert isset protocol
382
383 log DEBUG "Creating firewall chains for localhost..."
384
385 # Accept everything on lo
386 iptables "${protocol}" -A INPUT -i lo -j ACCEPT
387 iptables "${protocol}" -A OUTPUT -o lo -j ACCEPT
388 }
389
390 function firewall_filter_rh0_headers() {
391 local protocol="${1}"
392 assert isset protocol
393
394 # Only IPv6.
395 [ "${protocol}" = "ipv6" ] || return ${EXIT_OK}
396
397 # Filter all packets that have RH0 headers
398 # http://www.ietf.org/rfc/rfc5095.txt
399 iptables_chain_create "${protocol}" FILTER_RH0
400 iptables "${protocol}" -A FILTER_RH0 -m rt --rt-type 0 -j DROP
401
402 iptables "${protocol}" -A INPUT -j FILTER_RH0
403 iptables "${protocol}" -A FORWARD -j FILTER_RH0
404 iptables "${protocol}" -A OUTPUT -j FILTER_RH0
405 }
406
407 function firewall_filter_icmp() {
408 local protocol="${1}"
409 assert isset protocol
410
411 # Only IPv6.
412 [ "${protocol}" = "ipv6" ] || return ${EXIT_OK}
413
414 local chain="FILTER_ICMPV6"
415
416 # Create an extra chain for handling ICMP packets.
417 iptables_chain_create "${protocol}" "${chain}_COMMON"
418
419 local suffix
420 for suffix in INC FWD OUT; do
421 iptables_chain_create "${protocol}" "${chain}_${suffix}"
422 iptables "${protocol}" -A "${chain}_${suffix}" -j "${chain}_COMMON"
423 done
424 iptables "${protocol}" -A INPUT -p icmpv6 -j "${chain}_INC"
425 iptables "${protocol}" -A FORWARD -p icmpv6 -j "${chain}_FWD"
426 iptables "${protocol}" -A OUTPUT -p icmpv6 -j "${chain}_OUT"
427
428 # Packets that must always pass the firewall.
429 # Type 4: Parameter Problem
430 local type
431 for type in ttl-zero-during-reassembly bad-header; do
432 iptables "${protocol}" -A "${chain}_COMMON" \
433 -p icmpv6 --icmpv6-type "${type}" -j ACCEPT
434 done
435
436 # Packets that are accepted if they belong to an existing connection.
437 for type in echo-reply destination-unreachable packet-too-big \
438 unknown-header-type unknown-option; do
439 iptables "${protocol}" -A "${chain}_COMMON" \
440 -m conntrack --ctstate ESTABLISHED,RELATED \
441 -p icmpv6 --icmpv6-type "${type}" -j ACCEPT
442 done
443
444 # Packets that are always discarded.
445 # Type 100, 101, 200, 201: Private Experimentation
446 for type in 100 101 200 201; do
447 iptables "${protocol}" -A "${chain}_COMMON" \
448 -p icmpv6 --icmpv6-type "${type}" -j DROP
449 done
450
451 # Discard packets from local networks with hop limit smaller than $hoplimit.
452 # Type 148: Path solicitation
453 # Type 149: Path advertisement
454 local hoplimit=255
455 for type in {router,neighbour}-{advertisement,solicitation} 148 149; do
456 iptables "${protocol}" -A "${chain}_INC" \
457 -p icmpv6 --icmpv6-type "${type}" \
458 -m hl --hl-lt "${hoplimit}" -j DROP
459 done
460
461 # The firewall is always allowed to send ICMP echo requests.
462 iptables "${protocol}" -A "${chain}_OUT" \
463 -p icmpv6 --icmpv6-type echo-request -j ACCEPT
464
465 return ${EXIT_OK}
466 }
467
468 function firewall_zone_create_chains() {
469 local protocol="${1}"
470 assert isset protocol
471
472 local zone="${2}"
473 assert isset zone
474
475 log DEBUG "Creating firewall chains for zone '${zone}'."
476
477 local chain_prefix="ZONE_${zone^^}"
478
479 # Create filter chains.
480 iptables_chain_create "${protocol}" "${chain_prefix}_INPUT"
481 iptables "${protocol}" -A INPUT -i ${zone} -j "${chain_prefix}_INPUT"
482
483 iptables_chain_create "${protocol}" "${chain_prefix}_OUTPUT"
484 iptables "${protocol}" -A OUTPUT -o ${zone} -j "${chain_prefix}_OUTPUT"
485
486 # Custom rules.
487 iptables_chain_create "${protocol}" "${chain_prefix}_CUSTOM"
488
489 # Intrusion Prevention System.
490 iptables_chain_create "${protocol}" "${chain_prefix}_IPS"
491
492 # Create a chain for each other zone.
493 # This leaves us with n^2 chains. Duh.
494
495 local other_zone other_chain_prefix
496 for other_zone in $(zones_get_all); do
497 other_chain_prefix="${chain_prefix}_${other_zone^^}"
498 iptables_chain_create "${protocol}" "${other_chain_prefix}"
499
500 # Connect the chain with the FORWARD chain.
501 iptables "${protocol}" -A FORWARD -i "${zone}" -o "${other_zone}" \
502 -j "${other_chain_prefix}"
503
504 # Handle custom rules.
505 iptables "${protocol}" -A "${other_chain_prefix}" -j "${chain_prefix}_CUSTOM"
506
507 # Link IPS.
508 iptables "${protocol}" -A "${other_chain_prefix}" -j "${chain_prefix}_IPS"
509
510 # Rules.
511 iptables_chain_create "${protocol}" "${other_chain_prefix}_RULES"
512 iptables "${protocol}" -A "${other_chain_prefix}" -j "${other_chain_prefix}_RULES"
513
514 # Policy.
515 iptables_chain_create "${protocol}" "${other_chain_prefix}_POLICY"
516 iptables "${protocol}" -A "${other_chain_prefix}" -j "${other_chain_prefix}_POLICY"
517 done
518
519 ## Create mangle chain.
520 #iptables_chain_create "${protocol}" -t mangle "${chain_prefix}"
521 #iptables "${protocol}" -t mangle -A PREROUTING -i "${zone}" -j "${chain_prefix}"
522 #iptables "${protocol}" -t mangle -A POSTROUTING -o "${zone}" -j "${chain_prefix}"
523
524 ## Quality of Service
525 #iptables_chain_create "${protocol}" -t mangle "${chain_prefix}_QOS_INC"
526 #iptables "${protocol}" -t mangle -A "${chain_prefix}" -i "${zone}" -j "${chain_prefix}_QOS_INC"
527 #iptables_chain_create "${protocol}" -t mangle "${chain_prefix}_QOS_OUT"
528 #iptables "${protocol}" -t mangle -A "${chain_prefix}" -o "${zone}" -j "${chain_prefix}_QOS_OUT"
529
530 # Create NAT chain.
531 iptables_chain_create "${protocol}" -t nat "${chain_prefix}"
532 iptables "${protocol}" -t nat -A PREROUTING -i "${zone}" -j "${chain_prefix}"
533 iptables "${protocol}" -t nat -A POSTROUTING -o "${zone}" -j "${chain_prefix}"
534
535 # Network Address Translation
536 iptables_chain_create "${protocol}" -t nat "${chain_prefix}_DNAT"
537 iptables "${protocol}" -t nat -A PREROUTING -i "${zone}" -j "${chain_prefix}_DNAT"
538 iptables_chain_create "${protocol}" -t nat "${chain_prefix}_SNAT"
539 iptables "${protocol}" -t nat -A POSTROUTING -o "${zone}" -j "${chain_prefix}_SNAT"
540
541 # UPnP
542 iptables_chain_create "${protocol}" -t nat "${chain_prefix}_UPNP"
543 iptables "${protocol}" -t nat -A "${chain_prefix}" -j "${chain_prefix}_UPNP"
544
545 return ${EXIT_OK}
546 }
547
548 function firewall_parse_rules() {
549 local file=${1}
550 assert isset file
551 shift
552
553 # End if no rule file exists.
554 [ -r "${file}" ] || return ${EXIT_OK}
555
556 local cmd
557
558 local ${FIREWALL_RULES_CONFIG_PARAMS}
559 local line
560 while read -r line; do
561 # Skip empty lines.
562 [ -n "${line}" ] || continue
563
564 # Skip commented lines.
565 [ "${line:0:1}" = "#" ] && continue
566
567 # Parse the rule.
568 _firewall_parse_rule_line ${line}
569 if [ $? -ne ${EXIT_OK} ]; then
570 log WARNING "Skipping invalid line: ${line}"
571 continue
572 fi
573
574 cmd="iptables $@"
575
576 # Source IP address/net.
577 if isset src; then
578 list_append cmd "-s ${src}"
579 fi
580
581 # Destination IP address/net.
582 if isset dst; then
583 list_append cmd "-d ${dst}"
584 fi
585
586 # Protocol.
587 if isset proto; then
588 list_append cmd "-p ${proto}"
589
590 if list_match ${proto} ${FIREWALL_PROTOCOLS_SUPPORTING_PORTS}; then
591 if isset sport; then
592 list_append cmd "--sport ${sport}"
593 fi
594
595 if isset dport; then
596 list_append cmd "--dport ${dport}"
597 fi
598 fi
599 fi
600
601 # Always append the action.
602 list_append cmd "-j ${action}"
603
604 # Execute command.
605 ${cmd}
606 done < ${file}
607 }
608
609 function _firewall_parse_rule_line() {
610 local arg
611
612 # Clear all values.
613 for arg in ${FIREWALL_RULES_CONFIG_PARAMS}; do
614 assign "${arg}" ""
615 done
616
617 local key val
618 while read -r arg; do
619 key=$(cli_get_key ${arg})
620
621 if ! listmatch "${key}" ${FIREWALL_RULES_CONFIG_PARAMS}; then
622 log WARNING "Unrecognized argument: ${arg}"
623 return ${EXIT_ERROR}
624 fi
625
626 val=$(cli_get_val ${arg})
627 assign "${key}" "${val}"
628 done <<< "$(args $@)"
629
630 # action must always be set.
631 if ! isset action; then
632 log WARNING "'action' is not set: $@"
633 return ${EXIT_ERROR}
634 fi
635
636 for arg in src dst; do
637 isset ${arg} || continue
638
639 # Check for valid IP addresses.
640 if ! ip_is_valid ${!arg}; then
641 log WARNING "Invalid IP address for '${arg}=${!arg}': $@"
642 return ${EXIT_ERROR}
643 fi
644 done
645
646 if isset proto; then
647 # Make lowercase.
648 proto=${proto,,}
649
650 if ! list_match "${proto}" ${FIREWALL_SUPPORTED_PROTOCOLS}; then
651 log WARNING "Unsupported protocol type 'proto=${proto}': $@"
652 return ${EXIT_ERROR}
653 fi
654 fi
655
656 for arg in sport dport; do
657 isset ${arg} || continue
658
659 # Check if port is valid.
660 if ! isinteger ${arg}; then
661 log WARNING "Invalid port '${arg}=${!arg}': $@"
662 return ${EXIT_ERROR}
663 fi
664 done
665
666 return ${EXIT_OK}
667 }