📡 第五章 防火墙功能与防范威胁的对策
📄 一句话概括

软件型防火墙就是「通用操作系统 + 几个内核钩子 + 一堆规则」。 它便宜、灵活、可编程,代价是「每个包都要过一遍 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 秒实验

  1. 在一台 Linux 上执行 iptables -L -n -v,看现在有几条规则。
  2. 数一下这些规则分别挂在哪个链(钩子)上。
  3. 想一想:为什么 DNAT 不能放在 POSTROUTING?

常见误区

  1. 以为规则挂在哪个钩子都一样。挂错钩子的规则一条都不会生效。
  2. 以为规则越多越安全。规则多会拖慢吞吐,而且更容易写错顺序。
  3. 以为软件型防火墙只能当玩具。规则少的时候它比很多硬件设备还快。
自测三问(点开对答案) 1. netfilter 的五个钩子分别是什么? —— PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING。 2. 为什么 DNAT 必须在判路之前做? —— 因为路由要按修改后的目的地址来算,改晚了就会走错出口。 3. 软件型防火墙的性能主要由什么决定? —— 规则条数与平均比较次数,而不是钩子的数量。

下一节:把转发面换成专用芯片,就是「硬件型」。

相关笔记