Lan Tian @ Blog

I'm starting to provide Chinese / English versions of some articles, switch with the Language menu above. 我开始提供部分文章的中文、英文翻译,请使用顶部语言菜单切换。

如何引爆 DN42 网络(持续更新)

DN42 是一个测试网络,所有人都在帮助所有人。即使你不小心搞砸了,也没有人会指责你。你可以在 DN42 的 IRC 频道邮件列表或者非官方 Telegram 群组寻求帮助。

由于 DN42 是一个实验用网络,其中也有很多新手、小白参与,因此时不时会有新手配置出现错误,而对整个 DN42 网络造成影响,甚至炸掉整个网络。

现在,作为一名长者(x),我将教各位小白如何操作才能炸掉 DN42,以及如果你作为小白的邻居(指 Peer 关系),应该如何防止他炸到你。

注意:你不应该在 DN42 网络中实际执行这些操作,你应该更加注重对破坏的防御。

恶意破坏会导致你被踢出 DN42 网络。

本文信息根据 Telegram 群中的真实惨案改编。

更新记录

  • 2020-07-13:添加 Registry IPv6 地址段掩码填错的内容,和 Bird 不同协议左右互博的内容。
  • 2020-05-30:第一版,包含 OSPF、Babel、左右横跳。

OSPF 真好玩

错误操作

你刚刚加入 DN42,并且准备把你手上的几台服务器都连接进去。你通过邮件,IRC 或者 Telegram 找了几个人分别和你的几台服务器 Peer,但是你还没有配置好你的内部路由分发。

于是你准备配置 OSPF,并打开 Bird 的配置文件加了一个 protocol:

protocol ospf {
  ipv4 {
    import all;
    export all;
  };
  area 0.0.0.0 {
    interface zt0 {
      type broadcast;
      # 略掉一些不重要的参数
    };
  };
};

你心满意足地把配置文件复制到每台服务器上,然后 bird configure,看到你的各台服务器都通过 OSPF 获取到了其它服务器的路由。

突然,你的 IRC / Telegram 弹出了一个提示框,你点开来一看:

<mc**> shit.... as424242**** is hijacking my prefixes, for example 172.23.*.*/27
<he**> yup, I see some roa fails for them as well

恭喜你,你成功劫持了 DN42 网络(的一部分)。

发生了什么

当你的服务器通过 BGP 协议和其他人 Peer 时,每一条路由都包含了路径信息,包括它从哪里来,经过了哪些节点到达你这里。例如 172.22.76.184/29 这条路由可能就带有 4242422547 -> 4242422601 -> 424242**** 这条路径,其中 4242422547 是路由来源(就是我),而 4242422601 是你的邻居(此处以 Burble 举例)。

但是,你的内网在传递路由时使用的是 OSPF 协议,而 OSPF 在传递路由信息时不会保留 BGP 的路径,因为它并不认识这些东西。此时你的另一台服务器通过 OSPF 获取到了 172.22.76.184/29 这条路由,但是不包含任何路径信息,它在与邻居的 BGP 宣告中就会将这条路由使用你自己的 ASN 播出去,造成劫持效果。

画成图大概是这样的:

[2547] -> [2601] -> [你的 A 节点] -> [你的 B 节点] -> [你的 B 节点的邻居]
 2547      2547      2547           没了!           你的 ASN(BOOM)
           2601      2601
                     你的 ASN

正确的操作

  • 永远记住一点原则:OSPF,Babel 等 IGP(内部路由协议)不应处理 BGP 路由信息,BGP 路由就应该让 BGP 协议自己处理。
  • 同时,内部路由协议的路由也不应漏到 BGP 中,除非内部路由协议中处理的所有 IP 段都是你自己所有。
  • 所以你应该把 BGP 的 export filter 写成这样:
export filter {
  # 只允许向外发送来自 STATIC(手动配置)和 BGP 协议的路由
  if source ~ [RTS_STATIC, RTS_BGP] then accept;
  # 拒绝掉其它路由协议的路由
  reject;
}

如何防御

  • 最佳的方法是 ROA,即路由来源验证(Route Origin Authorization),限制每条路由的来源 ASN。
    • 对于 DN42,ROA 配置文件根据 Registry 的信息自动生成,可以在 DN42 Wiki 的 Bird 配置页面下载,并且可以设置 Cron 定时任务自动更新。
  • 如果你不想配置 ROA,你可以尝试与尽量多的人 Peer。
    • 由于 BGP 默认选择经过的 AS 最少的路径,如果你和很多人直连,即使有人在劫持路由,你的网络仍然会优先选择这些直连路径。
    • 但注意这样不能保证防住路由劫持,例如以下情况:
      • 真实 AS 到你路径比劫持者的长;
      • 劫持者与真实 AS 到你的 AS 路径等长,此时会选择哪个看脸;
      • 你有配置 DN42 Community Filter,导致劫持者的路由优先级比较高。

Babel 也很好玩

错误操作

Telegram 里的老哥说话很好听,一边帮助你修上面那个 Bug,一边向你推荐 Babel:

  • Babel 可以自动根据延迟选择最短路线;
  • Babel 配置非常简单。

但是,群友不推荐你使用 Bird 自带的 Babel 协议支持,因为 Bird 的 Babel 不能根据延迟选路。

你心动了,删掉了 OSPF 的配置文件,并装了一个 Babeld。很快你的每台机器上都出现了其它节点通过 Babel 发来的路由。你等了几分钟,似乎没有爆炸。

但是你注意到,你的 Bird 没有把这些路由通过 BGP 发出去。老哥们怂恿你开启 Bird Kernel Protocol 的 Learn:

protocol kernel sys_kernel_v4 {
  scan time 20;
  # 群友怂恿你添加这一行
  learn;
  # 不重要的略过
};

你照做了。几分钟后,你被 IRC 和 Telegram 里的人疯狂艾特。是的,你又把其他人的网络劫持了。

发生了什么

这和上面 OSPF 一段其实是相同的问题,Babel 在传递路由时丢弃了 BGP 的路径信息。只不过默认情况下,Bird 会忽略其它路由软件写入内核路由表的路由信息,除非你开了 learn。

正确的操作

与 OSPF 一段相同,配置 iBGP + 设置 Filter。

重复一遍:OSPF,Babel 等 IGP(内部路由协议)不应处理 BGP 路由信息,BGP 路由就应该让 BGP 协议自己处理。

如何防御

与 OSPF 一段相同,ROA + 多 Peer 可解。

左右横跳

左右横跳泛指多种错误,它们会造成 BGP 路由程序频繁切换获得的最优路径。由于最优路径会通过 Peering 传递给别人,这个切换过程就会造成大量的流量消耗。由于 DN42 内多数人用的是便宜的 VPS 做节点,因此长期下来结果只有以下两种:

  1. 你的邻居发现了流量消耗异常,主动切断了和你的 Peering;
  2. 你的主机商(可能还有你的邻居的主机商)发现你长期占用带宽(或者用完了流量),停掉了你的 VPS。

而且左右横跳错误可能会造成严重的影响:

  • 如果出错的 AS 和其它多个 AS 建立了 Peering,即使你断开了和他的直接连接,路由切换仍然可能从其它 AS 传递到你的 AS。
    • 为了解决一个 AS 的问题,可能需要断开好几个 AS。

历史上有过这些爆炸事故:

  • 某 Telegram 群友从 Fullmesh + Direct 转向 Multihop 时出现事故,造成了非常大量的路由切换。
    • 他在切换过程中没有断开 BGP,而 Babel 的配置错误导致大量路由被传递及撤销。
    • “thats like 1k updates every few seconds”(没过几秒就有 1000 条路由更新)。
    • 由于上述路由切换传递原因,多个较大 AS 被迫断开之间的连接。
  • (该群友先前还有多次路由切换事故)

如何防御

  • 最理想的方案是 Route Dampening,也就是限制一段时间能收到的路由更新数量。
    • 但是 Bird 不支持这个,没救了,等死吧,告辞.webp
  • 次优的方法是使用 Prometheus、Grafana 等工具对各个节点进行监控,在流量异常时收到提醒,上去手动处理。
    • 显而易见的是,如果你当时不在线,当你看到提醒时有可能已经几 G 的流量没了。
  • 再次优的方法是对 Peering 的端口进行限速。
    • 由于 DN42 内目前几乎没有大流量应用,这种方法的确能保证安全。
    • 缺点显而易见:性能下降。
  • 土豪的方法是买无限流量的服务器。

这段地址到底多长

错误操作

因为今年是 2020 年,你准备给你的网络加一组 IPv6 地址。按照我的 DN42 注册教程,你很快就给自己注册了一个 IPv6 地址块,并且很快被合并进了 Registry。

在你看来,一切都很正常。但在地球的另一边,一个人的手机/电脑上弹出消息,告诉他他的 DN42 ROA 记录生成器出现了错误。他打开 Registry,扶额叹息,并 commit 了这样一个修改:

DN42 Registry 中的错误

https://git.dn42.dev/dn42/registry/commit/9f45ee31cdea4a997d59a262c4a8ac8eb3cbd1f1

发生了什么

这位群友添加了 fd37:03b3:cae6:5158::/48 这样一个地址块。因为一个 IPv6 地址由 32 个 16 进制数构成(共 128 比特),而这个地址块显式定义了其中的前 16 个数(即 64 位),对应的子网掩码应该是 /64 或更低。

但是由于未知原因,这个错误没有被 DN42 Registry 的内容检查程序检查出来,当时也没有被操作合并的管理员发现,就成功进入了 Registry。

随后,ROA 记录生成器在解析 Registry 内容时遇到了这个格式错误的地址块,就直接报错退出了。

正确的操作

  • 用户在注册地址块时应该检查子网掩码的大小和地址块的有效性。
  • DN42 Registry 的检查程序,或者操作合并的管理员,应该发现这个错误。
  • ROA 生成器应该跳过这条有问题的记录,正常处理剩下的数据,而非报错退出。

万幸的是这个问题对整个 DN42 网络影响不大,只是 ROA 更新延迟了几小时而已。

如何防御

由于 DN42 从建立之初就在强调去中心特性,因此你可以写一个自己的 ROA 生成器作为备份。

虽然这次我的 ROA 生成器也挂掉了……

原因是不同人写的程序即使功能相同,也会在实现上有细微的差别。这样在遇到这样一个输入内容的 Bug 时,就有可能有人的程序仍能保持正常运行。

Bird 左右互搏

错误操作

我有一个朋友……行吧就是我自己。

因为我同时接了 DN42 和 NeoNetwork,还有一段自己的内网,所以为了防止把内网路由发到 DN42 和 NeoNetwork,我采取了以下方法:

  • 把所有来自 Kernel 协议(从内核获取路由)和 Direct 协议(获取系统网络界面(网卡)所在的网段)的路由打上一个 Community。
  • 在 DN42 和 NeoNetwork 的 Peering 中把它们过滤掉。
  • 这样我的内网 IP 就不会被广播出去,但因为 DN42 和 NeoNetwork 的路由被配置在 Static Protocol 中,所以不受影响。

配置完后一切看起来都很正常,直到几天后群友发现我的 Telegram Bot(就是我的 Looking Glass)Ping 不通任何 DN42 内的 IP。

发生了什么

刚开始一切都很正常,我的网段 172.22.76.184/29 被正常广播。直到某次 Direct 协议刷新了一次,从系统的某个网络界面获取到了 172.22.76.184/29 这个网段,并再次将它传进了路由表。

这条新的路由信息就把原先的路由覆盖了,同时因为这条路由来自 Direct 协议,被打上了 Community,就不再被广播了。并且 Static 如其名是“静态”协议,其内容不会改变,自然也不会产生新的路由再覆盖回去。

此时我相当于停止宣告了我的 IP 段,自然就无法收到回程数据包了。

正确的操作

在 Bird 中,尽量避免多个路由协议产生相同的路由条目,相互覆盖可能会造成不可预料的后果。

我最终选择添加 Filter 将 Direct 协议限制在我的内网网段,避免它再次覆盖我的 DN42 网段。