一个数据包离开笔记本,从未到达 VPN 网关,防火墙计数器也毫无变化。路由表早已决定了它的命运:一条 blackhole 条目匹配了它,内核在任何接口看到它之前就把它丢弃了。当某个目的地应当以关闭方式失败时,这种行为很有用,但它是一项范围很窄的路由操作。把它当作完整的断网开关,正是设计出错的地方。

丢弃操作究竟匹配什么

路由查找先选定一张表,然后在表中选出 最长的匹配前缀。blackhole 路由是动作等于丢弃的普通条目,因此它只影响在完整决策之后会选中该条目的数据包。用策略规则换一张表,或添加更具体的前缀,丢弃就不再适用。

正因如此,blackhole 的覆盖范围精确且容易表述:它覆盖该地址族在这张表中的这个前缀。它不覆盖会被另一张表胜出的目的地,也不会仅仅因为丢弃了 IPv4 前缀就覆盖 IPv6。

ip route get 会在部署任何东西之前显示决策,包括所用表和路由类型。读这段输出,比从一次失败的请求去推断哪条规则或前缀生效要快。

blackhole 与 unreachable 或 prohibit 并不相同

Linux 提供三种类似丢弃的路由类型,它们的区别在于发送方观察到什么。blackhole 不回复直接丢弃数据包,客户端看到的是超时。unreachable 路由返回 ICMP unreachable 消息,连接很快以主机或网络错误失败。prohibit 路由返回管理性禁止错误,更接近有意的策略拒绝。

选择你希望应用看到的行为。不想暴露策略时,静默丢弃是合适的;需要让用户理解目的地为何不可用时,立即报错更好。在同一张网络里混用这些类型,会产生把同一条策略描述成三种不同故障的支持工单。

诊断连接时,不要以为每条丢弃条目都可以互换。错误文本、时序和抓包结果都不同,而表中的路由类型会告诉你正在看哪一种。ip-route 手册列出了这些类型及其出错时的行为。

它与断网开关相比如何

断网开关是包过滤:一条防火墙规则,丢弃本会离开隧道的出站流量。blackhole 路由则是在协议栈更早阶段做出的转发决定。两者都能阻断流量,但它们看到的东西不同,漏掉的场景也不同。

MechanismWhat it blocksWhat it missesHow failure looks
Blackhole routeDestinations matching the prefix and tableOther tables, other address families, traffic with a different routeSilent timeout
Firewall kill switchEgress not bound to the tunnel interfaceRules that are too broad can break the endpoint or DHCPDropped packets with counters
WireGuard AllowedIPsDestinations not routed into the interfaceTraffic that never enters the interface at allNo tunnel route, direct egress

覆盖范围才是比较的关键。防火墙可以匹配属主、端口或接口,在保持端点可达的同时阻断某个应用;路由做不到。反过来,路由可以在完全不加载任何包过滤的情况下把整个前缀送进丢弃,这在最小化系统上很有用。

WireGuard 的 AllowedIPs 是密码密钥路由,而不是防火墙。它决定哪些目的前缀被接受到接口中,只列出隧道子网的配置文件,会把其他目的地交给普通默认路由。这就是全网隧道配置使用 0.0.0.0/0 的原因,也是 wg-quick 在可能时还会安装防火墙规则的原因。

把隧道端点留在丢弃范围之外

端点的外部地址必须能通过物理接口到达。若丢弃同时捕获了该地址,握手就发不出去,隧道永远起不来,看起来像 VPN 故障而不是路由策略。变更前后都要检查到端点的路由。

管理访问也属于同一次审查。如果通往路由器或客户端的唯一路径就是隧道,过宽的丢弃会把你锁在那台本可让你删除它的机器之外。保留有记录的带外路径、控制台或定时回滚。

IPv6 需要自己的条目和自己的审查。把 IPv4 丢进 blackhole 却把 IPv6 留在默认路由上的设计,恰好泄漏了这条策略本想拦住的流量,而这个错误在只测 IPv4 的环境里看不出来。

用定时器和回滚分阶段实施

应用路由时附带一次短暂自动删除,这样错误不会跨越重启继续存在。下面的命令使用文档专用网段;请换成你的设计实际丢弃的前缀:

sudo ip route replace blackhole 10.0.0.0/8
ip route get 10.1.2.3
sudo systemd-run --on-active=60 /sbin/ip route del blackhole 10.0.0.0/8

第二条命令应当报告 blackhole 路由以及它来自哪张表。第三条会安排在一分钟后删除,这个时间足够确认策略,也足够短,能在远程主机上从失误中恢复。行为确认之后,再用管理其他路由的同一套配置管理把该条目持久化。

收尾时记录前缀、表、地址族和回滚方式。丢弃是路由设计中刻意保留的例外,下一位工程师在判断它是否仍然适用之前,需要知道它当初要拦住哪些流量。

相关文章