第一百二十六章:海防
问道期涉及内核源码:
一
林小源在网络之海的入口看到了一道巨大的城墙——城墙上有无数哨兵,每一个都在仔细检查过往的船只。
"这是什么地方?"
"netfilter。"一个浑厚的声音从城墙上传来。
林小源抬头,看到一个身穿铠甲的将军站在城墙最高处。将军的铠甲上刻着五道门——PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING。每一道门都有哨兵把守。
"我是 netfilter 的守护者。"将军说,"每一个数据包进入内核,都必须经过我的检查。我在五个位置设置了钩子——数据包经过时,我会检查它是否符合规则。"
"规则?"
"iptables 规则。"将军从铠甲上摘下一枚徽章,上面写着"ACCEPT"。"规则由用户空间配置——允许 SSH?iptables -A INPUT -p tcp --dport 22 -j ACCEPT。拒绝所有?iptables -A INPUT -j DROP。NAT?iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE。"
"但别把我和 iptables 混成一个人。"将军把徽章翻过来,露出 NF_ACCEPT、NF_DROP、NF_STOLEN、NF_QUEUE、NF_REPEAT 几个古老裁决,"netfilter 是内核 hook 框架,用户空间工具只是给我布阵。iptables、nftables 都可以借我的钩子,真正落到内核里的是 hook 函数、优先级、规则表达式和 verdict。"
"那连接状态呢?"
"那是 conntrack。"将军指向城墙后的账房,"它记录连接的状态,让规则不必只看单个包。你可以允许 ESTABLISHED/RELATED 的回包,也可以在 NAT 时记住原始五元组和转换后的五元组。没有 conntrack,很多有状态防火墙和 NAT 都会变成瞎子。"
海防初试
netfilter hook 函数处理数据包后返回的 ACCEPT、DROP 等结果统称为什么?
二
"这些钩子在什么位置?"林小源问。
将军走到城墙的平面图前,上面画着数据包的流动路径:
"PREROUTING——数据包刚进入时。不管发往哪里,先过 PREROUTING。然后分两条路:发往本机的走 INPUT,转发的走 FORWARD。本机发出的走 OUTPUT。最后,不管是转发还是本机发出的,都走 POSTROUTING。"
"三条路。"
"对。"将军说,"INPUT 和 OUTPUT 是本机的流量——服务器自己收发的数据。FORWARD 是转发的流量——路由器转发别人的数据包。PREROUTING 和 POSTROUTING 是所有流量都经过的。"
"表呢?我听说有 filter、nat、mangle。"
"filter 表——默认表,做过滤。ACCEPT 允许,DROP 丢弃,REJECT 拒绝。"将军说,"nat 表——地址转换。SNAT 改源地址,DNAT 改目标地址,MASQUERADE 伪装。mangle 表——修改数据包的头部字段。"
"还有 raw 和 security。"将军又在地图边缘点了两下,"raw 常用于绕开或提前处理 conntrack,security 可与安全标记策略配合。不同表在不同 hook 点有自己的优先级。包不是简单走五道门,而是在每道门里按优先级穿过一串钩子。"
"那 NAT 在哪里做?"
"DNAT 常在 PREROUTING 改目的地,让路由决策看到新目标;SNAT 常在 POSTROUTING 改源地址,因为出接口和源地址选择已经更清楚。时机错了,路由和连接跟踪都会别扭。"
五门之试
NAT 中常在 PREROUTING 改目标地址的动作叫什么?
三
"nftables 是什么?"林小源注意到城墙的一角有一座新修的塔楼。
"iptables 的继任者。"将军的表情有些复杂,"nftables 语法更简洁,性能更好,框架更统一。但 iptables 用了二十多年,生态太深了——很多人还在用。"
"会完全取代吗?"
"最终会。"将军说,"但不是现在。现在两个都可用——iptables 基于 netfilter 的 x_tables 框架,nftables 基于 netfilter 的 nf_tables 框架。底层都是 netfilter 的钩子机制,只是上层的配置方式不同。"
林小源看着那道城墙,忽然明白了——netfilter 不是防火墙本身,而是防火墙的框架。iptables 和 nftables 都是基于 netfilter 的工具。钩子机制是核心——在数据包经过内核的关键位置设置检查点,让用户空间配置的规则在这些检查点上执行。
"没有 netfilter,就没有防火墙。"将军说,"也没有 NAT,也没有包过滤。它是网络安全的基石。"
"nftables 还把许多旧碎片收拢成一套表达式虚拟机。"将军拍了拍新塔楼,"table、chain、rule、set、map 可以组合得更紧凑;内核侧还有 netlink 规格描述,例如 nftables 的 family 和命令。iptables 仍可通过兼容层存在,但写新规则时,要知道现代主线已经偏向 nftables。"
"规则越多越安全吗?"林小源问。
"不。规则越多越要重视顺序、命中率和可观测性。热路径上每多一次匹配,就是每个包都要交的税。真正的海防,要把规则放在正确 hook、正确表、正确优先级。"
新塔之试
iptables 的现代继任者、基于 nf_tables 框架的用户空间规则系统叫什么?
/*
* netfilter 的钩子点:
*
* PREROUTING → FORWARD → POSTROUTING
* ↓ ↑
* INPUT → 本地进程 → OUTPUT
*
* 链 (chains):
* PREROUTING — 数据包进入时
* INPUT — 发往本机
* FORWARD — 转发
* OUTPUT — 本机发出
* POSTROUTING — 数据包离开时
*
* 表 (tables):
* filter — 过滤(默认)
* nat — 地址转换
* mangle — 修改数据包
*
* 规则 (rules):
* 匹配条件 → 动作
* -s 192.168.1.0/24 -j ACCEPT
*/
printf("=== netfilter — 网络防火墙 ===\n\n");
printf("netfilter 钩子点:\n\n");
printf(" PREROUTING → FORWARD → POSTROUTING\n");
printf(" ↓ ↑\n");
printf(" INPUT → 本地进程 → OUTPUT\n\n");
printf("--- 链 ---\n");
printf("PREROUTING: 数据包进入时\n");
printf("INPUT: 发往本机\n");
printf("FORWARD: 转发\n");
printf("OUTPUT: 本机发出\n");
printf("POSTROUTING: 数据包离开时\n\n");
printf("--- 表 ---\n");
printf("filter: 过滤(默认)\n");
printf(" ACCEPT — 允许\n");
printf(" DROP — 丢弃\n");
printf(" REJECT — 拒绝\n\n");
printf("nat: 地址转换\n");
printf(" SNAT — 源地址转换\n");
printf(" DNAT — 目标地址转换\n");
printf(" MASQUERADE — 伪装\n\n");
printf("mangle: 修改数据包\n\n");
printf("--- iptables 命令 ---\n");
printf("查看规则:\n");
printf(" iptables -L -n\n\n");
printf("允许 SSH:\n");
printf(" iptables -A INPUT -p tcp --dport 22 -j ACCEPT\n\n");
printf("拒绝所有:\n");
printf(" iptables -A INPUT -j DROP\n\n");
printf("NAT:\n");
printf(" iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE\n\n");
printf("--- nftables ---\n");
printf("iptables 的继任者:\n");
printf(" 更简洁的语法\n");
printf(" 更好的性能\n");
printf(" 统一的框架\n");#include <stdio.h>
/*
* netfilter 的钩子点:
*
* PREROUTING → FORWARD → POSTROUTING
* ↓ ↑
* INPUT → 本地进程 → OUTPUT
*
* 链 (chains):
* PREROUTING — 数据包进入时
* INPUT — 发往本机
* FORWARD — 转发
* OUTPUT — 本机发出
* POSTROUTING — 数据包离开时
*
* 表 (tables):
* filter — 过滤(默认)
* nat — 地址转换
* mangle — 修改数据包
*
* 规则 (rules):
* 匹配条件 → 动作
* -s 192.168.1.0/24 -j ACCEPT
*/
int main() {
printf("=== netfilter — 网络防火墙 ===\n\n");
printf("netfilter 钩子点:\n\n");
printf(" PREROUTING → FORWARD → POSTROUTING\n");
printf(" ↓ ↑\n");
printf(" INPUT → 本地进程 → OUTPUT\n\n");
printf("--- 链 ---\n");
printf("PREROUTING: 数据包进入时\n");
printf("INPUT: 发往本机\n");
printf("FORWARD: 转发\n");
printf("OUTPUT: 本机发出\n");
printf("POSTROUTING: 数据包离开时\n\n");
printf("--- 表 ---\n");
printf("filter: 过滤(默认)\n");
printf(" ACCEPT — 允许\n");
printf(" DROP — 丢弃\n");
printf(" REJECT — 拒绝\n\n");
printf("nat: 地址转换\n");
printf(" SNAT — 源地址转换\n");
printf(" DNAT — 目标地址转换\n");
printf(" MASQUERADE — 伪装\n\n");
printf("mangle: 修改数据包\n\n");
printf("--- iptables 命令 ---\n");
printf("查看规则:\n");
printf(" iptables -L -n\n\n");
printf("允许 SSH:\n");
printf(" iptables -A INPUT -p tcp --dport 22 -j ACCEPT\n\n");
printf("拒绝所有:\n");
printf(" iptables -A INPUT -j DROP\n\n");
printf("NAT:\n");
printf(" iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE\n\n");
printf("--- nftables ---\n");
printf("iptables 的继任者:\n");
printf(" 更简洁的语法\n");
printf(" 更好的性能\n");
printf(" 统一的框架\n");
return 0;
}道藏笔记
内核启示
netfilter 是内核的防火墙框架。
netfilter 钩子点:
- PREROUTING — 数据包进入时
- INPUT — 发往本机
- FORWARD — 转发
- OUTPUT — 本机发出
- POSTROUTING — 数据包离开时
表:
- filter — 过滤(ACCEPT、DROP、REJECT)
- nat — 地址转换(SNAT、DNAT)
- mangle — 修改数据包
- raw — 可提前处理或绕开 conntrack
- security — 可配合安全标记策略
关键边界:
- netfilter 是 hook 框架,不等同于 iptables
- conntrack 记录连接状态,支撑有状态防火墙和 NAT
- nftables 是现代规则框架,iptables 仍有兼容生态
netfilter 是"海防"——保护系统免受网络攻击。
netfilter 之试
海防一章中,负责在协议栈关键点挂钩、过滤或改写数据包的框架是什么?