首页
iYoRoy DN42 Network
关于
友情链接
Language
简体中文
English
Search
1
Docker下中心化部署EasyTier
1,735 阅读
2
给Android 4.9内核添加KernelSU支持
1,097 阅读
3
记一次为Android 4.9内核的ROM启用erofs支持
309 阅读
4
在TrueNAS上使用Docker安装1Panel
301 阅读
5
2025羊城杯初赛WriteUp
294 阅读
Android
运维
NAS
开发
网络技术
专题向研究
DN42
个人ISP
CTF
登录
Search
标签搜索
网络技术
BGP
Linux
BIRD
DN42
C&C++
Android
OSPF
MSVC
AOSP
Windows
Docker
caf/clo
TrueNAS
内部路由协议
服务
iBGP
Clearnet
DNS
STL
神楽悠笙
累计撰写
23
篇文章
累计收到
11
条评论
首页
栏目
Android
运维
NAS
开发
网络技术
专题向研究
DN42
个人ISP
CTF
页面
iYoRoy DN42 Network
关于
友情链接
Language
简体中文
English
搜索到
2
篇与
的结果
DN42&OneManISP - 使用VRF实现公网BGP和DN42共用一台机器
背景 目前同一区域内公网BGP和DN42分别用了一台VPS,也就是说同一个区域需要两台机器。从群友那里得知了VRF,便想着通过VRF实现同一台机器同时处理公网BGP并加入DN42。 注意:VRF方案因为其隔离性,会导致DN42无法访问主机的服务。如果你需要在服务器上跑诸如DNS之类的服务给DN42用,你可能需要再单独配置端口转发或者veth,但是不在本文讨论范围内。(这也是我实际生产环境最终还是没有采用VRF的原因) VRF的优点 虽然说DN42使用的IP段是私有地址,并且它的ASN用的都是内部ASN,理论上不会和公网BGP相互干扰,但是如果共用同一张路由表,可能会造成路由污染、管理复杂等问题。 VRF(Virtual Routing and Forwarding,虚拟路由转发)可以实现在一台机器上创建多个路由表,也就是说我们可以通过它将DN42的路由单独放到一个路由表里,以实现将DN42路由表和公网路由表相隔离。这么做的优点有: 绝对的安全与策略隔离:DN42路由表和公网路由表相隔离,从根本上杜绝了路由泄露的可能性。 清晰的运维管理:可以使用birdc show route table t_dn42和birdc show route table t_inet来分别查看和调试两张完全独立的路由表,一目了然。 故障域隔离:若果DN42的某个对等体发生Flap,这些影响将被完全限制在dn42的路由表内,不会消耗公网实例的路由计算资源,也不会影响公网的转发性能。 更符合现代网络设计理念:在现代网络工程中,为不同的路由域(生产、测试、客户、合作伙伴)使用VRF是标准做法。它将你的设备逻辑上划分成了多个虚拟路由器。 配置 系统部分 创建VRF设备 使用以下指令创建一个名为dn42-vrf的VRF设备并关联到系统的1042号路由表: ip link add dn42-vrf type vrf table 1042 ip link set dev dn42-vrf up # 启用 路由表号可以按照你自己的喜好修改,但是请避开以下几个保留路由表编号: 名称 ID 说明 unspec 0 未指定,基本不用 main 254 主路由表,大多数普通路由都放在这里 default 253 一般不用,保留 local 255 本机路由表,存放127.0.0.1/8、本机 IP、广播地址等,不能改 将现有的相应网卡关联到VRF 按照我目前的DN42网络为例,有若干WireGuard网卡和一个dummy网卡是用于DN42的,因此将这几个网卡都关联到VRF中: ip link set dev <网卡名> master dn42-vrf 需要注意的是,网卡关联到VRF之后可能会丢失地址,因此需要重新为其添加一次地址,如: ip addr add 172.20.234.225 dev dn42 完成之后,通过ip a应该能看到对应网卡的master是dn42-vrf: 156: dn42: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue master dn42-vrf state UNKNOWN group default qlen 1000 link/ether b6:f5:28:ed:23:04 brd ff:ff:ff:ff:ff:ff inet 172.20.234.225/32 scope global dn42 valid_lft forever preferred_lft forever inet6 fd18:3e15:61d0::1/128 scope global valid_lft forever preferred_lft forever inet6 fe80::b4f5:28ff:feed:2304/64 scope link valid_lft forever preferred_lft forever 持久化 我使用了ifupdown来实现开机自动加载dummy网卡和VRF设备。 对于VRF设备,创建文件/etc/network/interfaces.d/01-dn42-vrf并填入: auto dn42-vrf iface dn42-vrf inet manual pre-up ip link add $IFACE type vrf table 1042 up ip link set dev $IFACE up post-down ip link del $IFACE 然后使用ifup dn42-vrf启动。 对于dummy网卡,创建文件/etc/network/interfaces.d/90-dn42并填入: auto dn42 iface dn42 inet static address 172.20.234.225 netmask 32 pre-up ip link add $IFACE type dummy up ip link set $IFACE up master dn42-vrf # 此处master指定和dn42-vrf相关联 down ip link set $IFACE down post-down ip link del $IFACE iface dn42 inet6 static address fd18:3e15:61d0::1 netmask 128 因为ifupdown不支持同时配置v4和v6地址,因此需要分成两个iface。 我的dummy网卡名称为dn42,如果你的名称不一样请按需要修改。创建完后使用ifup dn42即可启动dummy网卡。 注意:VRF设备前面的标号需要比dummy网卡的大,以让VRF设备先于dummy网卡启动。 WireGuard隧道 添加PostUp使其关联到vrf并重新为其绑定地址。举个例子: [Interface] PrivateKey = [数据删除] ListenPort = [数据删除] Table = off Address = fe80::2024/64 PostUp = sysctl -w net.ipv6.conf.%i.autoconf=0 + PostUp = ip link set dev %i master dn42-vrf + PostUp = ip addr add fe80::2024/64 dev %i [Peer] PublicKey = [数据删除] Endpoint = [数据删除] AllowedIPs = 10.0.0.0/8, 172.20.0.0/14, 172.31.0.0/16, fd00::/8, fe00::/8 然后重新启动隧道即可。 Bird2部分 首先我们需要定义两张路由表,分别用于dn42的IPv4和IPv6: ipv4 table dn42_table_v4; ipv6 table dn42_table_v6 随后,在kernel protocol中指定VRF和系统路由表编号,并在IPv4、IPv6中指定前面创建的v4、v6路由表: protocol kernel dn42_kernel_v6{ + vrf "dn42-vrf"; + kernel table 1042; scan time 20; ipv6 { + table dn42_table_v6; import none; export filter { if source = RTS_STATIC then reject; krt_prefsrc = DN42_OWNIPv6; accept; }; }; }; protocol kernel dn42_kernel_v4{ + vrf "dn42-vrf"; + kernel table 1042; scan time 20; ipv4 { + table dn42_table_v4; import none; export filter { if source = RTS_STATIC then reject; krt_prefsrc = DN42_OWNIP; accept; }; }; } 除了kernel以外的protocol都加上VRF和IPv4、IPv6独立的table,但不需要指定系统路由表编号: protocol static dn42_static_v4{ + vrf "dn42-vrf"; route DN42_OWNNET reject; ipv4 { + table dn42_table_v4; import all; export none; }; } protocol static dn42_static_v6{ + vrf "dn42-vrf"; route DN42_OWNNETv6 reject; ipv6 { + table dn42_table_v6; import all; export none; }; } 总而言之就是: 一切和DN42有关的都给配置一个VRF和之前定义的路由表 只有kernel协议需要指定系统路由表编号,其他不需要 对于BGP、OSPF等也如法炮制,不过我选择将公网的RouterID和DN42的分开,因此还需要单独配置一个RouterID: # /etc/bird/dn42/ospf.conf protocol ospf v3 dn42_ospf_iyoroynet_v4 { + vrf "dn42-vrf"; + router id DN42_OWNIP; ipv4 { + table dn42_table_v4; import where is_self_dn42_net() && source != RTS_BGP; export where is_self_dn42_net() && source != RTS_BGP; }; include "ospf/*"; }; protocol ospf v3 dn42_ospf_iyoroynet_v6 { + vrf "dn42-vrf"; + router id DN42_OWNIP; ipv6 { + table dn42_table_v6; import where is_self_dn42_net_v6() && source != RTS_BGP; export where is_self_dn42_net_v6() && source != RTS_BGP; }; include "ospf/*"; }; # /etc/bird/dn42/ebgp.conf ... template bgp dnpeers { + vrf "dn42-vrf"; + router id DN42_OWNIP; local as DN42_OWNAS; path metric 1; ipv4 { + table dn42_table_v4; ... }; ipv6 { + table dn42_table_v6; ... }; } include "peers/*"; 完成后birdc c重载配置即可。 这时,我们可以通过ip route show vrf dn42-vrf来单独查看DN42的路由表: root@iYoRoyNetworkHKGBGP:~# ip route show vrf dn42-vrf 10.26.0.0/16 via inet6 fe80::ade0 dev dn42_4242423914 proto bird src 172.20.234.225 metric 32 10.29.0.0/16 via inet6 fe80::ade0 dev dn42_4242423914 proto bird src 172.20.234.225 metric 32 10.37.0.0/16 via inet6 fe80::ade0 dev dn42_4242423914 proto bird src 172.20.234.225 metric 32 ... 也可以在Ping的时候通过参数-I dn42-vrf来实现通过VRF Ping: root@iYoRoyNetworkHKGBGP:~# ping 172.20.0.53 -I dn42-vrf ping: Warning: source address might be selected on device other than: dn42-vrf PING 172.20.0.53 (172.20.0.53) from 172.20.234.225 dn42-vrf: 56(84) bytes of data. 64 bytes from 172.20.0.53: icmp_seq=1 ttl=64 time=3.18 ms 64 bytes from 172.20.0.53: icmp_seq=2 ttl=64 time=3.57 ms 64 bytes from 172.20.0.53: icmp_seq=3 ttl=64 time=3.74 ms 64 bytes from 172.20.0.53: icmp_seq=4 ttl=64 time=2.86 ms ^C --- 172.20.0.53 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3006ms rtt min/avg/max/mdev = 2.863/3.337/3.740/0.341 ms 注意事项 如果vrf设备重载了,所有原先和vrf相关联的设备都需要重载一次,否则无法正常工作 目前DN42是无法访问到配置了VRF的主机内的服务的,后续可能出一篇文章讲一下如何去让VRF内的流量可以访问到主机服务(挖坑ing) 参考文章: 用 BIRD 运行你的 MPLS 网络
2025年09月16日
44 阅读
0 评论
0 点赞
DN42探究日记 - Ep.2 通过OSPF搭建内部网络并启用iBGP
写在前面 本人是BGP小白,文章中可能会存在不严谨内容/小白理解/低级错误,请诸位大佬们手下留情。若发现存在问题,您愿意的话可以邮件联系我,我会在第一时间更正。如果您不能接受,建议现在就关闭此文章。 本文更新日志 {timeline} {timeline-item color="#50BFFF"} 2025年7月22日:文章第一版发布,使用VXLAN over WireGuard隧道 {/timeline-item} {timeline-item color="#50BFFF"} 2025年7月25日:更新隧道方案,使用type ptp;以支持直接通过WireGuard传输OSPF流量(特别感谢Nuro Trance大佬指导!) {/timeline-item} {timeline-item color="#50BFFF"} 2025年8月8日:添加iBGP部分的解释和配置 {/timeline-item} {timeline-item color="#4F9E28"} 2025年8月27日:更新节点拓扑结构图 {/timeline-item} {/timeline} 为什么需要内部路由 当节点数量增多,我们需要一个合适的方式处理自己AS的内部路由。因为BGP路由只负责将数据包路由到AS,这就导致了一个问题:假如我有A、B两个节点都与别人peer,但是在路由器看来这两台设备同属于一个AS,从A节点发出的请求回包可能被回到B上。这个时候,如果没有做内部路由,则A会无法收到回包。因此,我们需要保证自家内网各个设备之间都能连通。常见的几种方式如下: 通过ZeroTier等组网工具:这种方式较为简单,只需要在每个节点上配置一个客户端即可实现各个节点之间的P2P连接。 通过WireGuard等P2P工具手动建立$\frac{n(n+1)}{2}$条隧道,效果同1,但是节点一多工作量呈指数级上升 通过WireGuard等P2P工具手动建立<$\frac{n(n+1)}{2}$条隧道,再通过OSPF、Babel等内网寻路协议建立内网路由,优点是较为灵活,易于后期添加新节点,缺点就是较为危险,容易配置错误引爆DN42。 因此我决定冒险一下 节点拓扑 graph LR A[HKG<br>172.20.234.225<br>fd18:3e15:61d0::1] B[NKG<br>172.20.234.226<br>fd18:3e15:61d0::2] C[TYO<br>172.20.234.227<br>fd18:3e15:61d0::3] D[LAX<br>172.20.234.229<br>fd18:3e15:61d0::5] B <--> A C <--> A A <--> D C <--> D 更新Bird2至v2.16及以上 因为我希望使用IPv6 Link-Local地址传递IPv4的OSPF数据,而Bird在2.16及以后才支持这项功能,因此需要更新至v2.16。如果你不希望更新,可以先跳过此步骤。 使用以下指令安装最新版本的Bird2: sudo apt update && sudo apt -y install apt-transport-https ca-certificates wget lsb-release sudo 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/bird2 $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/cznic-labs-bird2.list sudo apt update && sudo apt install bird2 -y 配置隧道 [Interface] PrivateKey = <本地WireGuard私钥> ListenPort = <监听端口> Table = off Address = <IPv6 LLA>/64 PostUp = sysctl -w net.ipv6.conf.%i.autoconf=0 [Peer] PublicKey = <对端公钥> Endpoint = <对端公网接入点> AllowedIPs = 10.0.0.0/8, 172.20.0.0/14, 172.31.0.0/16, fd00::/8, fe00::/8, ff02::5 ff02::5是OSPFv3路由器专用的链路本地范围组播地址,需要添加进AllowedIPs。 如果你正在使用v2.16以前的Bird,请再为隧道配置一个IPv4地址,不一定非要是DN42 IPv4,其他私有地址也可以。请参考: {collapse} {collapse-item label="包含IPv4的WireGuard配置示例"} [Interface] PrivateKey = <本地WireGuard私钥> ListenPort = <监听端口> Table = off Address = <IPv6 LLA>/64 PostUp = ip addr add 100.64.0.225/32 peer 100.64.0.226/32 dev %i PostUp = sysctl -w net.ipv6.conf.%i.autoconf=0 [Peer] PublicKey = <对端公钥> Endpoint = <对端公网接入点> AllowedIPs = 10.0.0.0/8, 172.20.0.0/14, 100.64.0.0/16, 172.31.0.0/16, fd00::/8, fe00::/8, ff02::5 请将100.64.0.225、100.64.0.226替换为你本机的IPv4和对端的IPv4,并且记得加入AllowedIPs。 {/collapse-item} {/collapse} 启用OSPF 默认你已经配置好了如上一篇文章所写的bird基础配置 在/etc/bird下新建一个名为ospf.conf的文件,填入如下内容: protocol ospf v3 <name> { ipv4 { import where is_self_net() && source != RTS_BGP; export where is_self_net() && source != RTS_BGP; }; include "/etc/bird/ospf/*"; }; protocol ospf v3 <name> { ipv6 { import where is_self_net_v6() && source != RTS_BGP; export where is_self_net_v6() && source != RTS_BGP; }; include "/etc/bird/ospf/*"; }; 理论上来说应该使用OSPF v2处理IPv4,但是因为需要通过IPv6 LLA地址通信IPv4,因此此处IPv4也使用OSPF v3。 过滤规则保证只有本网段内的路由能够通过OSPF传递,并且过滤掉外部BGP协议的路由 千万不要随意使用import all;export all;,有可能会导致路由劫持并影响到整个DN42网络。OSPF只应该处理网段内部的路由。 {collapse} {collapse-item label="配置示例"} /etc/bird/ospf.conf protocol ospf v3 dn42_iyoroynet_ospf { ipv4 { import where is_self_net() && source != RTS_BGP; export where is_self_net() && source != RTS_BGP; }; include "/etc/bird/ospf/*"; }; protocol ospf v3 dn42_iyoroynet_ospf6 { ipv6 { import where is_self_net_v6() && source != RTS_BGP; export where is_self_net_v6() && source != RTS_BGP; }; include "/etc/bird/ospf/*"; }; {/collapse-item} {/collapse} 接着,新建/etc/bird/ospf文件夹,在其中创建area配置文件(如:/etc/bird/ospf/0.conf),填写区域信息: area 0.0.0.0 { interface "<DN42 dummy网卡>" { stub; }; interface "<wg0网卡名称>" { cost 80; # 按照你的网络情况修改 type ptp; }; interface "<wg1网卡名称>" { cost 100; # 按照你的网络情况修改 type ptp; }; # 以此类推 }; 0.0.0.0区域代表骨干网 此处dummy网卡指上一篇文章中所写的DN42虚拟网卡 cost值本应该是用于开销计算,但在DN42这种对带宽要求不大而对延迟较为敏感的场景下可以直接填写延迟,OSPF会自动走开销值之和最短的路由。 {collapse} {collapse-item label="配置示例"} /etc/bird/ospf/0.conf area 0.0.0.0 { interface "dn42" { stub; }; interface "dn42_hkg" { cost 80; type ptp; }; interface "dn42_hfe" { cost 150; type ptp; }; interface "dn42_lax"{ cost 100; type ptp; }; }; {/collapse-item} {/collapse} 最后,打开/etc/bird/bird.conf,在末尾引入OSPF的配置文件: include "ospf.conf"; 运行birdc configure,然后birdc show protocols应该就能看到OSPF的状态是Running了。如果不是,请检查配置步骤是否出错。 此时,在非直连的两台机器上互相ping应该就能通了: 配置iBGP 在从多个地点建立对等连接之前,您的各个节点必须首先完整掌握自身网络的拓扑结构。除了所有外部 BGP 连接外,这还需要配置另一个关键组件:内部 BGP(即 iBGP)。 必要性 iBGP可以保证AS内部所有运行BGP的路由器都能获知到达外部目的地的完整BGP路由信息,进而确保: 内部路由器可以选择最优的出口路径。 流量能够被正确地引导到负责连接特定外部网络的边界路由器。 即使存在多个边界路由器连接到同一个外部网络,内部路由器也能根据策略选择最佳出口。 相比于在AS内部使用默认路由指向边界路由器,iBGP提供了精确的外部路由信息,使得内部路由器能做出更智能的转发决策。 缺点与解决方案 为了防止路由信息在AS内部无控制地扩散导致环路,iBGP路由器不会将从某个iBGP邻居学到的路由再通告给其他iBGP邻居,这就要求传统的iBGP要求在同一个AS内所有运行iBGP的路由器之间必须建立全网状的iBGP邻居关系(Full Mesh)。(还是要建立$\frac{n(n+1)}{2}$条连接 ,没办法。不过OSPF起来之后iBGP配置还是比配置隧道简单的 ) 解决方案就是: 使用路由反射器(Route Reflector, RR): 由RR路由器管理整个AS内部所有的路由信息,缺点就是RR路由器故障将会导致整个网络瘫痪(这很不Decentralized) 通过BGP联盟(BGP Confederation) 搭建内部网络: 将AS内的路由器虚拟成一个个子AS,再把各个路由器之间的连接当作BGP处理,最后向外传递的时候抹去内部AS的路由路径。 后面两种方案我没有尝试过,下面是一些可能有用的参考文章。本文着重讨论iBGP的配置。 DN42 实验网络介绍及注册教程(2022-12 更新) - Lan Tian @ Blog Bird 配置 BGP Confederation,及模拟 Confederation(2020-06-07 更新) - Lan Tian @ Blog 编写iBGP配置文件 在/etc/bird下新建文件ibgp.conf,填入如下内容: template bgp ibgpeers { local as OWNAS; ipv4 { 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/*"; 导入和导出规则确保iBGP仅处理BGP协议学到的路由,并且过滤掉IGP的路由防止环回 next hop self是必须的,指示 BIRD 在向 iBGP 邻居导出路由时,将下一跳重写为边界路由器自身的IP地址(而非原始的外部下一跳)。因为内部路由器无法直接访问外部邻居地址,若不重写则会被认定为地址不可达。重写后,内部路由器只需通过 IGP 路由将流量送至边界路由器,由边界路由器完成最终的外部转发。 因为我希望使用IPv6地址建立MP-BGP,通过IPv6路由IPv4,因此在IPv4中启用了extended next hop 接着创建/etc/bird/ibgp文件夹,在其中为每台节点都创建一个iBGP Peer配置文件: protocol bgp 'dn42_ibgp_<节点>' from ibgpeers{ neighbor <对应节点的IPv6 ULA地址> as OWNAS; }; {collapse} {collapse-item label="样例"} /etc/bird/ibgp/hkg.conf: protocol bgp 'dn42_ibgp_HKG' from ibgpeers{ neighbor fd18:3e15:61d0::1 as OWNAS; }; {/collapse-item} {/collapse} 注意:每个节点上都需要建立(n-1)个iBGP连接,保证和AS内其他所有机器都建立连接,这也是为什么需要使用ULA地址 使用ULA地址确保即使两个节点之间的WireGuard断开,iBGP仍然能通过OSPF建立的内部路由建立,否则将会导致整个内部网络的崩溃 最后,在/etc/bird/bird.conf中加入对ibgp.conf的引入: include "ibgp.conf"; 并运行birdc configure应用配置即可。 参考文章: BIRD 与 BGP 的新手开场 - 海上的宫殿 萌新入坑 DN42 之 —— 基于 tailscale + vxlan + OSPF 的组网 – 米露小窝 使用 Bird2 配置 WireGuard + OSPF 实现网络的高可用 | bs' realm DN42 实验网络介绍及注册教程(2022-12 更新) - Lan Tian @ Blog 如何引爆 DN42 网络(2023-05-12 更新) - Lan Tian @ Blog Bird 配置 BGP Confederation,及模拟 Confederation(2020-06-07 更新) - Lan Tian @ Blog 深入解析OSPF路径开销、优先级和计时器 - 51CTO New release 2.16 | BIRD Internet Routing Daemon 第一章·第二节 如何在 Linux 上安装最新版本的 BIRD? | BIRD 中文文档 [DN42] 使用 OSPF ptp 搭建内网与IBGP配置 – Xe_iu's Blog | Xe_iu的杂物间 [译] dn42 多服务器环境中的 iBGP 与 IGP 配置 | liuzhen932 的小窝
2025年07月22日
136 阅读
1 评论
1 点赞