]> git.ipfire.org Git - people/ms/network.git/blobdiff - functions.iptables
aiccu: Add documentation.
[people/ms/network.git] / functions.iptables
index cf1e4769f86e4634d7235a17be0eda6e9585ace5..98658bbed02b6cec6d499d0451ec3527580e26c0 100644 (file)
 function iptables() {
        local arg
        local args
-       local table
+       local table="filter"
+       local src dst
+
+       # Default is both protocols.
+       local proto="6 4"
 
        # Check if the directory where we put our rules in is set and
        # exists.
        assert isset IPTABLES_TMPDIR
        assert [ -d "${IPTABLES_TMPDIR}" ]
 
-       table=filter
-
        # Parsing arguments
        while [ $# -gt 0 ]; do
                case "${1}" in
+                       # Select IPv4 protocol.
+                       -4)
+                               proto="4"
+                               shift
+                               ;;
+                       # Select IPv6 protocol.
+                       -6)
+                               proto="6"
+                               shift
+                               ;;
                        -t)
                                table=${2}
                                shift 2
@@ -44,94 +56,313 @@ function iptables() {
                                ;;
                        *)
                                args="${args} ${1}"
+
+                               # Save some values for further processing.
+                               case "${1}" in
+                                       -s)
+                                               src="${2}"
+                                               ;;
+                                       -d)
+                                               dst="${2}"
+                                               ;;
+                               esac
                                shift
                                ;;
                esac
        done
 
-       echo "${args:1:${#args}}" >> ${IPTABLES_TMPDIR}/${table}
+       # Check that the nat table is not used for IPv6.
+       if isoneof 6 ${proto}; then
+               assert [ "${table}" != "nat" ]
+       fi
+
+       # Detect the version of the IP protocol.
+       local src_proto
+       isset src && src_proto=$(ip_detect_protocol ${src})
+
+       local dst_proto
+       isset dst && dst_proto=$(ip_detect_protocol ${dst})
+
+       # Check that the source and destinations are not
+       # using different versions of the IP protocol.
+       if isset src_proto && isset dst_proto; then
+               assert [ "${src_proto}" = "${dst_proto}" ]
+       fi
+
+       local rulesfile
+       local p
+       for p in ${proto}; do
+               case "${p}" in
+                       6)
+                               listmatch ipv4 ${src_proto} ${dst_proto} \
+                                       && continue
+                               ;;
+                       4)
+                               listmatch ipv6 ${src_proto} ${dst_proto} \
+                                       && continue
+                               ;;
+               esac
+
+               rulesfile=$(iptables_rulesfile ipv${p} ${table})
+               assert isset rulesfile
+
+               print "${args:1:${#args}}" >> ${rulesfile}
+       done
+}
+
+# Calls the binary iptables command.
+function _iptables() {
+       local iptables_cmd=$(which iptables)
+       assert isset iptables_cmd
+
+       cmd ${iptables_cmd} $@
+}
+
+function iptables_status() {
+       _iptables -L -n -v
+}
+
+# Returns which tables exist for the given protocol.
+function iptables_tables() {
+       local proto=${1}
+       assert isset proto
+
+       case "${proto}" in
+               ipv6)
+                       print "filter mangle"
+                       ;;
+               ipv4)
+                       print "filter mangle nat"
+                       ;;
+               *)
+                       return ${EXIT_ERROR}
+                       ;;
+       esac
+
+       return ${EXIT_OK}
+}
+
+function iptables_rulesfile() {
+       local proto=${1}
+       proto=${proto/ipv/}
+
+       local chain=${2}
+       [ -z "${chain}" ] && chain="ruleset"
+
+       print "${IPTABLES_TMPDIR}/${chain}${proto}"
 }
 
 function iptables_init() {
        local policy=${1}
-       assert isoneof policy ACCEPT DROP
+       assert isset policy
 
+       # Create filter table and initialize chains.
        iptables "* filter"
-       iptables_chain_create -t filter INPUT       ${policy}
-       iptables_chain_create -t filter OUTPUT      ${policy}
-       iptables_chain_create -t filter FORWARD     ${policy}
+       iptables_chain_create -t filter INPUT   --policy=${policy}
+       iptables_chain_create -t filter OUTPUT  --policy=${policy}
+       iptables_chain_create -t filter FORWARD --policy=${policy}
 
+       # Create mangle table initialize chains.
        iptables -t mangle "* mangle"
-       iptables_chain_create -t mangle PREROUTING  ACCEPT
-       iptables_chain_create -t mangle INPUT       ACCEPT
-       iptables_chain_create -t mangle OUTPUT      ACCEPT
-       iptables_chain_create -t mangle FORWARD     ACCEPT
-       iptables_chain_create -t mangle POSTROUTING ACCEPT
+       iptables_chain_create -t mangle PREROUTING  --policy=ACCEPT
+       iptables_chain_create -t mangle INPUT       --policy=ACCEPT
+       iptables_chain_create -t mangle OUTPUT      --policy=ACCEPT
+       iptables_chain_create -t mangle FORWARD     --policy=ACCEPT
+       iptables_chain_create -t mangle POSTROUTING --policy=ACCEPT
 
-       iptables -t nat "* nat"
-       iptables_chain_create -t nat    PREROUTING  ACCEPT
-       iptables_chain_create -t nat    OUTPUT      ACCEPT
-       iptables_chain_create -t nat    POSTROUTING ACCEPT
+       # Add NAT table for IPv4.
+       iptables -4 -t nat "* nat"
+       iptables_chain_create -4 -t nat PREROUTING  --policy=ACCEPT
+       iptables_chain_create -4 -t nat OUTPUT      --policy=ACCEPT
+       iptables_chain_create -4 -t nat POSTROUTING --policy=ACCEPT
 }
 
-function iptables_commit() {
-       local chain
+# Load the created ruleset into the kernel.
+function iptables_load() {
+       # If first argument is present and true, we
+       # run in test mode.
+       local test="${1}"
 
-       # Check if the directory where we put our rules in is set and
-       # exists.
-       assert isset IPTABLES_TMPDIR
-       assert [ -d "${IPTABLES_TMPDIR}" ]
+       local rulesfile
+
+       # Concat the table rulesets into one big file.
+       local proto
+       for proto in 6 4; do
+               rulesfile=$(iptables_rulesfile ipv${proto})
+               assert isset rulesfile
+
+               local table
+               local tablefile
+               for table in $(iptables_tables ipv${proto}); do
+                       tablefile=$(iptables_rulesfile ipv${proto} ${table})
+
+                       fread ${tablefile}
+
+                       # Add the COMMIT statement for every table.
+                       if [ -s "${tablefile}" ]; then
+                               print "COMMIT"
+                       fi
+               done > ${rulesfile}
+
+               assert [ -s "${rulesfile}" ]
+       done
 
-       log INFO "Committing firewall configuration..."
-       iptables -t filter "COMMIT"
-       iptables -t mangle "COMMIT"
-       iptables -t nat    "COMMIT"
+       local error="false"
+       local ret
 
-       local iptables_ruleset="${IPTABLES_TMPDIR}/commit"
-       : > ${iptables_ruleset}
+       # First check if everything is correctly formatted.
+       for proto in 6 4; do
+               rulesfile=$(iptables_rulesfile ipv${proto})
+               assert isset rulesfile
 
-       # Concat the rules for every chain into one file.
-       local table
-       for table in filter mangle nat; do
-               cat ${IPTABLES_TMPDIR}/${table} \
-                       >> ${iptables_ruleset} 2>/dev/null
+               _iptables_load ipv${proto} ${rulesfile} true
+               if [ $? -ne ${EXIT_OK} ]; then
+                       log CRITICAL "Ruleset load check failed for IPv${proto}"
+                       error="true"
+               fi
        done
 
-       log DEBUG "Dumping iptables ruleset"
-       local counter=1
+       # Check if there has been an error in the load check.
+       if enabled error; then
+               iptables_dump CRITICAL
+
+               log CRITICAL "New firewall rules could not be loaded."
+               return ${EXIT_ERROR}
+       fi
+
+       # Dump the data, we are going to load.
+       iptables_dump
+
+       # If we are running in test mode, we are done here.
+       enabled test && return ${EXIT_OK}
+
+       # If we got until here, everything is fine to load the ruleset.
+       for proto in 6 4; do
+               rulesfile=$(iptables_rulesfile ipv${proto})
+
+               _iptables_load ipv${proto} ${rulesfile}
+       done
+       return ${EXIT_OK}
+}
+
+function _iptables_load() {
+       local proto=${1}
+       local file=${2}
+       local testmode=${3}
+
+       local command
+       case "${proto}" in
+               ipv6)
+                       command="ip6tables-restore"
+                       ;;
+               ipv4)
+                       command="iptables-restore"
+                       ;;
+       esac
+       assert isset command
+
+       if enabled testmode; then
+               command="${command} --test"
+       fi
+
+       local time_started=$(date -u "+%s")
+
+       cmd ${command} < ${file}
+       local ret=$?
+
+       case "${ret}" in
+               ${EXIT_OK})
+                       local time_finished=$(date -u "+%s")
+                       time_finished=$(( ${time_finished} - ${time_started} ))
+                       log INFO \
+                               "Successfully loaded new firewall ruleset for IPv${proto/ipv/} in ${time_finished}s!"
+                       ;;
+               *)
+                       if ! enabled testmode; then
+                               log CRITICAL "Error loading firewall ruleset for IPv${proto/ipv/}!"
+                       fi
+                       ;;
+       esac
+
+       return ${ret}
+}
+
+function iptables_dump() {
+       local log_facility=${1-DEBUG}
+
+       # Here is nothing to do, if we are not running in
+       # debug mode.
+       enabled DEBUG || return ${EXIT_OK}
+
+       local rulesfile
+       local counter
        local line
-       while read line; do
-               line=$(printf "%4d | %s\n" "${counter}" "${line}")
-               log DEBUG "${line}"
 
-               counter=$(( $counter + 1 ))
-       done < ${iptables_ruleset}
-       
-       iptables-restore < ${iptables_ruleset}
+       local proto
+       for proto in 6 4; do
+               rulesfile=$(iptables_rulesfile ipv${proto})
+               [ -e "${rulesfile}" ] || continue
+
+               log ${log_facility} "Firewall ruleset for IPv${proto}:"
+
+               counter=1
+               while read -r line; do
+                       printf -v line "%4d | %s" "${counter}" "${line}"
+                       log ${log_facility} "${line}"
+
+                       counter=$(( $counter + 1 ))
+               done < ${rulesfile}
+       done
 }
 
 function iptables_chain_create() {
+       local chain
+       local table="filter"
+       local policy="-"
+       local proto
        local args
-       if [ "${1}" = "-t" ]; then
-               args="${1} ${2}"
-               shift 2
-       fi
+       while [ $# -gt 0 ]; do
+               case "${1}" in
+                       -6|-4)
+                               proto=${1}
+                               ;;
+                       -t)
+                               table=${2}
+                               shift
+                               ;;
+                       --policy=*)
+                               policy=$(cli_get_val ${1})
+                               ;;
+                       *)
+                               chain=${1}
+                               ;;
+               esac
+               shift
+       done
+
+       assert isset chain
+       assert isset table
+       assert isoneof policy ACCEPT DROP "-"
 
-       iptables ${args} ":$1 ${2--} [0:0]"
+       iptables ${proto} -t ${table} ":${chain} ${policy} [0:0]"
 }
 
 function iptables_LOG() {
        local prefix=${1}
+       local ret
 
-       if [ "${FIREWALL_LOG_FACILITY}" = "syslog" ]; then
-               echo -n "LOG"
-               [ -n "$prefix" ] && echo -n " --log-prefix \"$prefix\""
-       else
-               echo -n "NFLOG"
-               [ -n "$prefix" ] && echo -n " --nflog-prefix \"$prefix\""
-               echo -n " --nflog-threshold 30"
-       fi
-       echo
+       case "${FIREWALL_LOG_METHOD}" in
+               nflog)
+                       ret="NFLOG --nflog-threshold ${FIREWALL_NFLOG_THRESHOLD}"
+                       isset prefix && ret="${ret} --nflog-prefix \"$prefix\""
+                       ;;
+               syslog)
+                       ret="LOG"
+                       isset prefix && ret="${ret} --log-prefix \"$prefix\""
+                       ;;
+       esac
+
+       print "${ret}"
 }
 
 function iptables_protocol() {