Try   HackMD

計算機網路 - nftables

簡介

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

(圖片來源:Netfilter hooks)

參考影片 (Pablo Neira Ayuso)

要理解 netfilter 希望達成什麼功能,從它的命令列前端理解起或許比較方便。netfilters 的一個命令列前端是 nftables (nft)。而關於 nftables,他的維護者之一的 Pablo Neira Ayuso 有不少演講。以下前三個演講都是他親自介紹,這三個演講中涵蓋了 nft 官方文件 中大部分的內容。而且這些解說都是獨立的講解,沒有使用 nftables 前身的 iptables 或各種 *tables 的經驗也可以看懂,是個人認為目前找到最棒的解說。

除了影片之外,netables 的維基百科上也有大抄

如果想要知道 nftables 第一眼看過去有點詭異的術語們是怎麼來的,可以看看 KEYNOTE: netf‌ilter archeology: 18 years from Linux 2.3 to 4.x 這個 netfilters 的歷史介紹。

Workshop Getting a Practical Grasp of nftables - Pablo Neira Ayuso

這個影片簡單介紹 nft 這個命令列工具的使用方法。裡面講到的各種術語,比如 chain、 rule 等等更詳細的說明可以參考下一個影片。如果你希望先知道這些名詞的定義再開始使用,那也可以先看下一個影片再回來看這個影片中的範例。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

如果覺得這個影片中的介紹太快的話,可以參考 NFTABLES [PART - 1] : "Concept and Syntax"NFTABLES [Part - 2] : LIVE DEMO! 這兩個同一系列的影片,裡面有比較詳細地介紹 nft 中的命令列參數的意義。

Netdev 1.1 - nftables from ingress

介紹 nftables 的架構與歷史沿革,以及裡面的一些用語。像是介紹 nft 的基本用語,像是 table-chain-rule 的階層關係、expressions,以及 set、map、element 這些 nftables 中的資料型態以及例子。最後有介紹一下 Linux 核心中處理 netfilter 的虛擬機器的程式。

上一個影片偏向介紹實際的命令如何使用,如果喜歡直接看可以動的例子可以先看上一個影片。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

NLUUG VJ16 - 21 - Pablo Neira Ayuso Goodbye iptables, Hello nftables

內容跟上一個影片大致相同,不過有比較多歷史回顧與架構介紹。在解釋概念時投影片有標底線。對 nft 超長一串命令列參數感到不適的可能可以看這個影片。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

其它參考影片

除此之外,ELCE 裡面也有關於他的演講。這兩個演講幾乎是一樣的,也是同一個人講的。其中一個是 Tutorial: Firewalls with NFtables - John Hawley, VMwareFirewalls with NFtables - John Hawley, VMware。雖然標題中有 Tutorial,但內容都是在講歷史沿革。

除此之外,如果想要一些 nftables 跟 iptables 的對照,可以參考 nftables from a user perspective

基本結構 - table, chain, rule

在 nftables 中,有三個主要的角色:rulechaintable。其中,rule 是指一個描述「該對具有什麼特徵的封包做什麼事」的基本單位。每一個 rule 都屬於一個 chain,而每一個 chain 都屬於一個 table。nftables 的語法會恰好按照 table-chain-rule 的階層排好。像是這樣:

table ip filter {
        chain input {
                 type filter hook input priority 0; policy accept;
                 # this is the 1º matching rule
                 ip saddr 1.1.1.1 ip daddr 2.2.2.2 tcp sport 111 tcp dport 222 jump other-chain
                 # this is the 3º matching rule
                 ip saddr 1.1.1.1 ip daddr 2.2.2.2 tcp sport 111 tcp dport 222 accept
        }

        chain other-chain {
                 # this is the 2º matching rule
                 counter packets 8 bytes 2020
        }
}

其中:

  1. 一個「該對具有什麼特徵的封包做什麼事」的描述,稱為一個 rule,是上述的階層中最底部的。以上面為例,ip saddr 1.1.1.1 ip daddr 2.2.2.2 tcp sport 111 tcp dport 222 jump other-chain 就是一個 rule

  2. rule 形成的集合稱為 chain,會是具有 chain <名稱> {...} 形式的區塊。舉例來說,上面有一個名稱為 input 及一個名稱為 other-chain 的 chain。可以幫一個 chain 指定它要處理哪個來源的封包,或者更精確地說,要 hook 住 netfilter 基礎設施哪個部分,也就是是最前面圖片的部分。這個「來源」會因為這個 chain 所處理的協定而有所不同。

  3. chain 和一些物件形成的集合稱為 table。一個 table 會是一個名稱具有 table <family> <name> {...} 形式的區塊。其中,後面的 <name> 是這個 table 的名稱,以這邊為例,名稱是 filter; 而前面的 <family> 是這個 table 用在哪一個通訊協定上。以這邊為例,ip 指得是 IPv4。而可以使用的協定可以在 nft 文件中的 ADDRESS FAMILIES 條目中找到。

    一個 table 中除了可以包含多個 chain 之外,還可以包含其他的物件。比如說下面這個例子包含了一個名稱為 mysetset。其中的 type ipv4_addr 表示這個 test 是一個用於描述 IPv4 位址的 set,而這個 set 中則包含 1.1.1.1 一個元素:

    ​​​​table ip filter {
    ​​​​       set myset {
    ​​​​               type ipv4_addr
    ​​​​               elements = { 1.1.1.1 }
    ​​​​       }
    ​​​​
    ​​​​       chain input {
    ​​​​               type filter hook input priority 0; policy accept;
    ​​​​               add @myset { ip saddr }
    ​​​​       }
    ​​​​}
    

檢視目前 nftables 的規定

在新增任何東西之前,可以用下面的命令看看目前設定了哪些東西:

$ nft list ruleset

新增 table

這時可以在命令列輸入以下的命令,新增一個名稱為 foo ,用於處理 IPv4 family 的 table:

$ nft add table ip foo

新增之後如果再檢視一次:

$ nft list ruleset

就會發現相較於新增之前,多出了以下輸出:

table ip foo {
}

新增 chain

一個 chainrule 形成的集合,而一個 rule 則是用來指定要「對滿足什麼特徵的封包做什麼事」。nft 的語法必須維持 table-chain-rule 的階層,所以在開始新增 rule 之前,要先有可以拿來裝這個 rulechain才行。比如說接著可以使用:

$ sudo nft add chain ip foo bar

指定在 IPv4 family 中名稱為 foo 的 table 裡面,新增一個名為 bar 的 chain。這時候使用 nft list ruleset 就會出現:

table ip foo {
	chain bar {
	}
}

一個 table 中可以有很多 chain,比如說:

$ sudo nft add chain ip foo barbar
$ sudo nft add chain ip foo barbarbar
$ sudo nft list ruleset

就會出現:

table ip foo {
	chain bar {
	}

	chain barbar {
	}

	chain barbarbar {
	}
}

這個名稱為 bar 的 chain 的種類是 filter,用來 hook 住 IPv4

Regular Chain - 沒有封包進入的 Chain

it is not attached to a Netfilter hooks, by itself a regular chain does not see any traffic. But one or more base chains can include rules that jump or goto this chain

Configuring chains, nftables Wiki

像上面這樣沒有指定要 hook 住哪個部分的 chain,稱為 regular chain,這樣的 chain 就只是一堆 rule 形成的集合,可以作為像是「在某個 chain 中滿足某個條件時,就 jump 到這個 chain 中」,讓整體比較易讀。比如說在下面這個例子中,如果碰到來源是 1.1.1.1:111,目的地是 2.2.2.2:222 的 TCP 封包,那麼處理的流程會是:

table ip filter {
        chain input {
                 type filter hook input priority 0; policy accept;
                 # this is the 1º matching rule
                 ip saddr 1.1.1.1 ip daddr 2.2.2.2 tcp sport 111 tcp dport 222 jump other-chain
                 # this is the 3º matching rule
                 ip saddr 1.1.1.1 ip daddr 2.2.2.2 tcp sport 111 tcp dport 222 accept
        }

        chain other-chain {
                 # this is the 2º matching rule
                 counter packets 8 bytes 2020
        }
}

另外一方面,如果希望這個 chain 被拿來處理流經某個部分的封包,或者說「這個 chain 要 hook 住哪個封包的處理階段」,就要在建立這個 chain 時多指定一些東西。在這之前,先刪除前面建立的這些 chain:

$ sudo nft delete chain ip foo bar
$ sudo nft delete chain ip foo barbar
$ sudo nft delete chain ip foo barbarbar

這時候應該要回到以下:

table ip foo {
}

Base Chain - 有封包進入的 Chain

Base chains are those that are registered into the Netfilter hooks, i.e. these chains see packets flowing through your Linux TCP/IP stack.

Configuring chains, nftables Wiki

如果希望指定這個 chain 被用來過濾哪個階段處理的封包,就要在新增時多指定。舉例來說,用以下命令:

$ sudo nft add chain ip foo "bar {type filter hook input priority 0;}"

上面這個命令用來在 IPv4 中的 foo table 中新增一個名為 bar 的 chain。這個 chain 的種類是 filter,用來 hook 住 IPv4 處理中的 input 階段,優先度是 0。這時候

$ sudo nft list ruleset

就會出現:

table ip foo {
	chain bar {
		type filter hook input priority filter; policy accept;
	}
}

Base Chain 的 type

filternatrouting 三種。不過,這個分類更像是「NAT、routing 與其他用途」,那個 filter 是「其他用途」。所以這邊就跳過介紹,而在 nft 文件中的 Table 5. Supported chain types 中可以找到詳細的說明:

       ┌───────┬───────────────┬─────────────────────┬──────────────────────────┐
       │Type   │ Families      │ Hooks               │ Description              │
       ├───────┼───────────────┼─────────────────────┼──────────────────────────┤
       │       │               │                     │                          │
       │filter │ all           │ all                 │ Standard chain type to   │
       │       │               │                     │ use in doubt.            │
       ├───────┼───────────────┼─────────────────────┼──────────────────────────┤
       │       │               │                     │                          │
       │nat    │ ip, ip6, inet │ prerouting, input,  │ Chains of this type      │
       │       │               │ output, postrouting │ perform Native Address   │
       │       │               │                     │ Translation based on     │
       │       │               │                     │ conntrack entries. Only  │
       │       │               │                     │ the first packet of a    │
       │       │               │                     │ connection actually      │
       │       │               │                     │ traverses this chain -   │
       │       │               │                     │ its rules usually define │
       │       │               │                     │ details of the created   │
       │       │               │                     │ conntrack entry (NAT     │
       │       │               │                     │ statements for           │
       │       │               │                     │ instance).               │
       ├───────┼───────────────┼─────────────────────┼──────────────────────────┤
       │       │               │                     │                          │
       │route  │ ip, ip6       │ output              │ If a packet has          │
       │       │               │                     │ traversed a chain of     │
       │       │               │                     │ this type and is about   │
       │       │               │                     │ to be accepted, a new    │
       │       │               │                     │ route lookup is          │
       │       │               │                     │ performed if relevant    │
       │       │               │                     │ parts of the IP header   │
       │       │               │                     │ have changed. This       │
       │       │               │                     │ allows to e.g. implement │
       │       │               │                     │ policy routing selectors │
       │       │               │                     │ in nftables.             │
       └───────┴───────────────┴─────────────────────┴──────────────────────────┘

Base Chain 的 hook - 在哪個階段過濾?

to filter packets at a particular processing step, you explicitly create a base chain with name of your choosing, and attach it to the appropriate Netfilter hook.

Configuring chains, nftables Wiki

(圖片來源:Nftables - Packet flow and Netfilter hooks in detail)

hook 用來指定要在封包處理的哪個階段過濾封包。因為不同通訊協定的處理流程比所不同,所以具體可以在哪些點 hook 住也有所不同。具體的內容可以在 nft 文件中,介紹不同 address family 的內容中找到。舉例來說,IPV4/IPV6/INET ADDRESS FAMILIES 中的 Table 1. IPv4/IPv6/Inet address family hooks,就介紹了 IPv4 與 IPv6 封包處理的哪些階段可以 hook 住:

       ┌────────────┬──────────────────────────────────────────────────────────────┐
       │Hook        │ Description                                                  │
       ├────────────┼──────────────────────────────────────────────────────────────┤
       │            │                                                              │
       │prerouting  │ All packets entering the system are processed by the         │
       │            │ prerouting hook. It is invoked before the routing process    │
       │            │ and is used for early filtering or changing packet           │
       │            │ attributes that affect routing.                              │
       ├────────────┼──────────────────────────────────────────────────────────────┤
       │            │                                                              │
       │input       │ Packets delivered to the local system are processed by the   │
       │            │ input hook.                                                  │
       ├────────────┼──────────────────────────────────────────────────────────────┤
       │            │                                                              │
       │forward     │ Packets forwarded to a different host are processed by the   │
       │            │ forward hook.                                                │
       ├────────────┼──────────────────────────────────────────────────────────────┤
       │            │                                                              │
       │output      │ Packets sent by local processes are processed by the output  │
       │            │ hook.                                                        │
       ├────────────┼──────────────────────────────────────────────────────────────┤
       │            │                                                              │
       │postrouting │ All packets leaving the system are processed by the          │
       │            │ postrouting hook.                                            │
       ├────────────┼──────────────────────────────────────────────────────────────┤
       │            │                                                              │
       │ingress     │ All packets entering the system are processed by this hook.  │
       │            │ It is invoked before layer 3 protocol handlers, hence before │
       │            │ the prerouting hook, and it can be used for filtering and    │
       │            │ policing. Ingress is only available for Inet family (since   │
       │            │ Linux kernel 5.10).                                          │
       └────────────┴──────────────────────────────────────────────────────────────┘

Base Chain 的 priority - hook 住同一個地方的優先順序

如果同樣的 hook 有指定多個 chain,那麼這些 chain 會依照他們的 priority 由小到大依序執行。而某些特定的數值有自己的代稱,可以在 nft 文件中的 Table 6. Standard priority names, family and hook compatibility matrix 中找到:

       ┌─────────┬───────┬──────────┬─────────────┐
       │Name     │ Value │ Families │ Hooks       │
       ├─────────┼───────┼──────────┼─────────────┤
       │         │       │          │             │
       │raw      │ -300  │ ip, ip6, │ all         │
       │         │       │ inet     │             │
       ├─────────┼───────┼──────────┼─────────────┤
       │         │       │          │             │
       │mangle   │ -150  │ ip, ip6, │ all         │
       │         │       │ inet     │             │
       ├─────────┼───────┼──────────┼─────────────┤
       │         │       │          │             │
       │dstnat   │ -100  │ ip, ip6, │ prerouting  │
       │         │       │ inet     │             │
       ├─────────┼───────┼──────────┼─────────────┤
       │         │       │          │             │
       │filter   │ 0     │ ip, ip6, │ all         │
       │         │       │ inet,    │             │
       │         │       │ arp,     │             │
       │         │       │ netdev   │             │
       ├─────────┼───────┼──────────┼─────────────┤
       │         │       │          │             │
       │security │ 50    │ ip, ip6, │ all         │
       │         │       │ inet     │             │
       ├─────────┼───────┼──────────┼─────────────┤
       │         │       │          │             │
       │srcnat   │ 100   │ ip, ip6, │ postrouting │
       │         │       │ inet     │             │
       └─────────┴───────┴──────────┴─────────────┘

新增 rule

這時候可以在 chain 底下新增 rule。舉例來說,在 IPv4 family 中名稱為 foo 的 table 中名稱為 bar 的 chain 新增以下規則:

$ nft add rule ip foo bar ip saddr 127.0.0.1 counter

這個 rule 為 ip saddr 127.0.0.1 counter 的意思是「如果有 IP 封包的來源位址是 127.0.0.1,那麼就計數」。如果反覆地使用 nft list ruleset,就會發現這個計數的值出現在後面。

$ nft list ruleset
table ip foo {
	chain bar {
		type filter hook input priority filter; policy accept;
		ip saddr 127.0.0.1 counter packets 4 bytes 252
	}
}
$ sudo nft list ruleset
table ip foo {
	chain bar {
		type filter hook input priority filter; policy accept;
		ip saddr 127.0.0.1 counter packets 8 bytes 504
	}
}

Rule 的兩個部分 - Expression 與 Statement

Each rule consists of zero or more expressions followed by one or more statements.

在新增 rule 的時候,除了指定要把這個 rule 新增在哪個 table 的哪個 chain 底下,最重要的就是這個 rule 要「針對哪些東西做什麼事」:

$ nft rule add <哪個協定的哪個 table 的哪個 chain> <對哪些東西> <做哪些事>

而在 nft 的文件當中,指定「對哪些東西」的部分稱為 expression,而「做哪些事」則稱為 statement。事實上,在 nftables 的維基百科 的目錄中,這兩者的標題與次標題分別就是 Expressions: Matching packetsStatements: Acting on packet matches。舉例來說,前面的 ip saddr 127.0.0.1 就是一個 expression,用來指定所有來源 IP 為 127.0.0.1 的封包; 而 counter 則是一個 statement,表示每碰到一個像這樣的封包就統計一次。

這個使用流程跟 regular expression 很像:找到滿足某個 expression 的東西,然後決定要對他做什麼。

Expression - 適用的對象為何?

Each expression tests whether a packet matches a specific payload field or packet/flow metadata. Multiple expressions are linearly evaluated from left to right: if the first expression matches, then the next expression is evaluated and so on.

If we reach the final expression, then the packet matches all of the expressions in the rule, and the rule's statements are executed

Simple rule management, nftables Wiki

可以參考 nft 文件的 EXPRESSIONS 部分,或是看 nftables Wiki 中的 Quick reference-nftables in 10 minutes 中的 Matches 章節。

Statement - 要對適用的對象做什麼事?

Each statement takes an action, such as setting the netfilter mark, counting the packet, logging the packet, or rendering a verdict such as accepting or dropping the packet or jumping to another chain.

Simple rule management, nftables Wiki

可以參考 nft 文件中的 STATEMENTS 部分,或是看 nftables Wiki 中的 Quick reference-nftables in 10 minutes 中的 Statements 章節。

各種資料結構

可以參考 nftables Wiki 的 Advanced data structures for performance packet classification 部分。比較常見的會裡面的 Generic set infrastructure 中列出來的那些。以下就影片中出現過的介紹。

set

有時候會希望一次對某些 IP 或某些 port 套用某個規則,這時候就需要有方法指定「一組 IPv4 位址形成的集合」「一組 port 形成的集合」。 set 就是用來達成這個功能,可以參考 nftables 官方維基的 Sets 條目。舉例來說,在剛剛的 chain 中加入一個名稱為 test,用來表示一堆 IPv4 位址的集合:

$ sudo nft add set ip foo "test {type ipv4_addr;}"

這時會出現:

table ip filter {
	set test {
		type ipv4_addr
	}

	chain input {
		type filter hook input priority filter; policy accept;
		counter packets 443 bytes 31668
	}
}

這個名稱為 test 的 set 目前什麼東西都沒有。但是可以使用 add element 來加入內容。舉例來說,如果要把 127.0.0.1 加入這個名稱為 test 的 set 中:

$ sudo nft add element ip foo test "{127.0.0.1}"

這時就會發現剛剛的 test 中多出了 127.0.0.1

table ip foo {
	set test {
		type ipv4_addr
		elements = { 127.0.0.1 }
	}

	chain bar {
		type filter hook input priority filter; policy accept;
		ip saddr 127.0.0.1 counter packets 10 bytes 630
	}
}

最後,可以使用 @ 來引用這個 set (或是其他物件)。比如可以在 foo 這個 table 中的 bar 這個 chain 加入:

$ sudo nft add rule ip foo bar ip saddr @test counter

來指定「如果 IP 的來源位址有出現在 test 當中,那就計數」。這時候就變成:

table ip foo {
	set test {
		type ipv4_addr
		elements = { 127.0.0.1 }
	}

	chain bar {
		type filter hook input priority filter; policy accept;
		ip saddr 127.0.0.1 counter packets 14 bytes 882
		ip saddr @test counter packets 2 bytes 126
	}
}

map

可以建立 key-value 的對應關係。比如說可以用以下命令在上述的 table 中,加入一個名稱為 test2 以 IPv4 位址為 key,conntrack mark 為 value 的 map

$ sudo nft add map ip foo "test2 {type ipv4_addr: mark;}"

這時候就會建立一個空的 map

table ip foo {
	set test {
		type ipv4_addr
		elements = { 127.0.0.1 }
	}

	map test2 {
		type ipv4_addr : mark
	}

	chain bar {
		type filter hook input priority filter; policy accept;
		ip saddr 127.0.0.1 counter packets 26 bytes 1638
		ip saddr @test counter packets 14 bytes 882
	}
}

要加入元素,可以使用 add element,並且指定要在哪個 table 的哪個 map 中加入新的 key-value pair。比如:

$ sudo nft add element ip foo "test2 {127.0.0.1: 0xa, 127.0.0.2: 0xb}"

就會發現 test2 裡面出現了新的元素:

table ip foo {
	set test {
		type ipv4_addr
		elements = { 127.0.0.1 }
	}

	map test2 {
		type ipv4_addr : mark
		elements = { 127.0.0.1 : 0x0000000a, 127.0.0.2 : 0x0000000b }
	}

	chain bar {
		type filter hook input priority filter; policy accept;
		ip saddr 127.0.0.1 counter packets 30 bytes 1890
		ip saddr @test counter packets 18 bytes 1134
	}
}

最後,如果想要使用這個 map,比如說「如果有封包的來源 IP 出現在 test2 的 key 當中,就把這個封包的 conntrack mark 設為對應的 value」:

$ sudo nft add rule ip foo bar ct mark set ip saddr map @test2

這時候整個規則會變成:

table ip foo {
	set test {
		type ipv4_addr
		elements = { 127.0.0.1 }
	}

	map test2 {
		type ipv4_addr : mark
		elements = { 127.0.0.1 : 0x0000000a, 127.0.0.2 : 0x0000000b }
	}

	chain bar {
		type filter hook input priority filter; policy accept;
		ip saddr 127.0.0.1 counter packets 20 bytes 1260
		ip saddr @test counter packets 12 bytes 756
		ct mark set ip saddr map @test2
	}
}

如果使用 conntrack 檢視:

$ sudo conntrack -L 

就會發限從 127.0.0.1 發出的封包中,mark 被設為 10,恰好就是剛剛的 0x0a

tcp      6 431999 ESTABLISHED src=172.20.10.8 dst=172.20.10.13 sport=22 dport=49367 src=172.20.10.13 dst=172.20.10.8 sport=49367 dport=22 [ASSURED] mark=0 use=1
udp      17 28 src=127.0.0.1 dst=127.0.0.53 sport=35412 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=35412 [ASSURED] mark=10 use=1
udp      17 29 src=127.0.0.1 dst=127.0.0.53 sport=41129 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=41129 [ASSURED] mark=10 use=1
udp      17 12 src=127.0.0.1 dst=127.0.0.53 sport=60459 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=60459 [ASSURED] mark=10 use=1
conntrack v1.4.6 (conntrack-tools): 4 flow entries have been shown.

如果把 test2 中的 127.0.0.1 對應的數值換掉,比如說換成 0x77

$ sudo nft delete element ip foo test2 "{127.0.0.1}"
$ sudo nft add element ip foo test2 "{127.0.0.1:0x77}"

所以現在規則變成:

table ip foo {
	set test {
		type ipv4_addr
		elements = { 127.0.0.1 }
	}

	map test2 {
		type ipv4_addr : mark
		elements = { 127.0.0.1 : 0x00000077, 127.0.0.2 : 0x0000000b }
	}

	chain bar {
		type filter hook input priority filter; policy accept;
		ip saddr 127.0.0.1 counter packets 34 bytes 2142
		ip saddr @test counter packets 26 bytes 1638
		ct mark set ip saddr map @test2
	}
}

這時候再使用 conntrack -L,就會發現之後的 mark10 變成 119,恰好就是 0x77

tcp      6 431999 ESTABLISHED src=172.20.10.8 dst=172.20.10.13 sport=22 dport=49367 src=172.20.10.13 dst=172.20.10.8 sport=49367 dport=22 [ASSURED] mark=0 use=1
udp      17 26 src=127.0.0.1 dst=127.0.0.53 sport=57975 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=57975 [ASSURED] mark=119 use=1
udp      17 29 src=127.0.0.1 dst=127.0.0.53 sport=43474 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=43474 [ASSURED] mark=119 use=1
conntrack v1.4.6 (conntrack-tools): 3 flow entries have been shown.

注意上面這個「先刪除再重新加入」的動作不是 atomic 的,所以在這兩個動作中間傳輸的封包 mark 就不會被設定(會是 0),因此就會有類似以下的結果:

tcp      6 431999 ESTABLISHED src=172.20.10.8 dst=172.20.10.13 sport=22 dport=49367 src=172.20.10.13 dst=172.20.10.8 sport=49367 dport=22 [ASSURED] mark=0 use=1
udp      17 29 src=127.0.0.1 dst=127.0.0.53 sport=55640 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=55640 [ASSURED] mark=119 use=1
udp      17 12 src=127.0.0.1 dst=127.0.0.53 sport=55621 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=55621 [ASSURED] mark=0 use=1
udp      17 14 src=127.0.0.1 dst=127.0.0.53 sport=51085 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=51085 [ASSURED] mark=119 use=1
udp      17 23 src=127.0.0.1 dst=127.0.0.53 sport=36802 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=36802 [ASSURED] mark=119 use=1
conntrack v1.4.6 (conntrack-tools): 5 flow entries have been shown.

其他

Adding new expressions to nftables 是一篇介紹如何在 netfilter 的虛擬機中加入一個 expression (abcde) 的實驗。