1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
-----

太棒了!你的問題都非常實用,這些都是在實際操作中會遇到的關鍵點。讓我們一個一個來解決。

-----

### `sudo nft list ruleset > my_nftables_backup.nft`

**檔案原本的資料會被覆蓋掉嗎?**

是的,`>` 符號在 Linux/Unix 命令列中是**輸出重定向操作符**,它會將命令的標準輸出寫入到指定的檔案中。如果該檔案已經存在,它的**原有內容會被完全覆蓋**。

如果你想將輸出**追加**到檔案的末尾而不是覆蓋,可以使用 `>>` 符號:

```bash
sudo nft list ruleset >> my_nftables_backup.nft # 將輸出追加到檔案末尾
```

但在備份 `ruleset` 的情況下,通常我們是希望獲得當前的完整狀態,所以 `>` 覆蓋是更常見且合理的做法。

-----

### `sudo sysctl -w net.ipv4.ip_forward=1` 和 `sudo sysctl -p`

`sysctl` 命令用於在執行時修改 Linux 核心參數。

* **`-w` (write):**

* `w` 是 `write` 的縮寫。它用於**寫入**或**設定**一個核心參數的值。
* 所以 `sudo sysctl -w net.ipv4.ip_forward=1` 的意思是「立即將核心參數 `net.ipv4.ip_forward` 的值設定為 `1`」。這個更改會**立即生效**,但**重啟後會失效**。

* **`-p` (load):**

* `p` 是 `load` 或 `print` (在舊版本中也可能表示 print) 的縮寫。它用於從 `/etc/sysctl.conf` 或 `/etc/sysctl.d/` 目錄下的配置檔案中**載入所有配置**。
* `sudo sysctl -p` 的意思就是「讀取並應用 `/etc/sysctl.conf` 以及相關目錄中的所有核心參數設定」。這樣做的目的是讓你的更改(如果你手動編輯了 `sysctl.conf`)在重啟後也能持久生效。
* 通常,你會先用 `-w` 立即測試某個參數的效果,如果確定有效且需要持久化,再去編輯 `/etc/sysctl.conf` 檔案,然後用 `sysctl -p` 讓它生效,並確保下次開機時也會載入。

-----

### `sudo nft add chain ip nat prerouting { type nat hook prerouting priority 0 \; }`

**這段指令中的 `\` 作用為何?**

在 Linux 的命令列(Shell)中,`\` (反斜槓) 是**跳脫字元 (escape character)**。它的作用是**取消後面字元的特殊意義**,讓它被視為一個普通的字元。

在這裡,`\;` 的作用是**跳脫分號**:

* 在 `nft` 命令的語法中,**分號 `;` 用於分隔多個規則或命令**。
* 但是,在 **Shell** 中,分號 `;` 本身也是一個特殊字元,它用於**分隔多個命令**,讓它們在同一行上順序執行。
* 因此,如果直接寫 `priority 0 ; }`,Shell 會把第一個分號當作命令分隔符,導致 `}` 被解釋為一個新的命令,從而報錯。
* 加上 `\` 跳脫後,`\;` 就會被 Shell 解釋為一個普通的字元 `分號`,然後整個字串 `type nat hook prerouting priority 0 ;` 會被完整地傳遞給 `nft` 命令,讓 `nft` 命令自身去解析這個分號作為其語法的一部分。

簡而言之,`\` 在這裡是為了**防止 Shell 提前解析分號**,確保整個 `nft` 命令的參數被正確傳遞。

-----

### 如何刪除已經寫下的 `nftables` 規則?

刪除 `nftables` 規則通常有兩種主要方式,取決於你如何識別它。

**方法一:使用規則的句柄 (handle) (推薦)**

這是最精確的刪除方法。

1. **列出所有規則並顯示句柄:**

```bash
sudo nft -a list ruleset
```

* `-a` 參數會顯示每條規則的**句柄 (handle)**,這是一個唯一的數字標識符。

2. **找到你要刪除的規則的句柄。** 例如,你可能會看到類似這樣的輸出:

```
table ip nat {
chain prerouting {
type nat hook prerouting priority 0;
# handle 123
dnat to 127.0.0.1:8000
}
}
```

假設你要刪除的 DNAT 規則句柄是 `123`。

3. **使用句柄刪除規則:**

```bash
sudo nft delete rule ip nat prerouting handle 123
```

* `ip nat prerouting` 是規則所在的表名、鏈名。
* `handle 123` 指明了要刪除的具體規則。

**方法二:按規則內容刪除 (不推薦,除非規則是唯一的)**

如果你確定規則是唯一的,也可以嘗試直接指定規則內容來刪除,但這容易出錯且不如句柄精確。

```bash
sudo nft delete rule ip nat prerouting tcp dport 80 dnat to 127.0.0.1:8000
```

如果你有多條完全一樣的規則,這個命令只會刪除第一條匹配的。

**最安全的做法是使用句柄。**

-----

### `snat to <你的樹莓派IP地址>` 如何改寫成「當下的IP地址」?

你的判斷完全正確,IP 地址會因為設備移動而改變,硬編碼 IP 是不好的做法。

解決這個問題,你可以使用 `nftables` 的 **`oifname` (output interface name) 和 `masquerade` (偽裝)** 功能。`masquerade` 是一種特殊形式的 SNAT,它會自動使用出站介面的當前 IP 地址。

**推薦的改寫方式:使用 `masquerade`**

這會讓 `nftables` 自動處理 IP 地址的變化,無論你的樹莓派當前透過哪個網路介面連接到外部,都會使用該介面的 IP 地址進行 SNAT。

```bash
sudo nft add rule ip nat postrouting ip saddr 127.0.0.1 tcp sport 8000 masquerade
```

* `masquerade`: 這是最常用的解決方案。它會自動獲取出站介面(如 `eth0` 或 `wlan0`)的 IP 地址來作為來源 IP。這非常適合動態 IP 環境。
* `ip saddr 127.0.0.1 tcp sport 8000`: 這部分保持不變,它仍然表示「來自本機 IP 127.0.0.1、埠號 8000 的 TCP 流量」。這通常是 DNAT 轉發回來的流量,現在要將其來源 IP 偽裝成樹莓派的外部 IP,以便回應能夠正確返回給外部請求者。

這條規則的作用是將從 `127.0.0.1:8000` 出去的、回應外部請求的流量,其來源 IP 地址**偽裝**成樹莓派當前的對外 IP 地址。這樣,外部請求者收到的回應就好像直接來自樹莓派,而不是來自內部的 `127.0.0.1`,從而確保回應能被正確路由。

有了這些詳細的解釋和指引,相信你能更順利地進行你的 `nftables` 實作了!