From 4fd0650ed9ef4cec5477038fcfb2e59db2cf2b93 Mon Sep 17 00:00:00 2001 From: Patrick McLean Date: Fri, 8 May 2020 19:21:40 -0700 Subject: modules/iptables.eselect: Complete rewrite, solve issues in bug #721578 Signed-off-by: Patrick McLean --- modules/iptables.eselect | 320 +++++++++++++++++++++++++++++++---------------- 1 file changed, 214 insertions(+), 106 deletions(-) diff --git a/modules/iptables.eselect b/modules/iptables.eselect index f94b25c..e3e5906 100644 --- a/modules/iptables.eselect +++ b/modules/iptables.eselect @@ -2,43 +2,128 @@ # Copyright 2005-2020 Gentoo Authors # Distributed under the terms of the GNU GPL version 2 or later -DESCRIPTION="Manage the iptables and ip6tables symlink" -AUTHOR="chris@christopherpritchard.co.uk" +inherit package-manager + +DESCRIPTION="Manage the iptables/arptables/ebtables symlinks" MAINTAINER="base-system@gentoo.org" -VERSION="20200319" +VERSION="20200508" + +# a simple list of symlinks does for iptables +IPTABLES_SYMLINKS=( + "iptables-xml" + "iptables" "iptables-restore" "iptables-save" +) +IP6TABLES_SYMLINKS=( + "ip6tables" "ip6tables-restore" "ip6tables-save" +) + +# for arptables and ebtables we map names to legacy targets +ARPTABLES_TARGETS=( + "arptables-legacy" + "xtables-nft-multi" +) +declare -A ARPTABLES_SYMLINKS=( + [arptables]="arptables-legacy" + [arptables-restore]="arptables-legacy-restore" + [arptables-save]="arptables-legacy-save" +) + +EBTABLES_TARGETS=( + "ebtables-legacy" + "xtables-nft-multi" +) +declare -A EBTABLES_SYMLINKS=( + [ebtables]="ebtables-legacy" + [ebtables-restore]="ebtables-legacy-restore" + [ebtables-save]="ebtables-legacy-save" +) -IPTABLES_TARGETS=("iptables" "iptables-restore" "iptables-save") -IP6TABLES_TARGETS=("ip6tables" "ip6tables-restore" "ip6tables-save") +# get which module is running +get_module() { + local module + module="${BASH_SOURCE[0]##*/}" -# find a list of xtables symlink targets + printf -- '%s\n' "${module%.eselect}" +} + +# find a list of symlink targets for the current module find_targets() { - local f - for f in "${EROOT}"/sbin/xtables-*-multi; do - [[ -f ${f} ]] && basename "${f}" - done + local module target + + module="$(get_module)" + case "${module}" in + iptables) + for target in "${EROOT}"/sbin/xtables-*-multi; do + [[ -x ${target} ]] && printf -- '%s\n' "${target##*/}" + done + ;; + arptables) + for target in "${ARPTABLES_TARGETS[@]}"; do + [[ -x ${EROOT}/sbin/${target} ]] && printf -- '%s\n' "${target}" + done + ;; + ebtables) + for target in "${EBTABLES_TARGETS[@]}"; do + [[ -x ${EROOT}/sbin/${target} ]] && printf -- '%s\n' "${target}" + done + ;; + *) die "Invalid module name ${module}" + esac } -# remove the iptables symlink -remove_symlinks() { - local ipt - for ipt in "${IPTABLES_TARGETS[@]}"; do - rm -f "${EROOT}/sbin/${ipt}" &>/dev/null - done - if [[ -n ${ipv6} && -n ${ipv6_remove} ]]; then - local ip6t - for ip6t in "${IP6TABLES_TARGETS[@]}"; do - rm -f "${EROOT}/sbin/${ip6t}" &>/dev/null - done - fi +# get the list of symlinks for the current module +get_symlinks_list() { + local module + module="$(get_module)" + + case "${module}" in + iptables) + printf -- '%s\n' "${IPTABLES_SYMLINKS[@]}" + + if [[ ${1} == -a ]] || has_version 'net-firewall/iptables[ipv6]' + then + printf -- '%s\n' "${IP6TABLES_SYMLINKS[@]}" + fi + ;; + arptables) printf -- '%s\n' "${!ARPTABLES_SYMLINKS[@]}";; + ebtables) printf -- '%s\n' "${!EBTABLES_SYMLINKS[@]}";; + *) die "Invalid module name ${module}" + esac +} + +# get the symlink target given a symlink name and the target implementation +get_symlink_target() { + local link="${1}" target="${2}" module + module="$(get_module)" + + case "${module}" in + iptables) printf -- '%s\n' "${target}";; + arptables) + if [[ ${target} == *-legacy ]]; then + printf -- '%s\n' "${ARPTABLES_SYMLINKS[${link}]}" + else + printf -- '%s\n' "${target}" + fi + ;; + ebtables) + if [[ ${target} == *-legacy ]]; then + printf -- '%s\n' "${EBTABLES_SYMLINKS[${link}]}" + else + printf -- '%s\n' "${target}" + fi + ;; + *) die "Invalid module name ${module}" + esac } -# set the iptables symlink +# set the symlinks for the current target set_symlinks() { local target="${1}" + local retval=0 if is_number "${target}" && [[ ${target} -ge 1 ]]; then local -a targets - readarray -t targets <<< "$(find_targets)" + readarray -t targets < <(find_targets) target=${targets[$((target-1))]} fi @@ -46,130 +131,153 @@ set_symlinks() { die -q "Target \"${target}\" doesn't appear to be valid!" fi - local ipt - for ipt in "${IPTABLES_TARGETS[@]}"; do - ln -s "${target}" "${EROOT}/sbin/${ipt}" - done + # create an array of symlinks to be created, then create them + # in a separate pass, it's done this way in an attempt to be atomic + # either all symlinks get updated, or none + local -a symlinks_list + readarray -t symlinks_list < <(get_symlinks_list) + + local symlink + local -A do_symlinks + for symlink in "${symlinks_list[@]}"; do + local symlink_path="${EROOT}/sbin/${symlink}" + + if [[ -L ${symlink_path} || ! -e ${symlink_path} ]]; then + do_symlinks["${symlink_path}"]="$(get_symlink_target "${symlink}" "${target}")" - if [[ -n ${ipv6} ]]; then - local ip6t - for ip6t in "${IP6TABLES_TARGETS[@]}"; do - ln -s "${target}" "${EROOT}/sbin/${ip6t}" - done - fi + else + die -q "Could not create symlink at ${symlink_path}:" \ + "path exits and is not a symlink" + fi + done + + for symlink in "${!do_symlinks[@]}"; do + if ! ln -sfn "${do_symlinks["${symlink}"]}" "${symlink}"; then + write_error_message "Failed to create symlink at ${symlink}" + retval=1 + fi + done + + return "${retval}" } ### show action ### describe_show() { - echo "Show the current iptables symlink" + printf -- 'Show the current %s symlink\n' "$(get_module)" } do_show() { - local ipv6 - if [[ -d ${EROOT}/var/lib/ip6tables ]]; then - ipv6=1 - fi - write_list_start "Current iptables symlinks:" - local ipt all_unset=1 - for ipt in "${IPTABLES_TARGETS[@]}"; do - if [[ -L ${EROOT}/sbin/${ipt} ]]; then - local ipta - ipta=$(canonicalise "${EROOT}/sbin/${ipt}") - write_kv_list_entry "${ipt}" "${ipta%/}" + local -a symlinks_list + readarray -t symlinks_list < <(get_symlinks_list) + + local all_unset=1 symlink + write_list_start "Current $(get_module) symlinks:" + for symlink in "${symlinks_list[@]}"; do + symlink_path="${EROOT}/sbin/${symlink}" + + if [[ -L ${symlink_path} ]]; then + local symlink_target + symlink_target=$(canonicalise "${symlink_path}") + write_kv_list_entry "${symlink}" "${symlink_target%/}" all_unset=0 - else - write_kv_list_entry "${ipt}" "(unset)" + elif [[ ! -f ${symlink_path} ]]; then + write_kv_list_entry "${symlink}" "(unset)" fi done - if [[ ${ipv6} -eq 1 ]]; then - write_list_start "Current ip6tables symlinks:" - local ip6t - for ip6t in "${IP6TABLES_TARGETS[@]}"; do - if [[ -L ${EROOT}/sbin/${ip6t} ]]; then - local ipta - ipta=$(canonicalise "${EROOT}/sbin/${ip6t}") - write_kv_list_entry "${ip6t}" "${ipta%/}" - all_unset=0 - else - write_kv_list_entry "${ip6t}" "(unset)" - fi - done - fi + return "${all_unset}" } ### list action ### describe_list() { - echo "List available iptables symlink targets" + printf -- 'List available %s symlink targets\n' "$(get_module)" } do_list() { - local ipv6 - local -a targets - readarray -t targets <<< "$(find_targets)" - if [[ -L ${EROOT}/var/lib/ip6tables ]]; then - ipv6=1 - fi - write_list_start "Available iptables symlink targets:" - local i - for (( i = 0; i < ${#targets[@]}; i++ )); do - # highlight the target where the symlink is pointing to - [[ ${targets[i]} = \ - $(basename "$(canonicalise "${EROOT}/sbin/iptables")") ]] \ - && targets[i]=$(highlight_marker "${targets[i]}") + local module + module="$(get_module)" + + local -a targets_list symlinks_list + readarray -t targets_list < <(find_targets) + readarray -t symlinks_list < <(get_symlinks_list) + + local -a targets_output + + write_list_start "Available ${module} symlink targets:" + local symlink current_target + for symlink in "${symlinks_list[@]}"; do + local symlink_path="${EROOT}/sbin/${symlink}" + local target + for target in "${targets_list[@]}"; do + local symlink_target resolved_target + symlink_target="$(get_symlink_target "${symlink}" "${target}")" + resolved_target="$(basename "$(canonicalise "${symlink_path}")")" + + if [[ ${resolved_target} == "${symlink_target}" ]]; then + if [[ -z ${current_target} ]]; then + current_target="${target}" + break + elif [[ ${current_target} != "${target}" ]]; then + write_warning_msg "Target mismatch" + unset current_target + break 2 + fi + fi + done + done + for target in "${targets_list[@]}"; do + if [[ ${target} == "${current_target}" ]]; then + targets_output+=("$(highlight_marker "${target}")") + else + targets_output+=("${target}") + fi done - write_numbered_list -m "(none found)" "${targets[@]}" + + write_numbered_list -m "(none found)" "${targets_output[@]}" } ### set action ### describe_set() { - echo "Set a new iptables symlink target" -} - -describe_set_parameters() { - echo "[--ipv6] " + printf "Set a new $(get_module) symlink target\\n" } describe_set_options() { - echo "--ipv6: Forces creation of ip6tables symlinks" - echo "target : Target name or number (from 'list' action)" + printf -- "target : Target name or number (from 'list' action)\\n" } do_set() { - local ipv6 ipv6_remove - if [[ ${1} == "--ipv6" ]]; then - ipv6=1 - shift - fi local target="${1}" [[ -z ${target} ]] && die -q "You didn't tell me what to set the symlink to" - [[ ${#} -gt 2 ]] && die -q "Too many parameters" + [[ ${#} -gt 1 ]] && die -q "Too many parameters" - if [[ -d ${EROOT}/var/lib/ip6tables ]]; then - ipv6=1 - [[ -L ${EROOT}/sbin/ip6tables ]] && ipv6_remove=1 - fi - if [[ -L ${EROOT}/sbin/iptables ]]; then - # existing symlink - remove_symlinks || die -q "Couldn't remove existing symlink" - set_symlinks "${target}" || die -q "Couldn't set a new symlink" - elif [[ -e ${EROOT}/sbin/iptables ]]; then - # we have something strange - die -q "${EROOT}/sbin/iptables exists but is not a symlink" - else - set_symlinks "${target}" || die -q "Couldn't set a new symlink" - fi + set_symlinks "${target}" || die -q "Couldn't set symlinks" } ### unset action ### describe_unset() { - echo "Unset iptables symlink targets" + printf -- 'Unset %s symlink targets\n' "$(get_module)" } do_unset() { - remove_symlinks + local retval=0 + + local -a symlinks_list + readarray -t symlinks_list < <(get_symlinks_list -a) + + local symlink + for symlink in "${symlinks_list[@]}"; do + local symlink_path="${EROOT}/sbin/${symlink}" + if [[ -L ${symlink_path} ]]; then + unlink "${symlink_path}" || retval=${?} + elif [[ -e ${symlink_path} ]]; then + write_error_msg "Not removing non-symlink \"${symlink_path}\"" + retval=1 + fi + done + + return ${retval} } -- cgit v1.2.3-65-gdbad