diff --git a/ipfn1.0 b/ipfn1.0 index 03daa89..f58f9dd 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,8 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.0.0 +# Version: 1.0.4 # Date: 2026-01-22 -# Refactored for nftables compatibility (fixed inet dnat ambiguity) +# Description: Automatic list display after addition with highlighting. # sudo権限チェック if [ "$(id -u)" -ne 0 ]; then @@ -13,13 +13,67 @@ TABLE_NAME="ipf_wrapper" QUIET=false PROTO="tcp" +# --- コンテナ検出 --- +is_container() { + if command -v systemd-detect-virt &> /dev/null; then + if systemd-detect-virt --quiet | grep -qE "container|vm"; then + return 0 + fi + fi + return 1 +} + # --- 初期化 --- init_nft() { nft add table inet ${TABLE_NAME} 2>/dev/null - nft add chain inet ${TABLE_NAME} prerouting { type nat hook prerouting priority dstnat \; } 2>/dev/null - nft add chain inet ${TABLE_NAME} output { type nat hook output priority dstnat \; } 2>/dev/null - nft add chain inet ${TABLE_NAME} postrouting { type nat hook postrouting priority srcnat \; } 2>/dev/null - nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority filter \; } 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 + nft add chain inet ${TABLE_NAME} postrouting { type nat hook postrouting priority 100 \; } 2>/dev/null + nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null +} + +# --- ルール表示 (強調表示対応) --- +# 引数 $1 に UUID を渡すと、その行を強調する +list_rules() { + local highlight_uuid=$1 + init_nft + echo "Forwarding Rules (ipfn):" + printf "%-8s %-6s %-20s %-20s %-10s\n" "HANDLE" "PROTO" "LOCAL" "TARGET" "UUID" + echo "---------------------------------------------------------------------------------" + + nft -a list chain inet ${TABLE_NAME} prerouting | grep "dnat" | while read -r line; do + handle=$(echo "$line" | grep -o 'handle [0-9]*' | awk '{print $2}') + proto=$(echo "$line" | grep -q "udp" && echo "udp" || echo "tcp") + target=$(echo "$line" | grep -oE '[0-9.]*:[0-9]+' | head -n 1) + lport=$(echo "$line" | grep -o 'dport [0-9]*' | awk '{print $2}') + full_uuid=$(echo "$line" | grep -o 'ipf-id:[a-z0-9-]*' | cut -d':' -f2) + short_uuid="${full_uuid:0:8}" + + # 強調判定 + if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then + # シアンの太字で表示 + printf "\e[1;36m%-8s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "$short_uuid" + else + printf "%-8s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "$short_uuid" + fi + done +} + +# --- 疎通確認 --- +test_rule() { + local handle=$1 + local rule_line=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") + if [[ -z "$rule_line" ]]; then + echo "Error: Rule handle $handle not found." + exit 1 + fi + local lport=$(echo "$rule_line" | grep -o 'dport [0-9]*' | awk '{print $2}') + echo "Testing connectivity to local port :$lport (via nc -z)..." + if nc -z -v -w 3 127.0.0.1 "$lport" 2>&1; then + echo -e "\e[32mSuccess: Port :$lport is responding.\e[0m" + else + echo -e "\e[31mFailed: Port :$lport is NOT responding.\e[0m" + fi } # --- ルール追加 --- @@ -30,42 +84,84 @@ add_rule() { local target_ip=$(echo "$port_ip_port" | cut -d':' -f2) local target_port=$(echo "$port_ip_port" | cut -d':' -f3) - # UUID生成 - local uuid="ipf-id:$(cat /proc/sys/kernel/random/uuid)" + local raw_uuid=$(cat /proc/sys/kernel/random/uuid) + local uuid="ipf-id:$raw_uuid" local comment="comment \"$uuid\"" - - # DNATファミリーの特定 (IPv4なら 'ip', IPv6なら 'ip6') local family="ip" - if [[ "$target_ip" =~ : ]]; then family="ip6"; fi + [[ "$target_ip" =~ : ]] && family="ip6" - # 1. PREROUTING (dnat ip to ... または dnat ip6 to ... に修正) - if ! nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$local_port" dnat $family to "$target_ip:$target_port" "$comment"; then - echo "Error: Failed to add PREROUTING rule." + nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$local_port" dnat $family to "$target_ip:$target_port" "$comment" + nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$local_port" dnat $family to "$target_ip:$target_port" "$comment" + nft add rule inet ${TABLE_NAME} forward iifname "lo" accept "$comment" + nft add rule inet ${TABLE_NAME} forward $family daddr "$target_ip" "$PROTO" dport "$target_port" ct state new,established,related accept "$comment" + nft add rule inet ${TABLE_NAME} forward $family saddr "$target_ip" "$PROTO" sport "$target_port" ct state established,related accept "$comment" + nft add rule inet ${TABLE_NAME} postrouting $family daddr "$target_ip" "$PROTO" dport "$target_port" masquerade "$comment" + + if [[ $QUIET == false ]]; then + echo "ipfn: Rule added successfully." + echo "" + list_rules "$raw_uuid" # 追加したルールのUUIDを渡して強調表示 + fi +} + +# --- ルール削除 --- +delete_rule() { + init_nft + local handle=$1 + local rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") + if [[ -z "$rule_info" ]]; then + echo "Error: Rule handle $handle not found." exit 1 fi - # 2. OUTPUT (ローカル発パケット用) - nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$local_port" dnat $family to "$target_ip:$target_port" "$comment" >/dev/null + local uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) + local lport=$(echo "$rule_info" | grep -o 'dport [0-9]*' | awk '{print $2}') + local target=$(echo "$rule_info" | grep -oE '[0-9.]*:[0-9]+' | head -n 1) - # 3. FORWARD - nft add rule inet ${TABLE_NAME} forward $family daddr "$target_ip" "$PROTO" dport "$target_port" ct state new,established,related accept "$comment" >/dev/null - nft add rule inet ${TABLE_NAME} forward $family saddr "$target_ip" "$PROTO" sport "$target_port" ct state established,related accept "$comment" >/dev/null + echo "About to delete the following rule group:" + echo " Handle: $handle" + echo " Local: :$lport" + echo " Target: $target" + echo -n "Are you sure? (y/N): " + read -r confirm - # 4. POSTROUTING (Masquerade) - # コンテナ環境検出関数(is_container)が定義されている前提 - if command -v systemd-detect-virt &>/dev/null && systemd-detect-virt --quiet --container; then - nft add rule inet ${TABLE_NAME} postrouting $family daddr "$target_ip" "$PROTO" dport "$target_port" masquerade "$comment" >/dev/null + if [[ "$confirm" =~ ^[yY]$ ]]; then + for chain in prerouting output forward postrouting; do + nft -a list chain inet ${TABLE_NAME} "$chain" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r h; do + nft delete rule inet ${TABLE_NAME} "$chain" handle "$h" + done + done + echo "ipfn: Rule group deleted." + echo "" + list_rules + else + echo "Aborted." fi - - [[ $QUIET == false ]] && echo "ipfn: Forwarded :$local_port -> $target_ip:$target_port ($PROTO)" } -# (他の関数 list_rules, delete_rule 等は前回の回答を継承) +# --- メイン処理 --- +while [[ $# -gt 0 ]]; do + case "$1" in + -L|-l) list_rules; exit 0 ;; + -d) [[ -z "$2" ]] && { echo "Error: Handle required."; exit 1; } + delete_rule "$2"; exit 0 ;; + -t) test_rule "$2"; exit 0 ;; + -p) PROTO="$2"; shift 2 ;; + -q) QUIET=true; shift ;; + -f) + sysctl -w net.ipv4.ip_forward=1 + sysctl -w net.ipv4.conf.all.route_localnet=1 + sysctl -w net.ipv4.conf.default.route_localnet=1 + echo "Enabled: IP forward and Localnet routing." + exit 0 ;; + -v) nft list table inet ${TABLE_NAME}; exit 0 ;; + -*) echo "Unknown option: $1"; exit 1 ;; + *) break ;; + esac +done -# メイン実行部 -case "$1" in - -L|-l) # リスト表示処理へ ;; - -d) # 削除処理へ ;; - -f) # sysctl設定へ ;; - *) add_rule "$1" ;; -esac +if [[ -n "$1" ]]; then + add_rule "$1" +else + echo "Usage: ipfn [PORT:IP:PORT | -L | -d HANDLE | -t HANDLE]" +fi