#!/usr/bin/env bash set -Eeuo pipefail SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) LIB_DIR=${SCRIPT_DIR}/lib # shellcheck source=lib/common.sh source "${LIB_DIR}/common.sh" on_error() { local exit_code=$1 local line_no=$2 log_err "脚本在第 ${line_no} 行发生错误,退出码 ${exit_code}。" exit "${exit_code}" } trap 'on_error "$?" "$LINENO"' ERR # shellcheck source=lib/storage.sh source "${LIB_DIR}/storage.sh" # shellcheck source=lib/persist.sh source "${LIB_DIR}/persist.sh" # shellcheck source=lib/iptables_ops.sh source "${LIB_DIR}/iptables_ops.sh" # shellcheck source=lib/env_check.sh source "${LIB_DIR}/env_check.sh" # shellcheck source=lib/rules_mgr.sh source "${LIB_DIR}/rules_mgr.sh" : "${IPF_SKIP_ENV_CHECK:=0}" usage() { cat < [desc] ./iptables-forward.sh --batch delete ./iptables-forward.sh --batch list ./iptables-forward.sh --batch save ./iptables-forward.sh --batch env ./iptables-forward.sh --help 说明: - proto: tcp / udp / both - ipver: 4 / 6 / both - 当 ipver=both 时,target_ip 需使用 IPv4,IPv6 形式,例如 127.0.0.1,::1 USAGE } bootstrap() { if [[ ${IPF_SKIP_ENV_CHECK} != 1 ]]; then env_check_all fi storage_init } render_main_menu() { local status persist count width status=$(env_status_summary) count=$(storage_count) if persist_available; then persist='[✓]' else persist='[!]' fi if use_box_ui; then width=$(term_width) if (( width > 78 )); then width=78 fi box_top "${width}" box_line "${width}" "${IPF_APP_NAME}" box_separator "${width}" box_line "${width}" "状态: ${status} 规则数: ${count} 持久化: ${persist}" box_separator "${width}" box_line "${width}" '[1] 查看所有转发规则' box_line "${width}" '[2] 添加新的转发规则' box_line "${width}" '[3] 删除现有转发规则' box_line "${width}" '[4] 查看系统环境状态' box_line "${width}" '[5] 立即保存到磁盘' box_line "${width}" '[0] 退出' box_bottom "${width}" else printf '%s\n' "${IPF_APP_NAME}" printf '状态: %s | 规则数: %s | 持久化: %s\n' "${status}" "${count}" "${persist}" printf '[1] 查看所有转发规则\n' printf '[2] 添加新的转发规则\n' printf '[3] 删除现有转发规则\n' printf '[4] 查看系统环境状态\n' printf '[5] 立即保存到磁盘\n' printf '[0] 退出\n' fi } main_menu_loop() { local choice while true; do render_main_menu choice=$(prompt_input '请选择 [0-5]' '') case ${choice} in 1) cmd_list ;; 2) cmd_add ;; 3) cmd_delete ;; 4) cmd_show_env_status ;; 5) cmd_save_rules pause_return ;; 0) log_info '已退出。' return 0 ;; *) log_warn '无效选择,请输入 0-5。' ;; esac done } run_batch() { local action=${1-} shift || true case ${action} in add) if (($# < 5)); then usage return 1 fi cmd_add_batch "$@" ;; delete) if (($# != 1)); then usage return 1 fi cmd_delete_uuid "$1" ;; list) storage_list ;; save) cmd_save_rules ;; env) env_check_all ;; *) usage return 1 ;; esac } main() { case ${1-} in --help|-h) usage return 0 ;; esac require_root || return $? bootstrap if (($# == 0)); then main_menu_loop return 0 fi case ${1-} in --batch) shift run_batch "$@" ;; *) usage return 1 ;; esac } main "$@"