#!/usr/bin/env bash set -euo pipefail : "${IPTABLES_MOCK_DIR:=/tmp/iptables-mock}" : "${IPTABLES_MOCK_LOG:=${IPTABLES_MOCK_DIR}/calls.log}" mkdir -p "${IPTABLES_MOCK_DIR}" touch "${IPTABLES_MOCK_LOG}" base=$(basename -- "$0") case ${base} in iptables|iptables-save) family=4 ;; ip6tables|ip6tables-save) family=6 ;; *) family=${IPTABLES_MOCK_FAMILY:-4} ;; esac state_file="${IPTABLES_MOCK_DIR}/state.v${family}" counter_file="${IPTABLES_MOCK_DIR}/add-counter" touch "${state_file}" "${counter_file}" serialize_args() { local out='' local arg for arg in "$@"; do out+="${arg}"$'\t' done printf '%s' "${out}" } line_key() { local table=$1 local chain=$2 shift 2 printf '%s|%s|%s' "${table}" "${chain}" "$(serialize_args "$@")" } log_call() { printf '%s %s\n' "${base}" "$*" >>"${IPTABLES_MOCK_LOG}" } load_rule_exists() { local key=$1 grep -Fqx -- "${key}" "${state_file}" } save_emit() { local table chain serialized while IFS='|' read -r table chain serialized || [[ -n ${table:-} ]]; do [[ -n ${table:-} ]] || continue IFS=$'\t' read -r -a args <<<"${serialized}" printf -- '-A %s' "${chain}" local arg for arg in "${args[@]}"; do [[ -n ${arg} ]] || continue printf ' %s' "${arg}" done printf '\n' done <"${state_file}" } increment_add_counter() { local count=0 count=$(cat "${counter_file}") count=$((count + 1)) printf '%s\n' "${count}" >"${counter_file}" printf '%s\n' "${count}" } if [[ ${base} == *save ]]; then save_emit exit 0 fi log_call "$@" table='filter' if (($# >= 2)) && [[ $1 == -t ]]; then table=$2 shift 2 fi operation=${1-} chain=${2-} shift 2 || true key=$(line_key "${table}" "${chain}" "$@") case ${operation} in -A) current=$(increment_add_counter) if [[ -n ${IPTABLES_MOCK_FAIL_ON_N:-} && ${current} == "${IPTABLES_MOCK_FAIL_ON_N}" ]]; then exit 1 fi printf '%s\n' "${key}" >>"${state_file}" ;; -D) if ! load_rule_exists "${key}"; then exit 1 fi grep -Fvx -- "${key}" "${state_file}" >"${state_file}.tmp" || true mv "${state_file}.tmp" "${state_file}" ;; -C) load_rule_exists "${key}" ;; *) printf 'unsupported operation: %s\n' "${operation}" >&2 exit 2 ;; esac