diff --git a/ipf b/ipf index 38be4cd..58be4ca 100755 --- a/ipf +++ b/ipf @@ -1,47 +1,53 @@ #!/bin/bash # Name: ipf -# Version: 1.4.7 +# Version: 1.5.2 # Date: 2026-01-22 -# Description: Modern port forwarding tool. Replaces complex iptables workflows. +# Description: -f for Kernel Sysctl & Force. Full logic restored. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" fi TABLE_NAME="ipf" -VERSION="1.4.7" +VERSION="1.5.2" PROTO="tcp" -FORCE_FLAG=false +FORCE_FLAG=false SKIP_TEST=false QUIET_MODE=false # --- 1. Functions --- +enable_forwarding() { + msg "\e[34m[System]\e[0m Enabling IP forwarding and localnet routing..." + sysctl -w net.ipv4.ip_forward=1 >/dev/null + for dev in /proc/sys/net/ipv4/conf/*/route_localnet; do + echo 1 > "$dev" 2>/dev/null + done +} + show_help() { echo "ipf version ${VERSION}" - echo "Usage: ipf [OPTIONS] [RULES]" + echo "Usage: ipf [OPTIONS] [RULE]" echo "" echo "Options:" + echo " -f Kernel: Enable forwarding & route_localnet" + echo " Global: Acts as 'Force' for delete/reset" echo " -l, -L List all forwarding rules" - echo " -d HANDLE/:PORT/all Delete by handle, port (e.g. :80), or 'all'" - echo " -R Reset: Clear ALL ipf rules immediately" + echo " -d HANDLE/:PORT/all Delete rule (Add -f to skip confirmation)" + echo " -R Reset: Clear ALL rules (Add -f to skip confirmation)" echo " -t [TARGET] Test connectivity (Handle, :Port, or Addr:Port)" - echo " -f Force: Skip confirmation and testing" - echo " -q Quiet mode: No output (implies -f)" - echo " -h, --help Show this help message" + echo " -y Same as -f (Force/Yes)" + echo " -q Quiet mode (implies -f)" + echo " -v, --version Show version" echo "" - echo "Rules Syntax:" - echo " LPORT:TIP:TPORT Forward LPORT to TIP:TPORT" - echo " LPORT:TPORT Forward LPORT to 127.0.0.1:TPORT" - echo " LPORT Forward LPORT to 127.0.0.1:LPORT" + echo "Quick Start:" + echo " ipf -f 80:10.0.0.1:80 # Setup kernel AND add rule" + echo " ipf -fd all # Force delete everything" } msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } init_nft() { - # 旧 ipf_wrapper があれば削除を試みる(移行用) - nft delete table inet ipf_wrapper 2>/dev/null - nft add table inet "${TABLE_NAME}" 2>/dev/null nft add chain inet "${TABLE_NAME}" prerouting { type nat hook prerouting priority -100 \; } 2>/dev/null nft add chain inet "${TABLE_NAME}" output { type nat hook output priority -100 \; } 2>/dev/null @@ -83,19 +89,12 @@ test_strict_handle() { sleep 0.1 local count_after=$(get_total_packets "$uuid") if (( count_after > count_before )); then - echo -e "\e[32mPASSED (Rule active & Target UP)\e[0m" + echo -e "\e[32mPASSED (Active)\e[0m" else - echo -e "\e[33mSKIPPED (Target UP, but blocked by prior rule)\e[0m" + echo -e "\e[33mSKIPPED (Blocked by prior rule)\e[0m" fi else - nc -z -v -w 1 127.0.0.1 "$lp" >/dev/null 2>&1 - sleep 0.1 - local count_after=$(get_total_packets "$uuid") - if (( count_after > count_before )); then - echo -e "\e[33mOFFLINE (Rule matched, but Target Down)\e[0m" - else - echo -e "\e[31mFAILED (Target Down & Rule blocked)\e[0m" - fi + echo -e "\e[31mOFFLINE (Target Down)\e[0m" fi } @@ -120,6 +119,32 @@ list_rules() { done } +add_rule() { + local raw=$1 + init_nft + [[ "$FORCE_FLAG" == true ]] && enable_forwarding + + if [[ "$raw" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} + elif [[ "$raw" =~ ^([0-9]+):([0-9]+)$ ]]; then lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[2]} + elif [[ "$raw" =~ ^([0-9]+)$ ]]; then lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[1]} + else msg "\e[31mError: Invalid rule format '$raw'\e[0m"; return 1; fi + + u_raw=$(cat /proc/sys/kernel/random/uuid); u="ipf-id:$u_raw"; fam="ip"; [[ "$tip" =~ : ]] && fam="ip6" + + nft insert rule inet "${TABLE_NAME}" prerouting "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" + nft insert rule inet "${TABLE_NAME}" output "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" + nft insert rule inet "${TABLE_NAME}" forward $fam daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$u\"" + nft insert rule inet "${TABLE_NAME}" forward $fam saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$u\"" + nft insert rule inet "${TABLE_NAME}" forward iifname "lo" accept comment "\"$u\"" + nft insert rule inet "${TABLE_NAME}" postrouting $fam daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$u\"" + + list_rules "$u_raw" + if [[ "$SKIP_TEST" == false ]]; then + new_h=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "$u_raw" | grep -o 'handle [0-9]*' | awk '{print $2}') + echo ""; test_strict_handle "$new_h" + fi +} + all_clear() { if [[ "$FORCE_FLAG" == false ]]; then echo -e "\e[33mWarning: This will delete ALL ipf rules.\e[0m" @@ -134,9 +159,10 @@ all_clear() { for arg in "$@"; do [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true - [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true + [[ "$arg" =~ f ]] && FORCE_FLAG=true + [[ "$arg" =~ y ]] && FORCE_FLAG=true [[ "$arg" == "-R" ]] && RESET_MODE=true - [[ "$arg" == "--version" ]] && { echo "ipf version ${VERSION}"; exit 0; } + [[ "$arg" == "-v" || "$arg" == "--version" ]] && { echo "ipf version ${VERSION}"; exit 0; } done if [[ "$RESET_MODE" == true ]]; then all_clear; fi @@ -147,9 +173,12 @@ if [[ $# -eq 0 ]]; then list_rules; exit 0; fi while [[ $# -gt 0 ]]; do case "$1" in -h|--help) show_help; exit 0 ;; - -L|-l) list_rules; exit 0 ;; + -l|-L) list_rules; exit 0 ;; + -f|-y|-q) + [[ "$1" == "-f" ]] && enable_forwarding + shift; [[ $# -eq 0 ]] && exit 0; continue ;; -*[d]*) - h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') + h_str=$(echo "$1" | sed -E 's/^-q?d?f?y?//') [[ -z "$h_str" ]] && { h_str="$2"; shift; } if [[ "$h_str" == "all" || "$h_str" == "*" ]]; then all_clear; fi handles=() @@ -195,25 +224,8 @@ while [[ $# -gt 0 ]]; do fi exit 0 ;; [0-9]*) - init_nft; raw="$1" - if [[ "$raw" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} - elif [[ "$raw" =~ ^([0-9]+):([0-9]+)$ ]]; then lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[2]} - elif [[ "$raw" =~ ^([0-9]+)$ ]]; then lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[1]} - fi - u_raw=$(cat /proc/sys/kernel/random/uuid); u="ipf-id:$u_raw"; fam="ip"; [[ "$tip" =~ : ]] && fam="ip6" - nft insert rule inet "${TABLE_NAME}" prerouting "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" - nft insert rule inet "${TABLE_NAME}" output "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" - nft insert rule inet "${TABLE_NAME}" forward $fam daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$u\"" - nft insert rule inet "${TABLE_NAME}" forward $fam saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$u\"" - nft insert rule inet "${TABLE_NAME}" forward iifname "lo" accept comment "\"$u\"" - nft insert rule inet "${TABLE_NAME}" postrouting $fam daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$u\"" - list_rules "$u_raw" - if [[ "$SKIP_TEST" == false ]]; then - new_h=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "$u_raw" | grep -o 'handle [0-9]*' | awk '{print $2}') - echo ""; test_strict_handle "$new_h" - fi - exit 0 ;; - *) msg "Error: Unknown option '$1'"; exit 1 ;; + add_rule "$1"; exit 0 ;; + *) msg "\e[31mError: Unknown option '$1'\e[0m"; show_help; exit 1 ;; esac shift done