From d61b305520c84a4711f1c1f18bf3f298f57d61d7 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 01:41:29 +0000 Subject: [PATCH 01/23] ipfn1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ご提示いただいた `iptables` ベースのポートフォワーディング管理スクリプト (`ipf1.0.4`) を、**`nftables` ベースに完全リファクタリング**しました。 ### 主な変更点と設計思想 1. **アーキテクチャの変更**: * `iptables` の連鎖的なルール追加ではなく、`nftables` の専用テーブル (`ipf_wrapper`) を作成し、そこにルールを集約します。これにより、既存のシステム設定(FirewalldやUFWなど)との競合を最小限に抑えます。 2. **管理ID (UUID) の導入**: * `iptables` ではルール番号(行番号)で管理していましたが、`nftables` は「ハンドル」を使用します。 * 1つのポート転送設定で複数のチェーン(PREROUTING, FORWARD, OUTPUTなど)にルールが分散するため、これらを **一意なID(UUID)をコメントとして付与することでグループ化** し、一括削除できるようにしました。 3. **JSON/CLI解析への移行**: * `iptables -L` のテキスト解析は脆弱なため、`nft` コマンドの出力をより堅牢に扱うロジックに変更しています。 --- ### リファクタリング済みコード: `nft-ipf` このスクリプトを `nft-ipf` (あるいは `ipf`) として保存し、実行権限を与えてください。 ```bash #!/bin/bash # Version: 2.0.0 (Refactored for nftables) # Date: 2026-01-22 # Original Author: krasherjoe # Refactored by: Gemini (AI Thought Partner) # Description: Manages port forwarding using nftables with UUID-based rule grouping. # sudo権限チェック if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" fi # グローバル設定 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 if command -v lxc-checkconfig &> /dev/null; then if lxc-checkconfig 2>&1 | grep -q "Running in an LXC container"; then return 0 fi fi return 1 } # バージョン表示 show_version() { cat << EOF nft-ipf ver.2.0.0 Date: 2026-01-22 Based on ipf ver.1.0.4 This tool uses 'nftables' instead of 'iptables'. Rules are managed in a dedicated table named '${TABLE_NAME}'. EOF } # ヘルプ表示 show_help() { cat << EOF Usage: $(basename "$0") [OPTIONS] [PORT:IP:PORT | -L | -d HANDLE] Examples: ipf 11434:10.1.1.2:11434 # Forward local port 11434 to 10.1.1.2:11434 ipf -L # List rules (shows Handles and UUIDs) ipf -d 4 # Delete rule with Handle 4 (deletes related group) ipf -f # Enable IP forwarding (sysctl) ipf -p udp 53:8.8.8.8:53 # Forward UDP Options: -h, --help Show this help -L, -l List rules -d HANDLE Delete rule by Handle ID (see -L) -q Quiet mode -v Show full nftables ruleset -f Enable IP forwarding -p PROTO Specify protocol (tcp|udp, default: tcp) --version Show version EOF } error() { echo -e "\e[31mError: $*\e[0m" >&2 exit 1 } # nftablesの初期化(テーブルが存在しなければ作成) init_nft() { if ! command -v nft &> /dev/null; then error "nftables (nft command) is not installed." fi # テーブルとベースチェーンの作成(べき等性を確保) nft add table inet ${TABLE_NAME} 2>/dev/null # チェーンの作成 (priorityは標準的なfilter/natに合わせて設定) # PREROUTING: DNAT用 nft add chain inet ${TABLE_NAME} prerouting { type nat hook prerouting priority dstnat \; } 2>/dev/null # OUTPUT: ローカルからのアクセス用 (DNAT) nft add chain inet ${TABLE_NAME} output { type nat hook output priority dstnat \; } 2>/dev/null # POSTROUTING: Masquerade用 nft add chain inet ${TABLE_NAME} postrouting { type nat hook postrouting priority srcnat \; } 2>/dev/null # FORWARD: 転送許可 nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority filter \; } 2>/dev/null } # --------------------------------------------------------- # メイン機能 # --------------------------------------------------------- # ルール一覧表示 list_rules() { init_nft echo "Forwarding Rules (Table: ${TABLE_NAME}):" echo "---------------------------------------------------------------------------------" printf "%-8s %-6s %-25s %-25s %-10s\n" "HANDLE" "PROTO" "LOCAL" "TARGET" "UUID" echo "---------------------------------------------------------------------------------" # PREROUTINGチェーンから主要な転送ルールを抽出して表示 # 形式: meta l4proto tcp dnat to 10.0.0.1:80 comment "UUID:..." nft -a list chain inet ${TABLE_NAME} prerouting | grep "dnat to" | 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 -o 'dnat to [0-9.:]*' | awk '{print $3}') # ローカルポート取得 lport=$(echo "$line" | grep -o 'dport [0-9]*' | awk '{print $2}') # UUID取得 (コメントから) uuid=$(echo "$line" | grep -o 'ipf-id:[a-z0-9-]*' | cut -d':' -f2) if [[ -n "$handle" ]]; then printf "%-8s %-6s %-25s %-25s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${uuid:0:8}..." fi done echo "---------------------------------------------------------------------------------" echo "Use 'ipf -d HANDLE' to delete a rule group." } # ルール追加 add_rule() { init_nft local port_ip_port=$1 local local_port=$(echo "$port_ip_port" | cut -d':' -f1) local target_ip=$(echo "$port_ip_port" | cut -d':' -f2) local target_port=$(echo "$port_ip_port" | cut -d':' -f3) # バリデーション if ! [[ "$local_port" =~ ^[0-9]+$ ]] || ! [[ "$target_port" =~ ^[0-9]+$ ]]; then error "Invalid ports." fi if ! [[ "$target_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then error "Invalid IP format." fi # IPフォワード確認 if [[ $(sysctl -n net.ipv4.ip_forward) -ne 1 ]]; then echo -e "\e[33mWarning: IP forwarding is disabled. Run 'ipf -f' to enable.\e[0m" fi # UUID生成 (ルールセットをグループ化するため) local uuid="ipf-id:$(cat /proc/sys/kernel/random/uuid)" local comment="comment \"$uuid\"" # 1. PREROUTING (外部からのDNAT) if ! nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$local_port" dnat to "$target_ip:$target_port" "$comment"; then error "Failed to add PREROUTING rule." fi # 2. OUTPUT (ローカルからのDNAT) nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$local_port" dnat to "$target_ip:$target_port" "$comment" >/dev/null # 3. FORWARD (転送許可) # ct state new,established,related accept nft add rule inet ${TABLE_NAME} forward ip daddr "$target_ip" "$PROTO" dport "$target_port" ct state new,established,related accept "$comment" >/dev/null # 戻りパケット許可 (汎用ルールとして追加してもよいが、ここではグループごとに明示的に許可) nft add rule inet ${TABLE_NAME} forward ip saddr "$target_ip" "$PROTO" sport "$target_port" ct state established,related accept "$comment" >/dev/null # 4. POSTROUTING (Masquerade - コンテナ環境のみ) if is_container; then if ! $QUIET; then echo -e "\e[33m[Container Detected] Enabling Masquerade for this rule.\e[0m" fi nft add rule inet ${TABLE_NAME} postrouting ip daddr "$target_ip" "$PROTO" dport "$target_port" masquerade "$comment" >/dev/null fi if ! $QUIET; then echo "Rule added: Local :$local_port -> $target_ip:$target_port ($PROTO)" fi } # ルール削除 delete_rule() { init_nft local handle=$1 if ! [[ "$handle" =~ ^[0-9]+$ ]]; then error "Invalid handle number: $handle" fi # 指定されたハンドルのルールからUUIDを取得 (PREROUTINGにあると仮定) local rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") local uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*') if [[ -z "$uuid" ]]; then error "Rule handle $handle not found or created by external tool (missing ipf-id)." fi if ! $QUIET; then echo "Deleting rule group with UUID: $uuid ..." fi # そのUUIDを持つすべてのルールをテーブル全体から検索してハンドルのリストを取得 # 注意: ルールを削除するとハンドルが変わる可能性があるため、一括取得して処理には注意が必要だが # nftablesではハンドルは不変。ただし、ループ削除する際は依存関係に注意。 # JSONフォーマットで取得してパースするのが最も確実だが、依存を減らすためgrepで対応 # テーブル全体のルールを出力し、UUIDにマッチする行のハンドルとチェーンを特定して削除 # シンプルなアプローチ: 各チェーンを走査してUUIDにマッチするハンドルを削除 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_del; do nft delete rule inet ${TABLE_NAME} "$chain" handle "$h_del" done done if ! $QUIET; then echo "Rule group deleted." fi } # カーネルパラメータ設定 enable_forwarding() { echo "Enabling IP forwarding..." 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 } # --------------------------------------------------------- # メイン処理 # --------------------------------------------------------- while [[ $# -gt 0 ]]; do case "$1" in -h|--help) show_help exit 0 ;; -L|-l) list_rules exit 0 ;; -v) nft list ruleset exit 0 ;; -f) enable_forwarding exit 0 ;; --version) show_version exit 0 ;; -d) if [[ $# -lt 2 ]]; then error "Missing handle for -d"; fi delete_rule "$2" exit 0 ;; -q) QUIET=true shift ;; -p) if [[ "$2" != "tcp" && "$2" != "udp" ]]; then error "Unsupported protocol"; fi PROTO="$2" shift 2 ;; -*) error "Unknown option: $1" ;; *) break ;; esac done if [[ $# -eq 0 ]]; then show_help exit 1 fi arg=$1 if [[ "$arg" =~ ^[0-9]+:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$ ]]; then add_rule "$arg" else error "Invalid format. Expected: PORT:IP:PORT" fi ``` ### 元のスクリプトからの改善・移行ポイント 1. **依存関係の削減**: * `iptables-save`, `iptables-restore` への依存を排除しました。 * 一時ファイル (`mktemp`) を使用した編集機能(`-e`)は、nftablesの構文エラーリスクが高いため削除し、CLIでの追加/削除に特化させました(安全性重視)。 2. **`iptables` との共存**: * `ipf_wrapper` という独自のテーブル (`inet` ファミリー) を使用するため、システムデフォルトのファイアウォール(iptables-nft や firewalld)と競合しにくくなっています。 3. **削除ロジックの強化**: * **旧**: 行番号で削除。DNATルールを消した後、関連するOUTPUTやFORWARDルールを手探りで検索して削除していました(不確実)。 * **新**: ルール作成時に一意なID (`ipf-id:UUID`) をコメントとして全ルール(PREROUTING, FORWARD, OUTPUT)に埋め込みます。削除時はこのIDを持つルールを一括で特定・削除するため、ゴミが残りません。 4. **編集機能の廃止**: * `-e` オプションは削除しました。nftablesは構造化されているため、テキストエディタでの一括編集よりも、設定ファイル (`/etc/nftables.conf`) での管理またはこのツールでの追加/削除が推奨されるためです。 ### 使い方 1. **スクリプトの保存**: `nft-ipf` という名前などで保存します。 2. **実行権限の付与**: `chmod +x nft-ipf` 3. **ルールの追加**: ```bash # TCP 8080 を 内部の 10.0.0.5:80 に転送 ./nft-ipf 8080:10.0.0.5:80 # UDP 5353 を転送 ./nft-ipf -p udp 5353:10.0.0.5:53 ``` 4. **確認と削除**: ```bash # リスト表示 (HANDLE番号を確認) ./nft-ipf -L # 削除 (HANDLE番号を指定) ./nft-ipf -d 5 ``` もし特定の機能(例:`-e` のテキスト編集機能など)がどうしても必要な場合は、ロジックを追加しますのでお知らせください。 --- ipfn1.0 | 291 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 ipfn1.0 diff --git a/ipfn1.0 b/ipfn1.0 new file mode 100644 index 0000000..f8d4cd4 --- /dev/null +++ b/ipfn1.0 @@ -0,0 +1,291 @@ +#!/bin/bash +# Version: 2.0.0 (Refactored for nftables) +# Date: 2026-01-22 +# Original Author: krasherjoe +# Refactored by: Gemini (AI Thought Partner) +# Description: Manages port forwarding using nftables with UUID-based rule grouping. + +# sudo権限チェック +if [ "$(id -u)" -ne 0 ]; then + exec sudo "$0" "$@" +fi + +# グローバル設定 +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 + if command -v lxc-checkconfig &> /dev/null; then + if lxc-checkconfig 2>&1 | grep -q "Running in an LXC container"; then + return 0 + fi + fi + return 1 +} + +# バージョン表示 +show_version() { + cat << EOF +nft-ipf ver.2.0.0 +Date: 2026-01-22 +Based on ipf ver.1.0.4 + +This tool uses 'nftables' instead of 'iptables'. +Rules are managed in a dedicated table named '${TABLE_NAME}'. +EOF +} + +# ヘルプ表示 +show_help() { + cat << EOF +Usage: $(basename "$0") [OPTIONS] [PORT:IP:PORT | -L | -d HANDLE] + +Examples: + ipf 11434:10.1.1.2:11434 # Forward local port 11434 to 10.1.1.2:11434 + ipf -L # List rules (shows Handles and UUIDs) + ipf -d 4 # Delete rule with Handle 4 (deletes related group) + ipf -f # Enable IP forwarding (sysctl) + ipf -p udp 53:8.8.8.8:53 # Forward UDP + +Options: + -h, --help Show this help + -L, -l List rules + -d HANDLE Delete rule by Handle ID (see -L) + -q Quiet mode + -v Show full nftables ruleset + -f Enable IP forwarding + -p PROTO Specify protocol (tcp|udp, default: tcp) + --version Show version +EOF +} + +error() { + echo -e "\e[31mError: $*\e[0m" >&2 + exit 1 +} + +# nftablesの初期化(テーブルが存在しなければ作成) +init_nft() { + if ! command -v nft &> /dev/null; then + error "nftables (nft command) is not installed." + fi + + # テーブルとベースチェーンの作成(べき等性を確保) + nft add table inet ${TABLE_NAME} 2>/dev/null + + # チェーンの作成 (priorityは標準的なfilter/natに合わせて設定) + # PREROUTING: DNAT用 + nft add chain inet ${TABLE_NAME} prerouting { type nat hook prerouting priority dstnat \; } 2>/dev/null + # OUTPUT: ローカルからのアクセス用 (DNAT) + nft add chain inet ${TABLE_NAME} output { type nat hook output priority dstnat \; } 2>/dev/null + # POSTROUTING: Masquerade用 + nft add chain inet ${TABLE_NAME} postrouting { type nat hook postrouting priority srcnat \; } 2>/dev/null + # FORWARD: 転送許可 + nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority filter \; } 2>/dev/null +} + +# --------------------------------------------------------- +# メイン機能 +# --------------------------------------------------------- + +# ルール一覧表示 +list_rules() { + init_nft + echo "Forwarding Rules (Table: ${TABLE_NAME}):" + echo "---------------------------------------------------------------------------------" + printf "%-8s %-6s %-25s %-25s %-10s\n" "HANDLE" "PROTO" "LOCAL" "TARGET" "UUID" + echo "---------------------------------------------------------------------------------" + + # PREROUTINGチェーンから主要な転送ルールを抽出して表示 + # 形式: meta l4proto tcp dnat to 10.0.0.1:80 comment "UUID:..." + nft -a list chain inet ${TABLE_NAME} prerouting | grep "dnat to" | 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 -o 'dnat to [0-9.:]*' | awk '{print $3}') + # ローカルポート取得 + lport=$(echo "$line" | grep -o 'dport [0-9]*' | awk '{print $2}') + # UUID取得 (コメントから) + uuid=$(echo "$line" | grep -o 'ipf-id:[a-z0-9-]*' | cut -d':' -f2) + + if [[ -n "$handle" ]]; then + printf "%-8s %-6s %-25s %-25s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${uuid:0:8}..." + fi + done + echo "---------------------------------------------------------------------------------" + echo "Use 'ipf -d HANDLE' to delete a rule group." +} + +# ルール追加 +add_rule() { + init_nft + + local port_ip_port=$1 + local local_port=$(echo "$port_ip_port" | cut -d':' -f1) + local target_ip=$(echo "$port_ip_port" | cut -d':' -f2) + local target_port=$(echo "$port_ip_port" | cut -d':' -f3) + + # バリデーション + if ! [[ "$local_port" =~ ^[0-9]+$ ]] || ! [[ "$target_port" =~ ^[0-9]+$ ]]; then + error "Invalid ports." + fi + if ! [[ "$target_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + error "Invalid IP format." + fi + + # IPフォワード確認 + if [[ $(sysctl -n net.ipv4.ip_forward) -ne 1 ]]; then + echo -e "\e[33mWarning: IP forwarding is disabled. Run 'ipf -f' to enable.\e[0m" + fi + + # UUID生成 (ルールセットをグループ化するため) + local uuid="ipf-id:$(cat /proc/sys/kernel/random/uuid)" + local comment="comment \"$uuid\"" + + # 1. PREROUTING (外部からのDNAT) + if ! nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$local_port" dnat to "$target_ip:$target_port" "$comment"; then + error "Failed to add PREROUTING rule." + fi + + # 2. OUTPUT (ローカルからのDNAT) + nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$local_port" dnat to "$target_ip:$target_port" "$comment" >/dev/null + + # 3. FORWARD (転送許可) + # ct state new,established,related accept + nft add rule inet ${TABLE_NAME} forward ip daddr "$target_ip" "$PROTO" dport "$target_port" ct state new,established,related accept "$comment" >/dev/null + # 戻りパケット許可 (汎用ルールとして追加してもよいが、ここではグループごとに明示的に許可) + nft add rule inet ${TABLE_NAME} forward ip saddr "$target_ip" "$PROTO" sport "$target_port" ct state established,related accept "$comment" >/dev/null + + # 4. POSTROUTING (Masquerade - コンテナ環境のみ) + if is_container; then + if ! $QUIET; then + echo -e "\e[33m[Container Detected] Enabling Masquerade for this rule.\e[0m" + fi + nft add rule inet ${TABLE_NAME} postrouting ip daddr "$target_ip" "$PROTO" dport "$target_port" masquerade "$comment" >/dev/null + fi + + if ! $QUIET; then + echo "Rule added: Local :$local_port -> $target_ip:$target_port ($PROTO)" + fi +} + +# ルール削除 +delete_rule() { + init_nft + local handle=$1 + + if ! [[ "$handle" =~ ^[0-9]+$ ]]; then + error "Invalid handle number: $handle" + fi + + # 指定されたハンドルのルールからUUIDを取得 (PREROUTINGにあると仮定) + local rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") + local uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*') + + if [[ -z "$uuid" ]]; then + error "Rule handle $handle not found or created by external tool (missing ipf-id)." + fi + + if ! $QUIET; then + echo "Deleting rule group with UUID: $uuid ..." + fi + + # そのUUIDを持つすべてのルールをテーブル全体から検索してハンドルのリストを取得 + # 注意: ルールを削除するとハンドルが変わる可能性があるため、一括取得して処理には注意が必要だが + # nftablesではハンドルは不変。ただし、ループ削除する際は依存関係に注意。 + + # JSONフォーマットで取得してパースするのが最も確実だが、依存を減らすためgrepで対応 + # テーブル全体のルールを出力し、UUIDにマッチする行のハンドルとチェーンを特定して削除 + + # シンプルなアプローチ: 各チェーンを走査してUUIDにマッチするハンドルを削除 + 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_del; do + nft delete rule inet ${TABLE_NAME} "$chain" handle "$h_del" + done + done + + if ! $QUIET; then + echo "Rule group deleted." + fi +} + +# カーネルパラメータ設定 +enable_forwarding() { + echo "Enabling IP forwarding..." + 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 +} + +# --------------------------------------------------------- +# メイン処理 +# --------------------------------------------------------- +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + show_help + exit 0 + ;; + -L|-l) + list_rules + exit 0 + ;; + -v) + nft list ruleset + exit 0 + ;; + -f) + enable_forwarding + exit 0 + ;; + --version) + show_version + exit 0 + ;; + -d) + if [[ $# -lt 2 ]]; then error "Missing handle for -d"; fi + delete_rule "$2" + exit 0 + ;; + -q) + QUIET=true + shift + ;; + -p) + if [[ "$2" != "tcp" && "$2" != "udp" ]]; then error "Unsupported protocol"; fi + PROTO="$2" + shift 2 + ;; + -*) + error "Unknown option: $1" + ;; + *) + break + ;; + esac +done + +if [[ $# -eq 0 ]]; then + show_help + exit 1 +fi + +arg=$1 +if [[ "$arg" =~ ^[0-9]+:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$ ]]; then + add_rule "$arg" +else + error "Invalid format. Expected: PORT:IP:PORT" +fi \ No newline at end of file From e38fcf5b3a2369e5745f61e6985c50255bc509e2 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 10:50:27 +0900 Subject: [PATCH 02/23] =?UTF-8?q?=E5=88=9D=E6=9C=9F=E3=81=AF=E3=81=93?= =?UTF-8?q?=E3=81=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) mode change 100644 => 100755 ipfn1.0 diff --git a/ipfn1.0 b/ipfn1.0 old mode 100644 new mode 100755 index f8d4cd4..9eeef35 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,5 +1,5 @@ #!/bin/bash -# Version: 2.0.0 (Refactored for nftables) +# Version: 1.0.0 (Refactored for nftables) # Date: 2026-01-22 # Original Author: krasherjoe # Refactored by: Gemini (AI Thought Partner) @@ -37,7 +37,7 @@ is_container() { # バージョン表示 show_version() { cat << EOF -nft-ipf ver.2.0.0 +ipfn ver.1.0.0 Date: 2026-01-22 Based on ipf ver.1.0.4 @@ -52,11 +52,11 @@ show_help() { Usage: $(basename "$0") [OPTIONS] [PORT:IP:PORT | -L | -d HANDLE] Examples: - ipf 11434:10.1.1.2:11434 # Forward local port 11434 to 10.1.1.2:11434 - ipf -L # List rules (shows Handles and UUIDs) - ipf -d 4 # Delete rule with Handle 4 (deletes related group) - ipf -f # Enable IP forwarding (sysctl) - ipf -p udp 53:8.8.8.8:53 # Forward UDP + ipfn 11434:10.1.1.2:11434 # Forward local port 11434 to 10.1.1.2:11434 + ipfn -L # List rules (shows Handles and UUIDs) + ipfn -d 4 # Delete rule with Handle 4 (deletes related group) + ipfn -f # Enable IP forwarding (sysctl) + ipfn -p udp 53:8.8.8.8:53 # Forward UDP Options: -h, --help Show this help @@ -126,7 +126,7 @@ list_rules() { fi done echo "---------------------------------------------------------------------------------" - echo "Use 'ipf -d HANDLE' to delete a rule group." + echo "Use 'ipfn -d HANDLE' to delete a rule group." } # ルール追加 @@ -148,7 +148,7 @@ add_rule() { # IPフォワード確認 if [[ $(sysctl -n net.ipv4.ip_forward) -ne 1 ]]; then - echo -e "\e[33mWarning: IP forwarding is disabled. Run 'ipf -f' to enable.\e[0m" + echo -e "\e[33mWarning: IP forwarding is disabled. Run 'ipfn -f' to enable.\e[0m" fi # UUID生成 (ルールセットをグループ化するため) @@ -288,4 +288,4 @@ if [[ "$arg" =~ ^[0-9]+:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$ ]]; then add_rule "$arg" else error "Invalid format. Expected: PORT:IP:PORT" -fi \ No newline at end of file +fi From 2dcac1f8e474cd7cee76d939a5b863cb81affda7 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 11:00:16 +0900 Subject: [PATCH 03/23] =?UTF-8?q?IPv6=E3=82=A2=E3=83=89=E3=83=AC=E3=82=B9?= =?UTF-8?q?=E3=81=8C=E6=B8=A1=E3=81=95=E3=82=8C=E3=81=9F=E5=A0=B4=E5=90=88?= =?UTF-8?q?=E3=81=AB=E3=82=82=E5=AF=BE=E5=BF=9C=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E5=B0=91=E3=81=97=E6=9F=94=E8=BB=9F=E6=80=A7?= =?UTF-8?q?=E3=82=92=E6=8C=81=E3=81=9F=E3=81=9B=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 284 +++++++------------------------------------------------- 1 file changed, 32 insertions(+), 252 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index 9eeef35..03daa89 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,291 +1,71 @@ #!/bin/bash -# Version: 1.0.0 (Refactored for nftables) +# Name: ipfn +# Version: 1.0.0 # Date: 2026-01-22 -# Original Author: krasherjoe -# Refactored by: Gemini (AI Thought Partner) -# Description: Manages port forwarding using nftables with UUID-based rule grouping. +# Refactored for nftables compatibility (fixed inet dnat ambiguity) # sudo権限チェック if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" fi -# グローバル設定 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 - if command -v lxc-checkconfig &> /dev/null; then - if lxc-checkconfig 2>&1 | grep -q "Running in an LXC container"; then - return 0 - fi - fi - return 1 -} - -# バージョン表示 -show_version() { - cat << EOF -ipfn ver.1.0.0 -Date: 2026-01-22 -Based on ipf ver.1.0.4 - -This tool uses 'nftables' instead of 'iptables'. -Rules are managed in a dedicated table named '${TABLE_NAME}'. -EOF -} - -# ヘルプ表示 -show_help() { - cat << EOF -Usage: $(basename "$0") [OPTIONS] [PORT:IP:PORT | -L | -d HANDLE] - -Examples: - ipfn 11434:10.1.1.2:11434 # Forward local port 11434 to 10.1.1.2:11434 - ipfn -L # List rules (shows Handles and UUIDs) - ipfn -d 4 # Delete rule with Handle 4 (deletes related group) - ipfn -f # Enable IP forwarding (sysctl) - ipfn -p udp 53:8.8.8.8:53 # Forward UDP - -Options: - -h, --help Show this help - -L, -l List rules - -d HANDLE Delete rule by Handle ID (see -L) - -q Quiet mode - -v Show full nftables ruleset - -f Enable IP forwarding - -p PROTO Specify protocol (tcp|udp, default: tcp) - --version Show version -EOF -} - -error() { - echo -e "\e[31mError: $*\e[0m" >&2 - exit 1 -} - -# nftablesの初期化(テーブルが存在しなければ作成) +# --- 初期化 --- init_nft() { - if ! command -v nft &> /dev/null; then - error "nftables (nft command) is not installed." - fi - - # テーブルとベースチェーンの作成(べき等性を確保) nft add table inet ${TABLE_NAME} 2>/dev/null - - # チェーンの作成 (priorityは標準的なfilter/natに合わせて設定) - # PREROUTING: DNAT用 nft add chain inet ${TABLE_NAME} prerouting { type nat hook prerouting priority dstnat \; } 2>/dev/null - # OUTPUT: ローカルからのアクセス用 (DNAT) nft add chain inet ${TABLE_NAME} output { type nat hook output priority dstnat \; } 2>/dev/null - # POSTROUTING: Masquerade用 nft add chain inet ${TABLE_NAME} postrouting { type nat hook postrouting priority srcnat \; } 2>/dev/null - # FORWARD: 転送許可 nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority filter \; } 2>/dev/null } -# --------------------------------------------------------- -# メイン機能 -# --------------------------------------------------------- - -# ルール一覧表示 -list_rules() { - init_nft - echo "Forwarding Rules (Table: ${TABLE_NAME}):" - echo "---------------------------------------------------------------------------------" - printf "%-8s %-6s %-25s %-25s %-10s\n" "HANDLE" "PROTO" "LOCAL" "TARGET" "UUID" - echo "---------------------------------------------------------------------------------" - - # PREROUTINGチェーンから主要な転送ルールを抽出して表示 - # 形式: meta l4proto tcp dnat to 10.0.0.1:80 comment "UUID:..." - nft -a list chain inet ${TABLE_NAME} prerouting | grep "dnat to" | 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 -o 'dnat to [0-9.:]*' | awk '{print $3}') - # ローカルポート取得 - lport=$(echo "$line" | grep -o 'dport [0-9]*' | awk '{print $2}') - # UUID取得 (コメントから) - uuid=$(echo "$line" | grep -o 'ipf-id:[a-z0-9-]*' | cut -d':' -f2) - - if [[ -n "$handle" ]]; then - printf "%-8s %-6s %-25s %-25s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${uuid:0:8}..." - fi - done - echo "---------------------------------------------------------------------------------" - echo "Use 'ipfn -d HANDLE' to delete a rule group." -} - -# ルール追加 +# --- ルール追加 --- add_rule() { init_nft - local port_ip_port=$1 local local_port=$(echo "$port_ip_port" | cut -d':' -f1) local target_ip=$(echo "$port_ip_port" | cut -d':' -f2) local target_port=$(echo "$port_ip_port" | cut -d':' -f3) - # バリデーション - if ! [[ "$local_port" =~ ^[0-9]+$ ]] || ! [[ "$target_port" =~ ^[0-9]+$ ]]; then - error "Invalid ports." - fi - if ! [[ "$target_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - error "Invalid IP format." - fi - - # IPフォワード確認 - if [[ $(sysctl -n net.ipv4.ip_forward) -ne 1 ]]; then - echo -e "\e[33mWarning: IP forwarding is disabled. Run 'ipfn -f' to enable.\e[0m" - fi - - # UUID生成 (ルールセットをグループ化するため) + # UUID生成 local uuid="ipf-id:$(cat /proc/sys/kernel/random/uuid)" local comment="comment \"$uuid\"" - # 1. PREROUTING (外部からのDNAT) - if ! nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$local_port" dnat to "$target_ip:$target_port" "$comment"; then - error "Failed to add PREROUTING rule." + # DNATファミリーの特定 (IPv4なら 'ip', IPv6なら 'ip6') + local family="ip" + if [[ "$target_ip" =~ : ]]; then family="ip6"; fi + + # 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." + exit 1 fi - # 2. OUTPUT (ローカルからのDNAT) - nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$local_port" dnat to "$target_ip:$target_port" "$comment" >/dev/null + # 2. OUTPUT (ローカル発パケット用) + nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$local_port" dnat $family to "$target_ip:$target_port" "$comment" >/dev/null - # 3. FORWARD (転送許可) - # ct state new,established,related accept - nft add rule inet ${TABLE_NAME} forward ip daddr "$target_ip" "$PROTO" dport "$target_port" ct state new,established,related accept "$comment" >/dev/null - # 戻りパケット許可 (汎用ルールとして追加してもよいが、ここではグループごとに明示的に許可) - nft add rule inet ${TABLE_NAME} forward ip saddr "$target_ip" "$PROTO" sport "$target_port" ct state established,related accept "$comment" >/dev/null + # 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 - # 4. POSTROUTING (Masquerade - コンテナ環境のみ) - if is_container; then - if ! $QUIET; then - echo -e "\e[33m[Container Detected] Enabling Masquerade for this rule.\e[0m" - fi - nft add rule inet ${TABLE_NAME} postrouting ip daddr "$target_ip" "$PROTO" dport "$target_port" masquerade "$comment" >/dev/null + # 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 fi - if ! $QUIET; then - echo "Rule added: Local :$local_port -> $target_ip:$target_port ($PROTO)" - fi + [[ $QUIET == false ]] && echo "ipfn: Forwarded :$local_port -> $target_ip:$target_port ($PROTO)" } -# ルール削除 -delete_rule() { - init_nft - local handle=$1 +# (他の関数 list_rules, delete_rule 等は前回の回答を継承) - if ! [[ "$handle" =~ ^[0-9]+$ ]]; then - error "Invalid handle number: $handle" - fi - - # 指定されたハンドルのルールからUUIDを取得 (PREROUTINGにあると仮定) - local rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") - local uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*') - - if [[ -z "$uuid" ]]; then - error "Rule handle $handle not found or created by external tool (missing ipf-id)." - fi - - if ! $QUIET; then - echo "Deleting rule group with UUID: $uuid ..." - fi - - # そのUUIDを持つすべてのルールをテーブル全体から検索してハンドルのリストを取得 - # 注意: ルールを削除するとハンドルが変わる可能性があるため、一括取得して処理には注意が必要だが - # nftablesではハンドルは不変。ただし、ループ削除する際は依存関係に注意。 - - # JSONフォーマットで取得してパースするのが最も確実だが、依存を減らすためgrepで対応 - # テーブル全体のルールを出力し、UUIDにマッチする行のハンドルとチェーンを特定して削除 - - # シンプルなアプローチ: 各チェーンを走査してUUIDにマッチするハンドルを削除 - 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_del; do - nft delete rule inet ${TABLE_NAME} "$chain" handle "$h_del" - done - done - - if ! $QUIET; then - echo "Rule group deleted." - fi -} - -# カーネルパラメータ設定 -enable_forwarding() { - echo "Enabling IP forwarding..." - 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 -} - -# --------------------------------------------------------- -# メイン処理 -# --------------------------------------------------------- -while [[ $# -gt 0 ]]; do - case "$1" in - -h|--help) - show_help - exit 0 - ;; - -L|-l) - list_rules - exit 0 - ;; - -v) - nft list ruleset - exit 0 - ;; - -f) - enable_forwarding - exit 0 - ;; - --version) - show_version - exit 0 - ;; - -d) - if [[ $# -lt 2 ]]; then error "Missing handle for -d"; fi - delete_rule "$2" - exit 0 - ;; - -q) - QUIET=true - shift - ;; - -p) - if [[ "$2" != "tcp" && "$2" != "udp" ]]; then error "Unsupported protocol"; fi - PROTO="$2" - shift 2 - ;; - -*) - error "Unknown option: $1" - ;; - *) - break - ;; - esac -done - -if [[ $# -eq 0 ]]; then - show_help - exit 1 -fi - -arg=$1 -if [[ "$arg" =~ ^[0-9]+:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$ ]]; then - add_rule "$arg" -else - error "Invalid format. Expected: PORT:IP:PORT" -fi +# メイン実行部 +case "$1" in + -L|-l) # リスト表示処理へ ;; + -d) # 削除処理へ ;; + -f) # sysctl設定へ ;; + *) add_rule "$1" ;; +esac From 534c488783fe57951de33dbedce0befd7767c7ec Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 11:21:18 +0900 Subject: [PATCH 04/23] add highlight --- ipfn1.0 | 162 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 129 insertions(+), 33 deletions(-) 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 From 1c6b087b0be92396e6608eaaa3a9e890f61aa080 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 11:30:55 +0900 Subject: [PATCH 05/23] -d --- ipfn1.0 | 118 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 61 insertions(+), 57 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index f58f9dd..29c2f45 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,8 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.0.4 +# Version: 1.0.5 # Date: 2026-01-22 -# Description: Automatic list display after addition with highlighting. +# Description: Added asterisk highlight for new handles and forced delete (-df). # sudo権限チェック if [ "$(id -u)" -ne 0 ]; then @@ -12,16 +12,7 @@ fi 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 -} +FORCE_DELETE=false # --- 初期化 --- init_nft() { @@ -32,14 +23,13 @@ init_nft() { 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 "---------------------------------------------------------------------------------" + printf "%-10s %-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}') @@ -49,33 +39,15 @@ list_rules() { 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" + # 新規ルールは HANDLE の前に * を付け、シアン太字で表示 + printf "\e[1;36m*%-9s %-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" + printf "%-10s %-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 -} - # --- ルール追加 --- add_rule() { init_nft @@ -98,13 +70,13 @@ add_rule() { 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 "ipfn: Rule added." echo "" - list_rules "$raw_uuid" # 追加したルールのUUIDを渡して強調表示 + list_rules "$raw_uuid" fi } -# --- ルール削除 --- +# --- ルール削除 (強制削除フラグ対応) --- delete_rule() { init_nft local handle=$1 @@ -118,20 +90,26 @@ delete_rule() { 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) - 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 + local do_delete=false + if [[ "$FORCE_DELETE" == true ]]; then + do_delete=true + else + echo "About to delete rule group:" + echo " Handle: $handle" + echo " Local: :$lport" + echo " Target: $target" + echo -n "Are you sure? (y/N): " + read -r confirm + [[ "$confirm" =~ ^[yY]$ ]] && do_delete=true + fi - if [[ "$confirm" =~ ^[yY]$ ]]; then + if [[ "$do_delete" == true ]]; 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 "ipfn: Rule handle $handle deleted." echo "" list_rules else @@ -140,28 +118,54 @@ delete_rule() { } # --- メイン処理 --- +# 引数の前処理 (強制削除フラグの検出) +TEMP_ARGS=() +while [[ $# -gt 0 ]]; do + case "$1" in + -df) FORCE_DELETE=true; TEMP_ARGS+=("-d") ;; + -f) # 削除コンテキストでのみ強制フラグとして動作 + FORCE_DELETE=true ;; + *) TEMP_ARGS+=("$1") ;; + esac + shift +done +set -- "${TEMP_ARGS[@]}" + 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 ;; + -t) # 疎通確認ロジック (ver 1.0.4と同様) + local handle=$2 + local rule_line=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") + if [[ -z "$rule_line" ]]; then echo "Error: Handle $handle not found."; exit 1; fi + local lp=$(echo "$rule_line" | grep -o 'dport [0-9]*' | awk '{print $2}') + echo "Testing :$lp..." + nc -z -v -w 3 127.0.0.1 "$lp" 2>&1 | grep -E "succeeded|connected" && echo -e "\e[32mSuccess\e[0m" || echo -e "\e[31mFailed\e[0m" + 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 ;; + -f_sys) # 内部用: 以前の -f (sysctl) は -f_sys またはメインループ外で処理 + sysctl -w net.ipv4.ip_forward=1 >/dev/null + sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null + echo "Kernel parameters updated."; exit 0 ;; -v) nft list table inet ${TABLE_NAME}; exit 0 ;; - -*) echo "Unknown option: $1"; exit 1 ;; *) break ;; esac + shift done +# 初期設定 (-f 単体で実行された場合などの互換性維持) +if [[ "$FORCE_DELETE" == true && $# -eq 0 ]]; then + # -f 単体ならカーネル設定を行う + sysctl -w net.ipv4.ip_forward=1 + sysctl -w net.ipv4.conf.all.route_localnet=1 + exit 0 +fi + if [[ -n "$1" ]]; then add_rule "$1" else - echo "Usage: ipfn [PORT:IP:PORT | -L | -d HANDLE | -t HANDLE]" + echo "Usage: ipfn [PORT:IP:PORT | -L | -d HANDLE | -df HANDLE | -t HANDLE]" fi From 8aeb72fefd592c8bd81609ecb73418cd961d06be Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 11:41:20 +0900 Subject: [PATCH 06/23] =?UTF-8?q?=E3=81=BB=E3=81=BC=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E3=81=8B=E3=81=AA=EF=BC=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 123 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index 29c2f45..feb7c81 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,8 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.0.5 +# Version: 1.0.6 # Date: 2026-01-22 -# Description: Added asterisk highlight for new handles and forced delete (-df). +# Description: Supports -dHANDLE / -dfHANDLE (no space) and asterisk highlighting. # sudo権限チェック if [ "$(id -u)" -ne 0 ]; then @@ -23,7 +23,7 @@ init_nft() { nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null } -# --- ルール表示 (アスタリスク強調対応) --- +# --- ルール表示 (アスタリスク & ハイライト対応) --- list_rules() { local highlight_uuid=$1 init_nft @@ -48,6 +48,24 @@ list_rules() { done } +# --- 疎通確認 --- +test_rule() { + local handle=$1 + init_nft + 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 lp=$(echo "$rule_line" | grep -o 'dport [0-9]*' | awk '{print $2}') + echo "Testing connectivity to local port :$lp..." + if nc -z -v -w 3 127.0.0.1 "$lp" 2>&1; then + echo -e "\e[32mSuccess: Port is responding.\e[0m" + else + echo -e "\e[31mFailed: Port is NOT responding.\e[0m" + fi +} + # --- ルール追加 --- add_rule() { init_nft @@ -69,14 +87,12 @@ add_rule() { 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." - echo "" - list_rules "$raw_uuid" - fi + echo "ipfn: Rule added." + echo "" + list_rules "$raw_uuid" } -# --- ルール削除 (強制削除フラグ対応) --- +# --- ルール削除 --- delete_rule() { init_nft local handle=$1 @@ -109,7 +125,7 @@ delete_rule() { nft delete rule inet ${TABLE_NAME} "$chain" handle "$h" done done - echo "ipfn: Rule handle $handle deleted." + echo "ipfn: Rule group deleted." echo "" list_rules else @@ -118,54 +134,53 @@ delete_rule() { } # --- メイン処理 --- -# 引数の前処理 (強制削除フラグの検出) -TEMP_ARGS=() -while [[ $# -gt 0 ]]; do - case "$1" in - -df) FORCE_DELETE=true; TEMP_ARGS+=("-d") ;; - -f) # 削除コンテキストでのみ強制フラグとして動作 - FORCE_DELETE=true ;; - *) TEMP_ARGS+=("$1") ;; - esac - shift -done -set -- "${TEMP_ARGS[@]}" - 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) # 疎通確認ロジック (ver 1.0.4と同様) - local handle=$2 - local rule_line=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") - if [[ -z "$rule_line" ]]; then echo "Error: Handle $handle not found."; exit 1; fi - local lp=$(echo "$rule_line" | grep -o 'dport [0-9]*' | awk '{print $2}') - echo "Testing :$lp..." - nc -z -v -w 3 127.0.0.1 "$lp" 2>&1 | grep -E "succeeded|connected" && echo -e "\e[32mSuccess\e[0m" || echo -e "\e[31mFailed\e[0m" - exit 0 ;; - -p) PROTO="$2"; shift 2 ;; - -q) QUIET=true; shift ;; - -f_sys) # 内部用: 以前の -f (sysctl) は -f_sys またはメインループ外で処理 - sysctl -w net.ipv4.ip_forward=1 >/dev/null - sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null - echo "Kernel parameters updated."; exit 0 ;; - -v) nft list table inet ${TABLE_NAME}; exit 0 ;; - *) break ;; + + # 強制削除 (-df または -df17) + -df*) + FORCE_DELETE=true + handle="${1#-df}" + if [[ -z "$handle" ]]; then handle="$2"; shift; fi + delete_rule "$handle" + exit 0 ;; + + # 通常削除 (-d または -d17) + -d*) + handle="${1#-d}" + if [[ -z "$handle" ]]; then handle="$2"; shift; fi + delete_rule "$handle" + exit 0 ;; + + # テスト (-t または -t17) + -t*) + handle="${1#-t}" + if [[ -z "$handle" ]]; then handle="$2"; shift; fi + test_rule "$handle" + exit 0 ;; + + -p) PROTO="$2"; shift 2 ;; + -q) QUIET=true; shift ;; + -f) + # 削除フラグとして機能させるための先読み + FORCE_DELETE=true + if [[ $# -eq 1 ]]; then + sysctl -w net.ipv4.ip_forward=1 + sysctl -w net.ipv4.conf.all.route_localnet=1 + echo "Kernel parameters updated." + exit 0 + fi + ;; + -v) nft list table inet ${TABLE_NAME}; exit 0 ;; + *) + if [[ "$1" =~ ^[0-9]+: ]]; then + add_rule "$1" + exit 0 + fi + ;; esac shift done -# 初期設定 (-f 単体で実行された場合などの互換性維持) -if [[ "$FORCE_DELETE" == true && $# -eq 0 ]]; then - # -f 単体ならカーネル設定を行う - sysctl -w net.ipv4.ip_forward=1 - sysctl -w net.ipv4.conf.all.route_localnet=1 - exit 0 -fi - -if [[ -n "$1" ]]; then - add_rule "$1" -else - echo "Usage: ipfn [PORT:IP:PORT | -L | -d HANDLE | -df HANDLE | -t HANDLE]" -fi +echo "Usage: ipfn [PORT:IP:PORT | -L | -d(HANDLE) | -df(HANDLE) | -t(HANDLE) | -f]" From cf88bb30fde5bc8a8f29d7feaaf317eb3fc3111d Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 11:45:51 +0900 Subject: [PATCH 07/23] =?UTF-8?q?=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E7=84=A1=E3=81=97=E3=81=A7=E3=83=AA=E3=82=B9=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 148 ++++++++++++++++++++++++-------------------------------- 1 file changed, 62 insertions(+), 86 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index feb7c81..1edd03d 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,8 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.0.6 +# Version: 1.0.7 # Date: 2026-01-22 -# Description: Supports -dHANDLE / -dfHANDLE (no space) and asterisk highlighting. +# Description: Default to list, auto-test on add, skip test with -f, and port collision warning. # sudo権限チェック if [ "$(id -u)" -ne 0 ]; then @@ -10,9 +10,9 @@ if [ "$(id -u)" -ne 0 ]; then fi TABLE_NAME="ipf_wrapper" -QUIET=false PROTO="tcp" -FORCE_DELETE=false +FORCE_FLAG=false +SKIP_TEST=false # --- 初期化 --- init_nft() { @@ -23,7 +23,7 @@ init_nft() { nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null } -# --- ルール表示 (アスタリスク & ハイライト対応) --- +# --- ルール表示 (アスタリスク強調) --- list_rules() { local highlight_uuid=$1 init_nft @@ -37,32 +37,23 @@ list_rules() { 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 - # 新規ルールは HANDLE の前に * を付け、シアン太字で表示 - printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "$short_uuid" + printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" else - printf "%-10s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "$short_uuid" + printf "%-10s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" fi done } # --- 疎通確認 --- -test_rule() { - local handle=$1 - init_nft - 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 lp=$(echo "$rule_line" | grep -o 'dport [0-9]*' | awk '{print $2}') - echo "Testing connectivity to local port :$lp..." - if nc -z -v -w 3 127.0.0.1 "$lp" 2>&1; then - echo -e "\e[32mSuccess: Port is responding.\e[0m" +test_rule_by_port() { + local lp=$1 + echo -n "Testing connectivity to :$lp... " + if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -E "succeeded|connected|open" >/dev/null; then + echo -e "\e[32mOK\e[0m" else - echo -e "\e[31mFailed: Port is NOT responding.\e[0m" + echo -e "\e[31mFAILED\e[0m" fi } @@ -74,6 +65,11 @@ add_rule() { local target_ip=$(echo "$port_ip_port" | cut -d':' -f2) local target_port=$(echo "$port_ip_port" | cut -d':' -f3) + # 重複チェック (警告のみ) + if nft list chain inet ${TABLE_NAME} prerouting | grep -q "dport $local_port"; then + echo -e "\e[33mWarning: Port :$local_port is already in use by another rule.\e[0m" + fi + local raw_uuid=$(cat /proc/sys/kernel/random/uuid) local uuid="ipf-id:$raw_uuid" local comment="comment \"$uuid\"" @@ -90,6 +86,12 @@ add_rule() { echo "ipfn: Rule added." echo "" list_rules "$raw_uuid" + + # 自動テスト + if [[ "$SKIP_TEST" == false ]]; then + echo "" + test_rule_by_port "$local_port" + fi } # --- ルール削除 --- @@ -97,90 +99,64 @@ 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 + [[ -z "$rule_info" ]] && { echo "Error: Handle $handle not found."; exit 1; } 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) + local lp=$(echo "$rule_info" | grep -o 'dport [0-9]*' | awk '{print $2}') + local tg=$(echo "$rule_info" | grep -oE '[0-9.]*:[0-9]+' | head -n 1) - local do_delete=false - if [[ "$FORCE_DELETE" == true ]]; then - do_delete=true - else - echo "About to delete rule group:" - echo " Handle: $handle" - echo " Local: :$lport" - echo " Target: $target" + if [[ "$FORCE_FLAG" == false ]]; then + echo "Delete Handle $handle (:$lp -> $tg)?" echo -n "Are you sure? (y/N): " read -r confirm - [[ "$confirm" =~ ^[yY]$ ]] && do_delete=true + [[ ! "$confirm" =~ ^[yY]$ ]] && { echo "Aborted."; exit 0; } fi - if [[ "$do_delete" == true ]]; 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 + 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 - echo "ipfn: Rule group deleted." - echo "" - list_rules - else - echo "Aborted." - fi + done + echo "ipfn: Deleted."; echo ""; list_rules } -# --- メイン処理 --- +# --- メインパース --- +# 先にフラグをスキャン +for arg in "$@"; do + [[ "$arg" == "-f" ]] && FORCE_FLAG=true && SKIP_TEST=true +done + +if [[ $# -eq 0 ]]; then + list_rules + exit 0 +fi + while [[ $# -gt 0 ]]; do case "$1" in -L|-l) list_rules; exit 0 ;; - - # 強制削除 (-df または -df17) - -df*) - FORCE_DELETE=true - handle="${1#-df}" - if [[ -z "$handle" ]]; then handle="$2"; shift; fi - delete_rule "$handle" - exit 0 ;; - - # 通常削除 (-d または -d17) - -d*) - handle="${1#-d}" - if [[ -z "$handle" ]]; then handle="$2"; shift; fi - delete_rule "$handle" - exit 0 ;; - - # テスト (-t または -t17) + -df*|-d*) + [[ "$1" == -df* ]] && FORCE_FLAG=true + h="${1#-df}"; h="${h#-d}" + [[ -z "$h" ]] && { h="$2"; shift; } + delete_rule "$h"; exit 0 ;; -t*) - handle="${1#-t}" - if [[ -z "$handle" ]]; then handle="$2"; shift; fi - test_rule "$handle" - exit 0 ;; - - -p) PROTO="$2"; shift 2 ;; - -q) QUIET=true; shift ;; - -f) - # 削除フラグとして機能させるための先読み - FORCE_DELETE=true + h="${1#-t}" + [[ -z "$h" ]] && { h="$2"; shift; } + # ハンドルからポートを引いてテスト + lp=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | grep -o 'dport [0-9]*' | awk '{print $2}') + test_rule_by_port "$lp"; exit 0 ;; + -f) # 単体実行時はカーネル設定 if [[ $# -eq 1 ]]; then - sysctl -w net.ipv4.ip_forward=1 - sysctl -w net.ipv4.conf.all.route_localnet=1 + sysctl -w net.ipv4.ip_forward=1 >/dev/null + sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null echo "Kernel parameters updated." exit 0 - fi - ;; - -v) nft list table inet ${TABLE_NAME}; exit 0 ;; + fi ;; *) if [[ "$1" =~ ^[0-9]+: ]]; then add_rule "$1" exit 0 - fi - ;; + fi ;; esac shift done - -echo "Usage: ipfn [PORT:IP:PORT | -L | -d(HANDLE) | -df(HANDLE) | -t(HANDLE) | -f]" From ee122e4597e9f47902b163b8660ba15081e156b6 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 11:46:54 +0900 Subject: [PATCH 08/23] =?UTF-8?q?=E5=89=8A=E9=99=A4=E3=81=AB=E7=AF=84?= =?UTF-8?q?=E5=9B=B2=E3=82=92=E4=BD=BF=E7=94=A8=E5=8F=AF=E8=83=BD=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 163 ++++++++++++++++++++++++-------------------------------- 1 file changed, 70 insertions(+), 93 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index 1edd03d..4633835 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,10 +1,9 @@ #!/bin/bash # Name: ipfn -# Version: 1.0.7 +# Version: 1.0.9 # Date: 2026-01-22 -# Description: Default to list, auto-test on add, skip test with -f, and port collision warning. +# Description: Supports range deletion (e.g., -d 80-85) and complex handle lists. -# sudo権限チェック if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" fi @@ -14,7 +13,6 @@ PROTO="tcp" FORCE_FLAG=false SKIP_TEST=false -# --- 初期化 --- init_nft() { 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 @@ -23,21 +21,18 @@ init_nft() { nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null } -# --- ルール表示 (アスタリスク強調) --- list_rules() { local highlight_uuid=$1 init_nft echo "Forwarding Rules (ipfn):" printf "%-10s %-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) - if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" else @@ -46,7 +41,40 @@ list_rules() { done } -# --- 疎通確認 --- +perform_delete() { + local handle=$1 + local rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") + [[ -z "$rule_info" ]] && return # 存在しないハンドルは静かにスルー + local uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) + 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: Handle $handle deleted." +} + +# --- メインパース部分の強化 --- +parse_handles() { + local input=$1 + local -a result=() + # カンマで分割 + IFS=',' read -r -a parts <<< "$input" + for part in "${parts[@]}"; do + if [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then + # 範囲指定 (start-end) の処理 + start=${BASH_REMATCH[1]} + end=${BASH_REMATCH[2]} + for ((i=start; i<=end; i++)); do result+=("$i"); done + else + # 単一指定 + result+=("$part") + fi + done + echo "${result[@]}" +} + +# (add_rule, test_rule_by_port は前バージョンと同じ) test_rule_by_port() { local lp=$1 echo -n "Testing connectivity to :$lp... " @@ -57,106 +85,55 @@ test_rule_by_port() { fi } -# --- ルール追加 --- add_rule() { init_nft local port_ip_port=$1 - local local_port=$(echo "$port_ip_port" | cut -d':' -f1) - local target_ip=$(echo "$port_ip_port" | cut -d':' -f2) - local target_port=$(echo "$port_ip_port" | cut -d':' -f3) - - # 重複チェック (警告のみ) - if nft list chain inet ${TABLE_NAME} prerouting | grep -q "dport $local_port"; then - echo -e "\e[33mWarning: Port :$local_port is already in use by another rule.\e[0m" - fi - + local lp=$(echo "$port_ip_port" | cut -d':' -f1) + local tip=$(echo "$port_ip_port" | cut -d':' -f2) + local tp=$(echo "$port_ip_port" | cut -d':' -f3) + [[ -n "$lp" ]] && nft list chain inet ${TABLE_NAME} prerouting | grep -q "dport $lp" && echo -e "\e[33mWarning: Port :$lp is already in use.\e[0m" local raw_uuid=$(cat /proc/sys/kernel/random/uuid) local uuid="ipf-id:$raw_uuid" local comment="comment \"$uuid\"" - local family="ip" - [[ "$target_ip" =~ : ]] && family="ip6" - - 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" + local family="ip"; [[ "$tip" =~ : ]] && family="ip6" + nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" "$comment" + nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" "$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" - - echo "ipfn: Rule added." - echo "" - list_rules "$raw_uuid" - - # 自動テスト - if [[ "$SKIP_TEST" == false ]]; then - echo "" - test_rule_by_port "$local_port" - fi + nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept "$comment" + nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept "$comment" + nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade "$comment" + echo "ipfn: Rule added."; echo ""; list_rules "$raw_uuid" + [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } } -# --- ルール削除 --- -delete_rule() { - init_nft - local handle=$1 - local rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") - [[ -z "$rule_info" ]] && { echo "Error: Handle $handle not found."; exit 1; } - - local uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) - local lp=$(echo "$rule_info" | grep -o 'dport [0-9]*' | awk '{print $2}') - local tg=$(echo "$rule_info" | grep -oE '[0-9.]*:[0-9]+' | head -n 1) - - if [[ "$FORCE_FLAG" == false ]]; then - echo "Delete Handle $handle (:$lp -> $tg)?" - echo -n "Are you sure? (y/N): " - read -r confirm - [[ ! "$confirm" =~ ^[yY]$ ]] && { echo "Aborted."; exit 0; } - fi - - 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: Deleted."; echo ""; list_rules -} - -# --- メインパース --- -# 先にフラグをスキャン -for arg in "$@"; do - [[ "$arg" == "-f" ]] && FORCE_FLAG=true && SKIP_TEST=true -done - -if [[ $# -eq 0 ]]; then - list_rules - exit 0 -fi +for arg in "$@"; do [[ "$arg" == "-f" ]] && FORCE_FLAG=true && SKIP_TEST=true; done +if [[ $# -eq 0 ]]; then list_rules; exit 0; fi while [[ $# -gt 0 ]]; do case "$1" in -L|-l) list_rules; exit 0 ;; -df*|-d*) [[ "$1" == -df* ]] && FORCE_FLAG=true - h="${1#-df}"; h="${h#-d}" - [[ -z "$h" ]] && { h="$2"; shift; } - delete_rule "$h"; exit 0 ;; - -t*) - h="${1#-t}" - [[ -z "$h" ]] && { h="$2"; shift; } - # ハンドルからポートを引いてテスト - lp=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | grep -o 'dport [0-9]*' | awk '{print $2}') - test_rule_by_port "$lp"; exit 0 ;; - -f) # 単体実行時はカーネル設定 - if [[ $# -eq 1 ]]; then - sysctl -w net.ipv4.ip_forward=1 >/dev/null - sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null - echo "Kernel parameters updated." - exit 0 - fi ;; - *) - if [[ "$1" =~ ^[0-9]+: ]]; then - add_rule "$1" - exit 0 - fi ;; + raw_h="${1#-df}"; raw_h="${raw_h#-d}" + [[ -z "$raw_h" ]] && { raw_h="$2"; shift; } + + # 範囲指定を含むハンドルリストを解析 + handles=($(parse_handles "$raw_h")) + + if [[ "$FORCE_FLAG" == false ]]; then + echo "Review handles to delete:" + for h in "${handles[@]}"; do + rule=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") + [[ -n "$rule" ]] && echo " $rule" + done + echo -n "Delete these rules? (y/N): " + read -r confirm + [[ ! "$confirm" =~ ^[yY]$ ]] && { echo "Aborted."; exit 0; } + fi + for h in "${handles[@]}"; do perform_delete "$h"; done + echo ""; list_rules; exit 0 ;; + -f) [[ $# -eq 1 ]] && { sysctl -w net.ipv4.ip_forward=1 >/dev/null; sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null; echo "Kernel parameters updated."; exit 0; } ;; + *) [[ "$1" =~ ^[0-9]+: ]] && { add_rule "$1"; exit 0; } ;; esac shift done From 526f037bba9c450dfe3997a1d86013f9f5452f88 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 11:51:01 +0900 Subject: [PATCH 09/23] =?UTF-8?q?=E3=82=B9=E3=82=A4=E3=83=83=E3=83=81BUG?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20=E4=BF=AE=E6=AD=A3=E5=BE=8C=E3=81=AE?= =?UTF-8?q?=E6=8C=99=E5=8B=95=20=20=20=20=20-t=20=E3=81=AE=E9=80=B2?= =?UTF-8?q?=E5=8C=96:=20=20=20=20=20=20=20=20=20=E3=83=AB=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=81=8C1=E3=81=A4=E3=81=AE=E5=A0=B4=E5=90=88:=20./ip?= =?UTF-8?q?fn=20-t=20=E3=81=A0=E3=81=91=E3=81=A7=E3=81=9D=E3=81=AE?= =?UTF-8?q?=E3=83=AB=E3=83=BC=E3=83=AB=E3=81=AE=E7=96=8E=E9=80=9A=E7=A2=BA?= =?UTF-8?q?=E8=AA=8D=E3=82=92=E3=81=97=E3=81=BE=E3=81=99=E3=80=82=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=E3=82=B9=E3=83=9A=E3=83=BC=E3=82=B9=E3=81=AA?= =?UTF-8?q?=E3=81=97:=20./ipfn=20-t17=20=E3=81=8C=E5=8B=95=E3=81=8D?= =?UTF-8?q?=E3=81=BE=E3=81=99=E3=80=82=20=20=20=20=20=E3=82=B9=E3=82=A4?= =?UTF-8?q?=E3=83=83=E3=83=81=E7=84=A1=E8=A6=96=E3=81=AE=E8=A7=A3=E6=B6=88?= =?UTF-8?q?:=20=20=20=20=20=20=20=20=20while=20=E3=83=AB=E3=83=BC=E3=83=97?= =?UTF-8?q?=E5=86=85=E3=81=AE=20shift=20=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?=E8=A6=8B=E7=9B=B4=E3=81=97=E3=80=81-f=20=E3=82=92=E5=90=AB?= =?UTF-8?q?=E3=82=81=E3=81=9F=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=81=8C=E7=A2=BA=E5=AE=9F=E3=81=AB=E8=A9=95=E4=BE=A1=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=BE?= =?UTF-8?q?=E3=81=97=E3=81=9F=E3=80=82=20=20=20=20=20=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E6=99=82=E3=81=AE=20-f:=20=20=20=20=20=20=20=20=20./ipfn=2080:?= =?UTF-8?q?10.0.0.1:80=20-f=20=E3=81=A8=E3=81=99=E3=82=8B=E3=81=A8?= =?UTF-8?q?=E3=80=81=E7=96=8E=E9=80=9A=E7=A2=BA=E8=AA=8D=E3=82=92=E3=82=B9?= =?UTF-8?q?=E3=82=AD=E3=83=83=E3=83=97=E3=81=97=E3=81=A6=E5=8D=B3=E5=BA=A7?= =?UTF-8?q?=E3=81=AB=E7=B5=82=E4=BA=86=E3=81=97=E3=81=BE=E3=81=99=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 146 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 79 insertions(+), 67 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index 4633835..b536d74 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,8 +1,7 @@ #!/bin/bash # Name: ipfn -# Version: 1.0.9 +# Version: 1.1.0 # Date: 2026-01-22 -# Description: Supports range deletion (e.g., -d 80-85) and complex handle lists. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" @@ -41,99 +40,112 @@ list_rules() { done } -perform_delete() { - local handle=$1 - local rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle") - [[ -z "$rule_info" ]] && return # 存在しないハンドルは静かにスルー - local uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) - 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: Handle $handle deleted." -} - -# --- メインパース部分の強化 --- -parse_handles() { - local input=$1 - local -a result=() - # カンマで分割 - IFS=',' read -r -a parts <<< "$input" - for part in "${parts[@]}"; do - if [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then - # 範囲指定 (start-end) の処理 - start=${BASH_REMATCH[1]} - end=${BASH_REMATCH[2]} - for ((i=start; i<=end; i++)); do result+=("$i"); done - else - # 単一指定 - result+=("$part") - fi - done - echo "${result[@]}" -} - -# (add_rule, test_rule_by_port は前バージョンと同じ) test_rule_by_port() { local lp=$1 echo -n "Testing connectivity to :$lp... " - if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -E "succeeded|connected|open" >/dev/null; then + if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -iqE "succeeded|connected|open"; then echo -e "\e[32mOK\e[0m" else echo -e "\e[31mFAILED\e[0m" fi } +get_port_by_handle() { + local h=$1 + nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | grep -o 'dport [0-9]*' | awk '{print $2}' +} + +parse_handles() { + local input=$1 + local -a result=() + IFS=',' read -r -a parts <<< "$input" + for part in "${parts[@]}"; do + if [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then + for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do result+=("$i"); done + else + result+=("$part") + fi + done + echo "${result[@]}" +} + add_rule() { init_nft - local port_ip_port=$1 - local lp=$(echo "$port_ip_port" | cut -d':' -f1) - local tip=$(echo "$port_ip_port" | cut -d':' -f2) - local tp=$(echo "$port_ip_port" | cut -d':' -f3) - [[ -n "$lp" ]] && nft list chain inet ${TABLE_NAME} prerouting | grep -q "dport $lp" && echo -e "\e[33mWarning: Port :$lp is already in use.\e[0m" + local lp=$(echo "$1" | cut -d':' -f1) + local tip=$(echo "$1" | cut -d':' -f2) + local tp=$(echo "$1" | cut -d':' -f3) + nft list chain inet ${TABLE_NAME} prerouting | grep -q "dport $lp" && echo -e "\e[33mWarning: Port :$lp is already in use.\e[0m" local raw_uuid=$(cat /proc/sys/kernel/random/uuid) local uuid="ipf-id:$raw_uuid" - local comment="comment \"$uuid\"" local family="ip"; [[ "$tip" =~ : ]] && family="ip6" - nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" "$comment" - nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" "$comment" - nft add rule inet ${TABLE_NAME} forward iifname "lo" accept "$comment" - nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept "$comment" - nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept "$comment" - nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade "$comment" - echo "ipfn: Rule added."; echo ""; list_rules "$raw_uuid" + nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" + list_rules "$raw_uuid" [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } } -for arg in "$@"; do [[ "$arg" == "-f" ]] && FORCE_FLAG=true && SKIP_TEST=true; done +# --- メインロジック --- if [[ $# -eq 0 ]]; then list_rules; exit 0; fi +# フラグ先読み +for arg in "$@"; do + [[ "$arg" == "-f" ]] && FORCE_FLAG=true && SKIP_TEST=true +done + while [[ $# -gt 0 ]]; do case "$1" in -L|-l) list_rules; exit 0 ;; -df*|-d*) [[ "$1" == -df* ]] && FORCE_FLAG=true - raw_h="${1#-df}"; raw_h="${raw_h#-d}" - [[ -z "$raw_h" ]] && { raw_h="$2"; shift; } - - # 範囲指定を含むハンドルリストを解析 - handles=($(parse_handles "$raw_h")) - + h_str="${1#-df}"; h_str="${h_str#-d}" + [[ -z "$h_str" ]] && { h_str="$2"; shift; } + handles=($(parse_handles "$h_str")) if [[ "$FORCE_FLAG" == false ]]; then echo "Review handles to delete:" - for h in "${handles[@]}"; do - rule=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") - [[ -n "$rule" ]] && echo " $rule" - done - echo -n "Delete these rules? (y/N): " - read -r confirm - [[ ! "$confirm" =~ ^[yY]$ ]] && { echo "Aborted."; exit 0; } + for h in "${handles[@]}"; do nft -a list table inet ${TABLE_NAME} | grep "handle $h" | sed 's/^/ /'; done + read -p "Delete these rules? (y/N): " confirm + [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 fi - for h in "${handles[@]}"; do perform_delete "$h"; done + for h in "${handles[@]}"; do + rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") + [[ -z "$rule_info" ]] && continue + uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) + for c in prerouting output forward postrouting; do + nft -a list chain inet ${TABLE_NAME} "$c" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do + nft delete rule inet ${TABLE_NAME} "$c" handle "$rh" + done + done + echo "Handle $h deleted." + done echo ""; list_rules; exit 0 ;; - -f) [[ $# -eq 1 ]] && { sysctl -w net.ipv4.ip_forward=1 >/dev/null; sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null; echo "Kernel parameters updated."; exit 0; } ;; - *) [[ "$1" =~ ^[0-9]+: ]] && { add_rule "$1"; exit 0; } ;; + -t*) + h_str="${1#-t}" + if [[ -z "$h_str" && ( "$2" =~ ^[0-9,-]+$ ) ]]; then h_str="$2"; shift; fi + + if [[ -z "$h_str" ]]; then + # ハンドル指定がない場合、ルールが1つならそれを採用 + count=$(nft list chain inet ${TABLE_NAME} prerouting | grep -c "dnat") + if [ "$count" -eq 1 ]; then + h_str=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle" | awk '{print $NF}') + else + echo "Error: Multiple rules exist. Specify a handle."; exit 1 + fi + fi + lp=$(get_port_by_handle "$h_str") + [[ -n "$lp" ]] && test_rule_by_port "$lp" || echo "Error: Handle $h_str not found." + exit 0 ;; + -f) + if [[ $# -eq 1 ]]; then + sysctl -w net.ipv4.ip_forward=1 >/dev/null + sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null + echo "Kernel parameters updated."; exit 0 + fi ;; + *) + if [[ "$1" =~ ^[0-9]+: ]]; then add_rule "$1"; exit 0; fi ;; esac shift done From a1abd44d491487c689c06071bb25c195fdb2e963 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 12:02:08 +0900 Subject: [PATCH 10/23] =?UTF-8?q?=E3=83=9D=E3=83=BC=E3=83=88=E7=95=AA?= =?UTF-8?q?=E5=8F=B7=E3=81=A7=E6=B6=88=E3=81=99:=20./ipfn=20-d=20:80=20=20?= =?UTF-8?q?=20=20=20=E8=A4=87=E6=95=B0=E3=81=AE=E3=83=9D=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=82=92=E4=B8=80=E6=B0=97=E3=81=AB=E6=B6=88=E3=81=99:=20./ipf?= =?UTF-8?q?n=20-d=20:80,:443,:8080=20=20=20=20=20=E3=83=8F=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=83=AB=E3=81=A8=E3=83=9D=E3=83=BC=E3=83=88=E3=82=92?= =?UTF-8?q?=E6=B7=B7=E3=81=9C=E3=82=8B:=20./ipfn=20-d=2017,:8080,20-22=20?= =?UTF-8?q?=20=20=20=20=E7=84=A1=E8=A8=80=E3=81=A7=E3=83=9D=E3=83=BC?= =?UTF-8?q?=E3=83=88=E5=89=8A=E9=99=A4:=20./ipfn=20-qd=20:80=20=20=20=20?= =?UTF-8?q?=20=E3=83=9D=E3=83=BC=E3=83=88=E7=95=AA=E5=8F=B7=E3=81=AE?= =?UTF-8?q?=E9=A0=AD=E3=81=AB=20:=20(=E3=82=B3=E3=83=AD=E3=83=B3)=20?= =?UTF-8?q?=E3=82=92=E4=BB=98=E3=81=91=E3=82=8B=E3=81=A0=E3=81=91=E3=81=A7?= =?UTF-8?q?=E3=80=81=E3=82=B9=E3=82=AF=E3=83=AA=E3=83=97=E3=83=88=E3=81=8C?= =?UTF-8?q?=E8=A3=8F=E5=81=B4=E3=81=A7=E3=80=8C=E3=83=8F=E3=83=B3=E3=83=89?= =?UTF-8?q?=E3=83=AB=E7=95=AA=E5=8F=B7=E3=81=B8=E3=81=AE=E5=A4=89=E6=8F=9B?= =?UTF-8?q?=E3=80=8D=E3=82=92=E8=A1=8C=E3=81=A3=E3=81=A6=E3=81=8B=E3=82=89?= =?UTF-8?q?=E5=89=8A=E9=99=A4=E3=82=92=E5=AE=9F=E8=A1=8C=E3=81=97=E3=81=BE?= =?UTF-8?q?=E3=81=99=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 157 ++++++++++++++++++++------------------------------------ 1 file changed, 57 insertions(+), 100 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index b536d74..6225433 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,7 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.1.0 +# Version: 1.1.5 # Date: 2026-01-22 +# Description: Supports deleting by port (e.g., -d :80). if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" @@ -11,6 +12,9 @@ TABLE_NAME="ipf_wrapper" PROTO="tcp" FORCE_FLAG=false SKIP_TEST=false +QUIET_MODE=false + +msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } init_nft() { nft add table inet ${TABLE_NAME} 2>/dev/null @@ -20,132 +24,85 @@ init_nft() { nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null } -list_rules() { - local highlight_uuid=$1 - init_nft - echo "Forwarding Rules (ipfn):" - printf "%-10s %-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) - if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then - printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" - else - printf "%-10s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" - fi - done -} - -test_rule_by_port() { - local lp=$1 - echo -n "Testing connectivity to :$lp... " - if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -iqE "succeeded|connected|open"; then - echo -e "\e[32mOK\e[0m" - else - echo -e "\e[31mFAILED\e[0m" - fi -} - -get_port_by_handle() { - local h=$1 - nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | grep -o 'dport [0-9]*' | awk '{print $2}' -} - -parse_handles() { +# --- 削除解析の心臓部 --- +parse_delete_targets() { local input=$1 - local -a result=() + local -a final_handles=() IFS=',' read -r -a parts <<< "$input" for part in "${parts[@]}"; do - if [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then - for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do result+=("$i"); done + if [[ "$part" =~ ^:([0-9]+)$ ]]; then + # ポート指定 (:80) の場合、ハンドルを逆引き + local target_port="${BASH_REMATCH[1]}" + local found_h=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') + if [[ -n "$found_h" ]]; then + for h in $found_h; do final_handles+=("$h"); done + else + msg "Warning: No rules found for port :$target_port" + fi + elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then + # 範囲指定 + for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do final_handles+=("$i"); done else - result+=("$part") + # 単一ハンドル + final_handles+=("$part") fi done - echo "${result[@]}" + echo "${final_handles[@]}" } -add_rule() { - init_nft - local lp=$(echo "$1" | cut -d':' -f1) - local tip=$(echo "$1" | cut -d':' -f2) - local tp=$(echo "$1" | cut -d':' -f3) - nft list chain inet ${TABLE_NAME} prerouting | grep -q "dport $lp" && echo -e "\e[33mWarning: Port :$lp is already in use.\e[0m" - local raw_uuid=$(cat /proc/sys/kernel/random/uuid) - local uuid="ipf-id:$raw_uuid" - local family="ip"; [[ "$tip" =~ : ]] && family="ip6" - nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" - list_rules "$raw_uuid" - [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } +# (中略: show_help, list_rules, test_rule_by_port, add_rule は ver.1.1.4 と同様) + +show_help() { + echo "Usage: ipfn [OPTIONS] [PORT:TARGET_IP:TARGET_PORT]" + echo "" + echo "Options:" + echo " -d HANDLE/PORT Delete by handle(17) or port(:80). Supports list(17,:80) or range(10-15)" + echo " -df HANDLE/PORT Force delete" + echo " -qd HANDLE/PORT Quiet delete" + echo " -l List rules" + echo " -t [HANDLE] Test connectivity" + echo " -f Skip test / Enable IP forward" + echo " -v Verbose (raw nftables)" + echo " -h Help" } -# --- メインロジック --- -if [[ $# -eq 0 ]]; then list_rules; exit 0; fi - -# フラグ先読み -for arg in "$@"; do - [[ "$arg" == "-f" ]] && FORCE_FLAG=true && SKIP_TEST=true -done - +# --- 以下、削除ロジックの統合 --- while [[ $# -gt 0 ]]; do case "$1" in + -h|--help) show_help; exit 0 ;; + -v|--verbose) nft list table inet ${TABLE_NAME}; exit 0 ;; -L|-l) list_rules; exit 0 ;; - -df*|-d*) - [[ "$1" == -df* ]] && FORCE_FLAG=true - h_str="${1#-df}"; h_str="${h_str#-d}" + -*[qd]*) + h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') [[ -z "$h_str" ]] && { h_str="$2"; shift; } - handles=($(parse_handles "$h_str")) + + # ポートとハンドルを統合して解析 + handles=($(parse_delete_targets "$h_str")) + [[ ${#handles[@]} -eq 0 ]] && { msg "No handles to delete."; exit 1; } + if [[ "$FORCE_FLAG" == false ]]; then - echo "Review handles to delete:" + msg "Review rules to delete:" for h in "${handles[@]}"; do nft -a list table inet ${TABLE_NAME} | grep "handle $h" | sed 's/^/ /'; done read -p "Delete these rules? (y/N): " confirm [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 fi + for h in "${handles[@]}"; do - rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") - [[ -z "$rule_info" ]] && continue - uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) + ri=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") + [[ -z "$ri" ]] && continue + uuid=$(echo "$ri" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) for c in prerouting output forward postrouting; do nft -a list chain inet ${TABLE_NAME} "$c" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do nft delete rule inet ${TABLE_NAME} "$c" handle "$rh" done done - echo "Handle $h deleted." + msg "Deleted rule associated with handle $h" done - echo ""; list_rules; exit 0 ;; - -t*) - h_str="${1#-t}" - if [[ -z "$h_str" && ( "$2" =~ ^[0-9,-]+$ ) ]]; then h_str="$2"; shift; fi - - if [[ -z "$h_str" ]]; then - # ハンドル指定がない場合、ルールが1つならそれを採用 - count=$(nft list chain inet ${TABLE_NAME} prerouting | grep -c "dnat") - if [ "$count" -eq 1 ]; then - h_str=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle" | awk '{print $NF}') - else - echo "Error: Multiple rules exist. Specify a handle."; exit 1 - fi - fi - lp=$(get_port_by_handle "$h_str") - [[ -n "$lp" ]] && test_rule_by_port "$lp" || echo "Error: Handle $h_str not found." - exit 0 ;; - -f) - if [[ $# -eq 1 ]]; then - sysctl -w net.ipv4.ip_forward=1 >/dev/null - sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null - echo "Kernel parameters updated."; exit 0 - fi ;; + list_rules; exit 0 ;; + # (中略: -t, -f, add_rule の case 処理は ver.1.1.4 と同じ) *) - if [[ "$1" =~ ^[0-9]+: ]]; then add_rule "$1"; exit 0; fi ;; + if [[ "$1" =~ ^[0-9]+: ]]; then add_rule "$1"; exit 0 + else msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1; fi ;; esac shift done From df59b6f1c4ea1b92b1918bd190709e6ac7092185 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 12:05:14 +0900 Subject: [PATCH 11/23] =?UTF-8?q?=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E6=8C=87=E5=AE=9ABUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 69 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index 6225433..bdab95a 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,8 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.1.5 +# Version: 1.1.6 # Date: 2026-01-22 -# Description: Supports deleting by port (e.g., -d :80). +# Description: Fixed argument parsing order and combined flags. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" @@ -24,14 +24,13 @@ init_nft() { nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null } -# --- 削除解析の心臓部 --- +# (parse_delete_targets, list_rules, test_rule_by_port, add_rule などは 1.1.5 と同様) parse_delete_targets() { local input=$1 local -a final_handles=() IFS=',' read -r -a parts <<< "$input" for part in "${parts[@]}"; do if [[ "$part" =~ ^:([0-9]+)$ ]]; then - # ポート指定 (:80) の場合、ハンドルを逆引き local target_port="${BASH_REMATCH[1]}" local found_h=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') if [[ -n "$found_h" ]]; then @@ -40,53 +39,65 @@ parse_delete_targets() { msg "Warning: No rules found for port :$target_port" fi elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then - # 範囲指定 for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do final_handles+=("$i"); done else - # 単一ハンドル final_handles+=("$part") fi done echo "${final_handles[@]}" } -# (中略: show_help, list_rules, test_rule_by_port, add_rule は ver.1.1.4 と同様) +# --- メインパース (順序を修正) --- -show_help() { - echo "Usage: ipfn [OPTIONS] [PORT:TARGET_IP:TARGET_PORT]" - echo "" - echo "Options:" - echo " -d HANDLE/PORT Delete by handle(17) or port(:80). Supports list(17,:80) or range(10-15)" - echo " -df HANDLE/PORT Force delete" - echo " -qd HANDLE/PORT Quiet delete" - echo " -l List rules" - echo " -t [HANDLE] Test connectivity" - echo " -f Skip test / Enable IP forward" - echo " -v Verbose (raw nftables)" - echo " -h Help" -} +# 先読みフラグ +for arg in "$@"; do + [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true + [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true +done + +if [[ $# -eq 0 ]]; then list_rules; exit 0; fi -# --- 以下、削除ロジックの統合 --- while [[ $# -gt 0 ]]; do case "$1" in -h|--help) show_help; exit 0 ;; -v|--verbose) nft list table inet ${TABLE_NAME}; exit 0 ;; -L|-l) list_rules; exit 0 ;; - -*[qd]*) + + # 先に単独の -f や -t を処理する + -f) + if [[ $# -eq 1 ]]; then + sysctl -w net.ipv4.ip_forward=1 >/dev/null + sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null + msg "Kernel parameters updated."; exit 0 + fi ;; + -t*) + h_str="${1#-t}" + if [[ -z "$h_str" && ( "$2" =~ ^[0-9,-]+$ ) ]]; then h_str="$2"; shift; fi + if [[ -z "$h_str" ]]; then + count=$(nft list chain inet ${TABLE_NAME} prerouting | grep -c "dnat") + if [ "$count" -eq 1 ]; then + h_str=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle" | awk '{print $NF}') + else + msg "Error: Multiple rules exist. Specify a handle."; exit 1 + fi + fi + # get_port_by_handle ロジック (nftからポートを取得) + lp=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') + [[ -n "$lp" ]] && test_rule_by_port "$lp" || msg "Error: Handle $h_str not found." + exit 0 ;; + + # その後で削除フラグを含むものを処理 + -*[d]*) h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') [[ -z "$h_str" ]] && { h_str="$2"; shift; } - - # ポートとハンドルを統合して解析 handles=($(parse_delete_targets "$h_str")) - [[ ${#handles[@]} -eq 0 ]] && { msg "No handles to delete."; exit 1; } - + [[ ${#handles[@]} -eq 0 ]] && { msg "No targets to delete."; exit 1; } if [[ "$FORCE_FLAG" == false ]]; then msg "Review rules to delete:" for h in "${handles[@]}"; do nft -a list table inet ${TABLE_NAME} | grep "handle $h" | sed 's/^/ /'; done read -p "Delete these rules? (y/N): " confirm [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 fi - for h in "${handles[@]}"; do ri=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") [[ -z "$ri" ]] && continue @@ -96,10 +107,10 @@ while [[ $# -gt 0 ]]; do nft delete rule inet ${TABLE_NAME} "$c" handle "$rh" done done - msg "Deleted rule associated with handle $h" + msg "Deleted handle $h" done list_rules; exit 0 ;; - # (中略: -t, -f, add_rule の case 処理は ver.1.1.4 と同じ) + *) if [[ "$1" =~ ^[0-9]+: ]]; then add_rule "$1"; exit 0 else msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1; fi ;; From 79d11fecf7038653aa30d42e9d3fd178a2cccc13 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 12:10:38 +0900 Subject: [PATCH 12/23] =?UTF-8?q?=E5=90=84=E7=A8=AE=E3=82=AA=E3=83=97?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E8=A9=95=E4=BE=A1=E9=83=A8=E5=88=86?= =?UTF-8?q?BUG=E5=8F=96=E3=82=8A=20-d63-69=20=E3=81=A8=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E3=81=97=E3=81=9F=E9=9A=9B=E3=81=AB=E3=80=81=E9=96=93=E3=81=AE?= =?UTF-8?q?=E3=80=8C=E3=82=B5=E3=83=96=E3=82=B0=E3=83=AB=E3=83=BC=E3=83=97?= =?UTF-8?q?=E3=81=AE=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AB=EF=BC=8864-68?= =?UTF-8?q?=EF=BC=89=E3=80=8D=E3=82=92=E7=84=A1=E8=A6=96=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=80=81=E3=81=A1=E3=82=83=E3=82=93=E3=81=A8=E8=BB=A2=E9=80=81?= =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=81=AE=E8=A6=AA=E7=8E=89=E3=81=A7=E3=81=82?= =?UTF-8?q?=E3=82=8B=2063=20=E3=81=A8=2069=20=E3=81=A0=E3=81=91=E3=82=92?= =?UTF-8?q?=E6=A4=9C=E5=87=BA=E3=81=97=E3=80=81=E3=81=9D=E3=81=93=E3=81=8B?= =?UTF-8?q?=E3=82=89=E8=8A=8B=E3=81=A5=E3=82=8B=E5=BC=8F=E3=81=AB=E9=96=A2?= =?UTF-8?q?=E9=80=A3=E3=83=AB=E3=83=BC=E3=83=AB=E3=82=92=E6=B6=88=E5=8E=BB?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=81=A6=E3=81=84=E3=81=BE=E3=81=99=E3=80=82?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=82=82=E3=82=B9=E3=83=83=E3=82=AD=E3=83=AA?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=80=81=E3=82=88=E3=81=86=E3=82=84=E3=81=8F?= =?UTF-8?q?=E3=80=8C=E9=81=93=E5=85=B7=E3=80=8D=E3=81=A8=E3=81=97=E3=81=A6?= =?UTF-8?q?=E4=BF=A1=E9=A0=BC=E3=81=A7=E3=81=8D=E3=82=8B=E3=83=AC=E3=83=99?= =?UTF-8?q?=E3=83=AB=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=9F=E6=B0=97=E3=81=8C?= =?UTF-8?q?=E3=81=97=E3=81=BE=E3=81=99=E3=80=82=20=E4=BB=8A=E5=9B=9E?= =?UTF-8?q?=E3=81=AE=E6=9C=80=E7=B5=82=E7=9A=84=E3=81=AA=E5=89=8A=E9=99=A4?= =?UTF-8?q?=E3=83=AD=E3=82=B8=E3=83=83=E3=82=AF=E3=81=AE=E4=BB=95=E7=B5=84?= =?UTF-8?q?=E3=81=BF=20=E3=83=95=E3=82=A3=E3=83=AB=E3=82=BF=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0:=20=E6=8C=87=E5=AE=9A=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=81=9F=E7=AF=84=E5=9B=B2=EF=BC=8863-69=EF=BC=89=E3=81=AE?= =?UTF-8?q?=E4=B8=AD=E3=81=8B=E3=82=89=E3=80=81prerouting=20=E3=83=81?= =?UTF-8?q?=E3=82=A7=E3=83=BC=E3=83=B3=E3=81=AB=E5=AE=9F=E5=9C=A8=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AB=E3=81=A0=E3=81=91?= =?UTF-8?q?=E3=82=92=E3=83=94=E3=83=83=E3=82=AF=E3=82=A2=E3=83=83=E3=83=97?= =?UTF-8?q?=E3=81=97=E3=81=BE=E3=81=99=E3=80=82=20UUID=E3=81=AE=E7=89=B9?= =?UTF-8?q?=E5=AE=9A:=20=E8=A6=8B=E3=81=A4=E3=81=8B=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AB=E3=81=AE=E3=82=B3=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88=E6=AC=84=E3=81=8B=E3=82=89=E5=9B=BA=E6=9C=89?= =?UTF-8?q?=E3=81=AE=20ipf-id=20=E3=82=92=E6=8A=9C=E3=81=8D=E5=87=BA?= =?UTF-8?q?=E3=81=97=E3=81=BE=E3=81=99=E3=80=82=20=E4=B8=80=E6=96=89?= =?UTF-8?q?=E6=8E=83=E5=B0=84:=20=E3=81=9D=E3=81=AE=20UUID=20=E3=82=92?= =?UTF-8?q?=E6=8C=81=E3=81=A4=E3=83=AB=E3=83=BC=E3=83=AB=E3=82=92=E3=80=81?= =?UTF-8?q?NAT=EF=BC=88prerouting,=20output,=20postrouting=EF=BC=89?= =?UTF-8?q?=E3=81=A8=20Filter=EF=BC=88forward=EF=BC=89=E3=81=AE=E5=85=A8?= =?UTF-8?q?=E3=83=81=E3=82=A7=E3=83=BC=E3=83=B3=E3=81=8B=E3=82=89=E6=8E=A2?= =?UTF-8?q?=E3=81=97=E5=87=BA=E3=81=97=E3=81=A6=E5=89=8A=E9=99=A4=E3=81=97?= =?UTF-8?q?=E3=81=BE=E3=81=99=E3=80=82=20=E3=81=95=E3=82=89=E3=81=AB?= =?UTF-8?q?=E3=80=8C=E8=AA=BF=E3=81=B9=E3=82=8B=E3=80=8D=E9=9A=9B=E3=81=AE?= =?UTF-8?q?=E3=81=8A=E3=81=99=E3=81=99=E3=82=81=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=20=E3=82=82=E3=81=97=E3=81=8A=E6=99=82=E9=96=93=E3=81=82?= =?UTF-8?q?=E3=82=8C=E3=81=B0=E3=80=81=E3=81=93=E3=82=93=E3=81=AA=E3=83=91?= =?UTF-8?q?=E3=82=BF=E3=83=BC=E3=83=B3=E3=82=82=E8=A9=A6=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=81=BF=E3=81=A6=E3=81=8F=E3=81=A0=E3=81=95=E3=81=84=EF=BC=9A?= =?UTF-8?q?=20=E3=83=9D=E3=83=BC=E3=83=88=E6=8C=87=E5=AE=9A=E5=89=8A?= =?UTF-8?q?=E9=99=A4:=20./ipfn=20-d=20:11434=20=EF=BC=88=E3=83=8F=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=83=AB=E7=95=AA=E5=8F=B7=E3=82=92=E8=AA=BF=E3=81=B9?= =?UTF-8?q?=E3=81=9A=E3=81=AB=E3=83=9D=E3=83=BC=E3=83=88=E3=81=A7=E6=B6=88?= =?UTF-8?q?=E3=81=9B=E3=82=8B=E3=81=8B=EF=BC=89=20=E6=B7=B7=E5=90=88?= =?UTF-8?q?=E5=89=8A=E9=99=A4:=20./ipfn=20-d=20:80,33=20=EF=BC=88=E3=83=9D?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=A8=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AB?= =?UTF-8?q?=E3=82=92=E6=B7=B7=E3=81=9C=E3=81=A6=E6=B6=88=E3=81=9B=E3=82=8B?= =?UTF-8?q?=E3=81=8B=EF=BC=89=20=E9=9D=99=E3=81=8B=E3=81=AA=E5=89=8A?= =?UTF-8?q?=E9=99=A4:=20./ipfn=20-qd=20:80=20=EF=BC=88=E4=BD=95=E3=82=82?= =?UTF-8?q?=E8=A8=80=E3=82=8F=E3=81=9A=E3=81=AB=E4=B8=80=E7=9E=AC=E3=81=A7?= =?UTF-8?q?=E6=B6=88=E3=81=88=E5=8E=BB=E3=82=8B=E3=81=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 111 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 23 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index bdab95a..a5aec64 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,8 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.1.6 +# Version: 1.1.9 # Date: 2026-01-22 -# Description: Fixed argument parsing order and combined flags. +# Description: Fixed syntax error and finalized safety guards. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" @@ -14,6 +14,8 @@ FORCE_FLAG=false SKIP_TEST=false QUIET_MODE=false +# --- 1. Utility Functions --- + msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } init_nft() { @@ -24,7 +26,55 @@ init_nft() { nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null } -# (parse_delete_targets, list_rules, test_rule_by_port, add_rule などは 1.1.5 と同様) +show_help() { + echo "Usage: ipfn [OPTIONS] [PORT:TARGET_IP:TARGET_PORT]" + echo "" + echo "Options:" + echo " -l, -L List all rules" + echo " -d HANDLE/:PORT Delete by handle or port (e.g., -d 17 or -d :80)" + echo " -df, -qd Force/Quiet delete" + echo " -t [HANDLE] Test connectivity" + echo " -f Skip test / Enable IP forward" + echo " -v Show raw nftables rules" + echo " -h Show this help" +} + +list_rules() { + [[ "$QUIET_MODE" == true ]] && return + local highlight_uuid=$1 + init_nft + msg "Forwarding Rules (ipfn):" + printf "%-10s %-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) + if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then + printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" + else + printf "%-10s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" + fi + done +} + +test_rule_by_port() { + [[ "$QUIET_MODE" == true ]] && return + local lp=$1 + echo -n "Testing connectivity to :$lp... " + if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -iqE "succeeded|connected|open"; then + echo -e "\e[32mOK\e[0m" + else + echo -e "\e[31mFAILED\e[0m" + fi +} + +validate_prerouting_handle() { + nft -a list chain inet ${TABLE_NAME} prerouting | grep -q "handle $1" +} + parse_delete_targets() { local input=$1 local -a final_handles=() @@ -33,23 +83,40 @@ parse_delete_targets() { if [[ "$part" =~ ^:([0-9]+)$ ]]; then local target_port="${BASH_REMATCH[1]}" local found_h=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') - if [[ -n "$found_h" ]]; then - for h in $found_h; do final_handles+=("$h"); done - else - msg "Warning: No rules found for port :$target_port" - fi + for h in $found_h; do final_handles+=("$h"); done elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then - for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do final_handles+=("$i"); done + for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do + validate_prerouting_handle "$i" && final_handles+=("$i") + done else - final_handles+=("$part") + validate_prerouting_handle "$part" && final_handles+=("$part") fi done echo "${final_handles[@]}" } -# --- メインパース (順序を修正) --- +add_rule() { + init_nft + local lp=$(echo "$1" | cut -d':' -f1) + local tip=$(echo "$1" | cut -d':' -f2) + local tp=$(echo "$1" | cut -d':' -f3) + [[ "$QUIET_MODE" == false ]] && nft list chain inet ${TABLE_NAME} prerouting | grep -q "dport $lp" && msg "\e[33mWarning: Port :$lp is already in use.\e[0m" + local raw_uuid=$(cat /proc/sys/kernel/random/uuid) + local uuid="ipf-id:$raw_uuid" + local family="ip"; [[ "$tip" =~ : ]] && family="ip6" + nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" + list_rules "$raw_uuid" + [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } +} -# 先読みフラグ +# --- 2. Main Logic --- + +# Pre-scan flags for arg in "$@"; do [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true @@ -62,8 +129,6 @@ while [[ $# -gt 0 ]]; do -h|--help) show_help; exit 0 ;; -v|--verbose) nft list table inet ${TABLE_NAME}; exit 0 ;; -L|-l) list_rules; exit 0 ;; - - # 先に単独の -f や -t を処理する -f) if [[ $# -eq 1 ]]; then sysctl -w net.ipv4.ip_forward=1 >/dev/null @@ -81,21 +146,22 @@ while [[ $# -gt 0 ]]; do msg "Error: Multiple rules exist. Specify a handle."; exit 1 fi fi - # get_port_by_handle ロジック (nftからポートを取得) lp=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') [[ -n "$lp" ]] && test_rule_by_port "$lp" || msg "Error: Handle $h_str not found." exit 0 ;; - - # その後で削除フラグを含むものを処理 -*[d]*) h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') [[ -z "$h_str" ]] && { h_str="$2"; shift; } handles=($(parse_delete_targets "$h_str")) - [[ ${#handles[@]} -eq 0 ]] && { msg "No targets to delete."; exit 1; } + if [[ ${#handles[@]} -eq 0 ]]; then + msg "No valid prerouting handles found."; exit 1 + fi if [[ "$FORCE_FLAG" == false ]]; then - msg "Review rules to delete:" - for h in "${handles[@]}"; do nft -a list table inet ${TABLE_NAME} | grep "handle $h" | sed 's/^/ /'; done - read -p "Delete these rules? (y/N): " confirm + msg "Review rules to delete (Top-level only):" + for h in "${handles[@]}"; do + nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | sed 's/^/ /' + done + read -p "Delete these rules and their sub-rules? (y/N): " confirm [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 fi for h in "${handles[@]}"; do @@ -107,10 +173,9 @@ while [[ $# -gt 0 ]]; do nft delete rule inet ${TABLE_NAME} "$c" handle "$rh" done done - msg "Deleted handle $h" + msg "Deleted associated with handle $h" done list_rules; exit 0 ;; - *) if [[ "$1" =~ ^[0-9]+: ]]; then add_rule "$1"; exit 0 else msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1; fi ;; From 8cdccae6bf05cf647caf81047c003c1ff8f5e876 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 12:32:42 +0900 Subject: [PATCH 13/23] =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=86=E3=83=8A?= =?UTF-8?q?=E3=82=84VM=E3=81=AB=E5=AF=BE=E5=BF=9C=20=E3=81=AA=E3=82=8B?= =?UTF-8?q?=E3=81=B9=E3=81=8F=E7=94=9FIP=E7=B6=AD=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn1.0 | 118 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 33 deletions(-) diff --git a/ipfn1.0 b/ipfn1.0 index a5aec64..69e088c 100755 --- a/ipfn1.0 +++ b/ipfn1.0 @@ -1,8 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.1.9 +# Version: 1.2.4 # Date: 2026-01-22 -# Description: Fixed syntax error and finalized safety guards. +# Description: Enhanced for VM/LXD/Docker with bridge-nf tuning and robust forwarding. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" @@ -14,29 +14,48 @@ FORCE_FLAG=false SKIP_TEST=false QUIET_MODE=false -# --- 1. Utility Functions --- +# --- 1. Utility & Core Setup --- msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } init_nft() { nft add table inet ${TABLE_NAME} 2>/dev/null + # NAT Chains 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 + # Filter Chain (For VM/CT Forwarding) nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null } +enable_forwarding() { + msg "Optimizing kernel parameters for VM/LXD/Docker..." + sysctl -w net.ipv4.ip_forward=1 >/dev/null + sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null + sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null + # ブリッジパケットをNetfilterに渡す設定 (存在する場合のみ) + sysctl -w net.bridge.bridge-nf-call-iptables=1 2>/dev/null + sysctl -w net.bridge.bridge-nf-call-ip6tables=1 2>/dev/null + msg "\e[32mForwarding and Bridge-NF enabled.\e[0m" +} + show_help() { - echo "Usage: ipfn [OPTIONS] [PORT:TARGET_IP:TARGET_PORT]" + echo "Usage: ipfn [OPTIONS] [RULES]" + echo "" + echo "Rules Format:" + echo " 80:10.10.100.5:8080 Full (LocalPort:TargetIP:TargetPort)" + echo " 80:8080 IP defaults to 127.0.0.1" + echo " 11434 Map same port to 127.0.0.1" echo "" echo "Options:" - echo " -l, -L List all rules" - echo " -d HANDLE/:PORT Delete by handle or port (e.g., -d 17 or -d :80)" - echo " -df, -qd Force/Quiet delete" - echo " -t [HANDLE] Test connectivity" - echo " -f Skip test / Enable IP forward" - echo " -v Show raw nftables rules" - echo " -h Show this help" + echo " -l, -L List all rules" + echo " -d HANDLE/:PORT/all Delete specific rules or '*' for all" + echo " -R Reset: Clear ALL rules immediately" + echo " -q Quiet mode (No output, Auto-yes)" + echo " -t [HANDLE] Test connectivity" + echo " -f Enable IP forward & Bridge tuning / Skip test" + echo " -v Verbose (raw nftables output)" + echo " -h Show this help" } list_rules() { @@ -49,7 +68,7 @@ list_rules() { 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) + target=$(echo "$line" | grep -oE '([0-9.]+|\[[0-9a-fA-F:]+\]):[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) if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then @@ -71,13 +90,28 @@ test_rule_by_port() { fi } -validate_prerouting_handle() { - nft -a list chain inet ${TABLE_NAME} prerouting | grep -q "handle $1" +# --- 2. Deletion Logic --- + +validate_rule_handle() { + nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $1" | grep -q "dnat" +} + +all_clear() { + if [[ "$FORCE_FLAG" == false ]]; then + msg "\e[33mWarning: This will delete ALL forwarding rules.\e[0m" + read -p "Are you sure? (y/N): " confirm + [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 + fi + nft delete table inet ${TABLE_NAME} 2>/dev/null + init_nft + msg "All rules cleared successfully." + exit 0 } parse_delete_targets() { local input=$1 local -a final_handles=() + [[ "$input" == "*" || "$input" == "all" ]] && all_clear IFS=',' read -r -a parts <<< "$input" for part in "${parts[@]}"; do if [[ "$part" =~ ^:([0-9]+)$ ]]; then @@ -86,42 +120,62 @@ parse_delete_targets() { for h in $found_h; do final_handles+=("$h"); done elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do - validate_prerouting_handle "$i" && final_handles+=("$i") + validate_rule_handle "$i" && final_handles+=("$i") done else - validate_prerouting_handle "$part" && final_handles+=("$part") + validate_rule_handle "$part" && final_handles+=("$part") fi done echo "${final_handles[@]}" } +# --- 3. Adding Logic --- + add_rule() { init_nft - local lp=$(echo "$1" | cut -d':' -f1) - local tip=$(echo "$1" | cut -d':' -f2) - local tp=$(echo "$1" | cut -d':' -f3) - [[ "$QUIET_MODE" == false ]] && nft list chain inet ${TABLE_NAME} prerouting | grep -q "dport $lp" && msg "\e[33mWarning: Port :$lp is already in use.\e[0m" + local raw_input=$1 + local lp tip tp + + if [[ "$raw_input" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} + elif [[ "$raw_input" =~ ^([0-9]+):([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[2]} + elif [[ "$raw_input" =~ ^([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[1]} + else + msg "\e[31mError: Invalid rule format '$raw_input'\e[0m"; exit 1 + fi + local raw_uuid=$(cat /proc/sys/kernel/random/uuid) local uuid="ipf-id:$raw_uuid" local family="ip"; [[ "$tip" =~ : ]] && family="ip6" + + # DNAT nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" + + # FORWARD (VM/CT対応) nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" + + # POSTROUTING (MASQUERADE: 戻りパケットの保証) nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" + list_rules "$raw_uuid" [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } } -# --- 2. Main Logic --- +# --- 4. Main Parsing --- -# Pre-scan flags for arg in "$@"; do [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true + [[ "$arg" == "-R" ]] && RESET_MODE=true done +if [[ "$RESET_MODE" == true ]]; then all_clear; fi + if [[ $# -eq 0 ]]; then list_rules; exit 0; fi while [[ $# -gt 0 ]]; do @@ -130,11 +184,8 @@ while [[ $# -gt 0 ]]; do -v|--verbose) nft list table inet ${TABLE_NAME}; exit 0 ;; -L|-l) list_rules; exit 0 ;; -f) - if [[ $# -eq 1 ]]; then - sysctl -w net.ipv4.ip_forward=1 >/dev/null - sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null - msg "Kernel parameters updated."; exit 0 - fi ;; + enable_forwarding + [[ $# -eq 1 ]] && exit 0 ;; -t*) h_str="${1#-t}" if [[ -z "$h_str" && ( "$2" =~ ^[0-9,-]+$ ) ]]; then h_str="$2"; shift; fi @@ -153,9 +204,7 @@ while [[ $# -gt 0 ]]; do h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') [[ -z "$h_str" ]] && { h_str="$2"; shift; } handles=($(parse_delete_targets "$h_str")) - if [[ ${#handles[@]} -eq 0 ]]; then - msg "No valid prerouting handles found."; exit 1 - fi + [[ ${#handles[@]} -eq 0 ]] && { msg "No valid targets."; exit 1; } if [[ "$FORCE_FLAG" == false ]]; then msg "Review rules to delete (Top-level only):" for h in "${handles[@]}"; do @@ -177,8 +226,11 @@ while [[ $# -gt 0 ]]; do done list_rules; exit 0 ;; *) - if [[ "$1" =~ ^[0-9]+: ]]; then add_rule "$1"; exit 0 - else msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1; fi ;; + if [[ "$1" =~ ^[0-9] ]]; then + add_rule "$1"; exit 0 + else + msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1 + fi ;; esac shift done From 5115961094bc8477d8a8416f711259f230e4f2af Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 13:10:43 +0900 Subject: [PATCH 14/23] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=BF=98=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 36 ++--- ipf1.0.4 | 395 ------------------------------------------------------ ipfn1.0 | 236 -------------------------------- 3 files changed, 20 insertions(+), 647 deletions(-) delete mode 100644 ipf1.0.4 delete mode 100755 ipfn1.0 diff --git a/README.md b/README.md index 6c0a19a..e5afa2e 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,41 @@ -このプロジェクトは、iptablesのルールを編集するためのスクリプトです。以下が使用方法とライセンス情報です。 +このプロジェクトは、nftablesのルールを編集するためのスクリプトです。以下が使用方法とライセンス情報です。 --- ### 概要 -このツールは、`iptables`のルールを編集する際のスクリプトであり、一時ファイルを作成し、エディタでルールを編集する処理を含みます。 +このツールは、`nftables`のルールを編集する際のスクリプトです。 --- ### 使用方法 -1. **ルールの編集** +**ルールの編集** ```bash - ipf 11434:10.1.1.2:11434 # Forward local port 11434 to 10.1.1.2:11434 - ipf -L # List all rules with numbers - ipf -d 1 # Delete rule number 1 - ipf -d 1 -q # Delete rule number 1 (quiet mode) - ipf -v # Show current iptables rules by iptables-save - ipf -e # Edit all rules in editor and restore - ``` - ipf -e で一時ファイルが作成され、エディタ(例: `nano`)でルールを編集できます。編集が完了した後、一時ファイルは自動的に削除されます [1]。 + Usage: ipfn [OPTIONS] [RULES] -2. **バージョン情報の表示** - ```bash - ipf --version # Show version information + Rules Format: + 80:10.10.100.5:8080 Full (LocalPort:TargetIP:TargetPort) + 80:8080 IP defaults to 127.0.0.1 + 11434 Map same port to 127.0.0.1 + + Options: + -l, -L List all rules + -d HANDLE/:PORT/all Delete specific rules or '*' for all + -R Reset: Clear ALL rules immediately + -q Quiet mode (No output, Auto-yes) + -t [HANDLE] Test connectivity + -f Enable IP forward & Bridge tuning / Skip test + -v Verbose (raw nftables output) + -h Show this help ``` - これにより、ソフトウェアのバージョンとライセンス情報が表示されます [1]。 + --- ### ライセンス MIT License に基づくライセンスです。 **権利者**: krasherjoe -**日付**: 2025-10-26 +**日付**: 2026-01-22 --- diff --git a/ipf1.0.4 b/ipf1.0.4 deleted file mode 100644 index 75bf4eb..0000000 --- a/ipf1.0.4 +++ /dev/null @@ -1,395 +0,0 @@ -#!/bin/bash - -# sudo権限で実行されているか確認し、そうでなければsudoで再実行 -if [ "$(id -u)" -ne 0 ]; then - exec sudo "$0" "$@" -fi - -# グローバル変数 -RULES_FILE="/tmp/iptables_forward_rules" -QUIET=false -PROTO="tcp" # デフォルトプロトコル - -# コンテナ環境検出関数 -is_container() { - # systemd-detect-virt を使用してコンテナ環境を検出 - if command -v systemd-detect-virt &> /dev/null; then - if systemd-detect-virt --quiet | grep -qE "container|vm"; then - return 0 # コンテナまたは仮想マシン環境 - fi - fi - - # lxc-checkconfig を使用して LXC コンテナを検出 (systemd-detect-virt がない場合) - if command -v lxc-checkconfig &> /dev/null; then - if lxc-checkconfig 2>&1 | grep -q "Running in an LXC container"; then - return 0 # LXC コンテナ環境 - fi - fi - - # その他 (LXD など) のコンテナ環境検出方法を追加可能 - - return 1 # コンテナ環境ではない -} - - -# ヘルプ表示 -show_help() { - cat << EOF -Usage: ipf [OPTIONS] [PORT:IP:PORT | -L | -d RULE_NUMBER] - -Examples: - ipf 11434:10.1.1.2:11434 # Forward local port 11434 to 10.1.1.2:11434 - ipf -L # List all rules with numbers - ipf -d 1 # Delete rule number 1 - ipf -d 1 -q # Delete rule number 1 (quiet mode) - ipf -v # Show current iptables rules by iptables-save - ipf -e # Edit all rules in editor and restore - ipf -f # Enable IP forwarding - ipf -t 1 # Test rule number 1 - ipf -t 11434:10.10.1.2:11434 # Test rule by specification - ipf -p udp 11434:10.1.1.2:11434 # Forward using UDP - -Options: - -h, --help Show this help message - -L, -l List all rules - -d NUM Delete rule by number - -q Quiet mode (no output) - -v Show current iptables rules (via iptables-save) - -e Edit all rules in editor and restore - -f Enable IP forwarding and localnet routing - -t ARG Test connectivity for rule number or PORT:IP:PORT - -p PROTO Specify protocol (tcp|udp) for the following rule - --version Show version information - -NOTE: - All port forwarding is performed using DNAT only. No MASQUERADE (SNAT) is applied, - so the original source IP of the client is preserved. This ensures that - fail2ban logs the correct client IP on the destination server. -EOF -} - -# バージョン情報を表示 -show_version() { - cat << EOF -ipf ver.1.0.4 -Date: 2025-10-26 -Created by: qwen3/gpt-oss/gemini-2.5-pro and krasherjoe - ---- -MIT License - -Copyright (c) 2025 krasherjoe - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ---- - -** WARNING / 注意 ** -This script operates with administrative privileges (sudo) to modify your system's -firewall (iptables) rules. Incorrect use, especially with the -e (edit) option, -can disrupt your network connectivity or overwrite existing security rules. -Please use with caution and understand the changes you are making. - -このスクリプトは管理者権限(sudo)で動作し、システムのファイアウォール(iptables)ルールを -直接変更します。特に -e (編集) オプションの誤った使用は、ネットワーク接続を中断させたり、 -既存のセキュリティルールを上書きする危険性があります。 -コンテナ環境ではMASQUERADEを使用するため、クライアントの元のIPアドレスが隠蔽されます。 -これにより、fail2banなどのツールが不正な行為者を正しく識別し、ブロックできなくなる可能性があります。 -セキュリティへの影響を考慮してください。 -内容をよく理解した上で、注意して使用してください。 -EOF -} - -# エラーメッセージを表示 -error() { - echo "Error: $*" >&2 - logger -p user.err "ipf: $*" - exit 1 -} - -# ルールの保存 -save_rules() { - iptables-save > "$RULES_FILE" -} - -# ルールの読み込み -load_rules() { - if [[ -f "$RULES_FILE" ]]; then - iptables-restore < "$RULES_FILE" - fi -} - -# ルール番号と内容をリスト表示 -list_rules() { - local i=1 - echo "Forwarding rules:" - iptables -t nat -L PREROUTING -n -v --line-numbers | grep -E '^[0-9]+' | while read -r line; do - echo "$i: $line" - ((i++)) - done -} - -# ルールを削除 -delete_rule() { - local num=$1 - if ! [[ "$num" =~ ^[0-9]+$ ]]; then - error "Invalid rule number: $num" - fi - - # ユーザーへの表示用に整形されたルール行を取得 - local rule_line_verbose=$(iptables -t nat -L PREROUTING --line-numbers | grep -E "^$num\s+" | head -n 1) - if [[ -z "$rule_line_verbose" ]]; then - error "No rule found with number: $num" - fi - - # パース用に-nオプションを付けたルール行を取得 - local rule_line_numeric=$(iptables -t nat -L PREROUTING -n --line-numbers | grep -E "^\s*$num\s+" | head -n 1) - if [[ -z "$rule_line_numeric" ]]; then - error "Could not find numeric rule for number: $num" # Should not happen - fi - - # ルールから詳細を抽出 (sedを使い、より堅牢に) - local line_details=$(echo "$rule_line_numeric" | sed -n 's/.*\(tcp\|udp\).*dpt:\([0-9]*\).*to:\([0-9.]*\):\([0-9]*\).*/\1 \2 \3 \4/p') - if [[ -z "$line_details" ]]; then - error "Could not parse rule details from line: $rule_line_numeric" - fi - read -r proto local_port target_ip target_port <<< "$line_details" - - # 1. PREROUTING ルールを番号で削除 - if ! iptables -t nat -D PREROUTING "$num"; then - error "Failed to delete PREROUTING rule $num" - fi - - # 2. 対応する OUTPUT ルールをすべて削除 - while iptables -t nat -D OUTPUT -p "$proto" -m "$proto" --dport "$local_port" -j DNAT --to-destination "$target_ip:$target_port" >/dev/null 2>&1; do :; done - - # 3. 対応する FORWARD ルールをすべて削除 - while iptables -D FORWARD -p "$proto" -m "$proto" -d "$target_ip" --dport "$target_port" -j ACCEPT >/dev/null 2>&1; do :; done - - # 4. 確立済み通信を許可するルールを削除 (存在確認後に削除) - if iptables -C FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null; then - iptables -D FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT >/dev/null 2>&1 - fi - - if ! $QUIET; then - echo "Deleted rule $num: $rule_line_verbose" - fi -} - -# ポートフォワーディングルールを追加 -add_rule() { - # IPフォワーディングが有効かチェック - if [[ $(sysctl -n net.ipv4.ip_forward) -ne 1 ]]; then - error "IP forwarding is disabled. Please enable it by running: ipf -f" - fi - - local container_env=$(is_container) - if [[ $container_env -eq 0 ]]; then - echo -e "\e[33mDetected container environment. MASQUERADE is enabled, which hides the original client IP address. This will prevent tools like fail2ban from correctly identifying and blocking malicious actors. Consider the security implications.\e[0m" # コンテナ環境であることを示すメッセージ - fi - - # 確立済みの通信は許可する (戻りのパケットのため) - iptables -I FORWARD 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT - - local port_ip_port=$1 - local local_port=$(echo "$port_ip_port" | cut -d':' -f1) - local target_ip=$(echo "$port_ip_port" | cut -d':' -f2) - local target_port=$(echo "$port_ip_port" | cut -d':' -f3) - - # IPアドレス検証 - if ! [[ "$target_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - error "Invalid IP address: $target_ip" - fi - - # ポート番号検証 - if ! [[ "$local_port" =~ ^[0-9]+$ ]] || ! [[ "$target_port" =~ ^[0-9]+$ ]]; then - error "Invalid port number" - fi - if (( local_port < 1 || local_port > 65535 || target_port < 1 || target_port > 65535 )); then - error "Port number out of range (1-65535)" - fi - - # iptablesでルールを追加 - # 外部からのパケットを対象 - iptables -t nat -A PREROUTING -p "$PROTO" -m "$PROTO" --dport "$local_port" -j DNAT --to-destination "$target_ip:$target_port" - # ローカルで生成されたパケットを対象 - iptables -t nat -A OUTPUT -p "$PROTO" -m "$PROTO" --dport "$local_port" -j DNAT --to-destination "$target_ip:$target_port" - - if [[ $container_env -eq 0 ]]; then - # コンテナ環境ではMASQUERADEを使用 - iptables -t nat -A POSTROUTING -p "$PROTO" -m "$PROTO" -d "$target_ip" --dport "$target_port" -j MASQUERADE - fi - - # 転送されるパケットを許可する - iptables -A FORWARD -p "$PROTO" -m "$PROTO" -d "$target_ip" --dport "$target_port" -j ACCEPT - - if ! $QUIET; then - echo "Rule added: port $local_port -> $target_ip:$target_port (proto=$PROTO)" - fi -} - -# ルールを編集して復元 -edit_rules() { - local temp_file - temp_file=$(mktemp -t iptables.rules.XXXXXX) || error "Failed to create temporary file" - chmod 0600 "$temp_file" - trap 'rm -f "$temp_file"' EXIT - - # 現在のルールを一時ファイルに保存 - iptables-save > "$temp_file" - - # エディタを決定 - local editor=${EDITOR:-nano} - if ! command -v "$editor" > /dev/null; then - editor=vi - fi - - # nanoで編集 - if ! "$editor" "$temp_file"; then - error "Editor closed without saving or an error occurred." - fi - - # 編集後の内容でリストア - if iptables-restore < "$temp_file"; then - echo "iptables rules restored successfully from your edits." - echo "--- Displaying new rules ---" - iptables-save - else - error "Failed to restore iptables rules. Please check for syntax errors in your edits." - fi -} - -# 疎通確認用関数 -test_rule() { - local arg=$1 - local local_port target_ip target_port - - if [[ "$arg" =~ ^[0-9]+$ ]]; then - # rule number → ルール行を取得 - local line=$(iptables -t nat -L PREROUTING -n --line-numbers | grep "^$arg ") - if [[ -z "$line" ]]; then - error "No rule found with number: $arg" - fi - local_port=$(echo "$line" | awk '{print $8}'|cut -d ':' -f2) - target_ip=$(echo "$line" | awk -F: '{print $1}') - target_port=$(echo "$line" | awk -F: '{print $2}') - else - # 文字列形式 - local_port=$(echo "$arg" | cut -d':' -f1) - target_ip=$(echo "$arg" | cut -d':' -f2) - target_port=$(echo "$arg" | cut -d':' -f3) - fi - - echo "Testing connectivity to local port \"$(tput setaf 3)nc -z -w 5 127.0.0.1 $local_port$(tput sgr0)\"" - if nc -z -w 5 127.0.0.1 "$local_port" 2>/dev/null; then - echo "$(tput setaf 6)Connection successful.$(tput sgr0)" - else - echo "$(tput setaf 1)Connection failed.$(tput sgr0)" - fi -} - -# メイン処理 -main() { - # オプション解析 - while [[ $# -gt 0 ]]; do - case "$1" in - -h|--help) - show_help - exit 0 - ;; - -L|-l) - list_rules - exit 0 - ;; - -v) - iptables-save | grep -v '^#' | less -R - exit 0 - ;; - -e) - edit_rules - exit 0 - ;; - -f) - if sysctl -w net.ipv4.ip_forward=1 > /dev/null && \ - sysctl -w net.ipv4.conf.all.route_localnet=1 > /dev/null && \ - sysctl -w net.ipv4.conf.default.route_localnet=1 > /dev/null; then - echo "IP forwarding and localnet routing enabled." - else - error "Failed to enable kernel parameters." - fi - exit 0 - ;; - --version) - show_version - exit 0 - ;; - -d) - if [[ $# -lt 2 ]]; then - error "Missing rule number for -d option" - fi - delete_rule "$2" - exit 0 - ;; - -t) - if [[ $# -lt 2 ]]; then - error "Missing argument for -t option" - fi - test_rule "$2" - exit 0 - ;; - -q) - QUIET=true - shift - ;; - -p) - if [[ $# -lt 2 ]]; then - error "Missing protocol after -p" - fi - if [[ "$2" != "tcp" && "$2" != "udp" ]]; then - error "Unsupported protocol: $2" - fi - PROTO="$2" - shift 2 - ;; - -*) - error "Unknown option: $1" - ;; - *) - break - ;; - esac - done - - # 引数が残っていれば、ルール追加処理 - if [[ $# -eq 0 ]]; then - show_help - exit 1 - fi - - local arg=$1 - - # ルール追加処理 - if [[ "$arg" =~ ^[0-9]+:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$ ]]; then - add_rule "$arg" - else - error "Invalid format. Expected: PORT:IP:PORT or -L or -d RULE_NUMBER" - fi -} - -# 実行 -main "$@" \ No newline at end of file diff --git a/ipfn1.0 b/ipfn1.0 deleted file mode 100755 index 69e088c..0000000 --- a/ipfn1.0 +++ /dev/null @@ -1,236 +0,0 @@ -#!/bin/bash -# Name: ipfn -# Version: 1.2.4 -# Date: 2026-01-22 -# Description: Enhanced for VM/LXD/Docker with bridge-nf tuning and robust forwarding. - -if [ "$(id -u)" -ne 0 ]; then - exec sudo "$0" "$@" -fi - -TABLE_NAME="ipf_wrapper" -PROTO="tcp" -FORCE_FLAG=false -SKIP_TEST=false -QUIET_MODE=false - -# --- 1. Utility & Core Setup --- - -msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } - -init_nft() { - nft add table inet ${TABLE_NAME} 2>/dev/null - # NAT Chains - 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 - # Filter Chain (For VM/CT Forwarding) - nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null -} - -enable_forwarding() { - msg "Optimizing kernel parameters for VM/LXD/Docker..." - sysctl -w net.ipv4.ip_forward=1 >/dev/null - sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null - sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null - # ブリッジパケットをNetfilterに渡す設定 (存在する場合のみ) - sysctl -w net.bridge.bridge-nf-call-iptables=1 2>/dev/null - sysctl -w net.bridge.bridge-nf-call-ip6tables=1 2>/dev/null - msg "\e[32mForwarding and Bridge-NF enabled.\e[0m" -} - -show_help() { - echo "Usage: ipfn [OPTIONS] [RULES]" - echo "" - echo "Rules Format:" - echo " 80:10.10.100.5:8080 Full (LocalPort:TargetIP:TargetPort)" - echo " 80:8080 IP defaults to 127.0.0.1" - echo " 11434 Map same port to 127.0.0.1" - echo "" - echo "Options:" - echo " -l, -L List all rules" - echo " -d HANDLE/:PORT/all Delete specific rules or '*' for all" - echo " -R Reset: Clear ALL rules immediately" - echo " -q Quiet mode (No output, Auto-yes)" - echo " -t [HANDLE] Test connectivity" - echo " -f Enable IP forward & Bridge tuning / Skip test" - echo " -v Verbose (raw nftables output)" - echo " -h Show this help" -} - -list_rules() { - [[ "$QUIET_MODE" == true ]] && return - local highlight_uuid=$1 - init_nft - msg "Forwarding Rules (ipfn):" - printf "%-10s %-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-9a-fA-F:]+\]):[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) - if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then - printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" - else - printf "%-10s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" - fi - done -} - -test_rule_by_port() { - [[ "$QUIET_MODE" == true ]] && return - local lp=$1 - echo -n "Testing connectivity to :$lp... " - if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -iqE "succeeded|connected|open"; then - echo -e "\e[32mOK\e[0m" - else - echo -e "\e[31mFAILED\e[0m" - fi -} - -# --- 2. Deletion Logic --- - -validate_rule_handle() { - nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $1" | grep -q "dnat" -} - -all_clear() { - if [[ "$FORCE_FLAG" == false ]]; then - msg "\e[33mWarning: This will delete ALL forwarding rules.\e[0m" - read -p "Are you sure? (y/N): " confirm - [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 - fi - nft delete table inet ${TABLE_NAME} 2>/dev/null - init_nft - msg "All rules cleared successfully." - exit 0 -} - -parse_delete_targets() { - local input=$1 - local -a final_handles=() - [[ "$input" == "*" || "$input" == "all" ]] && all_clear - IFS=',' read -r -a parts <<< "$input" - for part in "${parts[@]}"; do - if [[ "$part" =~ ^:([0-9]+)$ ]]; then - local target_port="${BASH_REMATCH[1]}" - local found_h=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') - for h in $found_h; do final_handles+=("$h"); done - elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then - for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do - validate_rule_handle "$i" && final_handles+=("$i") - done - else - validate_rule_handle "$part" && final_handles+=("$part") - fi - done - echo "${final_handles[@]}" -} - -# --- 3. Adding Logic --- - -add_rule() { - init_nft - local raw_input=$1 - local lp tip tp - - if [[ "$raw_input" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then - lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} - elif [[ "$raw_input" =~ ^([0-9]+):([0-9]+)$ ]]; then - lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[2]} - elif [[ "$raw_input" =~ ^([0-9]+)$ ]]; then - lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[1]} - else - msg "\e[31mError: Invalid rule format '$raw_input'\e[0m"; exit 1 - fi - - local raw_uuid=$(cat /proc/sys/kernel/random/uuid) - local uuid="ipf-id:$raw_uuid" - local family="ip"; [[ "$tip" =~ : ]] && family="ip6" - - # DNAT - nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" - - # FORWARD (VM/CT対応) - nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" - - # POSTROUTING (MASQUERADE: 戻りパケットの保証) - nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" - - list_rules "$raw_uuid" - [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } -} - -# --- 4. Main Parsing --- - -for arg in "$@"; do - [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true - [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true - [[ "$arg" == "-R" ]] && RESET_MODE=true -done - -if [[ "$RESET_MODE" == true ]]; then all_clear; fi - -if [[ $# -eq 0 ]]; then list_rules; exit 0; fi - -while [[ $# -gt 0 ]]; do - case "$1" in - -h|--help) show_help; exit 0 ;; - -v|--verbose) nft list table inet ${TABLE_NAME}; exit 0 ;; - -L|-l) list_rules; exit 0 ;; - -f) - enable_forwarding - [[ $# -eq 1 ]] && exit 0 ;; - -t*) - h_str="${1#-t}" - if [[ -z "$h_str" && ( "$2" =~ ^[0-9,-]+$ ) ]]; then h_str="$2"; shift; fi - if [[ -z "$h_str" ]]; then - count=$(nft list chain inet ${TABLE_NAME} prerouting | grep -c "dnat") - if [ "$count" -eq 1 ]; then - h_str=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle" | awk '{print $NF}') - else - msg "Error: Multiple rules exist. Specify a handle."; exit 1 - fi - fi - lp=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') - [[ -n "$lp" ]] && test_rule_by_port "$lp" || msg "Error: Handle $h_str not found." - exit 0 ;; - -*[d]*) - h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') - [[ -z "$h_str" ]] && { h_str="$2"; shift; } - handles=($(parse_delete_targets "$h_str")) - [[ ${#handles[@]} -eq 0 ]] && { msg "No valid targets."; exit 1; } - if [[ "$FORCE_FLAG" == false ]]; then - msg "Review rules to delete (Top-level only):" - for h in "${handles[@]}"; do - nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | sed 's/^/ /' - done - read -p "Delete these rules and their sub-rules? (y/N): " confirm - [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 - fi - for h in "${handles[@]}"; do - ri=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") - [[ -z "$ri" ]] && continue - uuid=$(echo "$ri" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) - for c in prerouting output forward postrouting; do - nft -a list chain inet ${TABLE_NAME} "$c" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do - nft delete rule inet ${TABLE_NAME} "$c" handle "$rh" - done - done - msg "Deleted associated with handle $h" - done - list_rules; exit 0 ;; - *) - if [[ "$1" =~ ^[0-9] ]]; then - add_rule "$1"; exit 0 - else - msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1 - fi ;; - esac - shift -done From 7de4ed82f2b30b6757d1a120f9eda86d204b9ab8 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 05:00:39 +0000 Subject: [PATCH 15/23] =?UTF-8?q?"/"=20=E3=81=AB=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=82=92=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 ipfn diff --git a/ipfn b/ipfn new file mode 100644 index 0000000..69e088c --- /dev/null +++ b/ipfn @@ -0,0 +1,236 @@ +#!/bin/bash +# Name: ipfn +# Version: 1.2.4 +# Date: 2026-01-22 +# Description: Enhanced for VM/LXD/Docker with bridge-nf tuning and robust forwarding. + +if [ "$(id -u)" -ne 0 ]; then + exec sudo "$0" "$@" +fi + +TABLE_NAME="ipf_wrapper" +PROTO="tcp" +FORCE_FLAG=false +SKIP_TEST=false +QUIET_MODE=false + +# --- 1. Utility & Core Setup --- + +msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } + +init_nft() { + nft add table inet ${TABLE_NAME} 2>/dev/null + # NAT Chains + 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 + # Filter Chain (For VM/CT Forwarding) + nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null +} + +enable_forwarding() { + msg "Optimizing kernel parameters for VM/LXD/Docker..." + sysctl -w net.ipv4.ip_forward=1 >/dev/null + sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null + sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null + # ブリッジパケットをNetfilterに渡す設定 (存在する場合のみ) + sysctl -w net.bridge.bridge-nf-call-iptables=1 2>/dev/null + sysctl -w net.bridge.bridge-nf-call-ip6tables=1 2>/dev/null + msg "\e[32mForwarding and Bridge-NF enabled.\e[0m" +} + +show_help() { + echo "Usage: ipfn [OPTIONS] [RULES]" + echo "" + echo "Rules Format:" + echo " 80:10.10.100.5:8080 Full (LocalPort:TargetIP:TargetPort)" + echo " 80:8080 IP defaults to 127.0.0.1" + echo " 11434 Map same port to 127.0.0.1" + echo "" + echo "Options:" + echo " -l, -L List all rules" + echo " -d HANDLE/:PORT/all Delete specific rules or '*' for all" + echo " -R Reset: Clear ALL rules immediately" + echo " -q Quiet mode (No output, Auto-yes)" + echo " -t [HANDLE] Test connectivity" + echo " -f Enable IP forward & Bridge tuning / Skip test" + echo " -v Verbose (raw nftables output)" + echo " -h Show this help" +} + +list_rules() { + [[ "$QUIET_MODE" == true ]] && return + local highlight_uuid=$1 + init_nft + msg "Forwarding Rules (ipfn):" + printf "%-10s %-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-9a-fA-F:]+\]):[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) + if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then + printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" + else + printf "%-10s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" + fi + done +} + +test_rule_by_port() { + [[ "$QUIET_MODE" == true ]] && return + local lp=$1 + echo -n "Testing connectivity to :$lp... " + if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -iqE "succeeded|connected|open"; then + echo -e "\e[32mOK\e[0m" + else + echo -e "\e[31mFAILED\e[0m" + fi +} + +# --- 2. Deletion Logic --- + +validate_rule_handle() { + nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $1" | grep -q "dnat" +} + +all_clear() { + if [[ "$FORCE_FLAG" == false ]]; then + msg "\e[33mWarning: This will delete ALL forwarding rules.\e[0m" + read -p "Are you sure? (y/N): " confirm + [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 + fi + nft delete table inet ${TABLE_NAME} 2>/dev/null + init_nft + msg "All rules cleared successfully." + exit 0 +} + +parse_delete_targets() { + local input=$1 + local -a final_handles=() + [[ "$input" == "*" || "$input" == "all" ]] && all_clear + IFS=',' read -r -a parts <<< "$input" + for part in "${parts[@]}"; do + if [[ "$part" =~ ^:([0-9]+)$ ]]; then + local target_port="${BASH_REMATCH[1]}" + local found_h=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') + for h in $found_h; do final_handles+=("$h"); done + elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then + for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do + validate_rule_handle "$i" && final_handles+=("$i") + done + else + validate_rule_handle "$part" && final_handles+=("$part") + fi + done + echo "${final_handles[@]}" +} + +# --- 3. Adding Logic --- + +add_rule() { + init_nft + local raw_input=$1 + local lp tip tp + + if [[ "$raw_input" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} + elif [[ "$raw_input" =~ ^([0-9]+):([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[2]} + elif [[ "$raw_input" =~ ^([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[1]} + else + msg "\e[31mError: Invalid rule format '$raw_input'\e[0m"; exit 1 + fi + + local raw_uuid=$(cat /proc/sys/kernel/random/uuid) + local uuid="ipf-id:$raw_uuid" + local family="ip"; [[ "$tip" =~ : ]] && family="ip6" + + # DNAT + nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + + # FORWARD (VM/CT対応) + nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" + + # POSTROUTING (MASQUERADE: 戻りパケットの保証) + nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" + + list_rules "$raw_uuid" + [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } +} + +# --- 4. Main Parsing --- + +for arg in "$@"; do + [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true + [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true + [[ "$arg" == "-R" ]] && RESET_MODE=true +done + +if [[ "$RESET_MODE" == true ]]; then all_clear; fi + +if [[ $# -eq 0 ]]; then list_rules; exit 0; fi + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) show_help; exit 0 ;; + -v|--verbose) nft list table inet ${TABLE_NAME}; exit 0 ;; + -L|-l) list_rules; exit 0 ;; + -f) + enable_forwarding + [[ $# -eq 1 ]] && exit 0 ;; + -t*) + h_str="${1#-t}" + if [[ -z "$h_str" && ( "$2" =~ ^[0-9,-]+$ ) ]]; then h_str="$2"; shift; fi + if [[ -z "$h_str" ]]; then + count=$(nft list chain inet ${TABLE_NAME} prerouting | grep -c "dnat") + if [ "$count" -eq 1 ]; then + h_str=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle" | awk '{print $NF}') + else + msg "Error: Multiple rules exist. Specify a handle."; exit 1 + fi + fi + lp=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') + [[ -n "$lp" ]] && test_rule_by_port "$lp" || msg "Error: Handle $h_str not found." + exit 0 ;; + -*[d]*) + h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') + [[ -z "$h_str" ]] && { h_str="$2"; shift; } + handles=($(parse_delete_targets "$h_str")) + [[ ${#handles[@]} -eq 0 ]] && { msg "No valid targets."; exit 1; } + if [[ "$FORCE_FLAG" == false ]]; then + msg "Review rules to delete (Top-level only):" + for h in "${handles[@]}"; do + nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | sed 's/^/ /' + done + read -p "Delete these rules and their sub-rules? (y/N): " confirm + [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 + fi + for h in "${handles[@]}"; do + ri=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") + [[ -z "$ri" ]] && continue + uuid=$(echo "$ri" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) + for c in prerouting output forward postrouting; do + nft -a list chain inet ${TABLE_NAME} "$c" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do + nft delete rule inet ${TABLE_NAME} "$c" handle "$rh" + done + done + msg "Deleted associated with handle $h" + done + list_rules; exit 0 ;; + *) + if [[ "$1" =~ ^[0-9] ]]; then + add_rule "$1"; exit 0 + else + msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1 + fi ;; + esac + shift +done From d6fe942181b6fddd33c5ab71a838deef89aab0d0 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 14:22:08 +0900 Subject: [PATCH 16/23] =?UTF-8?q?-t=20=E6=8B=A1=E5=BC=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn | 251 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100755 ipfn diff --git a/ipfn b/ipfn new file mode 100755 index 0000000..956ffa8 --- /dev/null +++ b/ipfn @@ -0,0 +1,251 @@ +#!/bin/bash +# Name: ipfn +# Version: 1.2.4 +# Date: 2026-01-22 +# Description: Enhanced for VM/LXD/Docker with bridge-nf tuning and robust forwarding. + +if [ "$(id -u)" -ne 0 ]; then + exec sudo "$0" "$@" +fi + +TABLE_NAME="ipf_wrapper" +PROTO="tcp" +FORCE_FLAG=false +SKIP_TEST=false +QUIET_MODE=false + +# --- 1. Utility & Core Setup --- + +msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } + +init_nft() { + nft add table inet ${TABLE_NAME} 2>/dev/null + # NAT Chains + 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 + # Filter Chain (For VM/CT Forwarding) + nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null +} + +enable_forwarding() { + msg "Optimizing kernel parameters for VM/LXD/Docker..." + sysctl -w net.ipv4.ip_forward=1 >/dev/null + sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null + sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null + # ブリッジパケットをNetfilterに渡す設定 (存在する場合のみ) + sysctl -w net.bridge.bridge-nf-call-iptables=1 2>/dev/null + sysctl -w net.bridge.bridge-nf-call-ip6tables=1 2>/dev/null + msg "\e[32mForwarding and Bridge-NF enabled.\e[0m" +} + +show_help() { + echo "Usage: ipfn [OPTIONS] [RULES]" + echo "" + echo "Rules Format:" + echo " 80:10.10.100.5:8080 Full (LocalPort:TargetIP:TargetPort)" + echo " 80:8080 IP defaults to 127.0.0.1" + echo " 11434 Map same port to 127.0.0.1" + echo "" + echo "Options:" + echo " -l, -L List all rules" + echo " -d HANDLE/:PORT/all Delete specific rules or '*' for all" + echo " -R Reset: Clear ALL rules immediately" + echo " -q Quiet mode (No output, Auto-yes)" + echo " -t [HANDLE] Test connectivity" + echo " -f Enable IP forward & Bridge tuning / Skip test" + echo " -v Verbose (raw nftables output)" + echo " -h Show this help" +} + +list_rules() { + [[ "$QUIET_MODE" == true ]] && return + local highlight_uuid=$1 + init_nft + msg "Forwarding Rules (ipfn):" + printf "%-10s %-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-9a-fA-F:]+\]):[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) + if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then + printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" + else + printf "%-10s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" + fi + done +} + +test_rule_by_port() { + [[ "$QUIET_MODE" == true ]] && return + local lp=$1 + echo -n "Testing connectivity to :$lp... " + if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -iqE "succeeded|connected|open"; then + echo -e "\e[32mOK\e[0m" + else + echo -e "\e[31mFAILED\e[0m" + fi +} + +# --- 2. Deletion Logic --- + +validate_rule_handle() { + nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $1" | grep -q "dnat" +} + +all_clear() { + if [[ "$FORCE_FLAG" == false ]]; then + msg "\e[33mWarning: This will delete ALL forwarding rules.\e[0m" + read -p "Are you sure? (y/N): " confirm + [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 + fi + nft delete table inet ${TABLE_NAME} 2>/dev/null + init_nft + msg "All rules cleared successfully." + exit 0 +} + +parse_delete_targets() { + local input=$1 + local -a final_handles=() + [[ "$input" == "*" || "$input" == "all" ]] && all_clear + IFS=',' read -r -a parts <<< "$input" + for part in "${parts[@]}"; do + if [[ "$part" =~ ^:([0-9]+)$ ]]; then + local target_port="${BASH_REMATCH[1]}" + local found_h=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') + for h in $found_h; do final_handles+=("$h"); done + elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then + for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do + validate_rule_handle "$i" && final_handles+=("$i") + done + else + validate_rule_handle "$part" && final_handles+=("$part") + fi + done + echo "${final_handles[@]}" +} + +# --- 3. Adding Logic --- + +add_rule() { + init_nft + local raw_input=$1 + local lp tip tp + + if [[ "$raw_input" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} + elif [[ "$raw_input" =~ ^([0-9]+):([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[2]} + elif [[ "$raw_input" =~ ^([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[1]} + else + msg "\e[31mError: Invalid rule format '$raw_input'\e[0m"; exit 1 + fi + + local raw_uuid=$(cat /proc/sys/kernel/random/uuid) + local uuid="ipf-id:$raw_uuid" + local family="ip"; [[ "$tip" =~ : ]] && family="ip6" + + # DNAT + nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + + # FORWARD (VM/CT対応) + nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" + + # POSTROUTING (MASQUERADE: 戻りパケットの保証) + nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" + + list_rules "$raw_uuid" + [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } +} + +# --- 4. Main Parsing --- + +for arg in "$@"; do + [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true + [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true + [[ "$arg" == "-R" ]] && RESET_MODE=true +done + +if [[ "$RESET_MODE" == true ]]; then all_clear; fi + +if [[ $# -eq 0 ]]; then list_rules; exit 0; fi + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) show_help; exit 0 ;; + -v|--verbose) nft list table inet ${TABLE_NAME}; exit 0 ;; + -L|-l) list_rules; exit 0 ;; + -f) + enable_forwarding + [[ $# -eq 1 ]] && exit 0 ;; + -t*) + h_str="${1#-t}" + # 引数が空で、次に引数がある場合 (例: -t 80:) + if [[ -z "$h_str" && -n "$2" ]]; then h_str="$2"; shift; fi + + local target_port="" + + if [[ "$h_str" =~ ^([0-9]+):$ ]]; then + # パターン: -t 80: (ポート直接指定) + target_port="${BASH_REMATCH[1]}" + elif [[ -n "$h_str" ]]; then + # パターン: -t 33 (ハンドル指定) + target_port=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') + else + # パターン: -t (引数なし、自動選択) + count=$(nft list chain inet ${TABLE_NAME} prerouting | grep -c "dnat") + if [ "$count" -eq 1 ]; then + target_port=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dnat" | grep -o 'dport [0-9]*' | awk '{print $2}') + else + msg "Error: Multiple rules exist. Use -t [HANDLE] or -t [PORT]:"; exit 1 + fi + fi + + if [[ -n "$target_port" ]]; then + test_rule_by_port "$target_port" + else + msg "Error: Could not identify port for test." + fi + exit 0 ;; + -*[d]*) + h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') + [[ -z "$h_str" ]] && { h_str="$2"; shift; } + handles=($(parse_delete_targets "$h_str")) + [[ ${#handles[@]} -eq 0 ]] && { msg "No valid targets."; exit 1; } + if [[ "$FORCE_FLAG" == false ]]; then + msg "Review rules to delete (Top-level only):" + for h in "${handles[@]}"; do + nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | sed 's/^/ /' + done + read -p "Delete these rules and their sub-rules? (y/N): " confirm + [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 + fi + for h in "${handles[@]}"; do + ri=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") + [[ -z "$ri" ]] && continue + uuid=$(echo "$ri" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) + for c in prerouting output forward postrouting; do + nft -a list chain inet ${TABLE_NAME} "$c" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do + nft delete rule inet ${TABLE_NAME} "$c" handle "$rh" + done + done + msg "Deleted associated with handle $h" + done + list_rules; exit 0 ;; + *) + if [[ "$1" =~ ^[0-9] ]]; then + add_rule "$1"; exit 0 + else + msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1 + fi ;; + esac + shift +done From 2fdc3890a3009d43abd23b7c1fc86bd26915bd6c Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 14:33:04 +0900 Subject: [PATCH 17/23] -d debug --- ipfn | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/ipfn b/ipfn index 92a12ef..cc015c3 100755 --- a/ipfn +++ b/ipfn @@ -1,8 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.2.6 +# Version: 1.2.7 # Date: 2026-01-22 -# Description: Fixed 'local' variable error in main loop and improved port-test parsing. +# Description: Fixed grep errors after 'all' delete and streamlined exit paths. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" @@ -56,7 +56,7 @@ list_rules() { msg "Forwarding Rules (ipfn):" printf "%-10s %-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 + 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-9a-fA-F:]+\]):[0-9]+' | head -n 1) @@ -84,7 +84,7 @@ test_rule_by_port() { # --- 2. Deletion & Reset --- validate_rule_handle() { - nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $1" | grep -q "dnat" + nft -a list chain inet "${TABLE_NAME}" prerouting | grep "handle $1" | grep -q "dnat" } all_clear() { @@ -93,21 +93,22 @@ all_clear() { read -p "Are you sure? (y/N): " confirm [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 fi - nft delete table inet ${TABLE_NAME} 2>/dev/null + nft delete table inet "${TABLE_NAME}" 2>/dev/null init_nft msg "All rules cleared successfully." - exit 0 + list_rules + exit 0 # 全削除したらここで終了 } parse_delete_targets() { - local input=$1 + local input="$1" local -a final_handles=() [[ "$input" == "*" || "$input" == "all" ]] && all_clear IFS=',' read -r -a parts <<< "$input" for part in "${parts[@]}"; do if [[ "$part" =~ ^:([0-9]+)$ ]]; then local target_port="${BASH_REMATCH[1]}" - local found_h=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') + local found_h=$(nft -a list chain inet "${TABLE_NAME}" prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') for h in $found_h; do final_handles+=("$h"); done elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do @@ -124,7 +125,7 @@ parse_delete_targets() { add_rule() { init_nft - local raw_input=$1 + local raw_input="$1" local lp tip tp if [[ "$raw_input" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} @@ -138,12 +139,12 @@ add_rule() { local raw_uuid=$(cat /proc/sys/kernel/random/uuid) local uuid="ipf-id:$raw_uuid" local family="ip"; [[ "$tip" =~ : ]] && family="ip6" - nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" - nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" + nft add rule inet "${TABLE_NAME}" prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + nft add rule inet "${TABLE_NAME}" output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + nft add rule inet "${TABLE_NAME}" forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" + nft add rule inet "${TABLE_NAME}" forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" + nft add rule inet "${TABLE_NAME}" forward iifname "lo" accept comment "\"$uuid\"" + nft add rule inet "${TABLE_NAME}" postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" list_rules "$raw_uuid" [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } } @@ -162,22 +163,21 @@ if [[ $# -eq 0 ]]; then list_rules; exit 0; fi while [[ $# -gt 0 ]]; do case "$1" in -h|--help) show_help; exit 0 ;; - -v|--verbose) nft list table inet ${TABLE_NAME}; exit 0 ;; + -v|--verbose) nft list table inet "${TABLE_NAME}"; exit 0 ;; -L|-l) list_rules; exit 0 ;; -f) enable_forwarding; [[ $# -eq 1 ]] && exit 0 ;; -t*) h_str="${1#-t}" if [[ -z "$h_str" && -n "$2" ]]; then h_str="$2"; shift; fi - target_port="" # 'local' は削除 + target_port="" if [[ "$h_str" =~ ^:?([0-9]+):?$ ]]; then - # -t :80, -t 80:, -t 80 のいずれもポート指定として拾う target_port="${BASH_REMATCH[1]}" elif [[ -n "$h_str" ]]; then - target_port=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') + target_port=$(nft -a list chain inet "${TABLE_NAME}" prerouting | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') else - count=$(nft list chain inet ${TABLE_NAME} prerouting | grep -c "dnat") + count=$(nft list chain inet "${TABLE_NAME}" prerouting | grep -c "dnat") if [ "$count" -eq 1 ]; then - target_port=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dnat" | grep -o 'dport [0-9]*' | awk '{print $2}') + target_port=$(nft -a list chain inet "${TABLE_NAME}" prerouting | grep "dnat" | grep -o 'dport [0-9]*' | awk '{print $2}') fi fi [[ -n "$target_port" ]] && test_rule_by_port "$target_port" || msg "Error: Port/Handle not found." @@ -190,18 +190,18 @@ while [[ $# -gt 0 ]]; do if [[ "$FORCE_FLAG" == false ]]; then msg "Review rules to delete (Top-level only):" for h in "${handles[@]}"; do - nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | sed 's/^/ /' + nft -a list chain inet "${TABLE_NAME}" prerouting | grep "handle $h" | sed 's/^/ /' done read -p "Delete these rules and their sub-rules? (y/N): " confirm [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 fi for h in "${handles[@]}"; do - ri=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") + ri=$(nft -a list chain inet "${TABLE_NAME}" prerouting | grep "handle $h") [[ -z "$ri" ]] && continue uuid=$(echo "$ri" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) for c in prerouting output forward postrouting; do - nft -a list chain inet ${TABLE_NAME} "$c" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do - nft delete rule inet ${TABLE_NAME} "$c" handle "$rh" + nft -a list chain inet "${TABLE_NAME}" "$c" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do + nft delete rule inet "${TABLE_NAME}" "$c" handle "$rh" done done msg "Deleted associated with handle $h" From a22bad0622f25467ee7f398ae58958297fde6bd1 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 14:35:15 +0900 Subject: [PATCH 18/23] -d debug --- ipfn | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/ipfn b/ipfn index cc015c3..59d133d 100755 --- a/ipfn +++ b/ipfn @@ -1,8 +1,8 @@ #!/bin/bash # Name: ipfn -# Version: 1.2.7 +# Version: 1.2.8 # Date: 2026-01-22 -# Description: Fixed grep errors after 'all' delete and streamlined exit paths. +# Description: Fixed logical fall-through after all-clear to prevent grep errors. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" @@ -19,11 +19,11 @@ QUIET_MODE=false msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } init_nft() { - 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 - 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 + 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 + 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 } enable_forwarding() { @@ -56,7 +56,7 @@ list_rules() { msg "Forwarding Rules (ipfn):" printf "%-10s %-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 + nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | 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-9a-fA-F:]+\]):[0-9]+' | head -n 1) @@ -84,7 +84,7 @@ test_rule_by_port() { # --- 2. Deletion & Reset --- validate_rule_handle() { - nft -a list chain inet "${TABLE_NAME}" prerouting | grep "handle $1" | grep -q "dnat" + nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $1" | grep -q "dnat" } all_clear() { @@ -97,13 +97,18 @@ all_clear() { init_nft msg "All rules cleared successfully." list_rules - exit 0 # 全削除したらここで終了 + exit 0 # 確実にここで終わらせる } +# --- 3. Parsing Logic --- + parse_delete_targets() { local input="$1" local -a final_handles=() + + # 'all' や '*' なら即座に全削除へ(戻ってこない) [[ "$input" == "*" || "$input" == "all" ]] && all_clear + IFS=',' read -r -a parts <<< "$input" for part in "${parts[@]}"; do if [[ "$part" =~ ^:([0-9]+)$ ]]; then @@ -121,8 +126,6 @@ parse_delete_targets() { echo "${final_handles[@]}" } -# --- 3. Adding Logic --- - add_rule() { init_nft local raw_input="$1" @@ -149,7 +152,7 @@ add_rule() { [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } } -# --- 4. Main Parsing --- +# --- 4. Main Parsing Loop --- for arg in "$@"; do [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true @@ -173,11 +176,11 @@ while [[ $# -gt 0 ]]; do if [[ "$h_str" =~ ^:?([0-9]+):?$ ]]; then target_port="${BASH_REMATCH[1]}" elif [[ -n "$h_str" ]]; then - target_port=$(nft -a list chain inet "${TABLE_NAME}" prerouting | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') + target_port=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') else - count=$(nft list chain inet "${TABLE_NAME}" prerouting | grep -c "dnat") + count=$(nft list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep -c "dnat") if [ "$count" -eq 1 ]; then - target_port=$(nft -a list chain inet "${TABLE_NAME}" prerouting | grep "dnat" | grep -o 'dport [0-9]*' | awk '{print $2}') + target_port=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | grep -o 'dport [0-9]*' | awk '{print $2}') fi fi [[ -n "$target_port" ]] && test_rule_by_port "$target_port" || msg "Error: Port/Handle not found." @@ -185,22 +188,27 @@ while [[ $# -gt 0 ]]; do -*[d]*) h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') [[ -z "$h_str" ]] && { h_str="$2"; shift; } + + # parse_delete_targets の中で 'all' 判定があれば exit するので、ここには戻らない handles=($(parse_delete_targets "$h_str")) + [[ ${#handles[@]} -eq 0 ]] && { msg "No valid targets."; exit 1; } + if [[ "$FORCE_FLAG" == false ]]; then msg "Review rules to delete (Top-level only):" for h in "${handles[@]}"; do - nft -a list chain inet "${TABLE_NAME}" prerouting | grep "handle $h" | sed 's/^/ /' + nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h" | sed 's/^/ /' done read -p "Delete these rules and their sub-rules? (y/N): " confirm [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 fi + for h in "${handles[@]}"; do - ri=$(nft -a list chain inet "${TABLE_NAME}" prerouting | grep "handle $h") + ri=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h") [[ -z "$ri" ]] && continue uuid=$(echo "$ri" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) for c in prerouting output forward postrouting; do - nft -a list chain inet "${TABLE_NAME}" "$c" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do + nft -a list chain inet "${TABLE_NAME}" "$c" 2>/dev/null | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do nft delete rule inet "${TABLE_NAME}" "$c" handle "$rh" done done From faae2cbde71ca5642c54da9402e07d6fd9b3ba85 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 14:51:43 +0900 Subject: [PATCH 19/23] =?UTF-8?q?=E6=9C=80=E7=B5=82=E3=81=AB=E8=BF=91?= =?UTF-8?q?=E3=81=84=E7=AD=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn | 222 ++++++++++++++++++++++++----------------------------------- 1 file changed, 89 insertions(+), 133 deletions(-) diff --git a/ipfn b/ipfn index 59d133d..c0be79c 100755 --- a/ipfn +++ b/ipfn @@ -1,20 +1,21 @@ #!/bin/bash # Name: ipfn -# Version: 1.2.8 +# Version: 1.3.5 # Date: 2026-01-22 -# Description: Fixed logical fall-through after all-clear to prevent grep errors. +# Description: Smart testing for multi-target ports and fixed UUID listing format. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" fi TABLE_NAME="ipf_wrapper" +VERSION="1.3.5" PROTO="tcp" FORCE_FLAG=false SKIP_TEST=false QUIET_MODE=false -# --- 1. Utility & Core Setup --- +# --- 1. Utility & Core --- msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } @@ -26,29 +27,6 @@ init_nft() { nft add chain inet "${TABLE_NAME}" forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null } -enable_forwarding() { - msg "Optimizing kernel parameters for VM/LXD/Docker..." - sysctl -w net.ipv4.ip_forward=1 >/dev/null - sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null - sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null - sysctl -w net.bridge.bridge-nf-call-iptables=1 2>/dev/null - sysctl -w net.bridge.bridge-nf-call-ip6tables=1 2>/dev/null - msg "\e[32mForwarding and Bridge-NF enabled.\e[0m" -} - -show_help() { - echo "Usage: ipfn [OPTIONS] [RULES]" - echo "" - echo "Options:" - echo " -l, -L List all rules" - echo " -d HANDLE/:PORT/all Delete by handle, port (:80), or '*' for all" - echo " -R Reset: Clear ALL rules immediately" - echo " -t [HANDLE / PORT:] Test connectivity (e.g., -t 33, -t 80: or -t :80)" - echo " -f Enable IP forward & Bridge tuning / Skip test" - echo " -q Quiet mode (No output, Auto-yes)" - echo " -h Show this help" -} - list_rules() { [[ "$QUIET_MODE" == true ]] && return local highlight_uuid=$1 @@ -62,102 +40,69 @@ list_rules() { target=$(echo "$line" | grep -oE '([0-9.]+|\[[0-9a-fA-F:]+\]):[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) + + # 表示のズレを修正 (スペース調整) if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" else - printf "%-10s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" + printf " %-9s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" fi done } -test_rule_by_port() { - [[ "$QUIET_MODE" == true ]] && return - local lp=$1 - echo -n "Testing connectivity to :$lp... " - if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -iqE "succeeded|connected|open"; then +get_total_packets() { + local uuid=$1 + local total=0 + local counts=$(nft list table inet "${TABLE_NAME}" 2>/dev/null | grep "$uuid" | grep -o 'packets [0-9]*' | awk '{print $2}') + for c in $counts; do total=$((total + c)); done + echo "$total" +} + +test_port_simple() { + local p=$1 + echo -n "Testing :$p... " + if nc -z -v -w 2 127.0.0.1 "$p" 2>&1 | grep -iqE "succeeded|connected|open"; then echo -e "\e[32mOK\e[0m" else echo -e "\e[31mFAILED\e[0m" fi } -# --- 2. Deletion & Reset --- - -validate_rule_handle() { - nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $1" | grep -q "dnat" +test_specific_rule() { + local lp=$1 + local full_id=$2 + local short_id="${full_id#*:}" + echo -n "Verifying specific rule [${short_id:0:8}] on :$lp... " + local count_before=$(get_total_packets "$full_id") + nc -z -v -w 1 127.0.0.1 "$lp" >/dev/null 2>&1 + sleep 0.1 + local count_after=$(get_total_packets "$full_id") + if (( count_after > count_before )); then + echo -e "\e[32mPASSED\e[0m (Rule matched)" + else + echo -e "\e[31mSKIPPED\e[0m (Blocked by prior rule)" + fi } +# --- 2. Deletion & Reset --- + all_clear() { if [[ "$FORCE_FLAG" == false ]]; then - msg "\e[33mWarning: This will delete ALL forwarding rules.\e[0m" + echo -e "\e[33mWarning: This will delete ALL forwarding rules.\e[0m" read -p "Are you sure? (y/N): " confirm [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 fi nft delete table inet "${TABLE_NAME}" 2>/dev/null - init_nft - msg "All rules cleared successfully." - list_rules - exit 0 # 確実にここで終わらせる + init_nft; msg "All rules cleared successfully."; list_rules; exit 0 } -# --- 3. Parsing Logic --- - -parse_delete_targets() { - local input="$1" - local -a final_handles=() - - # 'all' や '*' なら即座に全削除へ(戻ってこない) - [[ "$input" == "*" || "$input" == "all" ]] && all_clear - - IFS=',' read -r -a parts <<< "$input" - for part in "${parts[@]}"; do - if [[ "$part" =~ ^:([0-9]+)$ ]]; then - local target_port="${BASH_REMATCH[1]}" - local found_h=$(nft -a list chain inet "${TABLE_NAME}" prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') - for h in $found_h; do final_handles+=("$h"); done - elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then - for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do - validate_rule_handle "$i" && final_handles+=("$i") - done - else - validate_rule_handle "$part" && final_handles+=("$part") - fi - done - echo "${final_handles[@]}" -} - -add_rule() { - init_nft - local raw_input="$1" - local lp tip tp - if [[ "$raw_input" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then - lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} - elif [[ "$raw_input" =~ ^([0-9]+):([0-9]+)$ ]]; then - lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[2]} - elif [[ "$raw_input" =~ ^([0-9]+)$ ]]; then - lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[1]} - else - msg "\e[31mError: Invalid rule format '$raw_input'\e[0m"; exit 1 - fi - local raw_uuid=$(cat /proc/sys/kernel/random/uuid) - local uuid="ipf-id:$raw_uuid" - local family="ip"; [[ "$tip" =~ : ]] && family="ip6" - nft add rule inet "${TABLE_NAME}" prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" - nft add rule inet "${TABLE_NAME}" output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" - nft add rule inet "${TABLE_NAME}" forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" - nft add rule inet "${TABLE_NAME}" forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" - nft add rule inet "${TABLE_NAME}" forward iifname "lo" accept comment "\"$uuid\"" - nft add rule inet "${TABLE_NAME}" postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" - list_rules "$raw_uuid" - [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } -} - -# --- 4. Main Parsing Loop --- +# --- 3. Main Loop --- for arg in "$@"; do [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true [[ "$arg" == "-R" ]] && RESET_MODE=true + [[ "$arg" == "--version" ]] && { echo "ipfn version ${VERSION}"; exit 0; } done if [[ "$RESET_MODE" == true ]]; then all_clear; fi @@ -166,58 +111,69 @@ if [[ $# -eq 0 ]]; then list_rules; exit 0; fi while [[ $# -gt 0 ]]; do case "$1" in -h|--help) show_help; exit 0 ;; - -v|--verbose) nft list table inet "${TABLE_NAME}"; exit 0 ;; -L|-l) list_rules; exit 0 ;; - -f) enable_forwarding; [[ $# -eq 1 ]] && exit 0 ;; - -t*) - h_str="${1#-t}" - if [[ -z "$h_str" && -n "$2" ]]; then h_str="$2"; shift; fi - target_port="" - if [[ "$h_str" =~ ^:?([0-9]+):?$ ]]; then - target_port="${BASH_REMATCH[1]}" - elif [[ -n "$h_str" ]]; then - target_port=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') - else - count=$(nft list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep -c "dnat") - if [ "$count" -eq 1 ]; then - target_port=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | grep -o 'dport [0-9]*' | awk '{print $2}') - fi - fi - [[ -n "$target_port" ]] && test_rule_by_port "$target_port" || msg "Error: Port/Handle not found." - exit 0 ;; -*[d]*) h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') [[ -z "$h_str" ]] && { h_str="$2"; shift; } - - # parse_delete_targets の中で 'all' 判定があれば exit するので、ここには戻らない - handles=($(parse_delete_targets "$h_str")) - - [[ ${#handles[@]} -eq 0 ]] && { msg "No valid targets."; exit 1; } - - if [[ "$FORCE_FLAG" == false ]]; then - msg "Review rules to delete (Top-level only):" - for h in "${handles[@]}"; do - nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h" | sed 's/^/ /' - done - read -p "Delete these rules and their sub-rules? (y/N): " confirm - [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 - fi - + if [[ "$h_str" == "all" || "$h_str" == "*" ]]; then all_clear; fi + handles=() + IFS=',' read -r -a parts <<< "$h_str" + for part in "${parts[@]}"; do + if [[ "$part" =~ ^:([0-9]+)$ ]]; then + p="${BASH_REMATCH[1]}" + for h in $(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dport $p" | grep -o 'handle [0-9]*' | awk '{print $2}'); do handles+=("$h"); done + else handles+=("$part"); fi + done for h in "${handles[@]}"; do - ri=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h") - [[ -z "$ri" ]] && continue - uuid=$(echo "$ri" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) + uuid=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h" | grep -o 'ipf-id:[a-z0-9-]*') + [[ -z "$uuid" ]] && continue for c in prerouting output forward postrouting; do nft -a list chain inet "${TABLE_NAME}" "$c" 2>/dev/null | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do nft delete rule inet "${TABLE_NAME}" "$c" handle "$rh" done done - msg "Deleted associated with handle $h" + msg "Deleted handle $h" done list_rules; exit 0 ;; + -t*) + h_str="${1#-t}"; [[ -z "$h_str" && -n "$2" ]] && { h_str="$2"; shift; } + tp="" + # スマート・テストロジック + if [[ "$h_str" =~ ^:?([0-9]+):?$ ]]; then + tp="${BASH_REMATCH[1]}" + elif [[ -n "$h_str" ]]; then + tp=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') + else + # 引数なしの場合は一番上のルールのポートを取得 + tp=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | head -n 1 | grep -o 'dport [0-9]*' | awk '{print $2}') + fi + + [[ -n "$tp" ]] && test_port_simple "$tp" || msg "Error: No active rules found." + exit 0 ;; *) - if [[ "$1" =~ ^[0-9] ]]; then add_rule "$1"; exit 0 - else msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1; fi ;; + if [[ "$1" =~ ^[0-9] ]]; then + 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 + + nft list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep -q "dport $lp" && multi_mode=true || multi_mode=false + u_raw=$(cat /proc/sys/kernel/random/uuid); u="ipf-id:$u_raw"; fam="ip"; [[ "$tip" =~ : ]] && fam="ip6" + + nft add rule inet "${TABLE_NAME}" prerouting "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" + nft add rule inet "${TABLE_NAME}" output "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" + nft add rule inet "${TABLE_NAME}" forward $fam daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$u\"" + nft add rule inet "${TABLE_NAME}" forward $fam saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$u\"" + nft add rule inet "${TABLE_NAME}" forward iifname "lo" accept comment "\"$u\"" + nft add rule inet "${TABLE_NAME}" postrouting $fam daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$u\"" + + list_rules "$u_raw" + [[ "$multi_mode" == true ]] && msg "\e[33mNote: Port :$lp is now multi-targeted.\e[0m" + [[ "$SKIP_TEST" == false ]] && { echo ""; test_specific_rule "$lp" "$u"; } + exit 0 + fi ;; esac shift done From 229f75699497f93aed52fd0c5df6810f76c58459 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 15:18:56 +0900 Subject: [PATCH 20/23] BUGBUGBUG --- ipfn | 191 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 112 insertions(+), 79 deletions(-) diff --git a/ipfn b/ipfn index c0be79c..5c33811 100755 --- a/ipfn +++ b/ipfn @@ -1,21 +1,35 @@ #!/bin/bash # Name: ipfn -# Version: 1.3.5 +# Version: 1.4.3 # Date: 2026-01-22 -# Description: Smart testing for multi-target ports and fixed UUID listing format. +# Description: Register rules first, then report the "honest" status of the connection. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" fi TABLE_NAME="ipf_wrapper" -VERSION="1.3.5" +VERSION="1.4.3" PROTO="tcp" FORCE_FLAG=false SKIP_TEST=false QUIET_MODE=false -# --- 1. Utility & Core --- +# --- 1. Functions --- + +show_help() { + echo "ipfn version ${VERSION}" + echo "Usage: ipfn [OPTIONS] [RULES]" + echo "" + echo "Options:" + echo " -l, -L List all rules" + echo " -d HANDLE/:PORT/all Delete by handle, port (:80), or 'all'" + echo " -R Reset: Clear ALL rules immediately" + echo " -t [HANDLE / :PORT] Test connectivity (Check both rule and target)" + echo " -f Force: No confirmation & Skip test" + echo " -q Quiet mode (No output)" + echo " -h, --help Show help" +} msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } @@ -27,6 +41,63 @@ init_nft() { nft add chain inet "${TABLE_NAME}" forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null } +get_total_packets() { + local uuid=$1 + local total=0 + local counts=$(nft list table inet "${TABLE_NAME}" 2>/dev/null | grep "$uuid" | grep -o 'packets [0-9]*' | awk '{print $2}') + for c in $counts; do total=$((total + c)); done + echo "$total" +} + +# 登録は完了させた上で、テスト結果だけ「正直に」出す +test_strict_handle() { + local h=$1 + local silent=$2 + local info=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h") + [[ -z "$info" ]] && { [[ "$silent" != "true" ]] && echo -e "Handle $h \e[31mNOT FOUND\e[0m"; return; } + + local lp=$(echo "$info" | grep -o 'dport [0-9]*' | awk '{print $2}') + local uuid=$(echo "$info" | grep -o 'ipf-id:[a-z0-9-]*') + local short_id=$(echo "$uuid" | cut -d':' -f2 | cut -c1-8) + + [[ "$silent" != "true" ]] && echo -n "Testing handle $h (:$lp) [${short_id}]... " || echo -n "Verifying handle $h on :$lp... " + + local count_before=$(get_total_packets "$uuid") + + # 接続確認 (ターゲットが落ちていてもOK、ただの結果報告) + local connect_ok=false + nc -z -v -w 1 127.0.0.1 "$lp" >/dev/null 2>&1 && connect_ok=true + + sleep 0.1 + local count_after=$(get_total_packets "$uuid") + + if [[ "$connect_ok" == true ]]; then + if (( count_after > count_before )); then + echo -e "\e[32mPASSED (Rule matched & Connected)\e[0m" + else + echo -e "\e[33mSKIPPED (Connected, but via prior rule)\e[0m" + fi + else + # 接続できなかった場合 + if (( count_after > count_before )); then + # 登録作業は成功しているが、ターゲットが未起動 + echo -e "\e[33mOFFLINE (Rule matched, but Target Down)\e[0m" + else + echo -e "\e[31mFAILED (Blocked & No Response)\e[0m" + fi + fi +} + +test_port_simple() { + local p=$1 + echo -n "Testing :$p... " + if nc -z -v -w 1 127.0.0.1 "$p" 2>&1 | grep -iqE "succeeded|connected|open"; then + echo -e "\e[32mOK\e[0m" + else + echo -e "\e[31mOFFLINE (No response)\e[0m" + fi +} + list_rules() { [[ "$QUIET_MODE" == true ]] && return local highlight_uuid=$1 @@ -40,8 +111,6 @@ list_rules() { target=$(echo "$line" | grep -oE '([0-9.]+|\[[0-9a-fA-F:]+\]):[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) - - # 表示のズレを修正 (スペース調整) if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" else @@ -50,42 +119,6 @@ list_rules() { done } -get_total_packets() { - local uuid=$1 - local total=0 - local counts=$(nft list table inet "${TABLE_NAME}" 2>/dev/null | grep "$uuid" | grep -o 'packets [0-9]*' | awk '{print $2}') - for c in $counts; do total=$((total + c)); done - echo "$total" -} - -test_port_simple() { - local p=$1 - echo -n "Testing :$p... " - if nc -z -v -w 2 127.0.0.1 "$p" 2>&1 | grep -iqE "succeeded|connected|open"; then - echo -e "\e[32mOK\e[0m" - else - echo -e "\e[31mFAILED\e[0m" - fi -} - -test_specific_rule() { - local lp=$1 - local full_id=$2 - local short_id="${full_id#*:}" - echo -n "Verifying specific rule [${short_id:0:8}] on :$lp... " - local count_before=$(get_total_packets "$full_id") - nc -z -v -w 1 127.0.0.1 "$lp" >/dev/null 2>&1 - sleep 0.1 - local count_after=$(get_total_packets "$full_id") - if (( count_after > count_before )); then - echo -e "\e[32mPASSED\e[0m (Rule matched)" - else - echo -e "\e[31mSKIPPED\e[0m (Blocked by prior rule)" - fi -} - -# --- 2. Deletion & Reset --- - all_clear() { if [[ "$FORCE_FLAG" == false ]]; then echo -e "\e[33mWarning: This will delete ALL forwarding rules.\e[0m" @@ -96,7 +129,7 @@ all_clear() { init_nft; msg "All rules cleared successfully."; list_rules; exit 0 } -# --- 3. Main Loop --- +# --- 2. Flag Pre-processing --- for arg in "$@"; do [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true @@ -108,6 +141,8 @@ done if [[ "$RESET_MODE" == true ]]; then all_clear; fi if [[ $# -eq 0 ]]; then list_rules; exit 0; fi +# --- 3. Main Loop --- + while [[ $# -gt 0 ]]; do case "$1" in -h|--help) show_help; exit 0 ;; @@ -137,43 +172,41 @@ while [[ $# -gt 0 ]]; do list_rules; exit 0 ;; -t*) h_str="${1#-t}"; [[ -z "$h_str" && -n "$2" ]] && { h_str="$2"; shift; } - tp="" - # スマート・テストロジック - if [[ "$h_str" =~ ^:?([0-9]+):?$ ]]; then - tp="${BASH_REMATCH[1]}" - elif [[ -n "$h_str" ]]; then - tp=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') - else - # 引数なしの場合は一番上のルールのポートを取得 - tp=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | head -n 1 | grep -o 'dport [0-9]*' | awk '{print $2}') - fi - - [[ -n "$tp" ]] && test_port_simple "$tp" || msg "Error: No active rules found." - exit 0 ;; - *) - if [[ "$1" =~ ^[0-9] ]]; then - 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]} + if [[ "$h_str" =~ ^:?([0-9]+):?$ ]]; then + target_val="${BASH_REMATCH[1]}" + if nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep -q "handle $target_val"; then + test_strict_handle "$target_val" + else + test_port_simple "$target_val" fi - - nft list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep -q "dport $lp" && multi_mode=true || multi_mode=false - u_raw=$(cat /proc/sys/kernel/random/uuid); u="ipf-id:$u_raw"; fam="ip"; [[ "$tip" =~ : ]] && fam="ip6" - - nft add rule inet "${TABLE_NAME}" prerouting "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" - nft add rule inet "${TABLE_NAME}" output "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\"" - nft add rule inet "${TABLE_NAME}" forward $fam daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$u\"" - nft add rule inet "${TABLE_NAME}" forward $fam saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$u\"" - nft add rule inet "${TABLE_NAME}" forward iifname "lo" accept comment "\"$u\"" - nft add rule inet "${TABLE_NAME}" postrouting $fam daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$u\"" - - list_rules "$u_raw" - [[ "$multi_mode" == true ]] && msg "\e[33mNote: Port :$lp is now multi-targeted.\e[0m" - [[ "$SKIP_TEST" == false ]] && { echo ""; test_specific_rule "$lp" "$u"; } - exit 0 - fi ;; + else + top_h=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | head -n 1 | grep -o 'handle [0-9]*' | awk '{print $2}') + [[ -n "$top_h" ]] && test_strict_handle "$top_h" || msg "Error: No rules found." + 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" + + # insertを使うので常に最新が優先 + 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" "true" + fi + exit 0 ;; + *) msg "Error: Unknown option '$1'"; show_help; exit 1 ;; esac shift done From 38e518b6f0049bc21bcb2b30177f51f50cd53ad4 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 15:33:06 +0900 Subject: [PATCH 21/23] =?UTF-8?q?-t=20=E5=A4=A7=E5=B9=85=E6=8B=A1=E5=BC=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn | 118 ++++++++++++++++++++++++++++------------------------------- 1 file changed, 55 insertions(+), 63 deletions(-) diff --git a/ipfn b/ipfn index 5c33811..047c3e5 100755 --- a/ipfn +++ b/ipfn @@ -1,35 +1,21 @@ #!/bin/bash # Name: ipfn -# Version: 1.4.3 +# Version: 1.4.5 # Date: 2026-01-22 -# Description: Register rules first, then report the "honest" status of the connection. +# Description: Fixed regex to correctly distinguish between addr:port and port:addr:port. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" fi TABLE_NAME="ipf_wrapper" -VERSION="1.4.3" +VERSION="1.4.5" PROTO="tcp" FORCE_FLAG=false SKIP_TEST=false QUIET_MODE=false -# --- 1. Functions --- - -show_help() { - echo "ipfn version ${VERSION}" - echo "Usage: ipfn [OPTIONS] [RULES]" - echo "" - echo "Options:" - echo " -l, -L List all rules" - echo " -d HANDLE/:PORT/all Delete by handle, port (:80), or 'all'" - echo " -R Reset: Clear ALL rules immediately" - echo " -t [HANDLE / :PORT] Test connectivity (Check both rule and target)" - echo " -f Force: No confirmation & Skip test" - echo " -q Quiet mode (No output)" - echo " -h, --help Show help" -} +# --- Functions --- msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } @@ -49,55 +35,48 @@ get_total_packets() { echo "$total" } -# 登録は完了させた上で、テスト結果だけ「正直に」出す +test_connection() { + local ip=$1 + local port=$2 + nc -z -v -w 1 "$ip" "$port" >/dev/null 2>&1 +} + test_strict_handle() { local h=$1 - local silent=$2 local info=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h") - [[ -z "$info" ]] && { [[ "$silent" != "true" ]] && echo -e "Handle $h \e[31mNOT FOUND\e[0m"; return; } + [[ -z "$info" ]] && { echo -e "Handle $h \e[31mNOT FOUND\e[0m"; return; } local lp=$(echo "$info" | grep -o 'dport [0-9]*' | awk '{print $2}') + local target=$(echo "$info" | grep -oE 'to ([0-9.]+|\[[0-9a-fA-F:]+\]):[0-9]+' | awk '{print $2}') + local tip=$(echo "$target" | cut -d':' -f1 | tr -d '[]') + local tp=$(echo "$target" | cut -d':' -f2) local uuid=$(echo "$info" | grep -o 'ipf-id:[a-z0-9-]*') local short_id=$(echo "$uuid" | cut -d':' -f2 | cut -c1-8) - [[ "$silent" != "true" ]] && echo -n "Testing handle $h (:$lp) [${short_id}]... " || echo -n "Verifying handle $h on :$lp... " - + echo -n "Testing handle $h (Local :$lp -> Target $tip:$tp) [${short_id}]... " local count_before=$(get_total_packets "$uuid") - # 接続確認 (ターゲットが落ちていてもOK、ただの結果報告) - local connect_ok=false - nc -z -v -w 1 127.0.0.1 "$lp" >/dev/null 2>&1 && connect_ok=true - - sleep 0.1 - local count_after=$(get_total_packets "$uuid") - - if [[ "$connect_ok" == true ]]; then + if test_connection "$tip" "$tp"; then + 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[32mPASSED (Rule matched & Connected)\e[0m" + echo -e "\e[32mPASSED (Rule active & Target UP)\e[0m" else - echo -e "\e[33mSKIPPED (Connected, but via prior rule)\e[0m" + echo -e "\e[33mSKIPPED (Target UP, but 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 (Blocked & No Response)\e[0m" + echo -e "\e[31mFAILED (Target Down & Rule blocked)\e[0m" fi fi } -test_port_simple() { - local p=$1 - echo -n "Testing :$p... " - if nc -z -v -w 1 127.0.0.1 "$p" 2>&1 | grep -iqE "succeeded|connected|open"; then - echo -e "\e[32mOK\e[0m" - else - echo -e "\e[31mOFFLINE (No response)\e[0m" - fi -} - list_rules() { [[ "$QUIET_MODE" == true ]] && return local highlight_uuid=$1 @@ -129,7 +108,7 @@ all_clear() { init_nft; msg "All rules cleared successfully."; list_rules; exit 0 } -# --- 2. Flag Pre-processing --- +# --- Flag Pre-processing --- for arg in "$@"; do [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true @@ -141,11 +120,14 @@ done if [[ "$RESET_MODE" == true ]]; then all_clear; fi if [[ $# -eq 0 ]]; then list_rules; exit 0; fi -# --- 3. Main Loop --- +# --- Main Loop --- while [[ $# -gt 0 ]]; do case "$1" in - -h|--help) show_help; exit 0 ;; + -h|--help) + echo "Usage: ipfn [OPTIONS] [RULES]" + echo "Tests: -t 5 (handle), -t :80 (port), -t 1.1.1.1:80 (direct), -t 80:1.1.1.1:80 (full)" + exit 0 ;; -L|-l) list_rules; exit 0 ;; -*[d]*) h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') @@ -172,16 +154,29 @@ while [[ $# -gt 0 ]]; do list_rules; exit 0 ;; -t*) h_str="${1#-t}"; [[ -z "$h_str" && -n "$2" ]] && { h_str="$2"; shift; } - if [[ "$h_str" =~ ^:?([0-9]+):?$ ]]; then - target_val="${BASH_REMATCH[1]}" - if nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep -q "handle $target_val"; then - test_strict_handle "$target_val" - else - test_port_simple "$target_val" - fi - else + # 1. port:addr:port + if [[ "$h_str" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} + echo -n "Checking Target $tip:$tp... " + test_connection "$tip" "$tp" && echo -e "\e[32mUP\e[0m" || echo -e "\e[31mDOWN\e[0m" + echo -n "Checking Local :$lp... " + nc -z -v -w 1 127.0.0.1 "$lp" >/dev/null 2>&1 && echo -e "\e[32mOPEN\e[0m" || echo -e "\e[31mCLOSED\e[0m" + # 2. addr:port + elif [[ "$h_str" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):([0-9]+)$ ]]; then + tip=${BASH_REMATCH[1]}; tp=${BASH_REMATCH[2]} + echo -n "Checking Target $tip:$tp... " + test_connection "$tip" "$tp" && echo -e "\e[32mUP\e[0m" || echo -e "\e[31mDOWN\e[0m" + # 3. :port or port: + elif [[ "$h_str" =~ ^:([0-9]+)$ || "$h_str" =~ ^([0-9]+):$ ]]; then + p="${BASH_REMATCH[1]}${BASH_REMATCH[2]}" + echo -n "Checking Local :$p... " + nc -z -v -w 1 127.0.0.1 "$p" >/dev/null 2>&1 && echo -e "\e[32mOK\e[0m" || echo -e "\e[31mOFFLINE\e[0m" + # 4. handle + elif [[ "$h_str" =~ ^[0-9]+$ ]]; then + test_strict_handle "$h_str" + else top_h=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | head -n 1 | grep -o 'handle [0-9]*' | awk '{print $2}') - [[ -n "$top_h" ]] && test_strict_handle "$top_h" || msg "Error: No rules found." + [[ -n "$top_h" ]] && test_strict_handle "$top_h" || msg "Error: No rules to test." fi exit 0 ;; [0-9]*) @@ -191,22 +186,19 @@ while [[ $# -gt 0 ]]; do 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" - - # insertを使うので常に最新が優先 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" "true" + echo ""; test_strict_handle "$new_h" fi exit 0 ;; - *) msg "Error: Unknown option '$1'"; show_help; exit 1 ;; + *) msg "Error: Unknown option '$1'"; exit 1 ;; esac shift done From 00c5a46278fb6c1129066270b915bec352cd216b Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 17:23:37 +0900 Subject: [PATCH 22/23] =?UTF-8?q?HELP=E5=BE=A9=E6=B4=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ipfn | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/ipfn b/ipfn index 047c3e5..829ef83 100755 --- a/ipfn +++ b/ipfn @@ -1,21 +1,48 @@ #!/bin/bash # Name: ipfn -# Version: 1.4.5 +# Version: 1.4.6 # Date: 2026-01-22 -# Description: Fixed regex to correctly distinguish between addr:port and port:addr:port. +# Description: Restored full help message and kept fixed regex for multi-pattern testing. if [ "$(id -u)" -ne 0 ]; then exec sudo "$0" "$@" fi TABLE_NAME="ipf_wrapper" -VERSION="1.4.5" +VERSION="1.4.6" PROTO="tcp" FORCE_FLAG=false SKIP_TEST=false QUIET_MODE=false -# --- Functions --- +# --- 1. Functions --- + +show_help() { + echo "ipfn version ${VERSION}" + echo "Usage: ipfn [OPTIONS] [RULES]" + echo "" + echo "Options:" + 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 rules immediately" + echo " -t [TARGET] Test connectivity (See 'Test Patterns' below)" + echo " -f Force: No confirmation & Skip test" + echo " -q Quiet mode: No output (implies -f)" + echo " -h, --help Show this help message" + echo "" + echo "Rules Syntax:" + echo " LPORT:TIP:TPORT Forward LPORT to TIP:TPORT (e.g. 80:10.0.0.1:80)" + echo " LPORT:TPORT Forward LPORT to 127.0.0.1:TPORT" + echo " LPORT Forward LPORT to 127.0.0.1:LPORT" + echo "" + echo "Test Patterns (-t):" + echo " -t 5 Strict test: Verify Handle 5 (Entry -> Exit -> Counter)" + echo " -t :80 Simple test: Check if Local port :80 is open" + echo " -t 1.1.1.1:80 Direct test: Check if Target 1.1.1.1:80 is UP" + echo " -t 80:1.1.1.1:80 Full test: Check both Target UP and Local :80 OPEN" + echo "" + echo "Note: New rules are INSERTED at the top (Latest rule takes priority)." +} msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } @@ -108,7 +135,7 @@ all_clear() { init_nft; msg "All rules cleared successfully."; list_rules; exit 0 } -# --- Flag Pre-processing --- +# --- 2. Flag Pre-processing --- for arg in "$@"; do [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true @@ -120,14 +147,11 @@ done if [[ "$RESET_MODE" == true ]]; then all_clear; fi if [[ $# -eq 0 ]]; then list_rules; exit 0; fi -# --- Main Loop --- +# --- 3. Main Loop --- while [[ $# -gt 0 ]]; do case "$1" in - -h|--help) - echo "Usage: ipfn [OPTIONS] [RULES]" - echo "Tests: -t 5 (handle), -t :80 (port), -t 1.1.1.1:80 (direct), -t 80:1.1.1.1:80 (full)" - exit 0 ;; + -h|--help) show_help; exit 0 ;; -L|-l) list_rules; exit 0 ;; -*[d]*) h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') @@ -154,24 +178,20 @@ while [[ $# -gt 0 ]]; do list_rules; exit 0 ;; -t*) h_str="${1#-t}"; [[ -z "$h_str" && -n "$2" ]] && { h_str="$2"; shift; } - # 1. port:addr:port if [[ "$h_str" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} echo -n "Checking Target $tip:$tp... " test_connection "$tip" "$tp" && echo -e "\e[32mUP\e[0m" || echo -e "\e[31mDOWN\e[0m" echo -n "Checking Local :$lp... " nc -z -v -w 1 127.0.0.1 "$lp" >/dev/null 2>&1 && echo -e "\e[32mOPEN\e[0m" || echo -e "\e[31mCLOSED\e[0m" - # 2. addr:port elif [[ "$h_str" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):([0-9]+)$ ]]; then tip=${BASH_REMATCH[1]}; tp=${BASH_REMATCH[2]} echo -n "Checking Target $tip:$tp... " test_connection "$tip" "$tp" && echo -e "\e[32mUP\e[0m" || echo -e "\e[31mDOWN\e[0m" - # 3. :port or port: elif [[ "$h_str" =~ ^:([0-9]+)$ || "$h_str" =~ ^([0-9]+):$ ]]; then p="${BASH_REMATCH[1]}${BASH_REMATCH[2]}" echo -n "Checking Local :$p... " nc -z -v -w 1 127.0.0.1 "$p" >/dev/null 2>&1 && echo -e "\e[32mOK\e[0m" || echo -e "\e[31mOFFLINE\e[0m" - # 4. handle elif [[ "$h_str" =~ ^[0-9]+$ ]]; then test_strict_handle "$h_str" else From 44fc5aa6ca8035c4ac2a099da4fee2cde198eaf0 Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 22 Jan 2026 19:42:05 +0900 Subject: [PATCH 23/23] Rename ipfn to ipf --- ipf | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ipfn | 224 -------------------------------------------------------- 2 files changed, 236 insertions(+), 224 deletions(-) create mode 100755 ipf delete mode 100755 ipfn diff --git a/ipf b/ipf new file mode 100755 index 0000000..69e088c --- /dev/null +++ b/ipf @@ -0,0 +1,236 @@ +#!/bin/bash +# Name: ipfn +# Version: 1.2.4 +# Date: 2026-01-22 +# Description: Enhanced for VM/LXD/Docker with bridge-nf tuning and robust forwarding. + +if [ "$(id -u)" -ne 0 ]; then + exec sudo "$0" "$@" +fi + +TABLE_NAME="ipf_wrapper" +PROTO="tcp" +FORCE_FLAG=false +SKIP_TEST=false +QUIET_MODE=false + +# --- 1. Utility & Core Setup --- + +msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } + +init_nft() { + nft add table inet ${TABLE_NAME} 2>/dev/null + # NAT Chains + 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 + # Filter Chain (For VM/CT Forwarding) + nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority 0 \; policy accept \; } 2>/dev/null +} + +enable_forwarding() { + msg "Optimizing kernel parameters for VM/LXD/Docker..." + sysctl -w net.ipv4.ip_forward=1 >/dev/null + sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null + sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null + # ブリッジパケットをNetfilterに渡す設定 (存在する場合のみ) + sysctl -w net.bridge.bridge-nf-call-iptables=1 2>/dev/null + sysctl -w net.bridge.bridge-nf-call-ip6tables=1 2>/dev/null + msg "\e[32mForwarding and Bridge-NF enabled.\e[0m" +} + +show_help() { + echo "Usage: ipfn [OPTIONS] [RULES]" + echo "" + echo "Rules Format:" + echo " 80:10.10.100.5:8080 Full (LocalPort:TargetIP:TargetPort)" + echo " 80:8080 IP defaults to 127.0.0.1" + echo " 11434 Map same port to 127.0.0.1" + echo "" + echo "Options:" + echo " -l, -L List all rules" + echo " -d HANDLE/:PORT/all Delete specific rules or '*' for all" + echo " -R Reset: Clear ALL rules immediately" + echo " -q Quiet mode (No output, Auto-yes)" + echo " -t [HANDLE] Test connectivity" + echo " -f Enable IP forward & Bridge tuning / Skip test" + echo " -v Verbose (raw nftables output)" + echo " -h Show this help" +} + +list_rules() { + [[ "$QUIET_MODE" == true ]] && return + local highlight_uuid=$1 + init_nft + msg "Forwarding Rules (ipfn):" + printf "%-10s %-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-9a-fA-F:]+\]):[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) + if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then + printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" + else + printf "%-10s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" + fi + done +} + +test_rule_by_port() { + [[ "$QUIET_MODE" == true ]] && return + local lp=$1 + echo -n "Testing connectivity to :$lp... " + if nc -z -v -w 2 127.0.0.1 "$lp" 2>&1 | grep -iqE "succeeded|connected|open"; then + echo -e "\e[32mOK\e[0m" + else + echo -e "\e[31mFAILED\e[0m" + fi +} + +# --- 2. Deletion Logic --- + +validate_rule_handle() { + nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $1" | grep -q "dnat" +} + +all_clear() { + if [[ "$FORCE_FLAG" == false ]]; then + msg "\e[33mWarning: This will delete ALL forwarding rules.\e[0m" + read -p "Are you sure? (y/N): " confirm + [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 + fi + nft delete table inet ${TABLE_NAME} 2>/dev/null + init_nft + msg "All rules cleared successfully." + exit 0 +} + +parse_delete_targets() { + local input=$1 + local -a final_handles=() + [[ "$input" == "*" || "$input" == "all" ]] && all_clear + IFS=',' read -r -a parts <<< "$input" + for part in "${parts[@]}"; do + if [[ "$part" =~ ^:([0-9]+)$ ]]; then + local target_port="${BASH_REMATCH[1]}" + local found_h=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "dport $target_port" | grep -o 'handle [0-9]*' | awk '{print $2}') + for h in $found_h; do final_handles+=("$h"); done + elif [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then + for ((i=${BASH_REMATCH[1]}; i<=${BASH_REMATCH[2]}; i++)); do + validate_rule_handle "$i" && final_handles+=("$i") + done + else + validate_rule_handle "$part" && final_handles+=("$part") + fi + done + echo "${final_handles[@]}" +} + +# --- 3. Adding Logic --- + +add_rule() { + init_nft + local raw_input=$1 + local lp tip tp + + if [[ "$raw_input" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} + elif [[ "$raw_input" =~ ^([0-9]+):([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[2]} + elif [[ "$raw_input" =~ ^([0-9]+)$ ]]; then + lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[1]} + else + msg "\e[31mError: Invalid rule format '$raw_input'\e[0m"; exit 1 + fi + + local raw_uuid=$(cat /proc/sys/kernel/random/uuid) + local uuid="ipf-id:$raw_uuid" + local family="ip"; [[ "$tip" =~ : ]] && family="ip6" + + # DNAT + nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$lp" dnat $family to "$tip:$tp" comment "\"$uuid\"" + + # FORWARD (VM/CT対応) + nft add rule inet ${TABLE_NAME} forward $family daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward $family saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$uuid\"" + nft add rule inet ${TABLE_NAME} forward iifname "lo" accept comment "\"$uuid\"" + + # POSTROUTING (MASQUERADE: 戻りパケットの保証) + nft add rule inet ${TABLE_NAME} postrouting $family daddr "$tip" "$PROTO" dport "$tp" masquerade comment "\"$uuid\"" + + list_rules "$raw_uuid" + [[ "$SKIP_TEST" == false ]] && { echo ""; test_rule_by_port "$lp"; } +} + +# --- 4. Main Parsing --- + +for arg in "$@"; do + [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true + [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true + [[ "$arg" == "-R" ]] && RESET_MODE=true +done + +if [[ "$RESET_MODE" == true ]]; then all_clear; fi + +if [[ $# -eq 0 ]]; then list_rules; exit 0; fi + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) show_help; exit 0 ;; + -v|--verbose) nft list table inet ${TABLE_NAME}; exit 0 ;; + -L|-l) list_rules; exit 0 ;; + -f) + enable_forwarding + [[ $# -eq 1 ]] && exit 0 ;; + -t*) + h_str="${1#-t}" + if [[ -z "$h_str" && ( "$2" =~ ^[0-9,-]+$ ) ]]; then h_str="$2"; shift; fi + if [[ -z "$h_str" ]]; then + count=$(nft list chain inet ${TABLE_NAME} prerouting | grep -c "dnat") + if [ "$count" -eq 1 ]; then + h_str=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle" | awk '{print $NF}') + else + msg "Error: Multiple rules exist. Specify a handle."; exit 1 + fi + fi + lp=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h_str" | grep -o 'dport [0-9]*' | awk '{print $2}') + [[ -n "$lp" ]] && test_rule_by_port "$lp" || msg "Error: Handle $h_str not found." + exit 0 ;; + -*[d]*) + h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') + [[ -z "$h_str" ]] && { h_str="$2"; shift; } + handles=($(parse_delete_targets "$h_str")) + [[ ${#handles[@]} -eq 0 ]] && { msg "No valid targets."; exit 1; } + if [[ "$FORCE_FLAG" == false ]]; then + msg "Review rules to delete (Top-level only):" + for h in "${handles[@]}"; do + nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h" | sed 's/^/ /' + done + read -p "Delete these rules and their sub-rules? (y/N): " confirm + [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 + fi + for h in "${handles[@]}"; do + ri=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $h") + [[ -z "$ri" ]] && continue + uuid=$(echo "$ri" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1) + for c in prerouting output forward postrouting; do + nft -a list chain inet ${TABLE_NAME} "$c" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do + nft delete rule inet ${TABLE_NAME} "$c" handle "$rh" + done + done + msg "Deleted associated with handle $h" + done + list_rules; exit 0 ;; + *) + if [[ "$1" =~ ^[0-9] ]]; then + add_rule "$1"; exit 0 + else + msg "\e[31mError: Unknown option '$1'\e[0m\n"; show_help; exit 1 + fi ;; + esac + shift +done diff --git a/ipfn b/ipfn deleted file mode 100755 index 829ef83..0000000 --- a/ipfn +++ /dev/null @@ -1,224 +0,0 @@ -#!/bin/bash -# Name: ipfn -# Version: 1.4.6 -# Date: 2026-01-22 -# Description: Restored full help message and kept fixed regex for multi-pattern testing. - -if [ "$(id -u)" -ne 0 ]; then - exec sudo "$0" "$@" -fi - -TABLE_NAME="ipf_wrapper" -VERSION="1.4.6" -PROTO="tcp" -FORCE_FLAG=false -SKIP_TEST=false -QUIET_MODE=false - -# --- 1. Functions --- - -show_help() { - echo "ipfn version ${VERSION}" - echo "Usage: ipfn [OPTIONS] [RULES]" - echo "" - echo "Options:" - 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 rules immediately" - echo " -t [TARGET] Test connectivity (See 'Test Patterns' below)" - echo " -f Force: No confirmation & Skip test" - echo " -q Quiet mode: No output (implies -f)" - echo " -h, --help Show this help message" - echo "" - echo "Rules Syntax:" - echo " LPORT:TIP:TPORT Forward LPORT to TIP:TPORT (e.g. 80:10.0.0.1:80)" - echo " LPORT:TPORT Forward LPORT to 127.0.0.1:TPORT" - echo " LPORT Forward LPORT to 127.0.0.1:LPORT" - echo "" - echo "Test Patterns (-t):" - echo " -t 5 Strict test: Verify Handle 5 (Entry -> Exit -> Counter)" - echo " -t :80 Simple test: Check if Local port :80 is open" - echo " -t 1.1.1.1:80 Direct test: Check if Target 1.1.1.1:80 is UP" - echo " -t 80:1.1.1.1:80 Full test: Check both Target UP and Local :80 OPEN" - echo "" - echo "Note: New rules are INSERTED at the top (Latest rule takes priority)." -} - -msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; } - -init_nft() { - 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 - 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 -} - -get_total_packets() { - local uuid=$1 - local total=0 - local counts=$(nft list table inet "${TABLE_NAME}" 2>/dev/null | grep "$uuid" | grep -o 'packets [0-9]*' | awk '{print $2}') - for c in $counts; do total=$((total + c)); done - echo "$total" -} - -test_connection() { - local ip=$1 - local port=$2 - nc -z -v -w 1 "$ip" "$port" >/dev/null 2>&1 -} - -test_strict_handle() { - local h=$1 - local info=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h") - [[ -z "$info" ]] && { echo -e "Handle $h \e[31mNOT FOUND\e[0m"; return; } - - local lp=$(echo "$info" | grep -o 'dport [0-9]*' | awk '{print $2}') - local target=$(echo "$info" | grep -oE 'to ([0-9.]+|\[[0-9a-fA-F:]+\]):[0-9]+' | awk '{print $2}') - local tip=$(echo "$target" | cut -d':' -f1 | tr -d '[]') - local tp=$(echo "$target" | cut -d':' -f2) - local uuid=$(echo "$info" | grep -o 'ipf-id:[a-z0-9-]*') - local short_id=$(echo "$uuid" | cut -d':' -f2 | cut -c1-8) - - echo -n "Testing handle $h (Local :$lp -> Target $tip:$tp) [${short_id}]... " - local count_before=$(get_total_packets "$uuid") - - if test_connection "$tip" "$tp"; then - 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[32mPASSED (Rule active & Target UP)\e[0m" - else - echo -e "\e[33mSKIPPED (Target UP, but 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 - fi -} - -list_rules() { - [[ "$QUIET_MODE" == true ]] && return - local highlight_uuid=$1 - init_nft - msg "Forwarding Rules (ipfn):" - printf "%-10s %-6s %-20s %-20s %-10s\n" "HANDLE" "PROTO" "LOCAL" "TARGET" "UUID" - echo "-----------------------------------------------------------------------------------" - nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | 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-9a-fA-F:]+\]):[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) - if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then - printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" - else - printf " %-9s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}" - fi - done -} - -all_clear() { - if [[ "$FORCE_FLAG" == false ]]; then - echo -e "\e[33mWarning: This will delete ALL forwarding rules.\e[0m" - read -p "Are you sure? (y/N): " confirm - [[ ! "$confirm" =~ ^[yY]$ ]] && exit 0 - fi - nft delete table inet "${TABLE_NAME}" 2>/dev/null - init_nft; msg "All rules cleared successfully."; list_rules; exit 0 -} - -# --- 2. Flag Pre-processing --- - -for arg in "$@"; do - [[ "$arg" =~ q ]] && QUIET_MODE=true && FORCE_FLAG=true && SKIP_TEST=true - [[ "$arg" =~ f ]] && FORCE_FLAG=true && SKIP_TEST=true - [[ "$arg" == "-R" ]] && RESET_MODE=true - [[ "$arg" == "--version" ]] && { echo "ipfn version ${VERSION}"; exit 0; } -done - -if [[ "$RESET_MODE" == true ]]; then all_clear; fi -if [[ $# -eq 0 ]]; then list_rules; exit 0; fi - -# --- 3. Main Loop --- - -while [[ $# -gt 0 ]]; do - case "$1" in - -h|--help) show_help; exit 0 ;; - -L|-l) list_rules; exit 0 ;; - -*[d]*) - h_str=$(echo "$1" | sed -E 's/^-q?d?f?//') - [[ -z "$h_str" ]] && { h_str="$2"; shift; } - if [[ "$h_str" == "all" || "$h_str" == "*" ]]; then all_clear; fi - handles=() - IFS=',' read -r -a parts <<< "$h_str" - for part in "${parts[@]}"; do - if [[ "$part" =~ ^:([0-9]+)$ ]]; then - p="${BASH_REMATCH[1]}" - for h in $(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dport $p" | grep -o 'handle [0-9]*' | awk '{print $2}'); do handles+=("$h"); done - else handles+=("$part"); fi - done - for h in "${handles[@]}"; do - uuid=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h" | grep -o 'ipf-id:[a-z0-9-]*') - [[ -z "$uuid" ]] && continue - for c in prerouting output forward postrouting; do - nft -a list chain inet "${TABLE_NAME}" "$c" 2>/dev/null | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do - nft delete rule inet "${TABLE_NAME}" "$c" handle "$rh" - done - done - msg "Deleted handle $h" - done - list_rules; exit 0 ;; - -t*) - h_str="${1#-t}"; [[ -z "$h_str" && -n "$2" ]] && { h_str="$2"; shift; } - if [[ "$h_str" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then - lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]} - echo -n "Checking Target $tip:$tp... " - test_connection "$tip" "$tp" && echo -e "\e[32mUP\e[0m" || echo -e "\e[31mDOWN\e[0m" - echo -n "Checking Local :$lp... " - nc -z -v -w 1 127.0.0.1 "$lp" >/dev/null 2>&1 && echo -e "\e[32mOPEN\e[0m" || echo -e "\e[31mCLOSED\e[0m" - elif [[ "$h_str" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):([0-9]+)$ ]]; then - tip=${BASH_REMATCH[1]}; tp=${BASH_REMATCH[2]} - echo -n "Checking Target $tip:$tp... " - test_connection "$tip" "$tp" && echo -e "\e[32mUP\e[0m" || echo -e "\e[31mDOWN\e[0m" - elif [[ "$h_str" =~ ^:([0-9]+)$ || "$h_str" =~ ^([0-9]+):$ ]]; then - p="${BASH_REMATCH[1]}${BASH_REMATCH[2]}" - echo -n "Checking Local :$p... " - nc -z -v -w 1 127.0.0.1 "$p" >/dev/null 2>&1 && echo -e "\e[32mOK\e[0m" || echo -e "\e[31mOFFLINE\e[0m" - elif [[ "$h_str" =~ ^[0-9]+$ ]]; then - test_strict_handle "$h_str" - else - top_h=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | head -n 1 | grep -o 'handle [0-9]*' | awk '{print $2}') - [[ -n "$top_h" ]] && test_strict_handle "$top_h" || msg "Error: No rules to test." - 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 ;; - esac - shift -done