前言 本文用以记录自己加入 DN42 网络的历程,本人是初入 BGP 的小白,对于文章中出现的各种不严谨内容和各种低级错误,请大佬们手下留情,可在评论区指出。
欢迎和我 Peer:VCNET DN42
注册 iYoRoy 的博客有详细的过程,我就不再重复了:DN42探究日记 - Ep.1 加入DN42网络 。
选择 BGP Daemon 参考各位大佬的教程,我决定使用 Bird v3 为我的 BGP Daemon,以 Debian 13 为例:
1 2 3 4 5 apt update apt -y install apt-transport-https ca-certificates wget wget -O /usr/share/keyrings/cznic-labs-pkg.gpg https://pkg.labs.nic.cz/gpg echo "deb [signed-by=/usr/share/keyrings/cznic-labs-pkg.gpg] https://pkg.labs.nic.cz/bird3 trixie main" > /etc/apt/sources.list.d/cznic-labs-bird3.listapt update && apt -y install bird3 wireguard wireguard-tools
Debian 软件源中的 Bird 版本比较旧,需要安装上面的最新版
其他系统及 Bird v2 软件源可查看:CZ.NIC Labs 📦 Repos Setup Docs
bird.conf 修改 /etc/bird/bird.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 define OWNAS = 4242423322; define OWNIP = 172.23.100.180; define OWNIPv6 = fd48:8669:9f9f::8; define OWNNET = 172.23.100.160/27; define OWNNETv6 = fd48:8669:9f9f::/48; define OWNNETSET = [172.23.100.160/27+]; define OWNNETSETv6 = [fd48:8669:9f9f::/48+]; router id OWNIP; protocol device { scan time 10; } function is_self_net() { return net ~ OWNNETSET; } function is_self_net_v6() { return net ~ OWNNETSETv6; } function is_valid_network() { return net ~ [ 172.20.0.0/14{21,29}, # dn42 172.20.0.0/24{28,32}, # dn42 Anycast 172.21.0.0/24{28,32}, # dn42 Anycast 172.22.0.0/24{28,32}, # dn42 Anycast 172.23.0.0/24{28,32}, # dn42 Anycast 172.31.0.0/16+, # ChaosVPN 10.100.0.0/14+, # ChaosVPN 10.127.0.0/16{16,32}, # neonetwork 10.0.0.0/8{15,24} # Freifunk.net ]; } function is_valid_network_v6() { return net ~ [ fd00::/8{44,64} # ULA address space as per RFC 4193 ]; } protocol kernel { scan time 20; ipv6 { import none; export filter { if source = RTS_STATIC then reject; krt_prefsrc = OWNIPv6; accept; }; }; }; protocol kernel { scan time 20; ipv4 { import none; export filter { if source = RTS_STATIC then reject; krt_prefsrc = OWNIP; accept; }; }; } protocol static { route OWNNET reject; ipv4 { import all; export none; }; } protocol static { route OWNNETv6 reject; ipv6 { import all; export none; }; } include "rpki.conf"; include "ospf.conf"; include "ibgp.conf"; include "ebgp.conf";
首先修改自己的网络信息:
OWNAS 修改为你的 ASN,如:4242423322
OWNIP 修改为此节点所分配的 IPv4 地址,如:172.23.100.180
OWNIPv6 修改为此节点所分配的 IPv6 地址,如:fd48:8669:9f9f::8
OWNNET 修改为你所拥有的 IPv4 网段,如:172.23.100.160/27
OWNNETv6 修改为你所拥有的 IPv6 网段,如:fd48:8669:9f9f::/48
OWNNETSET 和 OWNNETSETv6 同 上两个,但不要忘记末尾的 +
rpki.conf 修改 /etc/bird/rpki.conf:
1 2 3 4 5 6 7 8 9 10 11 roa4 table dn42_roa; roa6 table dn42_roa_v6; protocol rpki dn42_rpki { roa4 { table dn42_roa; }; roa6 { table dn42_roa_v6; }; remote "rpki.akae.re" port 8082; refresh 30; retry 5; expire 600; }
什么是 ROA
ROA是数字签名对象,将地址绑定到AS号码,并由地址持有者签名。ROA提供了一种验证IP地址块持有者是否已授权特定AS在域间路由环境中为该地址块发起路由的方法。[RFC6482]中描述了ROA。ROA旨在满足为域间路由添加安全性的要求。
为 DN42 启用 ROA 检查,能有效防止 BGP 劫持和错误配置导致的路由泄露,而使用 RPKI,为我们省去了手动配置 ROA 表和更新的麻烦.
ebgp.conf 修改 /etc/bird/ebgp.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 template bgp dn42_peers { local as OWNAS; path metric 1; ipv4 { extended next hop; #import limit 1000 action block; #限制从他人哪里导入路由的数量 import keep filtered; import filter { if is_valid_network() && !is_self_net() then { if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then { print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; reject; } accept; } reject; }; export filter { if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then accept; reject; }; }; ipv6 { #import limit 1000 action block; import keep filtered; import filter { if is_valid_network_v6() && !is_self_net_v6() then { if (roa_check(dn42_roa_v6, net, bgp_path.last) != ROA_VALID) then { print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; reject; } accept; } reject; }; export filter { if is_valid_network_v6() && source ~ [RTS_STATIC, RTS_BGP] then accept; reject; }; }; } include "dn42_peers/*";
此文件定义了和他人互 Peer 的模板并启用了 ROA 过滤,和他人互 Peer 的配置保存在 /etc/bird/dn42_peers 内。
系统配置 sysctl 在 DN42 内,每个节点几乎都是其他人的路由器,而系统默认并不允许数据包转发并且有严格的数据包过滤,因此需要如下设置:
1 2 3 4 5 6 echo "net.ipv4.ip_forward=1" >> /etc/sysctl.confecho "net.ipv6.conf.default.forwarding=1" >> /etc/sysctl.confecho "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.confecho "net.ipv4.conf.default.rp_filter=0" >> /etc/sysctl.confecho "net.ipv4.conf.all.rp_filter=0" >> /etc/sysctl.confsysctl -p
尤其需要注意 人品过滤器 rp_filter ,有时 ping 不通他人很可能就是它导致的。
如果你不放心,可使用下面的 bash 脚本一键将 dn42 开头的网卡的 rp_filter 关闭:
1 2 3 4 #!/bin/bash for net_dev in /proc/sys/net/ipv4/conf/dn42*/rp_filter; do echo 0 > "$net_dev " done
Dummy 网卡 我们需要一个 Dummy 网卡绑定本机的 DN42 网络信息,在此,我选择使用 ifupdown 来统一配置我的所有节点。
编辑 /etc/network/interfaces:
1 2 3 4 5 6 7 8 auto dn42 iface dn42 inet static address 172.23.100.180 netmask 255.255.255.255 pre-up ip link add dn42 type dummy || true up ip link set dn42 up iface dn42 inet6 static address fd48:8669:9f9f::8/128
请记得修改为自己的 IP。
使用 ifup dn42 与 ifdown dn42 启用 & 禁用网卡。
容器 根据我自己的经验,我更推荐 Podman 而非 Docker。我曾花了两天时间解决无法对外 ping 的问题,最后发现是 Docker 的 iptables 规则引起的。如果你一定要用 Docker 且遇到类似问题,可尝试添加下面的 iptables 规则:
1 2 3 4 5 iptables -I DOCKER-USER 1 -i dn42+ -o zt+ -j ACCEPT iptables -I DOCKER-USER 1 -i zt+ -o dn42+ -j ACCEPT ip6tables -I DOCKER-USER 1 -i dn42+ -o zt+ -j ACCEPT ip6tables -I DOCKER-USER 1 -i zt+ -o dn42+ -j ACCEPT
与他人互 Peer WireGuard 与他人互 Peer 之前,通常需要双方建立 P2P 隧道,其原因可以借用蓝天的原话:
DN42 中几乎每个 Peering 都是建立在隧道软件(即 VPN)之上的,原因如下:
DN42 各个用户的节点分布在世界各地,隧道软件可以对数据进行基本的加密和保护;
DN42 使用的是私有地址,如果直接在互联网上传输,会被防火墙直接丢弃,甚至可能会被主机商认为你在 IP Spoofing(伪造来源 IP 地址),违反服务条款,造成严重后果。
而 DN42 的参与者们用的最多的就是 WireGuard 和 GRE/IPSec,而前者配置较为简单,也有一定的加密能力。
生成密钥对 1 wg genkey | tee privatekey | wg pubkey > publickey
请保存好自己的公私钥。
互 Peer 对于新人,可以通过 potat0 的 Telegram 机器人自动 Peer:@Potat0_DN42_Bot ,亦或是 iEdon 的网自动 Peer:iEdon Net ,还有 Kioubit 的网页自动 Peer:Kioubit Network .
互 Peer 信息 通过自动机器人或网页,亦或是他人的主页,我们既需要获得对方如下信息,也需要给对方提供自己的如下信息:
公钥
公网地址(Endpoint)
DN42 的 ASN
IPv6 LLA
是否支持ENH(Extended Next Hop),注意:若使用v6交换路由而不启用ENH则无法交换v4路由
在获得了对方上述信息后,我们需要在 /etc/wireguard 下创建一个文件,我的命名习惯为:dn42-424242XXXX.conf,填入如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Interface] ListenPort = <开放给对方的端口 UDP>Table = off MTU = 1420 PostUp = wg set %i private-key /etc/wireguard/dn42-privatekeyPostUp = ip addr add <你的 LLA 地址>/64 peer <对方的 LLA 地址>/64 dev %iPostUp = ip addr add <你的 ULA 地址>/128 dev %iPostUp = ip addr add <你的 IPv4 地址>/32 dev %i[Peer] PublicKey = <对方的公钥>Endpoint = <对方提供给你的 Endpoint>AllowedIPs = 172.20 .0.0 /14 , 10.0 .0.0 /8 , 172.31 .0.0 /16 , fd00::/8 , fe80::/64
关于端口,有一个通常做法:20000 + 对方 ASN 后四位
完整示例
1 2 3 4 5 6 7 8 9 10 11 12 13 [Interface] ListenPort = 22547 Table = off MTU = 1420 PostUp = wg set %i private-key /etc/wireguard/dn42-privatekeyPostUp = ip addr add fe80::3322 /64 peer fe80::2547 /64 dev %iPostUp = ip addr add fd48:8669 :9 f9f::8 /128 dev %iPostUp = ip addr add 172.23 .100.180 /32 dev %i[Peer] PublicKey = xelzwt1j0aoKjsQnnq8jMjZNLbLucBPwPTvHgFH/czs=Endpoint = alice.lantian.pub:23322 AllowedIPs = 172.20 .0.0 /14 , 10.0 .0.0 /8 , 172.31 .0.0 /16 , fd00::/8 , fe80::/64
保存并运行:
通过 wg show <文件名(不需要 .conf 后缀)> 即可查看隧道连接状况:
1 2 3 4 5 6 7 8 9 10 11 root@hkg ~ interface: dn42-4242422547 public key: AHEJ0dXDDxJF0EapR7Ssx2eQV9ReB/OvkWPu7ypWbkA= private key: (hidden) listening port: 22547 peer: xelzwt1j0aoKjsQnnq8jMjZNLbLucBPwPTvHgFH/czs= endpoint: 5.102.125.26:23322 allowed ips: 172.20.0.0/14, 10.0.0.0/8, 172.31.0.0/16, fd00::/8, fe80::/64 latest handshake: 25 seconds ago transfer: 96.59 MiB received, 532.45 MiB sent
开机自启动:
配置 eBGP 在 /etc/bird/dn42_peers 下新建 <ASN>.conf:
1 2 3 4 protocol bgp <BGP 会话名> from dn42_peers { neighbor <对方的 LLA 地址> % '<WireGuard 隧道名>' external; description "<对方联系信息 非必需>"; }
需要注意的是:之前的 eBGP 模板内已经默认启用了 Extended next hop(因为绝大部分 Peer 都是默认启用的),不再需要如下配置:
1 2 3 4 5 6 7 8 protocol bgp <BGP 会话名> from dn42_peers { neighbor <对方的 LLA 地址> % '<WireGuard 隧道名>' external; description "<对方联系信息 非必需>"; ipv4{ extended next hop; }; }
完整示例
1 2 3 4 protocol bgp DN42_4242422547_v6 from dn42_peers { neighbor fe80::2547 % 'dn42-4242422547' external; description "@lantian1998"; }
测试 LLA 是否可连通,可使用:
如:
1 2 3 4 5 6 7 8 9 10 root@hkg ~# ping6 fe80::2547%dn42-4242422547 PING fe80::2547%dn42-4242422547 (fe80::2547%dn42-4242422547) 56 data bytes 64 bytes from fe80::2547%dn42-4242422547: icmp_seq=1 ttl=64 time=1.80 ms 64 bytes from fe80::2547%dn42-4242422547: icmp_seq=2 ttl=64 time=1.70 ms 64 bytes from fe80::2547%dn42-4242422547: icmp_seq=3 ttl=64 time=1.79 ms 64 bytes from fe80::2547%dn42-4242422547: icmp_seq=4 ttl=64 time=1.81 ms ^C --- fe80::2547%dn42-4242422547 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3005ms rtt min/avg/max/mdev = 1.701/1.775/1.806/0.043 ms
当一切处理妥当后运行 birdc c 重载配置,使用 birdc s p 查看当前 BGP 会话:
1 2 3 4 5 6 7 8 9 10 11 root@hkg ~# birdc s p BIRD 3.2.0 ready. Name Proto Table State Since Info DN42_4242422547_v6 BGP --- up 2026-03-03 Established dn42_rpki RPKI --- up 2026-03-03 Established device1 Device --- up 2026-03-03 kernel1 Kernel master6 up 2026-03-03 kernel2 Kernel master4 up 2026-03-03 static1 Static master4 up 2026-03-03 static2 Static master6 up 2026-03-03 ...
不使用 MP-BGP 如果只想交互其中一种路由,则不需要对方的 LLA,而是需要对方的 IPv4 或 IPv6(取决于你要传播哪种路由)
并使用如下 eBGP 配置:
1 2 3 4 protocol bgp <BGP 会话名> from dn42_peers { neighbor <对方的 IPv4/v6 地址> as <对方ASN>; description "<对方联系信息 非必需>"; }
至此,已完成一台节点的互 Peer。
内网互联 目前,还只是单个节点和他人 Peer,而当我们有多个节点之后,就需要让节点内部相互联通,我目前的网络架构如下:
graph LR
subgraph zt[ZeroTier Underlay]
us[LAX, US]
hk[HKG, CN]
de[DEU, DE]
cn1[CNVO, CN]
cn2[Homelab, CN]
us <--> hk
us <--> de
us <--> cn1
us <--> cn2
hk <--> de
hk <--> cn1
hk <--> cn2
de <--> cn1
de <--> cn2
cn1 <--> cn2
end
我将 ZeroTier 作为 L2 使用并组成 Fullmesh 单纯是我懒得维护 n(n-1)/2 条 wg 隧道
内网 BGP 的办法有两种:OSPF 和 Babel 但我在 ZeroTier 下使用 Babel 发生了一些问题,遂用回了 OSPF
基于上面的信息,我决定暂时使用 OSPF Broadcast 的配置。
IGP 什么是 IGP
内部网关协议(英语:Interior Gateway Protocol,缩写为 IGP)是指在一个自治系统(AS)内部所使用的一种路由协议。 与此相对,外部网关协议用来在自治系统之间确定网络可达性、并通过内部网关协议来解析某个自治系统内部的路由。
ospf.conf 修改 /etc/bird/ospf.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protocol ospf v3 dn42_vcnet_ospf { ipv4 { import where is_self_net() && source != RTS_BGP; export where is_self_net() && source != RTS_BGP; }; include "ospf-area.conf"; }; protocol ospf v3 dn42_vcnet_ospf6 { ipv6 { import where is_self_net_v6() && source != RTS_BGP; export where is_self_net_v6() && source != RTS_BGP; }; include "ospf-area.conf"; };
ospf-area.conf 修改 /etc/bird/ospf-area.conf:
1 2 3 4 5 6 7 8 9 area 0.0.0.0 { # Dummy 网卡名称 interface "dn42" { stub; }; # ZeroTier 网卡名称 interface "ztugawjlkq" { cost 160; type broadcast; }; };
由于 ZeroTier 同一个网络的网卡名是不变的,这里 interface 的类型只能是 broadcast.
如果你使用 WireGuard 来组成 Fullmesh,应该使用 ptp,可以精细控制 cost.
使用 birdc c 重载配置后,可通过 birdc show ospf neighbors 查看 OSPF 邻居信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 root@hkg ~ # birdc show ospf neighbors BIRD 3.2.0 ready. dn42_vcnet_ospf: Router ID Pri State DTime Interface Router IP 172.23.100.165 1 Full/Other 35.257 ztugawjlkq fe80::3c49:e1ff:fe5a:792d 172.23.100.170 1 Full/BDR 38.259 ztugawjlkq fe80::3c29:c0ff:fefc:66f7 172.23.100.167 1 Full/Other 31.441 ztugawjlkq fe80::3ce2:48ff:febb:b986 172.23.100.166 1 Full/Other 24.565 ztugawjlkq fe80::3cf1:97ff:fee8:b68f dn42_vcnet_ospf6: Router ID Pri State DTime Interface Router IP 172.23.100.165 1 Full/Other 35.257 ztugawjlkq fe80::3c49:e1ff:fe5a:792d 172.23.100.170 1 Full/BDR 38.263 ztugawjlkq fe80::3c29:c0ff:fefc:66f7 172.23.100.167 1 Full/Other 31.441 ztugawjlkq fe80::3ce2:48ff:febb:b986 172.23.100.166 1 Full/Other 34.562 ztugawjlkq fe80::3cf1:97ff:fee8:b68f
iBGP 通常情况下,建立 iBGP 需要各个节点组成 Fullmesh,修改 /etc/bird/ibgp.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 template bgp ibgpeers { local as OWNAS; ipv4 { import filter { import where source = RTS_BGP && is_valid_network() && !is_self_net(); export where source = RTS_BGP && is_valid_network() && !is_self_net(); next hop self; extended next hop; }; ipv6 { import where source = RTS_BGP && is_valid_network_v6() && !is_self_net_v6(); export where source = RTS_BGP && is_valid_network_v6() && !is_self_net_v6(); next hop self; }; }; include "ibgp/*";
详细解释来自 iYoRoy:
导入和导出规则确保iBGP仅处理BGP协议学到的路由,并且过滤掉IGP的路由防止环回
next hop self是必须的 ,指示 BIRD 在向 iBGP 邻居导出路由时,将下一跳重写为边界路由器自身的IP地址(而非原始的外部下一跳)。因为内部路由器无法直接访问外部邻居地址,若不重写则会被认定为地址不可达。重写后,内部路由器只需通过 IGP 路由将流量送至边界路由器,由边界路由器完成最终的外部转发。
因为我希望使用IPv6地址建立MP-BGP,通过IPv6路由IPv4,因此在IPv4中启用了extended next hop
接下来,需要给每台节点设置一个 iBGP Peer 配置,在 /etc/bird/ibgp/ 下创建文件:
1 2 3 protocol bgp '<BGP 会话名>' from ibgpeers { neighbor <其他节点的 IPv6 ULA 地址> as OWNAS; }
完整示例
1 2 3 protocol bgp 'dn42_ibgp_us' from ibgpeers{ neighbor fd48:8669:9f9f::5 as OWNAS; }
运行 birdc c 重载配置即可。
优化路由 在完成上面的基础配置后,我们已经可以连上 DN42 的网络了,但此时的选路并非最优,尤其是当你的节点和非本地节点互 Peer 的时候。
Community属性介绍
团体属性是一组有相同特征的目的地址的集合。团体属性用来简化路由策略的应用和降低维护管理的难度,利用团体可以使多个AS中的一组BGP设备共享相同的策略。团体是一个路由属性,在BGP对等体之间传播,且不受AS的限制。BGP设备在将带有团体属性的路由发布给其它对等体之前,可以先改变此路由原有的团体属性。
简单来说,BGP Community 就是给路由打标签,不同的标签有助于我们对路由进行区分和管理。
来自 iYoRoy 的介绍:
…(本文)仅针对地理位置信息添加BGP Communities并进行优选。一般来说这样就足够了。(还有个原因是其他的我还没太搞明白) 注意: 我们应该只对自己的AS添加地理位置信息相关的BGP Communities ,不应当对邻居传递来的路由添加相关条目。若对邻居的路由加上自己的地区Communities则会造成伪造路由起源 ,引发路由劫持 。下游可能会误判流量路径,将本应直连的流量绕道至你的网络,增加延迟的同时大量消耗你的网络的流量。(Large Communities除外,Large Community有一套验证机制可以防止此类事情发生,但是不在本文讨论范围内)
我们需要首先找到自己节点所在的国家、地区,按照 BGP-communities #Route Origin 小节 所列出的代码,在 /etc/bird/bird.conf 里面添加下面两行:
1 2 define DN42_REGION = 52; # 52代表亚洲东部地区 define DN42_COUNTRY= 1344; # 1344代表香港
之后,修改 /etc/bird/ebgp.conf 的 dn42_peer 模板:
1 2 3 4 5 6 7 8 9 10 11 export filter { - if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then accept; + if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then{ + if (is_self_net()) then { # 检查是否是自己的路由 + bgp_community.add((64511, DN42_REGION)); # 打上大洲级别的区域信息 + bgp_community.add((64511, DN42_COUNTRY)); # 打上国家/地区信息 + } + accept; + } reject; };
上面是 IPv4 块里面的,修改 IPv6 块的 export filter 时,记得将 is_valid_network() 和 is_self_net() 修改为 is_valid_network_v6() 和 is_self_net_v6().
至此,我们成功给自己的路由打上了标签。
local_pref local_pref
当一条BGP路由器中存在多条去往同一目标网络的 BGP 路由时,BGP 协议会对这些 BGP 路由属性进行比较,从而筛选出最佳到达目标网络的通达路径;本地优先属性,只在IBGP对等体之间进行交换,即:同一AS内进行,不会通告给AS 域外;用于判断流量离开AS时选择的最佳路由;
现在,我们要利用他人的 Communities 标签进行路由优选。
下面是我暂时使用的规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function ebgp_calculate_pref() { int pref = 1000 ; if bgp_community ~ [(64511, DN42_REGION)] then pref = pref + 100 ; if bgp_community ~ [(64511, DN42_COUNTRY)] then pref = pref + 50 ; if bgp_path.len = 1 then pref = pref + 200 ; return pref; }
可保存在 /etc/bird/ebgp.conf 的开头,同时修改 eBGP 的 Peer 模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ipv4 { extended next hop; import keep filtered; import filter { if is_valid_network() && !is_self_net() then { if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then { print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; reject; } + bgp_local_pref = ebgp_calculate_pref(); accept; } reject; };
IPv6 同样,只需要在相同位置添加即可。
iBGP 的 local_pref 这部分是我在配置好 IGP 与 iBGP 后出现的一个问题:内部的某个节点(Node 1)有一条最优路由,而其他节点(Node 2,3)会绕道前面的节点(Node 1),即使其他节点有更合适的路由可选。
下面的配置有待观察。
修改 /etc/bird/ibgp.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 template bgp ibgpeers { local as OWNAS; ipv4 { - import where source = RTS_BGP && is_valid_network() && !is_self_net(); + import filter { + if source = RTS_BGP && is_valid_network() && !is_self_net() then { + bgp_local_pref = 200; + accept; + } + }; export where source = RTS_BGP && is_valid_network() && !is_self_net(); next hop self; extended next hop; }; ipv6 { - import where source = RTS_BGP && is_valid_network_v6() && !is_self_net_v6(); + import filter { + if source = RTS_BGP && is_valid_network_v6() && !is_self_net_v6() then { + bgp_local_pref = 200; + accept; + } + }; export where source = RTS_BGP && is_valid_network_v6() && !is_self_net_v6(); next hop self; }; };
上面的配置是将来自 iBGP 的外部路由降低优先级,让本地节点优先使用 eBGP 的路由。
BGP Dampening 什么是 BGP Flapping
BGP Flapping 指的是同一条路由的路径在短时间内发生大量变化,一般源于一个网络反复广播、撤销广播这一条路由。每次广播或撤销路由时,这个网络会把这条路由传递给所有与它相连的 Peer,这些 Peer 会根据这条路由计算出新的最佳路径,然后把新路径传递给它们的 Peer,与此类推。
你能想象吗:
Prefix
Duration
Changes
Rate
fd75:7775::/48
7d 20:13:20
18.314 million
52/s
最近这个由网段引发的路由更改竟然高达 1800 万!
为了抑制这种现象,我们会用上 Kioubit 开发的 FlapAlerted .
docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 services: flapalerted: image: ghcr.io/kioubit/flapalerted network_mode: host command: - "--asn" - "4242422547" - "--bgpListenAddress" - "127.0.0.1:1790" - "--httpAPIListenAddress" - "127.0.0.1:8080" - "-routeChangeCounter" - "120" - "-overThresholdTarget" - "5" - "-underThresholdTarget" - "30" restart: unless-stopped stayrtr: image: rpki/stayrtr network_mode: host command: - "--bind" - "127.0.0.1:8083" - "--metrics.addr" - "127.0.0.1:8084" - "--cache" - "http://127.0.0.1:8080/flaps/active/roa" - "--rtr.expire" - "3600" - "--rtr.refresh" - "300" - "--rtr.retry" - "300" restart: unless-stopped depends_on: - flapalerted
启动 FlapAlerted 和 StayRTR 后,新建 /etc/bird/flap.conf 将路由信息传给 FlapAlerted:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 protocol bgp dn42_flapalerted { local as OWNAS; # 修改成 FlapAlerted 设置的 ASN 和 BGP IP/端口。 # 这里我们使用和自己网络相同的 ASN,是为了利用 BGP 协议不会把来自 iBGP 的路由(即自己其它节点的路由)转发给 iBGP Peer 的特点。 # 除非你开启了 add paths 选项,否则来自自己其它节点的路由只会包含最优的路由,如果 Flapping 发生在次优路由就会被隐藏。 # 因此建议有多个节点的用户在每个节点上都单独和 FlapAlerted 建立连接。 neighbor <FlapAlerted 的地址> as OWNAS port 1790; ipv4 { # 开启 add paths 选项,把非最优路由也发给 FlapAlerted,让次优路由 Flapping 也可见。 add paths on; export all; import none; # 不需要从 FlapAlerted 接收任何路由 }; ipv6 { add paths on; export all; import none; }; } # 新建专用于 FlapAlerted 的 ROA 表 roa4 table roa_flap_v4; roa6 table roa_flap_v6; protocol rpki dn42_rpki_flapalerted { roa4 { table roa_flap_v4; }; roa6 { table roa_flap_v6; }; remote 10.22.44.5 port 8083; # 修改成 StayRTR 监听的端口 max version 1; retry keep 15; # 如果连接中断,每 10 秒重连一次 };
在前文,我们已经配置了 ROA 过滤,因此需要新建一个新 ROA 表,记得在 /etc/bird/bird.conf 里引入此配置文件。
现在,我们需要修改 eBGP 中模板的导入过滤规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ipv4 { extended next hop; import keep filtered; import filter { if is_valid_network() && !is_self_net() then { if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then { print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; reject; } + if (roa_check(roa_flap_v4, net, bgp_path.last) = ROA_INVALID) then { + # 路由频繁变更,被 FlapAlerted 劫持去了 AS0,Bird 认为路由来自错误的 ASN + reject; + } bgp_local_pref = ebgp_calculate_pref(); accept; } reject; };
IPv6 类似,只需要将 roa_flap_v4 修改为 roa_flap_v6 即可。
结语 最后,加入 DN42 的过程还是挺有乐趣的.
其他 Thanks: 感谢 Aluy 的提供的 AMS, NL 节点。 感谢 DN42 群友的帮助和指导。
推荐: 在这里介绍一下 Bird 中文社区的作品(转发自 Telegram):
BIRD-LSP 是一个专为 BIRD2 配置文件打造的现代化工具链项目,提供 Language Server Protocol (LSP) 支持、代码格式化 (Formatter & Parser) 与静态分析 (Linter) 能力。
VSCode 安装 | OpenVSX 安装
目前支持的特性 (v0.3.0):
🎨 语法高亮 | 基于 Tree-sitter 的高精度语法解析
🔍 实时诊断 | 内置 32+ 条 Lint 规则 + 跨文件分析
📝 代码格式化 | 基于 🦀 Rust + dprint 插件实现的高性能格式化库
🔎 悬停提示 | 对 conf 关键词提供 用法示例/类型提示/文档说明
🏗 符号导航 | 跳转到定义、查找引用(支持跨文件)
*目前 BIRD LSP 还处于 Beta 阶段,部署在生产环节之前请谨慎评估
👩💻 GitHub 开源地址, 欢迎 Star: https://github.com/bird-chinese-community/BIRD-LSP