#!/usr/bin/env bash if [[ -n ${IPF_RULES_MGR_SH_LOADED:-} ]]; then return 0 fi IPF_RULES_MGR_SH_LOADED=1 rules_new_uuid() { local raw if [[ -r /proc/sys/kernel/random/uuid ]]; then raw=$(tr -d '-' /dev/null 2>&1 || true log_err '写入规则数据库失败。' return 1 fi if ! persist_save; then storage_delete_unlocked "${uuid}" >/dev/null 2>&1 || true ipt_remove_rule "${uuid}" "${proto}" "${lport}" "${tip}" "${tport}" "${ipver}" >/dev/null 2>&1 || true log_err '保存持久化规则失败,已回滚。' return 1 fi log_ok "规则已添加,UUID=${uuid}。" printf '%s\n' "${uuid}" } cmd_add_batch() { storage_with_lock _cmd_add_batch_locked "$@" } cmd_add() { local proto_choice ipver_choice proto ipver lport tip tport desc print_section '添加新的转发规则' prompt_select_capture '请选择协议' 'TCP' 'UDP' 'TCP + UDP' proto_choice=${PROMPT_SELECT_RESULT} case ${proto_choice} in 0) proto=tcp ;; 1) proto=udp ;; 2) proto=both ;; *) return 1 ;; esac while true; do prompt_input_capture '请输入本地监听端口' '' lport=${PROMPT_INPUT_RESULT} validate_port "${lport}" && break log_warn '端口无效,请重新输入。' done prompt_select_capture '请选择 IP 版本' '仅 IPv4' '仅 IPv6' '同时 IPv4 + IPv6' ipver_choice=${PROMPT_SELECT_RESULT} case ${ipver_choice} in 0) ipver=4 ;; 1) ipver=6 ;; 2) ipver=both ;; *) return 1 ;; esac while true; do prompt_input_capture "$(rule_target_hint "${ipver}")" '' tip=${PROMPT_INPUT_RESULT} rule_validate_target "${tip}" "${ipver}" && break log_warn '目标地址无效,请重新输入。' done while true; do prompt_input_capture '请输入目标端口' '' tport=${PROMPT_INPUT_RESULT} validate_port "${tport}" && break log_warn '目标端口无效,请重新输入。' done prompt_input_capture '请输入描述(可留空)' '' desc=${PROMPT_INPUT_RESULT} desc=$(rule_sanitize_desc "${desc}") printf '协议: %s\n' "$(rule_proto_label "${proto}")" printf '监听端口: %s\n' "${lport}" printf '目标地址: %s\n' "$(rule_target_label "${tip}")" printf '目标端口: %s\n' "${tport}" printf 'IP 版本: %s\n' "$(rule_ipver_label "${ipver}")" printf '描述: %s\n' "${desc:-(空)}" if ! prompt_confirm '确认添加该规则?' y; then log_warn '已取消添加。' return 1 fi cmd_add_batch "${proto}" "${lport}" "${tip}" "${tport}" "${ipver}" "${desc}" >/dev/null } _cmd_delete_uuid_locked() { local uuid=${1-} local line proto lport tip tport ipver line=$(storage_get "${uuid}") || { log_err '未找到指定 UUID 的规则。' return 1 } proto=$(rule_field "${line}" proto) lport=$(rule_field "${line}" lport) tip=$(rule_field "${line}" tip) tport=$(rule_field "${line}" tport) ipver=$(rule_field "${line}" ipver) if ! ipt_remove_rule "${uuid}" "${proto}" "${lport}" "${tip}" "${tport}" "${ipver}"; then log_err '删除 iptables 规则失败。' return 1 fi if ! storage_delete_unlocked "${uuid}"; then ipt_apply_rule "${uuid}" "${proto}" "${lport}" "${tip}" "${tport}" "${ipver}" >/dev/null 2>&1 || true log_err '删除规则数据库记录失败,已尝试回滚。' return 1 fi if ! persist_save; then storage_add_unlocked "${line}" >/dev/null 2>&1 || true ipt_apply_rule "${uuid}" "${proto}" "${lport}" "${tip}" "${tport}" "${ipver}" >/dev/null 2>&1 || true log_err '持久化保存失败,已尝试回滚。' return 1 fi log_ok "规则 ${uuid} 已删除。" } cmd_delete_uuid() { storage_with_lock _cmd_delete_uuid_locked "$@" } cmd_delete() { local index answer line uuid rules_load_lines if ((${#RULES_CACHE[@]} == 0)); then log_info '当前没有可删除的规则。' pause_return return 0 fi cmd_list 0 while true; do prompt_input_capture '请输入要删除的规则编号' '' answer=${PROMPT_INPUT_RESULT} if [[ ${answer} =~ ^[0-9]+$ ]] && (( answer >= 1 && answer <= ${#RULES_CACHE[@]} )); then index=$((answer - 1)) break fi log_warn '编号无效,请重新输入。' done line=${RULES_CACHE[index]} uuid=$(rule_field "${line}" uuid) printf '即将删除: %s -> %s:%s (%s / %s)\n' \ "$(rule_field "${line}" lport)" \ "$(rule_target_label "$(rule_field "${line}" tip)")" \ "$(rule_field "${line}" tport)" \ "$(rule_proto_label "$(rule_field "${line}" proto)")" \ "$(rule_ipver_label "$(rule_field "${line}" ipver)")" if ! prompt_confirm '确认删除该规则?' n; then log_warn '已取消删除。' return 1 fi cmd_delete_uuid "${uuid}" } cmd_show_env_status() { print_section '环境状态' env_check_collect_issues env_check_print_report printf '规则总数: %s\n' "$(storage_count)" if persist_available; then printf '持久化: 可用\n' else printf '持久化: 不可用\n' fi pause_return } _cmd_save_rules_locked() { if persist_save; then log_ok '规则已保存到磁盘。' else log_err '规则保存失败。' return 1 fi } cmd_save_rules() { storage_with_lock _cmd_save_rules_locked }