中国三大运营商宽带BGP分流教程


目标:在国内一条软路由(Rocky9)上拉上 电信、联通、移动三条宽带,按目标 IP 所属的运营商自动选择对应出口线路,避免三大运营商之间跨网绕路导致的严重访问阻塞。
思路:用一台海外 Vultr 云主机接收 BGP 全表(它网络好、能拿到完整路由表),通过 WireGuard 隧道经 iBGP 把「按中国运营商 ASN 分好类」的路由下发给本地 Rocky9;本地再按 ASN 把目标 IP 分流到电信/联通/移动各自的拨号接口。


一、整体架构

   [Internet BGP 全表]
        │  eBGP(邻居 169.254.169.254 as 64515,Vultr 提供)
        ▼
  Vultr 云主机 (BIRD) ── router id 108.61.219.200 / AS 4288000739
        │        │
        │  filter:只保留中国运营商 ASN 的路由
        ▼
        │  WireGuard 隧道 (10.0.1.1)    ←── BGP 承载链路
        ▼
  本地 Rocky9 软路由 (BIRD) ── router id 10.0.1.2 / AS 64555
        │
        │  kernel:按目标 IP 所属运营商选择出口
        ├── ppp0 ── 电信宽带 ── 中国电信 ASN
        ├── ppp1 ── 联通宽带 ── 中国联通 ASN
        └── ppp2 ── 移动宽带 ── 中国移动 ASN

为什么三线分流有用:国内三大运营商骨干网互联(跨网)带宽紧张、绕路多,电信访问联通、移动都很慢。我们自己拉三条宽带后,目标 IP 属于哪家运营商就走哪家的出口线路——同网直连,绕开跨网拥堵。


二、第一步:WireGuard 隧道(连接 Vultr 云端与本地 Rocky9)

BGP 会话通过这条隧道承载,隧道内网段规划 10.0.1.0/24:云端 10.0.1.1、本地 10.0.1.2。

云端 Vultr(/etc/wireguard/wg0.conf)

[Interface]
Address = 10.0.1.1/24
ListenPort = 51820
PrivateKey = <云端生成的私钥>      # wg genkey 生成

[Peer]
# 本地 Rocky9
PublicKey = <本地公钥>            # 来自本地 wg pubkey
AllowedIPs = 10.0.1.2/32
PersistentKeepalive = 25         # 保活,穿透 NAT,让本地能主动连出到云

本地 Rocky9(/etc/wireguard/wg0.conf)

[Interface]
Address = 10.0.1.2/24
PrivateKey = <本地生成的私钥>

[Peer]
# Vultr 云端
PublicKey = <云端公钥>
Endpoint = <Vultr公网IP>:51820   # 云端是公网固定地址,本地主动连出
AllowedIPs = 10.0.1.1/32
PersistentKeepalive = 25

启用隧道(两端)

# 生成密钥
wg genkey | tee privatekey | wg pubkey > publickey

# 启用并自启动
systemctl enable --now wg-quick@wg0
# 验证
wg show wg0
ping -c3 10.0.1.1   # 云端,本地测
ping -c3 10.0.1.2   # 本地,云端测

云端是公网固定 IP,用 Endpoint 指向云端公网地址 + PersistentKeepalive 保活,本地在 NAT 后面也能稳定维持隧道。


三、第二步:云端 Vultr 配置(收全表 + 按运营商分类)

文件 ebgp.conf.txt,运行在 Vultr 云主机的 BIRD 上。

1. 中国运营商 ASN 集合

define CTELECOM_ASN = [ 4134, 4809, 23764 ];          # 电信 (163/CN2/CTG)
define CUNICOM_ASN  = [ 4837, 9929, 10099 ];          # 联通 (169/A网/CUG)
define CMOBILE_ASN  = [ 9808, 58453, 58807 ];         # 移动 (CM/CMI)
# 可选:教育网等其他
# define CEDU_ASN   = [ 4538 ];

2. 基础

router id 108.61.219.200;
log syslog all;

protocol device {}
protocol direct { ipv4; interface "enp1s0"; }          # 本地直连接口,不同系统会变

3. 入站过滤器:把不同运营商的路径打上不同的本地优先级与 Community

filter vultr_in_filter {
    if (bgp_path ~ CTELECOM_ASN) then { bgp_local_pref = 200; bgp_community = (64555,1001); accept; }
    if (bgp_path ~ CUNICOM_ASN)  then { bgp_local_pref = 200; bgp_community = (64555,1002); accept; }
    if (bgp_path ~ CMOBILE_ASN)  then { bgp_local_pref = 200; bgp_community = (64555,1003); accept; }
    reject;
}

4. 与 Vultr 的 eBGP 会话(接收全表)

protocol bgp vultr {
    local as 4288000739;               # Vultr 控制面板提供的你的私有 ASN
    neighbor 169.254.169.254 as 64515; # Vultr 固定邻居 IP 和 ASN
    # password "此处填入你的BGP会话密码";   # 原密码已移除,需要认证请自行填回
    multihop 2;                        # 必须:邻居是私有地址
    source address 108.61.219.200;     # 发起连接源 IP

    ipv4 {
        import filter vultr_in_filter;     # 应用分类过滤
        export none;                       # 不向 Vultr 宣告任何路由
        import limit 1000000 action warn;  # 收全表必须开
    };

    hold time 240;
    keepalive time 80;
}

5. 经 WireGuard 隧道向本地下发 iBGP

protocol bgp homelab {
    local as 64555;
    neighbor 10.0.1.2 as 64555;        # 经由 wg0 隧道的本地 Rocky9

    ipv4 {
        import none;                    # 不从本地收路由
        export all;                     # 把分类后的回国全表下发给本地
    };
}

四、第二步:本地 Rocky9 配置(按目标 IP 分流三大运营商)

文件 ibgp.conf.txt,运行在本地 Rocky9(软路由)上。

1. 基础与运营商 ASN

router id 10.0.1.2;
log syslog all;
protocol device {}

define CTELECOM_ASN = [ 4134, 4809, 23764 ];  # 电信
define CUNICOM_ASN  = [ 4837, 9929, 10099 ];  # 联通
define CMOBILE_ASN  = [ 9808, 58453, 58807 ]; # 移动

2. 内核协议:按目标 IP 所属运营商写进对应拨号接口

protocol kernel {
    ipv4 {
        table master4;
        import none;   # 不把本地路由灌进 BGP
        export filter {
            # dst 属于电信 → 走 ppp0(电信宽带)
            if (bgp_path ~ CTELECOM_ASN) then { ifname = "ppp0"; accept; }
            # dst 属于联通 → 走 ppp1(联通宽带)
            if (bgp_path ~ CUNICOM_ASN)  then { ifname = "ppp1"; accept; }
            # dst 属于移动 → 走 ppp2(移动宽带)
            if (bgp_path ~ CMOBILE_ASN)  then { ifname = "ppp2"; accept; }
            # 其余拒收
            reject;
        };
    };
    kernel table 2;    # BGP拉过来的路由没有放到主路由表254,测试环境我就放在表2
}

含义:命中哪家运营商的路径条目标记为对应出口 ppp0/ppp1/ppp2 并写入内核路由表,其余拒绝。这样目标 IP 属于电信就走电信出口、属于联通就走联通出口,同网直连不跨网。

3. 从云端(经 WireGuard)iBGP 接收分类路由

protocol bgp vultr {
    local as 64555;
    neighbor 10.0.1.1 as 64555;        # 经由 wg0 隧道的 Vultr 云端

    ipv4 {
        import all;                    # 接收云端下发的分类回国路由
        export none;                   # 不向云端发送路由
    };
}

五、落地顺序建议

  1. 两端装好 WireGuard,ping 通 10.0.1.x,确认隧道可用。
  2. 云端 Vultr 起 ebgp.conf.txt,birdc show protocols 确认 vultr 与 homelab 会话都 Established。
  3. 云端 birdc show route 抽查,确认只收三大运营商且已被打上不同 Community/优先级。
  4. 本地配好三条 PPPoE 拨号(ppp0/ppp1/ppp2),再起 ibgp.conf.txt。
  5. ip route 确认命中三大运营商的路径出接口分别为 ppp0/ppp1/ppp2。
  6. 内网客户端 actraceroute 测试,验证同网直连、不跨网。

六、附:完整可直接运行的配置(三份)

配置文件 A:云端 Vultr —— ebgp.conf.txt

define CTELECOM_ASN = [ 4134, 4809, 23764 ];
define CUNICOM_ASN  = [ 4837, 9929, 10099 ];
define CMOBILE_ASN  = [ 9808, 58453, 58807 ];

log syslog all;
router id 108.61.219.200;

protocol device {}
protocol direct { ipv4; interface "enp1s0"; }

filter vultr_in_filter {
    if (bgp_path ~ CTELECOM_ASN) then { bgp_local_pref = 200; bgp_community = (64555,1001); accept; }
    if (bgp_path ~ CUNICOM_ASN)  then { bgp_local_pref = 200; bgp_community = (64555,1002); accept; }
    if (bgp_path ~ CMOBILE_ASN)  then { bgp_local_pref = 200; bgp_community = (64555,1003); accept; }
    reject;
}

protocol bgp vultr {
    local as 4288000739;
    neighbor 169.254.169.254 as 64515;
    # password "此处填入你的BGP会话密码";   # 需要认证请填回
    multihop 2;
    source address 108.61.219.200;

    ipv4 {
        import filter vultr_in_filter;
        export none;
        import limit 1000000 action warn;
    };

    hold time 240;
    keepalive time 80;
}

protocol bgp homelab {
    local as 64555;
    neighbor 10.0.1.2 as 64555;     # 经 wg0 与本地 iBGP

    ipv4 {
        import none;
        export all;
    };
}

配置文件 B:本地 Rocky9 —— ibgp.conf.txt

router id 10.0.1.2;
log syslog all;
protocol device {}

define CTELECOM_ASN = [ 4134, 4809, 23764 ];
define CUNICOM_ASN  = [ 4837, 9929, 10099 ];
define CMOBILE_ASN  = [ 9808, 58453, 58807 ];

protocol kernel {
    ipv4 {
        table master4;
        import none;
        export filter {
            if (bgp_path ~ CTELECOM_ASN) then { ifname = "ppp0"; accept; }
            if (bgp_path ~ CUNICOM_ASN)  then { ifname = "ppp1"; accept; }
            if (bgp_path ~ CMOBILE_ASN)  then { ifname = "ppp2"; accept; }
            reject;
        };
    };
    kernel table 2;
}

protocol bgp vultr {
    local as 64555;
    neighbor 10.0.1.1 as 64555;     # 经 wg0 与云端 iBGP

    ipv4 {
        import all;
        export none;
    };
}

配置文件 C:WireGuard 隧道(两端)

云端 Vultr /etc/wireguard/wg0.conf

[Interface]
Address = 10.0.1.1/24
ListenPort = 51820
PrivateKey = <云端私钥>

[Peer]
PublicKey = <本地公钥>
AllowedIPs = 10.0.1.2/32
PersistentKeepalive = 25

本地 Rocky9 /etc/wireguard/wg0.conf

[Interface]
Address = 10.0.1.2/24
PrivateKey = <本地私钥>

[Peer]
PublicKey = <云端公钥>
Endpoint = <Vultr公网IP>:51820
AllowedIPs = 10.0.1.1/32
PersistentKeepalive = 25

把 <...> 替换为实际密钥与 IP,用 wg genkey / wg pubkey 生成,systemctl enable --now wg-quick@wg0 拉起即可。


七、常见坑

  1. import limit 必须要:运营商全表数据量极大,不设会内存/资源失控。
  2. multihop / source address 不要漏:Vultr 邻居是链路本地私有地址。
  3. 密码已移除:需要认证的会话请自行填回 password(当前文件里是占位)。
  4. 建立EBGP的本地ASN可能会变:具体请参考Vultr给的是多少。
  5. 接口名对不上:ifname = "ppp0/ppp1/ppp2" 必须与三条 PPPoE 拨号的实际接口名一致,否则路由装入但无法转发。
  6. WireGuard 保活:本地 NAT 要 PersistentKeepalive,否则隧道会被运营商 NAT 剪掉。
  7. BGP 走隧道:确认 iBGP 邻居地址是 10.0.1.x 隧道网段,且两端 wg 已建立,BGP 才能 Established。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理。