跳到主要内容

第 9 章 Netfilter Frameworks

章节引子:在高速公路上设卡

如果你觉得网络协议栈像一条川流不息的公路,数据包是上面跑的车辆,那么 Netfilter 就是交警部门。

但这不仅仅是一个隐喻。在 Linux 内核的设计里,网络流量确实是一呼而过的——网卡收到中断,内核分配一个 sk_buff,协议栈一路向上传递,最终释放内存。这个过程快得惊人,但如果你是一个「交警」,想要在这个过程中拦下一辆车检查证件,或者干脆让它改道,你应该在哪里动手?

早期的内核设计并没有预留这么多「检查站」。如果你想要过滤数据包,或者做 NAT(网络地址转换),你就得修改核心协议栈的代码,把逻辑硬编码在处理流程里。这是一种糟糕的扩展方式:每加一个功能,内核就变胖一点,而且容易出错。

Netfilter 的出现就是为了解决这个问题。它不修改主干道,而是在主干道的五个关键位置挂上了「钩子」。任何内核模块都可以在这些钩子上注册回调函数,当数据包经过时,内核会停下来问一句:「这里有人要处理这个包吗?」

这一章,我们要深入的就是这套机制。我们将看到它是如何嵌入到网络协议栈的各个角落,又是如何支撑起 iptables、连接跟踪和 NAT 这些我们每天都在用的功能的。

Netfilter Frameworks

现在让我们把视角拉高一点。在进入具体的代码实现之前,先看清全貌。

Netfilter 子系统不仅仅是一个防火墙,它是一个底层的流量拦截框架。基于它,内核实现了一系列我们在网络工程中不可或缺的功能:

  • 数据包选择:这是 iptables 的核心,决定哪些包要被抓出来处理。
  • 数据包过滤:也就是我们常说的防火墙规则——放过这个,丢掉那个。
  • 网络地址转换(NAT):修改包的 IP 或端口,这是让局域网共享一个公网 IP 上网的基础。
  • 数据包修改:在路由前或路由后,修改包头的内容(比如打上某种标记)。
  • 连接跟踪:这是最核心的机制之一,内核通过它来记住「这是一个已经建立的连接」,NAT 和状态防火墙全靠它。
  • 网络统计:收集流量数据,用于计费或监控。

这一章的任务,就是搞清楚这些功能是如何挂在 Netfilter 这个框架上运行的。

基于 Netfilter 的三大框架

在内核源码的 net/netfilter 目录下,除了核心框架,还躺着几个著名的项目。如果你维护过线上服务,大概率撞见过 LVS 或 IPVS——或者至少,你处理过的流量就在它们身上流过。

1. IPVS:传输层的负载均衡

IPVS(IP Virtual Server) 是基于 Netfilter 实现的传输层负载均衡解决方案。代码位于 net/netfilter/ipvs

当你只有一台入口服务器,但后面有十台 Web 服务器在跑时,你需要把进来的流量均匀地分发给这十台服务器。IPVS 就是在 Netfilter 的钩子上拦截流量,根据调度算法(如轮询、最少连接)修改目标地址,将数据包转发到后端真实的服务器。

历史注记:IPv4 的 IPVS 支持在很早的内核中就存在了。而 IPv6 的 IPVS 支持则是在 2.6.28 版本合入的,主要由 Google 的 Julius Volz 和 Vince Busam 开发。如果你想深入了解 LVS(Linux Virtual Server)的架构,可以参考 www.linuxvirtualserver.org

2. IP sets:管理海量 IP 地址

如果你写过防火墙规则,可能会遇到这种痛苦:想要封禁 10,000 个 IP 地址,你就得写 10,000 条 iptables 规则。这对性能是个灾难,因为每个包都要遍历这 10,000 条规则。

IP sets 就是为了解决这个问题而生的。它由两部分组成:

  • 内核模块net/netfilter/ipset
  • 用户空间工具ipset

IP sets 允许你在内核里维护一个集合(Set),里面可以是一堆 IP 地址、端口号甚至网段。你在 iptables 里只需要引用这个集合的名字,内核就会用哈希表或红黑树高效地查找。这个框架由 Jozsef Kadlecsik 开发。详情见 http://ipset.netfilter.org

3. iptables:最熟悉的老伙计

说到 Netfilter,就不能不提 iptables。它可能是历史上最流行的 Linux 防火墙工具。

一定要搞清楚这个关系:Netfilter 是内核里的框架,iptables 是用户空间的前端工具。你用 iptables 命令写规则,iptables 通过 setsockopt() 系统调用把这些规则告诉内核,内核里的 Netfilter 框架拿着这些规则去匹配数据包。

iptables 提供了完整的管理层功能:

  • 添加或删除规则
  • 显示统计信息
  • 管理不同的表
  • 清零计数器

根据协议的不同,内核里有几套并行的实现:

  • iptables:用于 IPv4(代码在 net/ipv4/netfilter/ip_tables.c
  • ip6tables:用于 IPv6(代码在 net/ipv6/netfilter/ip6_tables.c
  • arptables:用于 ARP 协议(代码在 net/ipv4/netfilter/arp_tables.c
  • ebtables:用于以太网桥接(二层防火墙,代码在 net/bridge/netfilter/ebtables.c

在用户空间,对应的命令行工具就是 iptablesip6tables。它们同样通过 setsockopt()getsockopt() 与内核通信。

下一代技术:xtables2 与 nftables

iptables 虽然经典,但这套古老的接口也有撑不住的时候——比如当你试图插入几万条规则时。为了解决这个问题,内核社区开始探索新的架构。

这其中有两个方向值得我们关注。

1. xtables2 这是一个由 Jan Engelhardt 主要开发的项目(截至本书编写时尚在进行中)。它的核心改进是抛弃了老旧的 setsockopt() 接口,转而使用 Netlink 基于消息的接口与内核通信。

这里有一个技术细节值得玩味:旧方案通过 setsockopt 传递规则时,往往需要把整张规则表从用户空间拷贝到内核空间,然后再进行解析。当规则数量达到几万条时,这个「大拷贝」的开销非常惊人。而 xtables2 使用 Netlink 这种基于消息的机制,可以实现增量的更新——只传改动的部分。这让它在面对大规模规则集时表现得更从容。项目详情见 http://xtables.de

2. nftables 这是真正的「继承者」,旨在完全替代 iptables。nftables 最核心的设计思想是:

  • 统一实现:不再区分 iptables、ip6tables、arptables 和 ebtables,所有协议的处理逻辑在一个统一的代码库里。
  • 虚拟机:它引入了一个简单的虚拟机来执行规则,这使得规则的表达能力更强,也更灵活。

虚拟机的引入并非为了炫技,而是为了解决灵活性带来的效率问题。传统的 iptables 规则是硬编码的匹配函数,而 nftables 允许用户在用户空间编写类似汇编的指令,内核通过一个微型解释器执行。这意味着你可以组合出非常复杂的逻辑,而无需每次都修改内核代码。

这个项目最早由 Patrick McHardy 在 2008 年的 Netfilter 研讨会上提出。内核基础设施和用户空间工具主要由 Patrick McHardy 和 Pablo Neira Ayuso 开发。如果你想看前瞻性的技术分析,可以参考这篇经典文章 “Nftables: a new packet filtering engine” (LWN, http://lwn.net/Articles/324989/)。


扩展与生态

Netfilter 的魅力在于它的可扩展性。除了上述核心框架,还有大量的内核模块(在 net/netfilter 下)扩展了它的功能。我们不会在本书中逐一罗列所有这些模块——毕竟,管理和使用这些规则更多是系统管理员的日常工作,而不是内核开发者的必修课。

如果你对具体的扩展模块(如 conntrack 的 helper、特定的 NAT 模块等)感兴趣,最好的参考资料除了源码本身,就是官方的 Netfilter 项目网站:**www.netfilter.org**。

全貌看清楚了,但这只是用户视角的地图。接下来我们要钻进内核,看看这些钩子到底挂在哪。