找回密码
 立即注册
首页 业界区 业界 nftables精讲(有NAT、限速、限流量、禁ping等例子) ...

nftables精讲(有NAT、限速、限流量、禁ping等例子)

祝娜娜 2025-10-1 17:49:13
目录

  • 前言
  • 基本概念
  • nft命令的语法
  • nft命令示例(基础版)
  • nft命令示例(进阶版)

    • 1.NAT与端口重定向
    • 2.限制网速
    • 3.限制流量
    • 4.连接数限制
    • 5.reject返回错误
    • 6.防syn flood
    • 7.其他例子请参考后面的“其他数据结构:范围、集合、映射等”

  • 匹配表达式和动作

    • 匹配表达式
    • 动作

  • 其他数据结构:范围、集合、映射等

    • 范围
    • 集合(sets)
    • 映射(maps)
    • 连接
    • 状态对象(stateful object)
    • 流表(flowtable)

  • nftables脚本

前言


  • 本文先说明语法规则,再展示命令示例,最后详细解释相关知识
  • 相信你认真看完本文,并按博文例子实践之后,你会完全从iptables过渡过来。
  • 本博文主要参考

    • https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes   以及该站点下的其他内容
    • 按首字母索引查看:https://wiki.nftables.org/wiki-nftables/index.php/Categoryages_using_deprecated_source_tags
    • man nftables或man nft

基本概念


  • 层级关系:分表、链、规则,表可以装多条链,链可以装多条规则。
  • 地址族类型:一共6种,ip、ip6、inet、arp、bridge、netdev。这6种分别表示“ipv4地址族”、“ipv6地址族”、“ipv4和ipv6双栈地址族”、“arp地址族”、“桥接地址族”(处理经过桥接设备的报文)、“netdev网络设备地址族”,netdev地址族是处理入站和出站报文专门用于创建绑定到单个网络接口的基础链,这类基础链能够监控指定接口上的所有网络流量(不分2、3层)。创建的每个表、链、规则都要指明地址族类型。不指定地址族时,表示使用默认值,即ip(作用于ipv4)
  • 链类型:链分基本链和常规链,常规链就是创建的时候花括号中不带任何内容,常规链常于跳转的目标链。

    • 基本链有3种filter、nat、route类型支持地址族支持的钩子说明filter全部全部标准链类型natip、ip6、inetinput、output、forward、prerouting、postroutingsnat、dnat、源地址伪装等routeip、ip6output会影响路由结果
    • 上面提到的钩子之间的关系是
      1.                                               本  地
      2.                                               处  理
      3.                                                ^  |      .-----------.
      4.                     .-----------.              |  |      |    路 由   |
      5.                     |           |-----> input /    \---> |    选 择   |----> output -\
      6. --> prerouting ---> |   路 由   |                         .-----------.               \
      7.                     |   选 择   |                                                       --> postrouting
      8.                     |           |                                                     /
      9.                     |           |---------------> forward ---------------------------
      10.                     .-----------.
      11.    4.2内核后多了ingress钩子,ingress钩子与其他钩子的关系如下:
      12.                                  .-----------.            
      13.                                  |           |-----> input ...
      14. ---> ingress ---> prerouting --->|   路 由   |
      15.                                  |   选 择   |
      16.                                  |           |
      17.                                  |           |-----> forward ...
      18.                                  .-----------.
      复制代码

  • 链的优先级:优先级指定数据包遍历具有相同钩子的链的顺序。您可以将此参数设为整数值,或使用标准优先级名称。

    • 标准优先级见下表(优先级filter、srcnat、dstnat在使用bridge地址族是,这三个标准优先级所代表的整数值会与下表不同,可以自行参看man nftables):名称所代表的数值地址族钩子raw-300ip, ip6, inet全部mangle-150ip, ip6, inet全部dstnat-100ip, ip6, inetpreroutingfilter0ip, ip6, inet, arp, netdev全部security50ip, ip6, inet全部srcnat100ip, ip6, inetpostrouting

  • 链的预设动作:某链下所有规则都不匹配的时候,就使用该链的预设动作(accept或drop)。如果匹配了某链下的某个规则,则使用该规则的动作(比如accpet、srcnat、dstnat、drop、jump等)。
  • 规则的构成: 规则由“匹配表达式(Expressions)”和“动作(Statements)”构成。关于匹配表达式和动作说明见后面章节的匹配表达式和动作
nft命令的语法


  • nft命令的选项
    1. -a  显示handle(句柄号),我们可以通过规则的handle号来执行删除、在某handle号代表的规则之前或之后插入规则,替换某handle号代表的规则。
    2. -D  名称=值,定义变量。仅可与 -f 选项配合使用。
    3. -c  检查语法,而不是真的执行
    4. -o  优化语句,可以和-c配合使用
    5. -n  数字格式显示,例如:没有此选项nft list ruleset显示某条规则为ct state established,related accept。使用nft -n list ruleset,则该规则显示为ct state 0x2,0x4 accept。
    6. -s 不显示动态状态信息(比如有counter的规则,nft -s list ruleset时,不会显示数据表个数和流量字节数)
    7. -S 显示服务名,比如22显示成ssh,80显示成http
    8. -N 反向解析(不显示ip地址,而是解析成名称)
    9. -j 以json格式输出
    10. -u 打印用户/组的guid
    11. -y 以数字形式打印链的优先级
    12. -p 以数字形式打印第四层协议
    13. -T 以数字形式打印时间
    14. -t 输出中省略集合内容。
    复制代码
  • shell转义注意事项
    在shell中,分号(;)表示语句结束,花括弧({})表示展开(比如rm /tmp/{a,b}.txt等效于rm /tmp/a.txt /tmp/b.txt),表示重定向,&表后台执行,()表示子shell命令组... 所以nft命令遇到; {} 等时要转义或用单引号包含。
    比如:nft add chain ip tb0 ch0 '{ type filter hook input priority 0; policy accept; }'
    再比如: nft add rule ip tb0 ch0 tcp dport '{22,80,443}' accept
    如果你不用shell解释器,而是直接使用nft解释器,那么就无需做shell转义。具体用法如下
    ```
    root@raspberrypi:~#
    root@raspberrypi:~# nft -i   # 输入nft -i就进入nft交互模式,解释器变成nft了,花括号外就不需要单引号包含了,免去了某些符合的shell转义问题。
    nft> add rule inet tb0 ch0 ip saddr 192.168.1.121 tcp dport {80,443} drop
    1. ```
    复制代码
  • 规则集(ruleset)操作
    nft list ruleset [地址族] # 显示规则集,也就是显示所有表、链、规则等。如果需要查看特定地址族的规则集,则需要加上地址族。
    nft -a list ruleset #显示规则集及handle号(句柄号,可以通过句柄号插入、删、改规则,删表或链一般不用handle号,而是直接用名字)
    nft flush ruleset #清理所有规则级
  • 表操作
    nft  table [地址族]  ['{ comment "备注";}' '{ flags 标记}']   #添加表,地址族可选(如果是非ipv4地址族,则必选),备注和标志,create和add类似,但是如果表名存在,则create会返回错误。
    nft add table [地址族]  '{ flags dormant; }'  # 临时禁用某个表(表的地址族不是ip,则必须指明地址族),使用 nft add table [地址族]  恢复表。
    nft delete table [地址族]     #删除表,如果待删表的地址族不是ip,则必须指明地址族
    请注意:后面的内容对于[地址族]不再强调“地址族如果不是ip,则必须指明地址族”
  • 链操作
    nft  chain [地址族]   '{ type  hook  priority ; policy ; [comment 备注]}' # 添加基本链,关于基本链类型、钩子函数名、 优先级见基本概念。链的预设动作可选值为accept和drop。如果不写policy ,则表示该链的预设动作是accept。 如果不加'{}' 不分,则表示该链是常规链,常用于规则跳转的目标链create和add差不多,只是create创建链的时候,如果链已经在,则返回错误。
    nft  chain [地址族]    # 删除链、列出链、清除链规则
    nft list chains [地址族]   # 列出所有链,或指定地址族的链,注意这里是chains,复数。
    nft rename chain [地址族] table     # 重命名链
  • 规则操作
    nft  rule [地址族]    [handle 句柄号 | index 规则序号] 语句... [comment 备注]  # 添加或插入一条规则,句柄号、规则序号、备注是可选。规则序号是一条链上的排序,从0开始排序。语句由匹配表达式和动作构成。(man 原文是 expressions和statements, man中statements有各种各样的动作,所以这里我就直接翻译成动作了)
    nft replace rule [地址族]   handle 句柄号 语句... [comment 备注]    # 替换规则(使用句柄号来表示旧规则)
    delete rule [地址族]     handle 句柄号      # 删除规则(按句柄号来删除)
nft命令示例(基础版)


  • 表、链、规则基础操作(匹配表达式、动作的解释参考后文的 匹配表达式和动作
    nft add table inet tb0   # 添加一个表,表名是tb0, 作用于inet地址族(ipv4和ipv6双栈)
    nft add chain inet tb0 ch0 '{ type filter hook input priority 0; policy accept; }'  # 在tb0表下添加一条名为ch0的基本链,链的类型是filter,钩子是input,优先级是0,链的预设动作是accept(此处可以省略"policy accept;", 因为不指明预设动作,就是使用accept)。
    nft add rule inet tb0 ch0 oifname eth0 tcp dport 22 drop  # 在tb0表的ch0链下添加一条规则,从eth0出去的tcp 22端口的数据包要丢弃(禁止本机通过eth0访问外部ssh)
    nft -a list ruleset  # 列出规则集,使用-a选项后,我们看到上一条规则的句柄号是2,下面的命令我们在句柄号2代表的规则前插入一条规则
    nft insert rule inet tb0 ch0 handle 2 oifname eth0 tcp dport '{80,443}' counter comment '"统计通过eth0访问http/https的浏览"' # 在句柄号为2的规则前插入一条规则,并给规则增加备注(为了防止shell转移,备注用单引号和双引号包裹,这样才可以让备注可以使用空格等字符)
    nft add table inet tb0 '{flags dormant;}'   # 临时禁用tb0表,测试发现通过eth0可以访问外部ssh了。
    nft add table inet tb0  # 重新启用tb0, 测试发现又不可以通过eth0访问外部ssh了。
    nft replace rule inet tb0 ch0 handle 2 oifname eth0  tcp dport 22222 drop comment '"禁止通过eth0访问外部22222端口"'  # 替换原来句柄号是2的规则,并给规则备注
    nft delete rule inet tb0 ch0 handle 2  # 通过句柄号删除限制本机通过eth0访问外部ssh的规则
    nft flush chain inet tb0 ch0  # 清空tb0表下的ch0链下的所有规则。
    nft add table ip tb1 '{ comment '\"测试表1\"'; }'  # 添加一个表tb1,并备注。注意因为花括号外已经使用了一对单引号,所以comment部分的双引号要"转义
    nft add chain inet tb1 ch1 '{ type filter hook output priority filter; comment '\"通过eth0出去的\"'; }' # 添加一条新链ch1, 并备注
    nft rename chain inet tb1 ch1 ch_test # 修改tb1表下的ch1链的名字为ch_test
  • 集合、映射等基础操作示例请参考后文,请点击:其他数据结构:范围、集合、映射等
nft命令示例(进阶版)


  • 进阶示例的知识点不是循序渐进的,相关知识点可能出现在本段落之后。
  • 1.NAT与端口重定向

    • NAT语法
      1. snat [[ip | ip6] to] 地址 [:端口号] [标志]
      2. dnat [[ip | ip6] to] 地址 [:端口号] [标志]
      3. masquerade [to :端口号] [标志]
      4. redirect [to :端口号] [标志]
      5. 地址 := 地址 | 地址1 - 地址2   # 单个地址或地址范围
      6. 端口号 := 端口号 | 端口号1 - 端口号2  # 单个端口号或端口号范围
      7. 标志  := persistent | fully-random  # 固定端口号或随机端口号
      复制代码
    • SNAT源地址转换
      应用场景:请求的数据要跨子网流出
      为什么要SNAT: 举例,假设电脑有两个接口,eth0(10.0.0.10,能连上互联网)和wlan0(192.168.1.11)。手机通过wifi连接到电脑的wlan0,手机的默认网关指向192.168.1.11(此时wlan0是热点模式),手机的请求数据包通过电脑的wlan0进入电脑,电脑的启用ipv4 forward转发之后,wlan0进来的数据包会被转发后经eth0出去,经eth0出去的ip数据包的源地址是192.168.1.11,远端服务器响应后返回的数据包没法回到电脑。所以就需要在ip数据包从eth0出去前把源地址替换成eth0的ip地址10.0.0.10。所以说SNAT和出去的数据包相关, 也就是路由选择之后,所以SNAT关联的钩子是postrouting钩子。
      nft add table ip tb0 # 添加表
      nft add chain ip tb0 ch_snat '{type nat hook postrouting priority srcnat; }'  # 添加链,使用postrouting钩子(路由选择之后)
      nft add rule ip tb0 ch_snat oifname eth0 ip saddr 192.168.1.11 snat to 10.0.0.10 # 对流出接口是eth0,源地址是192.168.1.11做SNAT,把源地址替换成eth0的ip地址10.0.0.10(SNAT用于知道源IP地址的场景)。
    • masquerade 源地址伪装,相当于动态SNAT
      nft add table ip tb0 # 添加表
      nft add chain ip tb0 ch_snat '{type nat hook postrouting priority srcnat; }'  # 添加链,使用postrouting钩子(路由选择之后)
      nft add rule ip tb0 ch_snat oifname eth0 ip saddr 192.168.1.0/24 masquerade # 源地址伪装(masquerade用于不知道源ip地址场景),假设电脑有两个网口eth0(10.0.0.10)和wlan0(192.168.1.11),eth0连接了互联网,现在在电脑开启ipv4 forward(命令sysctl -w net.ipv4.ip_forward=1)。wlan0是开启了热点功能,并启用了DHCP服务(手机连上该热点后,获取了一个192.168.1.X的网址,默认网关是192.168.1.11)。然后手机就可以通过电脑的网络上网了。
    • DNAT目标地址转换
      应用场景:请求的数据要跨子网流入
      为什么要DNAT:举例,假设主机A有两个接口eth0(10.0.0.10)和eth1(192.168.1.11)。 主机B上有web服务,监听在192.168.1.200,主机B的网线连接了主机A的eth1,,现在想要通过主机A的eth0跨子网请求主机B的web服务,那么当数据包从主机A的eth0进来的时候,目标地址eth0的IP地址,此时数据包还在路由选择之前,为了访问主机B的web服务,就需要把数据包的目标地址改成主机B的IP地址192.168.1.200。DNAT和进入的数据相关,还处于路由选择之前,所以要用prerouting钩子。
      nft add table ip tb0 # 添加表
      nft add chain ip tb0 ch_dnat '{type nat hook prerouting priority dstnat; }'  # 添加链,使用postrouting钩子(路由选择之后)
      nft add rule ip tb0 ch_dnat iifname eth0 tcp dport 80 dnat to 192.168.1.200 # 把从eth0进来的,且目标端口是80的数据包做目标地址转换,进来的数据包目标ip地址从eth0的10.0.0.10转换另一个主机的ip地址192.168.1.200。如果192.168.1.200的web服务监听在8000端口,则最后一段应该写成dstnat 192.168.1.200:8000
    • 端口重定向
      nft create table ip tb0; nft add chain ip tb0 ch0 '{type filter hook prerouting priority filter; }' # 添加表和链,链的钩子是prerouting
      nft add rule ip tb0 ch0 tcp dport 80 redirect to :8000  # 把访问80的端口重定向到8000, 测试http服务监听于8000端口,外部浏览器仍然可以通过80端口访问该http服务

  • 2.限制网速

    nft create table inet tb0; nft add chain inet tb0 ch0 '{ type filter hook input priority filter; }' # 添加表和链,地址族是inet(作用于ipv4和ipv6)
    nft add rule inet tb0 ch0 iif wlan0 ip saddr != 192.168.1.0/24 limit rate over 50 kbytes/second drop  # 对于从wlan0进入的且源ip地址不是192.168.1.0/24的数据包,超过50KB/秒就drop。可以用wget测试速度。
    nft add rule inet tb0 ch0 iif eth0 meta l4proto tcp limit rate over 200 kbytes/second burst 100 mbytes drop  # 对tcp流量限速,我们观察到突发流量100MB用完后,速率就下降到200KB/秒
    nft add rule inet tb0 ch0 iif eth1 ip saddr != 192.168.3.0/24 limit rate over 100 mbytes/minute counter  # 超过100MB/分钟就DROP,流量可以按字节数算(单位可以是bytes、kbytes、mbytes),也可按包个数算(100/s表示100个包每秒)。周期可以是second、minute、hour、day、week。
  • 3.限制流量

    nft add table ip tb0; nft add chain ip tb0 ch1 '{ type filter hook output priority 0 ; }'  # 添加表和链,地址族是ip
    nft add set ip tb0 s0 '{type ipv4_addr; timeout 1d; flags dynamic;}'   # 添加一个存放ipv4地址的集合,集合元素过期时间是1天,元素是动态加入的
    nft add rule ip tb0 ch1 oif wlan0 tcp sport '{80,443}' add @s0 '{ip saddr}'  # 把访问本机的http和https的源ipv4地址动态添加到集合s0。这里add @s0是动作,不用update,因为update会更新过期时间(动作的语法参考后面动作部分)
    nft add rule ip tb0 ch1 oif wlan0 ip saddr @s0 tcp sport '{80,443}' quota over 4096 mbytes counter drop  # 添加规则,动作是:限制集合s0中的源ipv4地址的访问本机的下行流量为4GB,统计流量,超过就drop。我的实测是s0使用2分钟超时,下载流量10MB,使用nft -a list ruleset查看流量后发现超过10MB后,客户端无法访问本机web服务,2分钟超时后,又能恢复访问。
    还有一种简洁的写法,上面的第3、4句添加的两条规则可以用一条规则解决: nft add rule ip tb0 ch1 oif wlan0 tcp sport '{80,443}' add @s0 '{ip saddr quota over 4096 mbytes }' counter drop
  • 4.连接数限制

    nft add table inet tb0; nft add chain inet tb0 ch0 '{ type filter hook input priority 0 ; }' # 添加表和链
    nft add set inet tb0 set-ssh '{type ipv4_addr;flags dynamic; }' # 添加集合,集合元素是ipv4地址, 标志是动态(动态加入元素)
    nft add rule inet tb0 ch0 tcp dport 22 ct state new add @set-ssh '{ ip saddr ct count over 2 }' counter reject with tcp reset  # 限制同一ip连进来的ssh连接数是2,超过就拒绝链接。测试发现第三次连ssh服务失败。这条语句有三个动作"add集合元素"、counter、reject
  • 5.reject返回错误

    • tcp
      nft create table inet tb0; nft add chain inet tb0 ch0 '{type filter hook input priority filter; }'  # 添加表和链,地址族是inet
      nft add rule inet tb0 ch0 iif wlan0 ip protocol tcp reject with tcp reset  # 使用 tcp reset拒绝来从wlan0进来tcp流量
    • icmp禁ping
      nft create table inet tb0; nft add chain inet tb0 ch0 '{type filter hook input priority filter; }'  # 添加表和链,地址族是ip
      nft add rule ip tb0 ch0 iif eth0 icmp type echo-request reject with icmp host-unreachable # 对来自eth0的ping请求做"主机不可达"错误icmp包返回。icmp或icmpv6的更多信息请参考匹配表达式和动作

  • 6.防syn flood

    nft create table inet tb0; nft add chain inet tb0 ch0 '{type filter hook input priority filter; }' # 添加表和链,地址族是inet
    nft add rule inet tb0 ch0 tcp flags '& (fin | syn | rst | ack) == syn' ct state new limit rate over 20/second burst 50 packets counter drop #限制新连接的tcp syn包20个每秒,突发流量50个包。还要结合syn cookie、源ip验证等来防止syn flood
  • 7.其他例子请参考后面的“其他数据结构:范围、集合、映射等”
匹配表达式和动作


  • 匹配表达式

    • 提示:这里给出的是常用的,不常用的匹配条件以及例子可以参考:https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes#Matches
    • ip:
        :源/目标ipv4地址,例如: ip saddr 192.168.1.22或ip saddr 192.168.1.0/24或ip saddr 192.168.1.2-192.168.1.254
      protocol  : ip协议,取值为tcp udp icmp等, 如果你是inet地址族的,要使用meta l4proto
      length : 连接所有包的长度
    • ip6:
        :源/目标ipv6地址,例如ip6 saddr ::/64
      length  : ipv6连接的payload长度。例如:ip6 length != 233
    • tcp/udp:
         :tcp/udp的源/目标端口,例如tcp sport {22,80,443} ucp dport gt 40000。
      tcp flags  :tcp标志,有8个标志 fin, syn, rst, psh, ack, urg, ecn, cwr。例如 tcp flags '& (fin|syn|ack|rst) == syn'
    • icmp/icmpv6:
      type  : 类型。例如icmp type echo-request表示ping请求。icmpv6 type echo-reply表示icmpv6 ping回复
    • ether:
      saddr  : 以太网源mac地址, 例如ether saddr 00:0f:11:0c:31:0d
      type   : 以太网帧类型,例如ether type vlan
    • vlan:
      id  : vlan id, 例如:vlan id 134
    • ct: 注意ct是关于连接的,和meta不一样,meta是关于包的。
      state  :连接的状态,可选值有:new, established, related, untracked 例如:ct state new
      direction : 连接的方向, 可选值有:original(初始的或请求的),reply(回复的) 例如:ct direction reply
      [original|reply]   :连接的流量(可以限制方向),例如ct original bytes gt 1048576表示请求方向大于1MB
      ip   :某个连接方向的源/目标IP
      count [over]  :连接数, 例如nft add rule ip tb_pi ch_in tcp dport 22 ip saddr 192.168.1.98 ct state new ct count gt 2 counter reject表示从192.168.1.98发起的ssh连接数超过2就拒绝,over表示大于(测试发现不能用gt,估计是ct内部不支持nft常用比较运算符,省略over表示等于某值)
      [original | reply] l3proto           # netfilter协议(ipv4/ipv6). 例如 ct original l3proto ipv4        
      [original | reply] protocol   :协议(icmp/udp/tcp), 例如ct original protocol 6 或 ct original protocol tcp, ct original protocol icmp
      mark : 连接标记
    • meta:
        : 数据进来、出去的网络接口名,例如meta iifname eth0、meta oifname '{eth0,wlan0}'、meta 'eth*' (支持通配符匹配,这里有单引号防止shell展开)
      : 数据进来或出去的网络接口序号,例如meta oif != eth0 。注意和iifname、oifname区分,iif/oif是索引,规则添加完后,如果接口重命名了,规则仍有效,iifname/oifname则不然。
        : 网口类型,例如meta iiftype ppp、meta oiftype ether
      length  :包长度,注意和ct的ip/ip6的length区分,ct的是关于连接的,meta是关于包的。例子:meta length gt 1000
      protocol  :协议(ip、ip6、arp、vlan)
      nfproto  :协议(ipv4,ipv6)
      l4proto   :协议(tcp、udp、icmp等) 。例如meta l4proto '{tcp, udp}' # 同时匹配udp/tcp协议
      mark [set]     : 数据包标记,注意和ct的mark区分
      pkttype  :包的广播类型,broadcast、 unicast、 multicast
    • 原始载荷表达式(RAW PAYLOAD)

      • 语法:@基址,偏移量,长度
      • 原始载荷表达式用于从指定偏移量加载指定长度的比特数据。位0对应数据包的第一个比特
      • 支持的载荷基址如下:基址描述ll链路层(如以太网头)nh网络层(如IPv4/IPv6头)th传输层(如TCP头)ih内部载荷(传输层头之后的载荷)
      • 示例:nft add rule inet tb1 ch0 meta l4proto {tcp, udp} @th,16,16 { 53, 80 } counter # 表示匹配tcp、udp协议的数据包,数据包参考th基址偏移16位,读取16位长度的数据(从tcp/udp数据包结构可知,偏移16位读取16位长度的数据是指tcp/udp的目标端口号)。 所以示例的含义是,匹配tcp和udp数据包,且目标端口号是53或80的数据包,执行流量统计。

    • 其他表达式:比如扩展头表达式、数值生成器(结合映射做负载均衡)、哈希表达式(结合映射做负载均衡)等表达式这里不详述。如果需要,自己查看man手册。
    • 比较运算符
      1. eq或== :等于
      2. ne或!= :不等于
      3. lt或<  : 小于
      4. gt或>  : 大于
      5. le或<= :小于等于
      6. ge或>=   : 大于等于
      复制代码
      1. 例子: `nft add rule inet tb0 ch0 iif wlan0 tcp dport ge 1024 drop` # tcp端口大于等于1024的流入数据丢弃。`ge 1024`也可以写成 `'>=' 1024`,因为>在shell中是重定向的意思,所以要用单引号包含
      复制代码

  • 动作

    • 动作分终止性动作(立即终止规则处理)和非终止性动作(继续处理后续规则)。终止性动作有accept、drop、queue、continue、return、jump、goto等, 非终止性动作有log、counter等。动作名描述accept接受数据包并停止后续规则评估drop丢弃数据包并停止后续规则评估queue将数据包排队至用户空间并停止后续规则评估continue继续使用下一条规则评估规则集return从当前链返回,并在最后一个链的下一条规则继续执行(在基础链中等同于accept)jump 跳转至的第一条规则继续执行,在收到返回语句后将执行下一条规则goto 与jump类似,但评估完新链后会继续在最后一个链执行,而非包含goto语句的链log写日志, 比如动作log prefix ExtSSH, 则可以用journalctl -k -g 'ExtSSH'查看日志reject类似drop但是返回拒绝数据包,reject后面可以不带任何类型,如果带的话,可以是:reject with tcp reset。或reject with  type  注意icmp、
      icmpv6、icmpx,其中icmp仅用于inet地址族。icmp的reject动作格式 可选类型主要是主机不可达、网络不可达,常用的icmp类型如下:icmp协议的host-unreachable、
      net-unreachable。icmpv6协议的no-route、addr-unreachable。icmpx(也就是inet)的host-unreachable、no-route。更多的请参考文首的网址counter统计流量snat、dnat、redirect、tproxy源地址转换、目标地址转换、端口重定向、透明代理其他动作比如数据包头修改(payload表达式 set value)、包元信息(meta ... set)修改、连接元信息修改(ct {mark | label | zone} set)、数据包复制到其他网口或ip地址(dup to动作
      镜像数据包到远程主机方便分析)、转发数据包到其他网口或ip地址(fwd to动作,类似dup,但是数据没有副本,直接转发)、负载均衡、add或update集合或映射...
    • 关于上面表格中的其他动作中的add或update集合或映射
      语法: {add | update} @集合名或映射名 { 匹配表达式  [timeout timeout] [comment string] }  # 匹配表达是类似于ip saddr(集合)或 ip saddr:tcp dport(映射)

其他数据结构:范围、集合、映射等


  • 范围

    例如 ip daddr 192.168.0.1-192.168.0.250  tcp dport 1-1024
  • 集合(sets)

    • 匿名集合:用逗号分开的值,例如tcp dport '{ 22、80、443 }'. 匿名集合的缺陷是,如果要更改集合,则需要替换规则。对于动态解决方案,使用命名集合。
    • 命名集合是一个列表或一组元素,您可以在表中的多个规则中引用。命名集合的另外一个好处在于,您可以更新命名的集合而不必替换使用集合的规则。

      • 命名集合的元素类型可以是:ipv4地址(ipv4_addr)、ipv6地址(ipv6_addr)、mac地址(ether_addr)、传输层协议(inet_proto)比如tcp/udp/icmp,互联网服务(inet_service)比如ssh/http。数据包标记(mark)
      • 命名集合的语法规则:
        add set [地址族]  set { type 元素类型 | typeof 表达式 ; [flags 标志 ;] [timeout 超时;] [gc-interval gc间隔;] [elements = { 元素[, ...] } ;] [size 大小 ;] [comment 备注 ;] [policy 策略;] [auto-merge ;] } # 添加集合。
        ·   type: 集合元素类型,可选值ipv4_addr/ipv6_addr/ether_addr/inet_proto/inet_service/mark 也可以用typeof 表达式推导得出(不知道是什么类型的时候,用typeof很好用),比如typeof iifname、type of ct direction
        ·   flags: 集合标志,可选值:constant、dynamic、interval、timeout等。constant表示如果该集合被规则引用了,那么就无法增删集合的元素,dynamic与constant相反,我测试发现,不指定dynamic也能动态的增删集合元素。
        ·   timeout: 超时,单位是d、h、m、s(天、时、分、秒),timeout是元素在集合中停留的时间,比如timeout=30s,则元素被添加进集合30秒后自动删除,当元素是通过规则动态添加进集合时,该选项必选。
        ·   gc-interval: gc间隔,垃圾回收间隔,单位是d、h、m、s, 当 timeout或flags timeout指定是,该选项必选。
        ·   elements: 新创建的集合的初始元素
        ·   size:集合的容量,当元素是动态从规则集中添加的时候,该选项必选。
        ·   policy :可选值performance(预设值)、memory。
        ·   counter :针对每个元素做流量统计,比如 nft add set inet tb1 set0 '{type ipv4_addr; counter}' 则nft list ruleset的时候,能看到每个ipv4地址的流量
        ·   auto-merge:自动合并区间,比如192.168.1.1-192.168.1.30与192.168.1.25-192.168.1.50,会自动合并成192.168.1.1-192.168.1.50
        {delete | list | flush} set [family]   # 删除集合、列出集合元素、清除集合元素
        list sets [地址族] # 列出所有集合
        delete set [地址族]  handle  # 通过句柄号删除集合
        {add | delete} element [地址族]   { 元素[, ...] }  # 给集合添加或删除元素
      • 命名集合示例
        nft add table inet tb1 # 添加表,地址族是inet
        nft add set inet tb1 set-v4-addr '{type ipv4_addr; timeout 12h; }'  # 在表下面添加一个元素类型是ipv4地址的命名集合,元素是ipv4地址,元素超时12小时后就会从集合中移除
        nft add element inet tb1 set-v4-addr '{ 192.168.1.10 , 192.168.1.17, 192.168.1.26 timeout 48h }'  # 向刚创建的命名集合里添加元素, 前两个元素过期时间是默认的12小时,最后一个元素过期时间是48小时。
        nft add chain inet tb1 ch0 '{type filter hook input priority filter; }' # 在表下添加一条链,以便此链可以引用上面的命名集合
        nft add rule inet tb1 ch0 meta l4proto tcp ip saddr @set-v4-addr counter  # 在链里面引用命名集合,源ip地址是集合中的3个ipv4地址时,做tcp流量统计
        nft add rule inet tb1 ch0 ip daddr 192.168.1.240 tcp dport 80 add @set-v4-addr '{ip saddr}' # 凡是访问了192.168.1.240的http服务的主机,都动态的加入到set-v4-addr命名集合中(语句格式:add @集合名 '{元素}')
        nft delete element inet tb1 set-v4-addr '{192.168.1.10, 192.168.1.17}' # 从命名集合中删除两个元素,执行nft list sets发现元素少了两个
        nft delete set inet tb1 set-v4-addr  # 删除命名集合,但是提示Error: Could not process rule: Device or resource busy,因为链引用了该命名集合
        nft delete rule inet tb1 ch0 handle 13 # 删除引用set-v4-addr集合的链后,再执行上面的语句就可以成功删除命名集合了。
        nft add set inet tb1 set-addr4-interval '{ type ipv4_addr; flags interval; }' # 添加一个可以装ipv4地址范围的集合,注意这里的flags interval;
        nft add element inet tb1 set-addr4-interval '{192.168.2.1-192.168.2.254,192.168.1.0/24}' # 向该命名集合添加两个元素,两个元素都表示ipv4地址范围。


  • 映射(maps)

    • 匿名映射
      匿名映射的元素是 { 键:值 } 语句。这里映射元素"键:值",可以有多个,用逗号分开,键和值的类型可以是:ipv4_addr/ipv6_addr/ether_addr/inet_proto/inet_service/mark/等(counter、quota不可以做键类型,但是可以作为值类型)。匿名映射的缺点是,如果要修改映射,则必须替换该匿名映射所属的规则(具体例子请参考后面的示例)。
    • 命名映射

      • 命名映射的语法规则

        • 映射操作语法规则
          add map [地址族]   { type 元素类型 | typeof 表达式  [flags 标记 ;] [elements = { 元素[, ...] } ;] [size 映射容量;] [comment 备注;] [policy 策略;] }  # 添加集合。
          ·   type : 映射的元素数据类型(键类型),可选值:ipv4_addr/ipv6_addr/ether_addr/inet_proto/inet_service/mark/counter/quota等,也可以用typeof 表达式推导。
          ·   flags : 映射标记, 可选值:constant/interval。
          ·   elements: 映射初始元素(键:值对)
          ·   size : 映射最大容量
          ·   policy :映射策略,可选值performance(默认)/memory
          {delete | list | flush} map [地址族]    # 删除、列出、清除映射
          list maps [地址族]  # 列出所有映射,可以限定地址族

      • 命名映射的示例

    • 判决映射(vmaps/verdict maps)**

      • 概念: 判决映射我个人的理解是一种{ 匹配表达式 : 动作 } 的特殊映射,也就是说这种映射的键和常规映射的键的类型是一样的,值的类型是verdict。(verdict表示各种动作,比如accept、drop、queue、continue、return、jump、goto)。判决映射也分匿名判决映射和命名判决映射。

    • 映射的示例(判决映射、映射)
      nft create table inet tb1; nft create chain inet tb1 ch0 '{type filter hook input priority filter; }' # 添加表和链,地址族是inet
      nft add chain inet tb1 ch-counter  # 添加一条名为ch-counter的常规链,作为跳转目标链,注意常规链是不带'{ type xxx hook xxxx priority xxx; policy xxx }'的。
      nft add rule inet tb1 ch-counter tcp dport 443 counter # 给ch-counter的常规链添加一条规则,用于统计请求443端口的流量
      nft add rule inet tb1 ch0 ip saddr vmap '{ 192.168.1.12:drop, 192.168.2.0/24:accept, 192.168.3.0/24: jump ch-counter}' # 添加一条规则,使用匿名判决映射,注意vmap关键字,注意ip saddr vmap表示ip saddr对应到匿名映射中的三个成员, 如果ip saddr是192.168.1.12则drop,如果ip saddr是192.168.2.0/24则accept, 如果ip saddr是192.168.3.0/24,则跳转到ch-counter链。
      nft add map inet tb1 vmap0 '{type ipv4_addr:verdict;}' # 在tb1表下新增一个命名判决映射vmap0,映射的类型是。ipv4地址映射到verdict。
      nft add element inet tb1 vmap0 '{192.168.33.11:accept, 192.168.33.22:drop, 192.168.33.33:jump ch-counter}' # 给命名判决映射添加元素
      nft add rule inet tb1 ch0 ip saddr @vmap0  # 在规则中引用命名判决映射vmap0
      nft add chain inet tb1 ch-snat '{type nat hook postrouting priority srcnat; }'  # 添加一条SNAT链,用于下面映射举例
      nft add map inet tb1 map-snat '{type ipv4_addr:ipv4_addr; flags interval; }'  # 添加一个名为map-snat的映射,映射元素的键和值类型都是ipv4地址,flags是范围,亦即:ipv4地址范围映射到ipv4地址
      nft add element  inet tb1 map-snat '{ 192.168.1.0/24:192.168.3.55, 192.168.2.0/24:192.168.3.55 }' # 给映射添加两个元素
      nft add rule inet tb1 ch-snat snat ip to ip saddr map @map-snat  # 添加一条snat的规则引用映射, 注意,因为tb1是inet地址族,所以snat to之间要指明是ip还是ip6地址族。这里是数据包的源ip地址映射到地址转换后的源ip地址。

  • 集合或映射的元素操作语法规则
    1. {add | create | delete | get } element [地址族] <所属表名> <集合名> { 元素[,...] } # 元素相关命令允许修改命名集合(sets)和映射(maps)的内容。
    2.      元素 := 键表达式 选项 [: 值表达式 ]
    3.      选项 := [timeout 时间定义 ] [expires 时间定义 ] [comment 字符串]
    4.      时间定义 := [Xd][Xh][Xm][X[s]] # X表示数字,d、h、m、s表示天、时、分、秒
    5.    键表达式(key_expression)通常是与集合类型匹配的值。值表达式(value_expression)在集合中不允许使用,但在向映射添加元素时是必需的(需匹配其类型定
    6.    义中的数据部分)。当从映射中删除元素时,值表达式可以指定但非必需(因为键表达式能唯一标识元素)。create命令与add命令类似,区别在于列出的所有元素必须
    7.    都不存在(即不能重复添加已存在的元素)。get命令可用于检查元素是否包含在集合中,对于非常大或区间类型的集合(interval sets),这种检查可能并不简单。
    8.    对于区间集合,get命令会返回包含该元素的区间,而不仅仅是元素本身。
    9.        timeout:带有超时标志(timeout)的集合/映射的超时值   
    10.        expires: 给定元素的过期时间,仅用于规则集复制(ruleset replication)
    11.        comment: 每个元素的注释字段
    复制代码
  • 连接

    语法格式:匹配表达式.匹配表达式, 可以连接多个,连接可以作为集合的元素、映射元素的键或值等,直接上例子解释:
    nft create table inet tb1; nft add chain inet tb1 ch0 '{type filter hook input priority filter; }' # 添加表和链,地址族是inet
    nft add set inet tb1 set-a2p '{ type ipv4_addr . inet_service; }' # 新建一个集合,集合的元素类型是"ipv4地址 . 端口号"的连接,务必注意点号前后都有空格。
    nft add element inet tb1 set-a2p '{192.168.22.10 . 80, 192.168.22.11 . 443}' # 给集合添加两个元素。
    nft add rule inet tb1 ch0 ip saddr . tcp dport @set-a2p counter # 新建规则,引用集合。目的是统计源ip是192.168.22.10目标端口号是80端口、源ip是192.168.22.11目标端口号是443端口的网络流量
    nft add map inet tb1 map-a2p '{type  ipv4_addr . inet_service : verdict;}'  # 增加一个映射,"ipv4地址 . 端口号"作为映射的键,动作作为值
    nft add element inet tb1 map-a2p '{192.168.22.66 . 80:accept, 192.168.22.77 . 22:drop}' # 添加映射元素
    nft add rule inet tb1 ch0 ip saddr . tcp dport @map-a2p #引用上面的映射(源ip地址是192.168.22.66,tcp目标端口是80就执行accept;源ip地址是192.168.22.77,tcp目标端口是22就执行drop)
  • 状态对象(stateful object)

    • 状态对象是表下面的,他们能聚合规则的状态信息,通过类型 name 名字引用,例如counter name "cnt0"。 状态对象主要有:counter(流量计数器)、quota(配额)、limit(限速)等
    • 语法规则如下:
      1. {add | delete | list | reset} 状态对象 [地址族] 所属表名 对象名 [{ 状态对象的选项; [comment 备注;]}] # 新建/删除/列出/重置 状态对象。这里状态对象的选项随状态对象的种类不同而不同,比如counter的选项是 "packets 数值 bytes 数值 ;"  quota的选项是“ [over|until] 数值 流量单位 [ used 数值 流量单位 ] ”(流量单位是bytes、kbytes、mbytes) 。
      2. delete 状态对象 [地址族] 所属表名 handle handle # 用句柄号删除状态对象
      3. list 状态对象 [地址族] #列出状态对象
      4. reset 状态对象s [地址族] # 重置所有状态对象,注意复数s,比如重置所有inet地址族的counter,则使用nft reset counters inet
      5. reset 状态对象s [地址族] table 所属表名 #重置某表下的所有状态对象,注意复数s
      复制代码
    • 示例
      nft create table inet tb1; nft create chain inet tb1 ch0 '{type filter hook input priority filter; }' # 添加表和链,地址族是inet
      nft add counter inet tb1 cnt-web-down '{comment '\"统计http、https下行流量\"';}'  # 新建一个counter,统计流量
      nft add rule inet tb1 ch0 iif wlan0 tcp sport '{80,443}' counter name "cnt-web-down" # 统计从wlan0进入且tcp源端口是80或443的流量。
      nft add counter inet tb1 cnt-80-down; nft add counter inet tb1 cnt-443-down  # 建立两个名为“cnt-80-down”和“cnt-443-down”流量计数器
      nft add rule inet tb1 ch0 iif wlan0 counter name tcp sport map'{80:"cnt-80-down",443:"cnt-443-down"}'  # 创建一条规则,结合匿名映射,分别统计http和https的流量
      nft add limit inet tb1 lmt-web-down rate over 100 kbytes/second  # 添加一个限速100KBytes/秒的limit对象
      nft add rule inet tb1 ch0 iif wlan0 tcp sport '{80,443}' limit name "lmt-web-down" drop # 引用该限速器,因为规则所属的链的预设动作是accept,所以这里超过100kB/s的时候,要drop。 wget测试网速生效
      nft add quota inet tb1 quo-web-down over 100 mbytes # 添加一个配额100MB
      nft add rule inet tb1 ch0 iif wlan0 tcp sport '{80,443}' quota name "quo-web-down" drop # 添加一条规则应用配额,下载超过100MB就drop,wget测试发现,下载到100MB的时候,下载停止了。执行nft reset quotas,刚才的配额又归零了。

  • 流表(flowtable)

    • 流表用于加速数据包转发,通过元组(输入接口、源/目的地址、源/目的端口、协议)缓存转发信息。
    • 语法:
      {add | create} flowtable [地址族] 所属表名 流表名 { hook 钩子 priority 优先级 ; devices = { 设备,... } ; } #添加流表
      list flowtables [地址族]  # 列出所有流表
      {delete | list} flowtable [地址族] 所属表名 流表名 # 删除或列出某表下的流表
      delete flowtable [地址族] 所属表名 handle 句柄号 # 通过句柄号删除流表

      • 流表参数:
        优先级支持整数或标准名称(如filter表示0),支持算术运算(如filter+5表示5)
        地址族支持ip/ip6/inet(inet为IPv4/IPv6混合表)

    • 示例:nft add flowtable inet tb1 ftb0 '{hook ingress priority filter; devices = {wlan0,eth0};}' # 添加一个inet地址族的流表

nftables脚本

有三种形式:

  • shell形式
    1. #!/bin/bash
    2. nft flush ruleset
    3. nft add table ip tb0 '{ comment '"测试表"'; }'
    4. nft add chain ip tb0 ch0 '{ type filter hook input priority filter; comment '"测试链"';}'
    5. nft add rule ip tb0 ch0 iifname eth0 tcp dport 22 drop comment '"测试规则"'
    复制代码
    上面是/tmp/nft.sh的内容,准确说这是shell脚本,不是nft脚本,执行方法有两种:1、直接执行 bash /tmp/nft.sh。2、增加可执行权限chmod a+x /tmp/nft.sh 再执行:/tmp/nft.sh
  • 与nft命令基本相同的形式
    1. #!/usr/sbin/nft -f
    2. flush ruleset
    3. add table ip tb0 { comment "测试表"; }
    4. add chain ip tb0 ch0 { type filter hook input priority filter; comment "测试链";}
    5. add rule ip tb0 ch0 iifname eth0 tcp dport 22 drop comment "测试规则"
    复制代码
    上面是/tmp/a.nft的内容,注意和shell版本比较:首行的解释器、引号和转义、每行首少了nft命令。执行方式有两种:1、赋x权限chmod a+x /tmp/a.nft,执行/tmp/a.nft  2、直接用解释器导入nft -f /tmp/a.nft
  • 与nft list ruleset显示结果基本相同的形式
    1. #!/usr/sbin/nft -f
    2. flush ruleset
    3. table ip tb0 {
    4.     comment "测试表"
    5.     chain ch0 {
    6.             comment "测试链"
    7.             type filter hook input priority filter; policy accept;
    8.             iifname "eth0" tcp dport 22 drop comment "测试规则"
    9.         }
    10. }
    复制代码
    执行方法有两种:1、赋x权限chmod a+x /tmp/a.nft,再执行/tmp/a.nft 2、直接用解释器导入nft -f /tmp/a.nft
  • 其他知识点:比如脚本定义变量、脚本中导入外部脚本(include "test.nft")等知识点,这里不详述,可以参看man nftables
  • 开机启动nftables服务
    systemctl enable nftables #这样每次启动的时候,nftables服务会载入/etc/nftables.conf配置文件,配置文件的格式建议使用上面第3种形式。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册