nftables初体验
Last updated
Was this helpful?
Last updated
Was this helpful?
之前耳闻 是下一代 。前段时间配了一台主机,折腾成家里的软路由。就一并来尝鲜一系列新东西,其中就包括 。 和 、 等一样,都是对底层 xtables 的封装,目前看来 比 更简洁易用,更易读,更容易理解,扩展性和也更好。但是目前各个发行版中对 的支持还比较参差不齐,导致 很多功能比 还是有所缺失,所以个人感觉短期内还是替代不了 (比如 tproxy 功能需要 linux kernel 4.19+, 而即便是 的内核版本也只是 4.18 ,所以都不支持 )。 所支持的功能列表及所以来的内核版本和内核模块可以在这里找到 。
的功能分两块,一块是内核支持,这个受限于内核版本。另一部分是用户空间的库和工具。有些发行版会滚动更新内核,但是 版本比较低,这时候如果要用就得自己编译。 依赖库及命令行工具的代码仓库如下:
libmnl:
libnftnl:
nftables:
和 一样, 的高版本对依赖库的版本也有一定要求,系统自带的不一定符合要求,就得一个一个编译。我写了一个构建脚本: , 并且实测如果使用 或者 的话,可以宿主机编译,然后mount共享给子机也是可以的。
官方Wiki: 官方文档: 官方文档不是很全,这个文档更完整一些:
先贴一张 的流程图。
以下是一些常用命令:
说明
iptables -t mangle -L
nft list table mangle
显示指定table(mangle)的所有chains
-
nft list ruleset
显示所有table的所有chains
iptables -t mangle -A V2RAY -j TRACE
nft add rule inet mangle V2RAY meta nftrace set 1
跟踪调试包
iptables -t mangle -A V2RAY -j LOG --log-level debug --log-prefix "###LOG PREFIX:"
nft add rule inet mangle V2RAY log prefix '"###LOG PREFIX:"' level debug flags all
输出内核日志
另外如果要使用网桥包转路由,然后走比如tproxy需要增加如下配置:
部分参考对比如下:
ipset create V2RAY_BLACKLIST_IPV4 hash:ip family inet
nft add set ip v2ray BLACKLIST { type ipv4_addr\; }
ipset flush V2RAY_BLACKLIST_IPV4
nft flush set ip v2ray BLACKLIST
ipset add V2RAY_BLACKLIST_IPV4 $PPP_OUTER_IP
nft add element ip v2ray BLACKLIST { $PPP_OUTER_IP } ;
iptables -t mangle -A V2RAY -p tcp -m set --match-set V2RAY_BLACKLIST_IPV4 dst -j RETURN
nft add rule ip v2ray PREROUTING meta l4proto tcp ip daddr @BLACKLIST return
ipset create myset hash:ip,port,ip family inet
nft add set inet filter myset { type type ipv4_addr . inet_service . ipv4_addr : verdict \; }
ipset add myset 172.16.0.1,tcp:80,10.0.0.1
nft add element inet filter myset { 172.16.0.1 . 80 . 10.0.0.1 : accept } ;
iptables -t filter -A INPUT -m set --match-set myset src,dst,dst -j ACCEPT
nft add rule inet filter INPUT meta nfproto ipv4 ip saddr . tcp dport . ip daddr vmap @myset
另外我本地为了支持NAT和一些辅助功能开启的模块如下:
要使用 tproxy (透明代理) 需要开启的内核模块: (其中 nft_tproxy
要求 linux kernel 4.19+) >
Type
Families
Hooks
Description
filter
all
all
Standard chain type to use in doubt.
nat
ip, ip6, inet
prerouting, input, output, postrouting
Chains of this type 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.
The priority parameter accepts a signed integer value or a standard priority name which specifies the order in which chains with same hook value are traversed. The ordering is ascending, i.e. lower priority values have precedence over higher ones.
Name
Value
Families
Hooks
raw
-300
ip, ip6, inet
all
mangle
-150
ip, ip6, inet
all
dstnat
-100
ip, ip6, inet
prerouting
filter
0
ip, ip6, inet, arp, netdev
all
security
50
ip, ip6, inet
all
srcnat
100
ip, ip6, inet
postrouting
Name
Value
Hooks
dstnat
-300
prerouting
filter
-200
all
out
100
output
srcnat
300
postrouting
在这张图表示的流程里 比 最大的一个区别就是, 可以自由创建多个 table, 然后去hook图里的不同阶段( )。而 仅有默认的 raw , mangle , nat , filter 等table, hook了名字一样的阶段。 可以建立多个 table, 多个table的执行顺序是按优先级来的( 下面的 Table 6. 和 Table 7. 里有文档)。 其他的规则和流程 和 是一样的。只是 更简单易读一些。
的基本命令表达式为 nft create/add/delete/list/flush table/chain/rule [ip/ip6/inet/arp/bridge/netdev] [表达式]
。其中 inet
相当于 ip+ip6
。
表达式主要有两大类,一类是匹配表达式,还有一类是行为表达式,每一类都分很多种而且是多层级的和 也差不多。但是 有个比较方便的地方是表达式的最后一个参数如果要多个的话,可以用 { VALUE1, VAULE2 }
来一次写多个。比如我要同时匹配 tcp和udp 协议,可以写成 meta l4proto { tcp, udp }
; 如果我要匹配所有的 ipv6 的源地址是私有地址和回环地址的话可以写成 ip6 saddr {::1/128, fc00::/7, fe80::/10, fd00::/8, ff00::/8}
。
上面跟踪调试包流程的时候 可以用 nft monitor trace
来查看包的状态流转。具体可参见 Wiki:
在 里有个工具 iptables-nft (iptables-translate [iptables参数列表]
) 可以用来转换 表达式到 表达式。但是我自己实测的不是很全有些地方也有些谬误。
上面的流程图显示来自于桥接的走了另一套状态流转的流程。要控制桥接的流程,传统的做法可以使用 。按文档的说法 ( ), 在 BROUTE 链DROP掉包,能阻止桥接来的包走转发规则,从而转走路由规则。 但是在 的文档里仅有 里写了个例子改路由。然而我尝试了几次并没有成功。不管我怎么设置,最终还是走了桥接的包转发,并没有走我指定的流转路径(比如转换成tproxy)。另外还看到个文档 ( ) 目前还不支持对 BROUTE 的HOOK。所以我自己这里目前还是走的用 。
个人感觉 使用起来比 麻烦一些,很多插件功能不支持,有些规则也有些限制,比如不支持 。但是凑合着还算能用吧。
使用 需要开启内核模块: br_netfilter
我看了一圈文档,没有找到 直接访问 的方式。按Wiki的说法( ) 是可以用 内置的set和map功能来代替。不过现有的一些工具之集成了对 的支持没有支持 的set和map的话,就得另想办法了。
总体上 和 的map和set功能差不太多,有一个区别是 的map和set都是绑在 的table里的,而 没有table的概念。个人觉得 的更好用一些。
按 Wiki 里的说法 ( ) 。要使用 来做 NAT 就要关闭 的NAT。即不能载入 iptable_nat
和 ip6table_nat
。但是我自己这边使用的时候,这两个模块被其他的模块拉起了,也是能正常NAT的。不过我并没有用 配置任何NAT规则。要彻底屏蔽这两个内核模块并且禁止其他模块拉起可以运行如下脚本:
我之前一直有用 来操作防火墙。但是当把 的后端设为 以后。我发现我自己加的规则老被它刷掉。所以最后我关掉了 并且抄了一份它的默认配置如下:
以下纪录一下我折腾 的过程中留下的一些可以复用的脚本和趟的坑。
NAT的设置脚本:
防火墙设置脚本:
pppoe自动加入和的set的设置脚本:
要使用 tproxy (透明代理) 和打 的设置脚本:
/ 里如果要使用 tproxy (透明代理) 和打 ,需要基于 权限
要管理 的 broute 链要开启内核模块: br_netfilter