DN11 是受 DN42 启发而搭建的一个实验性网络,目前用于杭电范围内,对网络感兴趣的同学学习使用,DN11和DN42非常相似,他们之间的经验都有广泛借鉴意义。
DN11配网第二期,接上文 “使用 Bird2 配置 WireGuard + OSPF 实现网络的高可用”,这是一篇用于 DN11 的 BGP 配置教程
随着 DN11 的扩大,使用 OSPF 进行集中管理已经变得麻烦起来了,此外还能看到一堆隧道IP污染路由表
也有不少人手上有多个内网,不得不打破 DN11 原先一个人使用一个/24网段的约定
近期又在考虑和 VidarNetwork 并网的事情,最终合并完的网络会变得相当庞大,或许是时候转移到BGP上去了
准备
选择一个AS Number
BGP 是用于连接不同 AS 的路由协议,现在定义每一个人选取自己的AS号管理自己的AS。对于已经有AS号的,可以使用已有的AS号,没有AS号的建议选择 421111xxxx 并登记进文档。
选择自己的网段
首先,不要一次性宣告大于/24的网段,/24已经很够你折腾,如果你有多个路由设备并且确实有多个真实子网,那么你可以宣告两个或者三个/24的网段。
也不要为了省网段资源而采用/25这种做法,没有必要,只会给你带来更多的麻烦。
如果你是新加入DN11的成员,虽然DN11改为BGP的同时我们放开了网段的限制,但是真别用常见网段
- 172.16.x.0/24 (这是DN11之前使用的网段,.17 .18这些段docker会用,不要宣告)
- 10.x.x.0/24 (注意避开移动的内网段,不要使用太高或者太低的地址)
- 11.x.x.0/24 (美国国防部的公网段其中之一,他们一般当内网段用)
- 100.64.x.0/24 (比较少见的保留段)
- 192.168.x.0 (真的很容易冲突,不要用)
此外你还需要避开HDU已经使用了的内网段,具体有哪些段可以在飞书知识库上查找
隧道
本小节写给 Openwrt 用户,ros 用户应该都会,linux 用户参考 wg-quick 使用教程。但无论你是什么用户,请务必看完本章节的内容,铺设隧道这件事有一些实践上的做法可供参考。
安装wg-quick-op
我给 openwrt 专门写了一个用来配置 wireguard 的工具,他会帮你处理开机启动,ddns 的问题的同时你可以使用 wg-quick 配置文件的所有配置。主要是基于 wg-quick-go 修改,定制了一些实用功能。
github:https://github.com/BaiMeow/wg-quick-op
目前还在 beta 阶段还没发 release(或许等你看到这篇文章的时候 release 就发了)
配置隧道
在/etc/wireguard
下创建配置文件,一条隧道对应一个配置文件,配置文件命名为xxx.conf
。下面给出配置文件示例:
以下示例已过时,Address 字段不需要填写,填写反而会产生冲突。
[Interface]
PrivateKey = <PrivateKey>
ListenPort = <Port>
Address = <my tunnel IP>
PostUp = /sbin/ip addr add dev %i <my tunnel IP> peer <another tunnel IP>
Table = off
[Peer]
Endpoint = <EndPoint IP>
PublicKey = <Public Key>
AllowedIPs = 0.0.0.0/0
这里主要参考了 DN42 的配置
- PrivateKey 用
wg genkey
生成一个 - ListenPort wireguard监听端口,注意打开防火墙或者配置端口映射
- Address 隧道IP,建议取网段的最后一个可用地址,比如
172.16.4.254/32
, 无论多少拉多少条隧道都可以使用同一个隧道 IP ,这个 IP 仅 bird2 使用,注意不要给其他设备使用 - PostUp 隧道建立时执行的命令,这个命令添加了一条对等路由,例如
/sbin/ip addr add dev %i 172.16.4.254/32 peer 172.16.2.254/32
- Table = off 请务必使用 off,路由由 bird2 来接管,不需要 wireguard 创建
- Endpoint 填对面的 IP 和监听的端口
- PublicKey 填对面的公钥,公钥可用用
wg pubkey
命令,然后粘贴公钥进去按 ctrl+d 获取 - AllowedIPs 允许所有 IP 通过 Wireguard 接口
使用wg-quick-op up 接口名
来连接这个接口,没有意外的话,现在你能够 ping 通对面的对端IP了
故障排查
STEP1
首先你需要检查隧道有没有连接上,执行 wg show 接口名
,看 latest handshake,如果握手时间在两分钟内都是正常的,如果大于两分钟或者没有这个字段,说明 wireguard 连接没有连上。
这一般是因为端口没开,检查路由器的入站配置,如果是旁路由,还得检查一下端口映射是否正确。这也有可能是DNS记录的地址过期导致的,检查一下 wireguard 的 endpoint 地址是否确实是对面的IP地址。
STEP2
如果连接上了还是没有 ping 通,请检查路由表,有没有到对端的路由,并再次检查你的 wireguard 配置并重启接口
BGP
配置 bird2
本小节写给 bird2 用户,ros 和其他用户可供参考,但是依旧推荐阅读
下面给出BGP配置示例,以下示例适用于AS内只有一台路由设备的配置,如果你的AS内有多个路由设备还要做不少额外配置,之后可以另外写一篇文章来谈谈这个问题
log syslog all;
debug protocols all;
# 可以采用隧道地址,也可以采用路由所在的IP,在自己的网段内且不重复即可
router id 172.16.4.254;
# 分表,给后期的其他配置留一点回旋的余地
ipv4 table BGP_table;
protocol device{
}
# 从 master4 导出所有路由表到 kernel
protocol kernel{
ipv4 {
export all;
import none;
};
}
# 宣告 172.16.4.0/24 段
protocol static {
ipv4 {
table BGP_table;
import all;
export none;
};
# 只是为了让BGP的路由表里出现这条路由,不要担心 reject
# 这个动作其实无所谓,reject 这个动作并不会被发到其他 AS
# 后续将在导出到 master4 的时候删除这条路由,本地也不会有这一条
# 请修改为你自己要宣告的段
route 172.16.4.0/24 reject;
}
# 定义BGP模板
template bgp BGP_peers {
# 修改为隧道地址和你的ASN
local 172.16.4.254 as 4220084444;
ipv4 {
table BGP_table;
import all;
export filter {
if source ~ [RTS_STATIC, RTS_BGP] then accept;
reject;
};
};
}
# 从 BGP_table 导入路由到 master4
protocol pipe {
table master4;
peer table BGP_table;
import filter {
# 过滤 protocol static 添加的 reject
if source = RTS_STATIC then reject;
# 添加 prefsrc 防止路由器使用隧道ip发包,使隧道ip仅用于路由协议
# ip地址改为你的路由器ip地址
krt_prefsrc = 172.16.4.5;
accept;
};
export none;
}
# 从模板定义一个BGP邻居
# protocol bgp protocol名称 from 模板名称
protocol bgp hakuya from BGP_peers {
# 对端隧道地址%接口 as ASN
neighbor 172.16.0.254%hakuya as 4220081919;
}
所有相关配置指导已经写在配置文件里了,修改好覆盖bird原有的配置文件即可
添加iptables规则(过时内容请忽略)
本小节内容已过时,仅配置 krt_prefsrc 即可实现本小节所做的一切,具体参考上一小节。
# iptables -t nat -A POSTROUTING -s <tunnel ip> -p tcp --dport 179 -j RETURN
# iptables -t nat -A POSTROUTING -s <tunnel ip> -j SNAT --to-source <lan ip>
iptables -t nat -A POSTROUTING -s 172.16.4.254 -p tcp --dport 179 -j RETURN
iptables -t nat -A POSTROUTING -s 172.16.4.254 -j SNAT --to-source 172.16.4.5
对于 OpenWRT 设备,将这几条命令加到 网络-防火墙-自定义规则
再点重启防火墙即可
这两条 iptables 给除了 BGP(tcp 179端口) 以外的流量做了一个 snat,目的是为了让路由器间能够互相 ping 通
具体分析一下这个问题,先假设一个场景,现在有一个BGP网络,他们的子网,隧道IP,子网内的设备IP分别是
net | 子网 | 隧道IP | 设备IP |
---|---|---|---|
A | 172.16.1.0/24 | 172.16.1.254 | 172.16.1.1 |
B | 172.16.2.0/24 | 172.16.2.254 | 172.16.2.1 |
C | 172.16.3.0/24 | 172.16.3.254 | 172.16.3.1 |
ABC 之间两两相连
如果我们在 A 设备上 ping C 设备,A设备选择了隧道的网卡发包,那么这个 ICMP 包的 src 为 172.16.1.254 dst 为 172.16.3.1
全部连接正常的情况下,流量直接走 A 到 C 的隧道,发过去发回来,这没有问题
但是如果因为一些不可抗拒力导致 A 和 C 的隧道无法建立连接,理想状态下,这条流量应该会借道 B,绕路到 C,然后再回来
去程是没有问题的,但是在回程的时候,虽然 A 和 C 的隧道已经断开了,但是 ip link 依然是 up 的状态,结果 C 到 172.16.1.254/32 的下一跳接口依然是那条断开的隧道,最终导致数据包在回程的时候丢失。
这主要是因为,我们在建立 BGP 会话之前,需要让两个运行 BGP 协议的路由设备之间互相能够直接连接,在此基础上才能够配置 BGP 来交换各自的路由表。而让这两个设备能够互相直连这一步,必须由我们先行设置一个静态路由。这条静态路由是死的,不能适应隧道断开这些事情。
解决方案有两个:
- 检查gateway的状态,不通的时候弃用这条路由(routerOS可以很轻易地做到,但是OpenWRT没有提供相关功能)
- 把源地址为隧道IP的流量 snat 到本机的 Lan IP
我们现在对 OpwnWRT采用的是第二个做法,既然隧道 IP 这一条是死的,我们可以直接弃用,只让 BGP 使用即可,通过做 snat 可以防止路由设备发出源地址为这个地址的包。
添加更多邻居
# protocol bgp protocol名称 from 模板名称
protocol bgp hakuya from BGP_peers {
# 对端隧道地址%接口 as ASN
neighbor 172.16.0.254%hakuya as 4220081919;
}
将这一块内容复制粘贴在文件后面,再修改修改内容即可
故障排查
STEP 1 检查 bird 配置文件时候有还未修改为自己的信息的地方
STEP 2
birdc c
应用配置了没有?
STEP 3
用birdc s p
看看具体卡什么状态了,然后问问群友
End But not Ended
现在 BGP 连接应该已经建立起来了,可以使用birdc s p
查看所有protocol的连接状态,一切顺利的话,你的 BGP 连接应该已经 Established 了
如果你的AS里有不少子网,这一切还只是折腾 BGP 的开始,在后面还有 BGP Large Community ,BGP confederation ,RR 等内容。
等 DN11 整体迁移到 BGP 后再继续做后部分的教程。