Implement iptables forward manager core
This commit is contained in:
236
lib/iptables_ops.sh
Normal file
236
lib/iptables_ops.sh
Normal file
@@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ -n ${IPF_IPTABLES_OPS_SH_LOADED:-} ]]; then
|
||||
return 0
|
||||
fi
|
||||
IPF_IPTABLES_OPS_SH_LOADED=1
|
||||
|
||||
: "${IPTABLES_BIN:=iptables}"
|
||||
: "${IP6TABLES_BIN:=ip6tables}"
|
||||
: "${IPTABLES_SAVE_BIN:=iptables-save}"
|
||||
: "${IP6TABLES_SAVE_BIN:=ip6tables-save}"
|
||||
|
||||
ipt_comment_tag() {
|
||||
printf 'MGMT:%s\n' "$1"
|
||||
}
|
||||
|
||||
ipt_protocols_for() {
|
||||
case ${1-} in
|
||||
tcp|udp) printf '%s\n' "$1" ;;
|
||||
both) printf 'tcp\nudp\n' ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ipt_families_for() {
|
||||
case ${1-} in
|
||||
4|6) printf '%s\n' "$1" ;;
|
||||
both) printf '4\n6\n' ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ipt_bin_for_family() {
|
||||
case ${1-} in
|
||||
4) printf '%s\n' "${IPTABLES_BIN}" ;;
|
||||
6) printf '%s\n' "${IP6TABLES_BIN}" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ipt_save_bin_for_family() {
|
||||
case ${1-} in
|
||||
4) printf '%s\n' "${IPTABLES_SAVE_BIN}" ;;
|
||||
6) printf '%s\n' "${IP6TABLES_SAVE_BIN}" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ipt_target_for_family() {
|
||||
local raw=${1-}
|
||||
local family=${2-}
|
||||
if [[ ${raw} == *,* ]]; then
|
||||
case ${family} in
|
||||
4) printf '%s\n' "${raw%%,*}" ;;
|
||||
6) printf '%s\n' "${raw#*,}" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
return 0
|
||||
fi
|
||||
printf '%s\n' "${raw}"
|
||||
}
|
||||
|
||||
ipt_to_destination() {
|
||||
local tip_raw=${1-}
|
||||
local tport=${2-}
|
||||
local family=${3-}
|
||||
local tip
|
||||
tip=$(ipt_target_for_family "${tip_raw}" "${family}")
|
||||
if [[ ${family} == 6 ]]; then
|
||||
printf '[%s]:%s\n' "${tip}" "${tport}"
|
||||
else
|
||||
printf '%s:%s\n' "${tip}" "${tport}"
|
||||
fi
|
||||
}
|
||||
|
||||
_ipt_exec_rule() {
|
||||
local bin=$1
|
||||
local table=$2
|
||||
local action=$3
|
||||
local chain=$4
|
||||
shift 4
|
||||
if [[ -n ${table} ]]; then
|
||||
"${bin}" -t "${table}" "-${action}" "${chain}" "$@"
|
||||
else
|
||||
"${bin}" "-${action}" "${chain}" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
_ipt_check_rule() {
|
||||
local bin=$1
|
||||
local table=$2
|
||||
local chain=$3
|
||||
shift 3
|
||||
if [[ -n ${table} ]]; then
|
||||
"${bin}" -t "${table}" -C "${chain}" "$@" >/dev/null 2>&1
|
||||
else
|
||||
"${bin}" -C "${chain}" "$@" >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
_ipt_apply_expected_rules() {
|
||||
local action=$1
|
||||
local uuid=$2
|
||||
local proto=$3
|
||||
local lport=$4
|
||||
local tip_raw=$5
|
||||
local tport=$6
|
||||
local ipver=$7
|
||||
local family protocol bin tip comment destination
|
||||
comment=$(ipt_comment_tag "${uuid}")
|
||||
|
||||
while IFS= read -r family; do
|
||||
[[ -n ${family} ]] || continue
|
||||
bin=$(ipt_bin_for_family "${family}")
|
||||
tip=$(ipt_target_for_family "${tip_raw}" "${family}")
|
||||
destination=$(ipt_to_destination "${tip_raw}" "${tport}" "${family}")
|
||||
|
||||
while IFS= read -r protocol; do
|
||||
[[ -n ${protocol} ]] || continue
|
||||
if ! _ipt_exec_rule "${bin}" nat "${action}" PREROUTING \
|
||||
-p "${protocol}" --dport "${lport}" \
|
||||
-j DNAT --to-destination "${destination}" \
|
||||
-m comment --comment "${comment}"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _ipt_exec_rule "${bin}" nat "${action}" POSTROUTING \
|
||||
-p "${protocol}" -d "${tip}" --dport "${tport}" \
|
||||
-j MASQUERADE \
|
||||
-m comment --comment "${comment}"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _ipt_exec_rule "${bin}" '' "${action}" FORWARD \
|
||||
-p "${protocol}" -d "${tip}" --dport "${tport}" \
|
||||
-m conntrack --ctstate NEW,ESTABLISHED,RELATED \
|
||||
-j ACCEPT \
|
||||
-m comment --comment "${comment}"; then
|
||||
return 1
|
||||
fi
|
||||
done < <(ipt_protocols_for "${proto}")
|
||||
done < <(ipt_families_for "${ipver}")
|
||||
}
|
||||
|
||||
ipt_apply_rule() {
|
||||
local uuid=$1 proto=$2 lport=$3 tip_raw=$4 tport=$5 ipver=$6
|
||||
if ! _ipt_apply_expected_rules A "${uuid}" "${proto}" "${lport}" "${tip_raw}" "${tport}" "${ipver}"; then
|
||||
ipt_remove_rule "${uuid}" "${proto}" "${lport}" "${tip_raw}" "${tport}" "${ipver}" >/dev/null 2>&1 || true
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
ipt_remove_rule() {
|
||||
local uuid=$1 proto=$2 lport=$3 tip_raw=$4 tport=$5 ipver=$6
|
||||
local -a families protocols
|
||||
local family protocol bin tip comment destination
|
||||
local missing=0
|
||||
comment=$(ipt_comment_tag "${uuid}")
|
||||
mapfile -t families < <(ipt_families_for "${ipver}")
|
||||
mapfile -t protocols < <(ipt_protocols_for "${proto}")
|
||||
|
||||
for ((family_index=${#families[@]} - 1; family_index >= 0; family_index--)); do
|
||||
family=${families[family_index]}
|
||||
[[ -n ${family} ]] || continue
|
||||
bin=$(ipt_bin_for_family "${family}")
|
||||
tip=$(ipt_target_for_family "${tip_raw}" "${family}")
|
||||
destination=$(ipt_to_destination "${tip_raw}" "${tport}" "${family}")
|
||||
|
||||
for ((proto_index=${#protocols[@]} - 1; proto_index >= 0; proto_index--)); do
|
||||
protocol=${protocols[proto_index]}
|
||||
[[ -n ${protocol} ]] || continue
|
||||
|
||||
if _ipt_check_rule "${bin}" '' FORWARD \
|
||||
-p "${protocol}" -d "${tip}" --dport "${tport}" \
|
||||
-m conntrack --ctstate NEW,ESTABLISHED,RELATED \
|
||||
-j ACCEPT \
|
||||
-m comment --comment "${comment}"; then
|
||||
_ipt_exec_rule "${bin}" '' D FORWARD \
|
||||
-p "${protocol}" -d "${tip}" --dport "${tport}" \
|
||||
-m conntrack --ctstate NEW,ESTABLISHED,RELATED \
|
||||
-j ACCEPT \
|
||||
-m comment --comment "${comment}" || return 1
|
||||
else
|
||||
missing=1
|
||||
fi
|
||||
|
||||
if _ipt_check_rule "${bin}" nat POSTROUTING \
|
||||
-p "${protocol}" -d "${tip}" --dport "${tport}" \
|
||||
-j MASQUERADE \
|
||||
-m comment --comment "${comment}"; then
|
||||
_ipt_exec_rule "${bin}" nat D POSTROUTING \
|
||||
-p "${protocol}" -d "${tip}" --dport "${tport}" \
|
||||
-j MASQUERADE \
|
||||
-m comment --comment "${comment}" || return 1
|
||||
else
|
||||
missing=1
|
||||
fi
|
||||
|
||||
if _ipt_check_rule "${bin}" nat PREROUTING \
|
||||
-p "${protocol}" --dport "${lport}" \
|
||||
-j DNAT --to-destination "${destination}" \
|
||||
-m comment --comment "${comment}"; then
|
||||
_ipt_exec_rule "${bin}" nat D PREROUTING \
|
||||
-p "${protocol}" --dport "${lport}" \
|
||||
-j DNAT --to-destination "${destination}" \
|
||||
-m comment --comment "${comment}" || return 1
|
||||
else
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if (( missing == 1 )); then
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
ipt_find_by_uuid() {
|
||||
local uuid=${1-}
|
||||
local family save_bin found=1 line
|
||||
[[ -n ${uuid} ]] || return 1
|
||||
|
||||
for family in 4 6; do
|
||||
save_bin=$(ipt_save_bin_for_family "${family}")
|
||||
if ! command_is_available "${save_bin}"; then
|
||||
continue
|
||||
fi
|
||||
while IFS= read -r line || [[ -n ${line} ]]; do
|
||||
[[ ${line} == *"MGMT:${uuid}"* ]] || continue
|
||||
printf '%s|%s\n' "${family}" "${line}"
|
||||
found=0
|
||||
done < <("${save_bin}")
|
||||
done
|
||||
|
||||
return "${found}"
|
||||
}
|
||||
Reference in New Issue
Block a user