1️⃣ Netfilter

提到 Linux 防火墙,很多人会先想到 iptablesnftablesfirewalldufw,它们本质上都只是 用户态配置工具

真正负责 “拦包改包放行 /丢弃” 的核心,是 Linux 内核里的 Netfilter

从上图可以看出:流量流动 是先从网卡,再到 linux 内核,最后抵达到网络应用。

流量过滤 指的是先让流量从网卡进来,然后在 linux 内核里进行过滤处理,最后进入网络应用。这样网络应用前面就有一道防火墙,防火墙就位于 linux 内核的实现中。

挂载点

Netfilter内核协议栈不同的位置 注册 钩子函数 HOOK 来对数据包进行过滤或者修改操作,这些位置称为 挂载点

Netlfilter 通常有 5 个 HOOK 点:

  • PREROUTING:数据包进入路由表之前
  • INPUT:通过路由表后目的地为本机
  • FORWARD:通过路由表后,目的地不为本机
  • OUTPUT:由本机产生,向外发送
  • POSTROUTIONG:发送到网卡接口之前。

2️⃣ iptables

iptables 是建立在 Netfilter 之上的 ip 数据包过滤器,通过向 Netfilter挂载点 上注册 钩子函数HOOK 来实现对数据包过滤。

iptables 通过把这些规则 rule 挂载在 Netfilter 不同挂载点 的不同链 chain上,对进出内核协议栈的数据包进行 过滤 或者 修改 操作。

1️⃣ 表 Tables、链 Chains 、规则 Rules 三者联系

Tables

Tables“按处理目的分类” 的规则集合。

  • filter:做放行/拦截(ACCEPT/DROP/REJECT
  • nat:做地址转换(DNAT/SNAT/MASQUERADE
  • mangle:做改包/打标(TTL/TOS/MARK 等)
  • raw:更早阶段的处理,常用于影响连接跟踪(conntrack

Chains

Chains“表 Tables 在某个挂载点 HOOK 的入口规则列表。

  • PREROUTING:由 PREROUTING HOOK 触发;
  • INPUT:由 INPUT HOOK 触发;
  • FORWARD:由 FORWARD HOOK 触发;
  • OUTPUT:由 OUTPUT HOOK 触发;
  • POSTROUTIONG:由 POSTROUTIONG HOOK 触发;

规则 Rules

规则 rule 是一条 “匹配条件 + 动作” 的行为。

  • 规则 rule 存在于链 Chain 中;当某条链 Chain 被执行时,数据包会按顺序逐条匹配规则,命中后执行动作。
  • 优先级:Table 有优先级,由高到低排列为:raw -> mangle -> nat -> filter。当有多个 Table 包含同一类型的 Chain 时,所有的 Table 都会按照上面 Table 优先级被遍历,执行 Table 中实际的 Chain 下的规则。

2️⃣ 整体框架

3️⃣ 数据包处理流程图

  • 数据包流入: 数据包通过 网卡 进入 Netfliter
    • 首先对数据包进行 DNAT,将公网地址 ip 转换成局域网地址;
    • 进行路由判断:
      • 如果 目的地址 为本机地址 local_address,则进行包过滤;
      • 如果 目的地址 dst 不为本机地址 local_address,则准备进行 数据包转发,经过filter 表中 Forward 链的规则匹配后,如果允许对 目的地址 dst 进行转发,则进行包转发;
      • 通过 Nat 表中 PostRouting 链的规则查看当前数据包的转发是否要做 SNAT,处理完成后发出数据包。
      • 值得注意的是,当数据包流入后,经过路由选择发现不是发给 Local 的包,则会通过 FORWARD 链直接到达 POSTROUTING 链,而不会再走 OUTPUT链。
  • 数据包流出:
    • 当数据包从本机准备发出时,会先经过 Nat 表的Output 链进行规则检查;
    • 随后,会经过 Filter 表的 Output 链进行规则检查;
    • 最后根据 Nat 表中 PostRouting 链的规则查看当前数据包的发出是否要做 SNAT,处理完成后发出数据包。

3️⃣ iptables 语法

iptables 命令的大致语法如下:

1
iptables [-t TABLE] [COMMAND] [OPTIONS]

3️⃣ nftables

nftablesLinux 新一代防火墙/包处理框架,底层仍然依托内核的 Netfilter hooks。可以把 nftables 理解为:同一套 Netfilter 的入口机制,但规则语言、组织方式、可维护性和性能更现代。

1️⃣ 为什么要有 nftables

  • iptables 的历史包袱: 多工具割裂
    • iptables 体系里,IPv4IPv6ARP等由 不同工具 分别管理,维护成本高、规则分散。
    • nftablesnft 统一表达与管理,通过用不同 “地址族 family 区分协议栈。
  • 入口链更灵活: 不再被固定链名限制
    • iptables 的入口链基本是内置的,只能建 “自定义链” 并从内置链 jump 过去。
    • nftables 允许自定义基础链 base chain,并显式绑定 hook + priority + policy
  • 大规则集更好维护:set / map / vmap / concat“海量规则” 变成 “数据结构”
    • iptables 里,一旦需要维护大量 “白名单 IP、端口、网段、组合条件”,就需要不断追加规则:
      • 100 个 IP 白名单 → 100 条规则
      • 50 个 Port 放行 → 50 条规则
      • “不同来源网段 + 不同端口” 组合 → 规则爆炸
    • nftables 的强项是把这些 “很多条 if-then” 收敛成 集合 set/映射 map来管理。

2️⃣ 表 Tables、链 Chains 、规则 Rules 三者联系

Tables

nftablesTable 不是 iptables“固定四张表”,而是自己创建的命名空间,并且先通过 family 来表示处理的是什么类型的数据包,以下是 family 的类型:

  • ip:仅 IPv4
  • ip6:仅 IPv6
  • inet:IPv4 + IPv6 通用
  • arp: ARP
  • bridge:二层桥接帧
  • netdev:更靠近网卡入口

Chains

基础链 base chain

自动绑定 Netfilter hook,会自动接住流量。
创建 自定义基础链 时,必须给出

  • typefilter / nat / route 等;
  • hookinput / forward / output / prerouting / postrouting;
  • priority:同 hook 多链的执行顺序
  • policy: 默认策略,可选但常用
1
2
3
sudo nft add table inet firewall

sudo nft add chain inet firewall input { type filter hook input priority 0; policy drop;}

常规链 regular chain

不绑定 hook,不会自动触发,只能被 jump / goto 调用,用于拆分、复用规则。

规则 Rules

规则 rule 是一条 “匹配条件 + 动作” 的行为。

  • accept / drop / reject
  • dnat / snat / masquerade
  • log / counter
  • jump / goto / return

3️⃣ nftablesetmap 以及 vmap

nftablessetmap 非常方便,这使得 “查找规则” 变成 “查表”

  • set:只有 “元素集合”,用来做 成员测试in / not in
  • map:是 “键 → 值” 的映射,用来做 查值
  • vmap:特殊 mapvalue 不是普通数据而是 verdict,用来做按条件分发执行路径。

1️⃣ set

set 适合于 成员测试,即判断某值是否属于集合。常发生以下场景:

  • IP 白名单/黑名单(很多地址/网段)
  • 端口白名单(很多端口)
  • 任何 “同一字段要匹配多个候选值” 的场景

命名 set

命名 set 是一个可复用可更新“元素集合”

最大价值在于:可以在 不替换规则 的情况下 更新集合内容,适合动态名单。

  • 案例代码

    1
    nft add set inet firewall office_ips { type ipv4_addr; elements = { 10.0.0.0/8, 203.0.113.10 }; }
    • set office_ips:创建一个名为 office_ips 的集合对象
    • type ipv4_addr:声明集合元素类型
      • 常见类型包括 ipv4_addr / ipv6_addr / inet_service/ inet_proto / ether_addr
    • elements:集合的初始元素(可以是单 IP,也可以是网段/CIDR
  • 如何在规则中使用 set

    1
    ip saddr @office_ips tcp dport 22 acceptcounter drop
    • ip saddr @office_ips:源 IPv4 地址是否属于集合 office_ips
    • tcp dport 22:端口条件
    • counter drop:丢弃并计数,便于排障

匿名 set

直接写在规则里 { 22,80,443 }

缺点:想改集合就必须 “替换整条规则”

set 存放区间 interval

如果希望集合能存 “范围/区间”(比如 192.0.2.0-192.0.2.255),创建时要加:

1
nft add set inet firewall office_ips { type ipv4_addr\; flags interval\; }

2️⃣ map/vmap

map / vmap 适合于 键值映射(查表):

  • mapkey → value(查到的是普通值,用于后续继续判断/赋值等)
  • vmapkey → verdict(查到的是动作:accept/drop/reject/jump …)

常见场景:

  • 端口 port → 策略/动作(22 放行、23 丢弃、80 放行…)
  • 协议 protocol → 分发到不同链(TCP/UDP 分别跳转到不同“子程序链”)
  • 把大量 if-else 规则分支收敛成一条查表规则

命名 vmap

命名 vmap 是一个可复用、可更新的 “映射表(字典)”。

最大价值在于:把 “条件 → 动作” 的分支写成一张表,后续变更只需要改表的 elements,不用改规则结构。

  • 案例代码:
    1
    nft add map inet firewall svc_map { type inet_service : verdict; elements = { 22 : accept, 23 : drop, 80 : accept }; }
    • svc_map:创建一个名为 svc_map 的映射表对象
    • type inet_service : verdict:声明 key/value 类型
      • inet_servicekey 是端口(22/80/443)
      • verdictvalue 是动作(accept``/drop/reject/jump …)
    • elements:映射表内容(端口到动作的映射)
  • 如何在规则中使用 vmap
    1
    tcp dport vmap @svc_map counter drop
    • tcp dport:取包的 目的端口 作为 key
    • vmap @svc_map:查表,查到什么 verdict 就执行什么( acceptdrop
    • counter drop:兜底策略(不在表里的端口会继续往下走,最终被丢弃并计数)

      重要行为: 如果 dport 不在 svc_map 里,这条 vmap 规则不会产生 verdict,会继续执行后续规则,因此通常需要一个兜底 drop``/reject

  • 维护命名 vmap
    1
    2
    3
    nft add element inet firewall svc_map { 443 : accept }

    nft delete element inet firewall svc_map { 23 : drop }

vmap 的“最强形态”:分发到不同链

1
ip protocol vmap { tcp : jump tcp_packets, udp : jump udp_packets }
  • 入口规则只负责“分类”
  • tcp_packets / udp_packets 子链里写各自细节(端口白名单、日志、限速、统计…)

4️⃣ iptablesnftables 的区别

特性 iptables nftables 优势解读
统一管理 iptables、ip6tables、arptables 等多个独立命令 统一使用 nft 一个命令 极大地简化了管理,一个规则集可同时处理 IPv4/v6。
语法 冗长,基于命令行参数 简洁、直观,类似编程语言 更易读、易写、易于脚本化。
性能 规则越多,遍历匹配越慢 利用 集合(sets)和映射(maps) 等高效数据结构,实现快速查找 在处理大量 IP 地址黑白名单时,性能优势极其明显。
规则更新 非原子性,修改时存在短暂的无防火墙状态 原子性(Atomic),所有规则作为一个整体进行更新,要么全部成功,要么全部失败 更安全、更可靠,杜绝了更新过程中的安全空窗期。
内核实现 在内核中有大量协议相关的代码 内核只提供一个通用的虚拟机,大部分逻辑在用户空间规则中实现 更简洁的内核代码,更少的内核 bug 可能性。

参考文章