软件型防火墙就是「通用操作系统 + 几个内核钩子 + 一堆规则」。 它便宜、灵活、可编程,代价是「每个包都要过一遍 CPU」。
上一节讲历史,这一节开始讲形态。先讲最便宜的那一种。 Linux 上的实现叫 netfilter,配置工具叫 iptables 或 nftables。
一、它就是内核里的几个检查点
报文进入内核之后,会沿着协议栈往上走。 netfilter 在这条路上插了五个「钩子」(hook),每个钩子上可以挂一串规则。
钩子 位置 典型用途
PREROUTING 刚收到,未判路 改目的地址(DNAT) INPUT 判定给本机 过滤进本机的包 FORWARD 判定要转发 过滤穿过的包 OUTPUT 本机要发 过滤本机产生的 POSTROUTING 即将上线路 改源地址(SNAT)
「改地址」的位置是有讲究的:DNAT 必须在判路之前改(否则路由会按旧目的地址算), SNAT 必须在出口确定之后改(因为要选源地址)。两个位置都不能挪。
二、三种流向,各过几个钩子
发给本机:PREROUTING → INPUT,两个。穿过本机:PREROUTING → FORWARD → POSTROUTING,三个。 本机发出:OUTPUT → POSTROUTING,两个。
这个差别不是理论: 转发路径比「发给本机」多一个钩子,所以同样规则数下,转发反而略慢一点——因为要额外做一次路由判定(判「是不是给本机的」)。
反过来也有个反直觉的结论:如果规则全挂在 INPUT 上,那 FORWARD 的流量一条都不查。 所以「配了规则不生效」的第一件事,是问「这个流量到底走哪个钩子」。
三、规则是「一条一条比」的
这是软件型防火墙最本质的性能特点: 一个包进来,从第一条规则开始比,命中就执行、不命中就下一条。 规则越多,平均要比的次数越多。
所以有一条经验法则:把命中率最高的规则放在最前面。 因为「平均比较次数」直接决定吞吐。
算一笔账:假设每条规则比对要 50 纳秒,1000 条规则平均比 500 次, 那就是 25 微秒一个包——对应大约 4 万 pps,不到 30 Mbps。 这就是「软件型防火墙不适合大流量」的真实原因。
对策有三条:减少规则数、把热规则前移、用 nftables 的集合匹配。 第三条能把「比 N 条」变成「查一次哈希」,是真正有效的优化。
四、动手 Demo:Linux 的五个钩子,一个包过几个
这段把五个钩子摆出来,再逐个列出「发给本机」「穿过本机」「本机发出」三种流向各走哪几个、各几个。
flows = [
(u"发给本机", [u"PREROUTING", u"INPUT"]),
(u"穿过本机", [u"PREROUTING", u"FORWARD", u"POSTROUTING"]),
(u"本机发出", [u"OUTPUT", u"POSTROUTING"]),
]
for name, hooks in flows:
P(u" %s:%d 个 —— %s。" % (name, len(hooks), u" → ".join(hooks)))
实跑输出:
--------------------------------------------------------------------
Linux 的五个钩子,一个包过几个
--------------------------------------------------------------------
**软件型防火墙就是在内核转发路径上插了几个检查点。**
**Linux 的 netfilter 插了五个。**
钩子 在哪 管什么
-----------------------------------------------------------
PREROUTING 刚收到,还没判路 改目的地址(DNAT)
INPUT 判定是给本机的 过滤进本机的包
FORWARD 判定要转发的 过滤穿过的包
OUTPUT 本机要发出去 过滤本机产生的
POSTROUTING 马上要上线路 改源地址(SNAT)
**三种流向各走几个钩子**:
发给本机:2 个 —— PREROUTING → INPUT。
穿过本机:3 个 —— PREROUTING → FORWARD → POSTROUTING。
本机发出:2 个 —— OUTPUT → POSTROUTING。
**转发只过三个钩子**,**所以「转发比本机收快」不是玄学,是少走两个检查点。**
五个钩子的顺序不要死记,记两条规律就够: 「进来的先判路再分叉(INPUT 还是 FORWARD),出去的先分叉再改源地址」。 DNAT 在判路前、SNAT 在出线前,两头一卡,中间自然就对了。
「转发只过三个钩子」这句话还有个用处: 当你发现「本机能通、借道不通」时,问题一定在 FORWARD 上——因为那一段是转发独有、本机不收的路径。
最后提醒一句:软件型防火墙的性能瓶颈不在「钩子数量」,在「规则条数」。 钩子只是决定了查哪一串,真正花时间的是那一串有多长。
30 秒实验
- 在一台 Linux 上执行 iptables -L -n -v,看现在有几条规则。
- 数一下这些规则分别挂在哪个链(钩子)上。
- 想一想:为什么 DNAT 不能放在 POSTROUTING?
常见误区
- 以为规则挂在哪个钩子都一样。挂错钩子的规则一条都不会生效。
- 以为规则越多越安全。规则多会拖慢吞吐,而且更容易写错顺序。
- 以为软件型防火墙只能当玩具。规则少的时候它比很多硬件设备还快。
自测三问(点开对答案)
1. netfilter 的五个钩子分别是什么? —— PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING。 2. 为什么 DNAT 必须在判路之前做? —— 因为路由要按修改后的目的地址来算,改晚了就会走错出口。 3. 软件型防火墙的性能主要由什么决定? —— 规则条数与平均比较次数,而不是钩子的数量。下一节:把转发面换成专用芯片,就是「硬件型」。
- ☐ (本节完,下一节 05.03.02 硬件型防火墙)