前言

我是卖钩子起家的。 —朱八八

近期项目有个需求,要修改之前的NAT规则,实现新的NPTv6转换协议(有空再写篇NPTv6转换器的博文吧),于是就是看之前的实现原理,过了一遍整个框架的大致处理逻辑,发现之前的实现上和Linux内核那套实际也差不多,也是在项目自己实现的netfilter上处理的,而自己实现的这个netfilter感觉和内核的实现也是大差不差,实际一些细节上可能有所出入,但是基本的几个钩子过程上的处理好像都差不多了。

Netfilter

之前就知道netfilter,也知道有钩子(hooks)这个东西,但是它具体是干什么的却不是很清楚。

定义:netfilter 是 Linux 内核中一套用于处理网络数据包的“框架”。

Netfilter 框架体现

组成部分 说明
钩子点定义 内核中明确了协议栈的关键位置插入处理逻辑的位置(5 个)
nf_hook_ops 提供注册钩子函数的标准接口
链式处理机制 所有注册函数被串联执行,每个函数可决定包的命运
内核模块(如 ip_tables) 实现具体的逻辑、策略规则(如 NAT、防火墙)

Netfilter 是内核网络协议栈中的钩子机制框架,它在数据包流经的关键路径埋设了钩子点(锚点),开发者或用户通过内核模块(如 iptablesnftables)在这些点上注册处理逻辑,用户空间工具通过规则配置间接控制这些逻辑,实现包过滤、NAT、流控等功能。

可以把协议栈想象成一条流水线,而 Netfilter 就是在流水线上开了几个窗口(锚点),你可以通过这几个窗口:

  • 观察包(LOG)
  • 拦截包(DROP)
  • 放行包(ACCEPT)
  • 修改包(SNAT/DNAT)
  • 标记包(MARK)

用户态可以告诉这些窗口要干啥(规则),而真正干活的是挂在窗口上的内核模块(处理逻辑)

钩子点

1
2
3
4
5
6
7
8
9
10
                   用户态应用

┌──────────────────┼──────────────────┐
│ │ │
PRE_ROUTING → ROUTING → LOCAL_IN LOCAL_OUT
│ │ │
FORWARD LOCAL_PROCESS POST_ROUTING
▼ ▼ ▼
─────内核协议栈─────

内核里的钩子定义include/linux/netfilter_ipv4.h

1
2
3
4
5
6
7
8
enum nf_inet_hooks {
NF_INET_PRE_ROUTING, // 0
NF_INET_LOCAL_IN, // 1
NF_INET_FORWARD, // 2
NF_INET_LOCAL_OUT, // 3
NF_INET_POST_ROUTING, // 4
NF_INET_NUMHOOKS // 5, 非钩子,只表示数量
};

NF_INET_PRE_ROUTING(0)

时机:数据包刚到达网络接口,在进入本地协议栈、做路由判断之前。

用途

  • 修改目标地址(DNAT
  • 早期丢包
  • 检查来源合法性(如源地址过滤)

常用表/链

  • raw
  • nat 表中的 PREROUTING
  • mangle 表的 PREROUTING

NF_INET_LOCAL_IN(1)

时机:数据包已经确认是要发往本机的,准备进入用户空间处理前。

用途

  • 本地防火墙过滤(例如允许某些端口)
  • 最后机会拦截入站包

常用表/链

  • filter 表的 INPUT
  • mangle 表的 INPUT

NF_INET_FORWARD(2)

时机:包不是发往本地,而是要转发出去,比如路由器场景。

用途

  • 转发规则控制(启用路由功能时)
  • 防火墙控制通过主机的转发流量

常用表/链

  • filter 表的 FORWARD
  • mangle 表的 FORWARD

NF_INET_LOCAL_OUT(3)

时机:包是本机用户进程生成的,还没经过路由判定。

用途

  • 修改或拦截本机发出的数据包
  • 阻止某些进程发送数据

常用表/链

  • raw 表的 OUTPUT
  • filter 表的 OUTPUT
  • nat 表的 OUTPUT

NF_INET_POST_ROUTING(4)

时机:包已经确定路由(无论来自本地还是转发),马上要发出去了。

用途

  • 修改源地址(SNAT
  • 最后阶段处理出站包

常用表/链

  • nat 表的 POSTROUTING
  • mangle 表的 POSTROUTING

总结表格

钩子点名 时机 典型用途 涉及表/链
PRE_ROUTING (0) 包一到就触发 DNAT、防火墙、QoS raw, mangle, nat
LOCAL_IN (1) 确定是发往本机 防火墙过滤入站流量 filter, mangle
FORWARD (2) 包需要转发 控制路由流量 filter, mangle
LOCAL_OUT (3) 本机发出的包 本地防火墙、拦截 raw, filter, nat
POST_ROUTING (4) 发出去前最后一步 SNAT、标记出站包 mangle, nat

钩子与协议栈

有个问题,钩子和协议栈有啥关系呢?是属于协议栈里面的东西?还是剥离出协议栈的?

实现角度

Netfilter 是嵌入在协议栈中的“钩子机制”

✅ 钩子点确实是协议栈中的一部分,嵌在协议栈各个关键函数中。

举个真实例子(来自内核源码,以 IPv4 为例):

1
2
3
4
5
6
7
8
9
10
c复制编辑// linux/net/ipv4/ip_input.c

int ip_rcv(struct sk_buff *skb, struct net_device *dev, ...)
{
...
if (nf_hook(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, ...))
return 0; // 如果 Netfilter 拦截处理了,就不往下走
...
return ip_rcv_finish(skb);
}

这说明:

  • 数据包刚到内核,就先走了 NF_INET_PRE_ROUTING 钩子(这一步属于 Netfilter)。
  • 如果没有被丢弃/处理掉,再继续走 ip_rcv_finish() 进入协议栈真正的 IP 层处理。

类似的钩子在 ip_forward()ip_output()ip_local_deliver() 等函数中也有嵌入。

👉 所以这些钩子是 直接嵌入在协议栈处理流程里的函数调用点

架构设计

Netfilter 是“协议栈的扩展机制”

虽然代码嵌在协议栈中,但从架构上,Linux 把 Netfilter 视作一个插件式的包处理框架 —— 类似于协议栈的“扩展点”。

这有几个关键特性:

特性 说明
协议无关设计 Netfilter 支持 IPv4、IPv6、桥接、甚至子系统如 Bluetooth
插件式钩子注册机制 外部模块(如 iptables)通过 nf_register_net_hook() 动态插入逻辑
可以不使用 若未加载 ip_tables 或未配置规则,这些钩子点不会做任何事,性能无损
与协议实现解耦 钩子点与核心协议处理逻辑(如 TCP/IP stack)互不干扰,遵循 SRP(单一职责)

总结

类比 协议栈 Netfilter
高速公路 网络协议栈 是道路上的“收费站/监控卡口”
主干函数 ip_rcv()ip_forward() 在中间插入了 nf_hook() 调用
是否属于协议栈 ✅ 是 ✅ 属于,是内核网络协议处理逻辑的一部分

Netfilter 的钩子点本质上属于协议栈的组成部分,嵌在网络协议栈的数据路径中,但它通过模块化的机制把协议栈处理“开放出来”,让其他模块可以动态插入处理逻辑。

内核态

从之前与协议栈的关系其实也能看出netfilter其实是属于内核态的东西了。

模块 所属空间 说明
Netfilter 本身 ✅ 内核态 在协议栈中嵌入,C 语言实现,运行在 Ring 0
iptables/nftables 工具 ❎ 用户态 控制命令/规则编辑器,用于设置规则
netlink/socket 接口 通信桥梁 用户态和内核态之间的数据通道

iptables

定义

iptables 是一个用户态命令行工具,用来配置 Linux 内核中的 Netfilter 子系统。

你可以用它来定义防火墙规则,例如:

  • 放行/拦截某个端口的包
  • 做 NAT 转换
  • 标记流量
  • 日志记录等

⚠️ 注意:iptables 自身不处理包,它只是告诉内核该怎么处理包。

四表五链

🔷 表(tables):定义你要做什么类型的操作

表名 用途 常见链
filter 默认表,做包过滤 INPUT, OUTPUT, FORWARD
nat 做 NAT(SNAT, DNAT) PREROUTING, POSTROUTING, OUTPUT
mangle 修改包(TTL, TOS, MARK) 所有 5 条链都支持
raw 跳过连接跟踪(conntrack)等 PREROUTING, OUTPUT

🔗 链(chains):对应 Netfilter 的钩子点

链名 时机 对应钩子
PREROUTING 包刚到达接口 NF_INET_PRE_ROUTING
INPUT 发往本机 NF_INET_LOCAL_IN
FORWARD 要转发的包 NF_INET_FORWARD
OUTPUT 本机发出的包 NF_INET_LOCAL_OUT
POSTROUTING 包要发出去了 NF_INET_POST_ROUTING

nftables

定义

nftables(简称 nft)——这是 iptables 的“继任者”,也是 Linux 网络包过滤的现代工具

nft 是 Linux 防火墙的新一代用户态工具,配置内核中的 Netfilter 子系统,替代 iptablesip6tablesarptablesebtables

对比

特性 iptables nftables
配置工具 iptablesip6tables 一个统一的:nft
表和链结构 每种功能一套表 所有功能合一,结构更统一
表达式语法 基于命令行参数拼接 类似脚本语言,结构清晰
性能 较低,线性匹配规则 内核中是状态机匹配,效率高
可扩展性 差,新功能要补模块 高,很多特性通过扩展字段即可支持
状态和计数支持 有限 强大,可组合条件、使用映射等
支持 IPv4+IPv6 合并规则 ✅ 一条规则支持两者

状态机与性能优化

nft 在内核中用的是 BPF-style 的规则状态机(不像 iptables 的链式匹配),这意味着:

  • 规则可跳转、组合、复用,性能更高
  • 支持高级数据结构:集合、映射、计数器
  • 动态添加规则不会阻塞主规则集,适合高并发环境

使用

1
sudo nft list ruleset

示例:

1
2
3
4
5
6
7
8
table inet my_table {
chain input {
type filter hook input priority 0;

tcp dport 22 accept # 允许 SSH
ip saddr 192.168.1.0/24 drop # 拒绝内网段
}
}

从现实来看它的表规则也更加清楚了

1
2
3
4
5
6
7
一个命名空间:

[table] 表:网络协议(inet/ip/ip6/arp)

[chain] 链:hook 点(input/output/...)

[rules] 规则:表达式(packet match → action)

UFW与firewalld

往事重提

其实认识iptable最初是通过firewalld认识的,相信每个人用红帽系的云服务都会遇到服务不通然后研究如何用firewalld开放端口的经历吧。换到ubuntu上面,想用命令firewalld的命令开放端口,结果又发现没这个东西了,研究一下才知道ubuntu是用ufw,当时好像是搜索的时候才知道原来ufw也就是iptables的一个前端,按照这个思路再一搜,果然firwalld也是基于iptables实现的啊。

ufw

ufw 的核心功能

ufw 是一个简化的命令行防火墙工具,旨在使防火墙配置更加简单,尤其适合不熟悉 iptablesnftables 的用户。它默认使用 iptables(或在支持的系统中,nft)来管理网络规则。

  • 简化规则管理ufw 提供了非常简单的命令来添加、删除、列出和管理规则。
  • 适用于桌面和服务器:尤其适合桌面环境,但也可以用于服务器。

ufwiptables 的关系

  • ufw 实际上是 iptables 的一个前端工具,通过简单的命令来配置复杂的防火墙规则。
  • ufw 会生成相应的 iptables 规则并将它们添加到 iptables 中,因此所有的规则最终都是由 iptables(或 nft)来执行。

例如,当你运行 ufw allow 22 时,ufw 会在后台生成类似于以下的 iptables 规则:

1
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

ufwnft 的关系

  • 在支持 nftables 的系统上,ufw 会自动切换到 nft,并使用 nft 来配置防火墙规则。
  • 在现代的 Linux 发行版上,ufw 会检测系统是否支持 nftables,如果支持,它将自动使用 nft 进行管理。

firewalld

firewalld 的核心功能

firewalld 是一个动态管理防火墙规则的工具,广泛用于 CentOSRHELFedora 等发行版中。它提供了一种区域化(zone-based)防火墙策略,并且能通过命令行或图形界面进行动态配置。

  • 区域管理firewalld 将网络接口和服务划分为不同的区域,每个区域有不同的防火墙规则。
  • 动态调整:不同于 iptablesfirewalld 支持实时更改防火墙规则而不需要重启防火墙服务。

firewalldiptables 的关系

  • firewalldiptables 的封装器,提供了一个更友好的界面来操作 iptables 规则。
  • firewalld 内部使用 nftables(如果系统支持)iptables 来实际执行规则配置,依赖于系统的内核模块。
  • firewalld 配置的背后,实际上运行的是 iptablesnft,因此最终的规则是通过这些工具来应用的。

firewalldnft 的关系

  • firewalld 在支持 nft 的系统中,默认会使用 nft 来配置和管理规则。
  • firewalld 通过对 nftables(或 iptables)的封装来动态管理防火墙规则。

对比

特性 ufw firewalld
目标用户 简单易用,面向桌面和服务器 更适合服务器,支持动态管理
配置方式 简单命令行,适合快速设置 动态管理,支持区域化配置
底层实现 依赖 iptablesnft 依赖 iptablesnft
动态规则支持 不支持动态修改 支持动态修改规则
配置语言 无(只需简单命令) 基于区域/服务的配置系统
典型命令 ufw allow, ufw deny firewall-cmd --zone=public --add-port=22/tcp

conntrack

连接追踪(conntrack 是在 Netfilter 框架内实现的,并且它的创建和销毁并不是单纯地发生在某个特定的钩子过程中。它的工作是依赖于 Netfilter 的各个钩子点,但连接的状态管理和生命周期是与协议栈的处理过程紧密相连的

连接追踪的基本工作流程

  1. 连接追踪的创建:当一个新的网络连接(例如,TCP 建立三次握手的初始 SYN 包)进入 Netfilter 时,连接追踪会在 NF_INET_PRE_ROUTING 钩子点的早期阶段创建一个新的连接跟踪条目。它会基于包的元数据(源IP、目标IP、端口号、协议类型等)来判断是否需要建立新的连接条目。如果没有找到现有的条目,就会创建一个新的连接条目。
  2. 连接状态管理
    • 每当一个数据包通过 Netfilter 时,conntrack 会检查该数据包是否属于已知连接,或者是否需要为其创建一个新的连接追踪条目。
    • 每个数据包在经过 Netfilter 处理时,都会被分配一个连接的“状态”,这些状态包括:
      • NEW:新的连接。
      • ESTABLISHED:已经建立的连接。
      • RELATED:与某个已存在连接相关联的流量。
      • INVALID:无效连接(如反向的 SYN 包)。
  3. 连接追踪的销毁:当连接的生命周期结束(如 TCP 会话的四次挥手结束或连接超时),连接追踪条目会被删除或标记为超时。在 NF_INET_POST_ROUTING 钩子阶段,某些数据包离开时,连接追踪可能会被更新或销毁。

具体的钩子点与连接追踪的关系

  1. NF_INET_PRE_ROUTING(数据包刚进入协议栈前)
    • 在这个阶段,连接追踪模块会检查数据包是否属于已有的连接,如果没有,且数据包是发起连接的第一个包(例如 TCP SYN),则会创建新的连接追踪条目。
  2. NF_INET_LOCAL_IN(本机接收的数据包)
    • 如果数据包是发往本机的,连接追踪会更新该数据包的连接状态。若是响应包(如 TCP ACK 包),连接追踪将确认该包属于已建立的连接。
  3. NF_INET_FORWARD(转发的数据包)
    • 如果数据包是转发的,连接追踪会根据原始源和目标地址判断该包是否属于已有连接,更新连接状态。
  4. NF_INET_LOCAL_OUT(本机发送的包)
    • 在这里,连接追踪会检查本机发出的包是否已经有对应的连接条目,若是响应包,则状态会更新。
  5. NF_INET_POST_ROUTING(包即将离开协议栈)
    • 当包离开系统时,连接追踪会完成最终的状态更新,若连接终止(如 TCP 四次挥手),对应的连接追踪条目会被销毁。

连接追踪与协议栈的关系

  • 协议栈和连接追踪协作:虽然连接追踪是在 Netfilter 中实现的,它和协议栈是密切协作的。在协议栈处理过程中,连接的状态变化(如从 SYNESTABLISHED)是由协议栈本身决定的,而连接追踪通过监控数据包流动来实时更新其连接条目。
  • 协议栈层面上的连接:协议栈层面上的连接(如 TCP 连接)与连接追踪是紧密关联的。当协议栈处理连接(例如,TCP 握手时),它会向 Netfilter 注册状态,Netfilter 使用这些信息来维护连接追踪条目。因此,连接追踪是对协议栈连接状态的一种外部管理

总结

  • 连接追踪(conntrack)是在 Netfilter 的钩子过程中创建和管理的,但它的生命周期和状态变化与协议栈的连接状态密切相关。
  • 在每个钩子点,Netfilter 都会查询或更新 conntrack 条目,但连接的具体生命周期管理是与协议栈的状态机协作的
  • 连接追踪条目在包到达协议栈时创建,并在协议栈连接结束(例如 TCP 连接关闭)或超时后销毁。

conntrack工具

conntrack 工具是用于查看和管理 Linux 系统中 连接追踪表(Connection Tracking Table,简称 conntrack table)的工具。它是一个非常有用的工具,可以帮助我们诊断和调试网络连接,并且在 Netfilter 框架下提供连接追踪的管理和查询功能。

  • conntrack 工具Netfilter 的一部分,用来查看和管理 连接追踪表conntrack table),这些表是由 Netfilter 在内核中维护的。
  • conntrack 工具与 iptablesnftables 配合,提供连接状态跟踪和管理功能。iptablesnftables 使用连接追踪表来决定是否允许数据包进入、离开或被拒绝。

总结

做点实际项目还是有提升自己对内核上的一些机制的了解,起码有个了解的契机和窗口了。但是感觉还是停在表面上,比如钩子具体在哪里起作用的,钩子是如何工作的,在协议栈上又是什么的行为,这些自己也就只有个大概印象,感觉还是要深入看一下内核源码,才能有些具体的了解。也要看工作上有没有这方面的安排了,有空闲时间更想看看Service Mesh的东西,这个感觉还是感兴趣点,但是有点协议栈的知识的话对分布式网络也会有更好的认识吧/。