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 来交换各自的路由表。而让这两个设备能够互相直连这一步,必须由我们先行设置一个静态路由。这条静态路由是死的,不能适应隧道断开这些事情。

解决方案有两个:

  1. 检查gateway的状态,不通的时候弃用这条路由(routerOS可以很轻易地做到,但是OpenWRT没有提供相关功能)
  2. 把源地址为隧道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 3birdc s p看看具体卡什么状态了,然后问问群友

End But not Ended

现在 BGP 连接应该已经建立起来了,可以使用birdc s p 查看所有protocol的连接状态,一切顺利的话,你的 BGP 连接应该已经 Established 了

如果你的AS里有不少子网,这一切还只是折腾 BGP 的开始,在后面还有 BGP Large Community ,BGP confederation ,RR 等内容。

等 DN11 整体迁移到 BGP 后再继续做后部分的教程。