跳至正文
老丹的足迹 —— 代码写给机器,游记写给自己,感悟写给时间
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面

Linux防火墙内核实现深度解析:从iptables到nftables的完整技术图谱

你所敲下的每一个iptables命令,在新系统上可能早已不是你以为的那个iptables了。这不是一次简单的版本升级,而是一场从内核数据结构、算法设计到命令行工具的彻底重构。

一、引言——一次”正常”的报错引发的思考

2026年春天,一位系统管理员像往常一样登录新交付的Arch Linux服务器,熟练地敲下一行维护了十年的防火墙命令:

iptables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT

然而,这次迎接他的不是熟悉的”OK”,而是一段令他困惑的报错信息。他第一反应是:”linux系统的iptables是不是改了?”

答案是肯定的。这背后涉及的是Linux防火墙框架二十年来最深刻的一次变革——从xtables到nftables的内核级替换。这场变革不是一次简单的版本升级,而是从内核数据结构、算法设计到命令行工具的彻底重构。

二、从内核到应用——先看全貌

2.1 宏观架构图

在深入细节之前,我们先从整体上理解:当你敲下iptables命令时,数据流是如何在内核和用户态之间流转的。

这张图的核心信息:目前Linux系统内实际上并存着两套防火墙实现:

  • 新路径(绿色):iptables命令默认指向iptables-nft兼容层,底层实际执行的是nftables内核模块
  • 旧路径(灰色):iptables-legacy作为备选,直接操作传统的xtables内核模块

2.2 第一问:iptables到底还能不能用?

结论先行:能。在新内核上,旧有的iptables命令依然可用,绝大多数脚本和命令都能正常运行。

但”能用”的背后有一个重要变化:你敲下的iptables命令,底层可能已经不再是你以为的那个iptables了。

怎么知道我的iptables是”新”还是”旧”?

iptables --version
  • 输出 iptables v1.8.4 (nf_tables) → 你正在使用新内核的兼容层,底层是nftables。
  • 输出 iptables v1.8.4 (legacy) → 你还在使用传统的xtables内核模块。

如果遇到兼容问题,大多数发行版都提供了回退方案,可以安装iptables-legacy包切换回旧模式。

三、用户态到内核态的两条路径

理解了”能用”之后,我们需要更细致地看清整个系统的架构。目前,Linux系统中实际上并存着两套完整的工具链:

工具名称真实身份通信对象当前角色
iptablesiptables-nftnftables内核兼容层(翻译官)
iptables-legacyiptables原版xtables内核旧后端(备选)
nftnftables原生工具nftables内核新原生(主力)

3.1 内核中的多种iptables实现

你提到的”iptables在内核不是有好几种吗”完全正确。在旧内核(xtables框架)中,针对不同的网络协议层,存在完全独立的内核模块和用户态工具,各自为政。

协议族内核模块用户态工具管理对象
IPv4ip_tables.koiptablesIPv4 数据包过滤/NAT
IPv6ip6_tables.koip6tablesIPv6 数据包过滤/NAT
ARParptables.koarptablesARP 报文过滤
网桥/以太网帧ebtables.koebtables二层(以太网帧)过滤

这四套系统各自完全独立:

  • 各自维护自己的表(filter/nat/mangle…)
  • 各自挂载到netfilter的不同钩子
  • 规则互不共享,无法跨协议复用

例如,你需要为双栈服务器开放80端口时,必须分别执行:

iptables -A INPUT -p tcp --dport 80 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT

3.2 内核源码中的对应路径

如果你翻阅Linux内核源码,会看到这种”分立”在目录结构上的直观体现:

3.3 nftables如何统一

而 nftables 用一个统一的 nf_tables.c 核心模块,配合地址族参数(NFPROTO_IPV4 / NFPROTO_IPV6 / NFPROTO_INET)来区分处理对象,代码复用率极高。inet地址族甚至可以同时覆盖IPv4和IPv6,一套规则、双重生效:

# nftables方式:一条规则同时生效于IPv4和IPv6
nft add rule inet filter input tcp dport 443 accept

四、”表”的架构革命——从静态内置到动态可定义

“表(table)”是iptables和nftables中最核心的概念之一。在这次底层变革中,”表”的角色发生了根本性的变化。

4.1 iptables的”静态内置表”架构

在旧内核(xtables)中,表是预先定义好的、固定的,你无法增删,只能使用内核编译时决定的那几张。

关键特征:

  • 表的种类固定:永远就是这5张表(filter/nat/mangle/raw/security)。
  • 表与钩子绑定固定:每张表能挂载到哪些钩子,是内核写死的。例如nat表无法挂载到INPUT钩子。
  • 用户无法自定义表:只能往现有表里加规则,不能新建一张自定义表。

4.2 nftables的”动态可定义表”架构

在新内核(nftables)中,表变成了用户可以自由创建和删除的容器,不再有内置限制。

关键特征:

  • 表由用户定义:创建的表叫什么名字、处理哪个地址族(IPv4/IPv6/inet),完全由你决定。
  • 表与钩子解耦:创建链(chain)时可以自由指定它挂载到哪个钩子。
  • 一个钩子可以有多个表:不同用户可以创建不同的表,各自独立管理。

4.3 新旧”表”的内核存储结构对比

4.4 “表”的对比总结

维度iptables (xtables)nftables
表的创建内核编译时固定,用户无法增删用户可通过 nft add table 自由创建
表的种类仅5种(filter/nat/mangle/raw/security)名称自定义,无限种
地址族绑定每个表隐式绑定到IPv4或IPv6(需独立工具)创建时可指定ip/ip6/inet(同时支持双栈)
表与钩子关系内核写死:每张表只能挂载到特定钩子用户自由指定
多租户隔离不支持,所有规则混在同一张表里支持:不同用户/容器可创建独立表
内核数据结构全局静态数组/链表,单例模式动态链表,可任意增删节点

五、数据结构与算法——性能差异的根源

5.1 iptables:线性链表 O(N)

iptables将所有规则存储在一个平坦的线性链表中。数据包进入时,系统从链头开始逐一匹配,命中即停,否则遍历全表。

内核数据结构(定义在include/linux/netfilter/x_tables.h):

struct xt_table {
    struct list_head list;          // 全局表链表
    unsigned int valid_hooks;       // 该表挂载的钩子位图
    struct xt_table_info *private;  // 实际规则集
};

struct xt_table_info {
    unsigned int number;            // 规则数量
    unsigned int *entries;          // 规则入口偏移数组
    struct xt_counters *counters;   // 计数器
};

5.2 nftables:哈希表与表达式树 O(1)

nftables采用哈希表和表达式树等高级数据结构。数据包进入时,系统通过计算哈希值直接定位到对应的规则集。

内核数据结构(定义在include/net/netfilter/nf_tables.h):

struct nft_table {
    struct list_head list;          // 表链表
    u16 family;                     // 地址族
    struct list_head chains;        // 属于该表的所有链
};

struct nft_chain {
    struct list_head list;          // 链链表
    struct nft_table *table;        // 所属表
    struct list_head rules;         // 属于该链的所有规则
};

struct nft_rule {
    struct list_head list;          // 规则链表
    struct nft_expr *exprs;         // 表达式链表(核心差异!)
};

struct nft_expr {                   // 单个表达式
    const struct nft_expr_ops *ops; // 操作函数指针
    unsigned char data[];           // 表达式私有数据
};

5.3 核心差异:规则是”对象”还是”表达式流”

  • iptables:规则是一个”黑盒”对象,匹配条件和动作硬编码在一起,内核只能整体处理。每条规则在内存中是连续存储的变长结构。
  • nftables:规则是表达式的有序列表,每个表达式独立实现eval()方法。内核通过遍历表达式链表完成规则执行。这种设计使得扩展新功能时无需修改内核核心代码。
特性iptablesnftables
遍历方式线性链表遍历表达式流式求值
匹配粒度整条规则作为一个单元每个表达式独立求值
短路机制需匹配所有条件后才执行动作任一表达式求值失败即可终止
优化潜力规则顺序固定,难以优化表达式可独立优化和重组
查找复杂度O(N)O(1)

5.4 性能实测对比(基于社区benchmark)

测试场景iptables(legacy)nftables
100条规则,单流吞吐量约950 Mbps约980 Mbps
1000条规则,单流吞吐量约720 Mbps约950 Mbps
5000条规则,单流吞吐量约320 Mbps约930 Mbps
规则集加载时间(5000条)约850 ms约120 ms
规则集原子切换不支持< 10 ms

结论:nftables在小规模规则集下性能略优于iptables,在大规模规则集下具有碾压级优势。

六、规则更新机制——原子性对决

生产环境最忌讳的,是在变更过程中出现”无规则空窗期”。

6.1 iptables:非原子更新,存在风险

iptables的规则更新通过setsockopt系统调用逐条下发。当使用iptables-restore恢复大批量规则时,采用”先清空所有规则,再逐条载入新规则”的策略。在清空和载入之间的那几毫秒甚至几秒内,服务器可能处于完全无防护状态。

// net/netfilter/x_tables.c
struct xt_table_info *xt_replace_table(struct xt_table *table,
                                       struct xt_table_info *newinfo)
{
    struct xt_table_info *oldinfo;
    oldinfo = table->private;
    table->private = newinfo;  // 先替换指针
    synchronize_rcu();         // 等待所有CPU完成当前遍历
    return oldinfo;
}

6.2 nftables:原子事务更新,万无一失

nftables通过netlink事务机制实现原子更新。用户提交的规则集文件中的所有变更被打包成一个事务列表,内核先对所有变更进行整体校验,确认无误后一次性提交。

关键机制:

  • 任何一条不合法都会导致整个事务失败(全部回滚)。
  • 所有变更在确认无误后一次性提交,中间不存在任何不完整的中间状态。

七、钩子挂载与调度机制

无论iptables还是nftables,都依赖于Linux内核netfilter框架提供的5个标准钩子点:

7.1 iptables:固定优先级挂载

iptables使用固定优先级挂载钩子。所有表在同一个钩子上按硬编码顺序执行:

static struct nf_hook_ops ipt_ops[] __read_mostly = {
    {
        .hook     = ipt_hook,
        .pf       = NFPROTO_IPV4,
        .hooknum  = NF_INET_LOCAL_IN,
        .priority = NF_IP_PRI_FILTER,  // 固定优先级
    },
};

7.2 nftables:动态优先级,流水线处理

nftables允许用户自定义链在钩子上的执行顺序:

struct nft_chain_hook {
    u32 type;      // 钩子类型
    u32 priority;  // 优先级(数值越小越先执行)
    s32 num;       // 钩子编号
    struct nf_hook_ops ops;
};

用户可以通过配置指定优先级:

# 优先级 -100 的链会在标准链之前执行
nft add chain ip filter input { type filter hook input priority -100 \; }
# 优先级 100 的链会在标准链之后执行
nft add chain ip filter input { type filter hook input priority 100 \; }

这种设计实现了类似”流水线”的处理模型。

八、应用控制程序——”马甲”与”真身”

你的理解完全正确:对应的应用控制程序确实不一样了。

目前系统中有两套应用控制程序并存:

  1. iptables(新版/兼容层):其真实身份是iptables-nft。它是一个兼容层程序,负责解析iptables风格的指令,翻译成nftables能理解的nft命令,最终由nftables内核执行。这是为了兼容旧脚本而存在的”翻译官”。
  2. nft(原生工具):这是nftables框架原生的管理工具,直接与内核的nftables子系统通信,是配置新一代防火墙的核心命令。

两者是独立的程序文件,但iptables命令默认指向了兼容层,而非原生的iptables-legacy。

九、各大发行版现状

Linux发行版默认状态切换方式
Arch Linux2026年4月起,默认使用iptables-nft(nf_tables后端)可选iptables-legacy包回退
RHEL 8/9默认使用iptables-nft可安装iptables-legacy切换
Fedora默认使用iptables-nft可通过alternatives切换
Ubuntu 20.10+默认使用iptables-nft可使用update-alternatives切换
Debian 10+默认使用iptables-nft可安装iptables-legacy
OpenSUSE Leap 15.3+默认使用iptables-nft可切换

十、总结——一张表看懂所有差异

维度iptables(旧时代/xtables)nftables(新时代)
内核引擎xtablesnftables
内核模块数量多模块分立(ip_tables/ip6_tables/arptables/ebtables)统一核心(nf_tables)
表的创建内核固定5张,用户不可增删用户自由创建,数量不限
表与钩子内核硬编码绑定用户自由指定
地址族支持多工具分立(iptables/ip6tables)统一工具 + inet地址族
数据结构线性链表 O(N)哈希表/树 O(1)
规则结构匹配+动作硬编码为一个对象表达式链表,灵活组合
更新方式拆分操作,有”空窗期”原子事务,无风险
多租户隔离不支持支持(不同用户可建不同表)
扩展方式需编译内核模块内核模块 + 用户态组合
调试能力有限nft monitor实时追踪

给不同场景用户的建议

场景一:你是普通运维,习惯用iptables命令
→ 放心继续使用。旧脚本和命令依然有效,底层会自动翻译执行。

场景二:你在写新项目,追求性能和现代化管理
→ 建议直接学习nft命令,享受原子更新和哈希查找的性能优势。

场景三:你维护的软件(如Docker、Kubernetes)出现异常
→ 排查是否与新后端(nf_tables)的兼容性问题。可暂时切换到iptables-legacy模式作为快速回退方案。

这场从iptables到nftables的变革,不是一次简单的版本号跳跃,而是一场从内核数据结构、算法设计到命令行工具的彻底重构。理解这些底层差异,将帮助你在遇到性能或兼容性问题时做出更准确的判断——当你再次敲下iptables --version并看到(nf_tables)时,你已经不再是那个只会复制粘贴命令的运维新手了。

作者

老丹

关注我
其他文章
上一个

ZLMediaKit:国产高性能流媒体服务器框架深度解析

下一个

深入理解 TUN/TAP:软件定义网络的基石与全景解析

关于博主

    老丹是一名C/C++后台开发工程师,信奉“无抽象不设计,无性能不生产”。

  • 技术栈:Modern C++、Linux环境编程、多线程/并发、网络编程等。
  • 信条:能用constexpr解决的问题绝不拖到运行时,能靠RAII避免的泄漏绝不写析构。
  • 正在填坑:从解封装到渲染的C++全链路实现,正在驯服FFmpeg与H.264/H.265。
  • 输出原则:这里的每一段代码都经过-Wall -Wextra -Werror -O2的洗礼。

近期文章

  • Linux系统的安全基石:深入理解可插拔认证模块(PAM) 2026年7月27日
  • vsftpd 完全指南:从核心原理到Docker容器化部署 2026年7月27日
  • 互联网的”导航”安全卫士:深入解读DNSSEC 2026年7月27日
  • Ubuntu DNS 配置完全指南 2026年7月27日
  • 在 Ubuntu 中使用 Certbot 的操作指南 2026年7月27日

文章分类

  • C/C++开发 (13)
  • Docker容器 (3)
  • Linux工具包 (10)
  • Linux服务配置 (33)
  • Linux系统 (10)
  • OpenWrt路由 (2)
  • Shell脚本 (3)
  • 安防技术 (4)
  • 数据安全 (30)
  • 网络协议 (17)
  • 计算机理论 (22)
联系我们:📍 地址:中国·广东省深圳市   |   ✉️ 邮箱:support@tanglinux.com   |   💬 QQ:870866607
版权所有:老丹的足迹粤ICP备2026061170号-1       公安备案图标 粤公网安备44030002013274号