跳至内容
liuzhen932 的小窝
返回

[译] dn42 多服务器环境中的 iBGP 与 IGP 配置

原文标题:Multiple servers on dn42: iBGP and IGPs

原文链接:https://jlu5.com/blog/dn42-multiple-servers-ibgp-igps

原文作者:James, 29th Aug 2020

本文译者:liuzhen932,首发于 liuzhen932 的小窝

本文是系列文章中的一篇,我将在此讨论 dn42 ——一个用于研究网络技术的去中心化 VPN 社区。您可以通过其官方 Wiki 了解更多关于 dn42 的信息:https://dn42.dev/

本文所有示例均基于 Bird 2,并假设您遵循了 dn42 官方指南中的配置规范:https://dn42.dev/howto/Bird2

接入 dn42 网络的过程相对直接——在完成资源注册后,您需要与其他参与者协调建立隧道连接。然而,dn42 的真正价值远不止于简单地添加对等节点。当您需要将网络扩展到多台设备或多个地理位置时,就需要更深入的配置了。

在从多个地点建立对等连接之前,您的各个节点必须首先完整掌握自身网络的拓扑结构。除了所有外部 BGP 连接外,这还需要配置另一个关键组件:内部 BGP(即 iBGP)。

iBGP 配置示例

注意:仅当您的网络尚未配置 IGP(内部网关协议)时使用此示例——若已有 IGP 部署,请直接跳至后文「优化版 iBGP 示例」章节!

    protocol bgp ibgp_node2 {
        local as OWNAS;
        neighbor fd86:bad:11b7::1 as OWNAS;
        direct;

        ipv4 {
            next hop self;
            # 可选成本,如基于延迟的成本
            cost 50;

            import all;
            export all;
        };

        ipv6 {
            next hop self;
            cost 50;

            import all;
            export all;
        };
    }

初看之下,iBGP 的配置与外部 BGP 会话非常相似。二者的核心区别在于:iBGP 对等体的 AS 编号与本端 AS 相同

在最基础的配置中,所有路由器之间必须建立全互联(Full Mesh)的 iBGP 连接。这是因为 iBGP 不会自动转发从其他对等体接收到的路由信息——如果缺少某条连接,部分节点将无法获得完整的路由表。

尽管看似简单,但以下配置细节至关重要:

next-hop-self 演示示意图

next hop self 演示示意图

你可能疑惑:为何 iBGP 的默认行为与 eBGP 差异如此显著?答案在于 BGP 协议的设计假设——内部网关协议(IGP) 应与 iBGP 协同工作,共同管理域内路由。

Interior Gateway Protocols (IGPs)

什么是 IGP?与 BGP 设计用于在不同网络之间(即自治系统之间)路由流量不同,IGP 旨在内部网络中路由流量。IGP 有多种实现方式——本页面将重点介绍 OSPF v3 和 Babel,这两种支持 IPv6 的协议均被 Bird 原生支持。

从结构上看,它们与 BGP 存在显著差异:

IGP 冗余与环回/虚拟接口

尽管 IGP 不需要全互联链路,但为目标节点提供多条路径可增强网络冗余。当某条路径失效时,IGP 能够快速响应,使得 SSH 等 TCP 连接 和 BGP 会话保持不中断。

这种快速故障转移机制为 BGP 带来了显著优势,因此 iBGP 会话默认配置为多跳模式(multihop)。若 BGP 会话直接连接到邻居,当该连接中断时,BGP 会话也会随之断开。启用多跳模式后,BGP 可通过 IGP 提供的冗余路由自动切换到备用路径。

Hans van Kranenburg 的网络实验项目深入探讨了 OSPF 并解释了这一行为:OSPF 动态路由示例

OSPF 故障转移路径

OSPF 故障转移路径(图示引用自上述链接)

若需通过 IGP 通告 BGP 对等 IP,需将其绑定到接口。这可通过创建 环回接口(loopback)或虚拟接口 实现。在 Linux 系统中,两者并无本质区别,但本人偏好使用虚拟接口(dummy interface),因其能逻辑隔离默认环回 IP 与 dn42 专用地址。

以下为 Debian ifupdown(interfaces(5))配置示例:

    iface igp-dummy0 inet static
        address 172.20.229.113
        netmask 255.255.255.255
        network 172.20.229.113
        pre-up ip link add igp-dummy0 type dummy

    iface igp-dummy0 inet6 static
        address fd86:bad:11b7::1/128

在我的配置中,服务器的公共 dn42 IP 地址被设置在环回接口上。这意味着节点间 VPN 隧道使用的 IP 必须与之不同。对此,我选择在隧道接口使用链路本地 IPv6 地址,以及 192.168.x.x 网段的 IPv4 地址。

IGP 示例:OSPF

OSPF 是一种成熟且广泛支持的路由协议。目前有两个常用版本:仅支持 IPv4 的 OSPF v2,以及同时支持 IPv4 和 IPv6 的 OSPF v3。OSPF v3 通过链路本地 IPv6 地址运行,使用独立协议类型(协议号 89)。

OSPF 功能复杂但高度完善。其核心特性是区域(Area),可将路由器划分为逻辑子域并相互隐藏细节。在大型网络中,这通过减少每台路由器需维护的路由条目数量显著节省资源。

对于小型 dn42 网络,通常只需使用骨干区域(area 0)即可满足需求。

注意:Bird 需要两个独立会话才能在 OSPFv3 中同时运行 IPv4 和 IPv6。因此,建议将区域配置拆分为单独文件并通过 include 语句引用。

/etc/bird/ospf.conf 配置示例:

    protocol ospf v3 int_ospf {
        ipv4 {
            # 只使用 OSPF 对内部网络进行路由 - 忽略从 BGP 发送的所有内容。(稍后我会详细说明原因)
            import where is_self_net() && source != RTS_BGP;
            export where is_self_net() && source != RTS_BGP;
        };

        include "/etc/bird/ospf_backbone.conf";
    };

    protocol ospf v3 int_ospf6 {
        ipv6 {
            import where is_self_net_v6() && source != RTS_BGP;
            export where is_self_net_v6() && source != RTS_BGP;
        };

        include "/etc/bird/ospf_backbone.conf";
    };

/etc/bird/ospf_backbone.conf:

    area 0 {
        # 接口名称中支持通配符,不过单独定义通配符会使成本计算更加灵活
        interface "igp-nodeABC" {
            cost 123;
        };

        interface "igp-nodeDEF" {
            cost 88;
        };

        # 每台机器上的 loopback 接口
        interface "igp-dummy*" {
            # 将接口标记为 stub 后,Bird 就不会向其发送 OSPF 流量,但仍会将与之相关的前缀路由到其他对等体
            stub;
        };
    };

IGP 示例:Babel

Babel 是一种设计简洁的现代化协议,可在最小化配置下工作。该协议同时支持 IPv4 和 IPv6,通过 UDP 端口 6696 在 IPv6 链路本地地址上运行。不过,Babel 属于较新的协议,其普及程度尚不及 OSPF。

Babel 协议有两个软件实现版本:参考实现 babeld 以及 Bird 路由套件中的实现。babeld 增加了若干扩展功能,例如基于延迟自动生成成本指标。理论上可通过内核路由表实现 Bird 对其路由信息的学习,但尚未进行相关测试。本示例采用 Bird 的 Babel 实现,并手动配置了部分 cost 参数。

/etc/bird/babel.conf:

    # Babel 没有存根区域的概念。相反,我们应该使用 Bird 的
    # "direct" 协议来读取连接到接口上的前缀,并将其发送到网络上
    protocol direct {
        ipv4;
        ipv6;
        interface "igp-dummy*";
    };

    protocol babel int_babel {
        ipv4 {
            import where source != RTS_BGP && is_self_net();
            export where source != RTS_BGP && is_self_net();
        };
        ipv6 {
            import where source != RTS_BGP && is_self_net_v6();
            export where source != RTS_BGP && is_self_net_v6();
        };

        interface "igp-nodeABC" {
            # 注意:Babel 的成本度量与 BGP 和 OSPF 略有不同
            # rxcost 指定的是邻居向我们发送流量的成本,而不是向邻居发送流量的成本
            # 当然,如果两端的成本保持一致,这不会产生任何影响
            rxcost 123;
        };

        interface "igp-nodeDEF" {
            rxcost 88;
        };
    };

优化版 iBGP 示例

在完成网络中的 IGP 配置后,基于 IGP 运行 iBGP 的典型配置如下所示。

特别需要注意的是,此处移除了 directcost 指令。目标邻居 IP 地址保持不变,但其前缀现在通过 IGP 进行路由,因此 iBGP 可以直接使用默认的多跳(multihop)模式运行。

    protocol bgp ibgp_node2 {
        local as OWNAS;
        neighbor fd86:bad:11b7::1 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;
        };

        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;
        };
    }

重要提示

在这些示例中,我设置了过滤器以明确区分网络中的 BGP 和非 BGP 路由。这一点至关重要,因为若 IGP 和 iBGP 同时通告相同路由,将导致不可预测的行为。具体而言:

补充说明 2:关于 next hop self 的使用。使用 IGP 时,是否启用 next hop self 是一种设计选择。尽管 OSPF 和 Babel 可无缝处理为对等体创建的接口路由,但你可能希望保持 IGP 的简洁性,仅包含实际归属的前缀。若需要,可将匹配 dn42 对等体的接口模式添加至 IGP,并移除 next hop self 选项。

补充说明 3:关于链路成本配置。由于 IGP 已配置链路间成本,此处无需重复配置。Bird 在计算到边界路由器的内部成本时,会自动读取 OSPF 的链路度量值。对于 Babel,此功能已在 Bird v2.0.8 中实现支持。

我应该使用什么?

在本文中,我概述了设计网络的 3 种可能性:纯 iBGP、OSPF 和 Babel。

IGP 的主要优势在于,如果直接连接中断,它可以将 BGP 连接切换到间接路径。但是,也有理由支持仅使用一种协议的更简单网络配置。

我的建议?尝试不止一种,找到最适合您的方案。

Pure BGPOSPFBabel
优点只需配置一个协议
比 OSPF 等使用广播的协议占用更少带宽
完善的 IGP 得到广泛支持
区域允许在网络的不同部分之间进行分离,这有助于扩大大型网络的规模
内置支持在不受信任的网络上进行身份验证
配置非常简单
原生双协议栈支持
Babel-RTT 扩展提供基于延迟的自动成本度量,这在 dn42 等覆盖网络上非常有用(Bird 尚不支持此功能)
缺点缺乏冗余:如果两个节点之间的直接路径发生故障,iBGP 无法故障切换到另一条链路,因为邻居是直接连接的
没有邻居发现功能;邻居必须明确添加(或通过配置自动化添加)
需要完整的网状链路,除非使用路由反射器
比 IGP 的收敛速度慢
学习曲线较长
Bird 需要为 IPv4 和 IPv6 分别建立会话
尚不支持身份验证(这似乎是一个 WIP)
Babel 比较新,因此对硬件路由器和网络操作系统的支持可能有限

译者的话

本文档基于 James 的原创文章翻译整理,结合 DN42 技术社区的实践需求,系统梳理了多节点环境下 iBGP 与 IGP 协同配置的核心逻辑。原文通过对比纯 BGP 全互联、OSPF 分层路由及 Babel 简易协议的优劣,为不同规模网络提供了可落地的架构选型思路。翻译过程中,我们保留了 Bird 2 路由器的配置细节与拓扑示例,同时针对中文读者补充了 Linux 接口配置的注释说明。

记住:DN42 是一个测试网络,所有人都在帮助所有人。即使你不小心搞砸了,也没有人会指责你。你可以在 DN42 的 IRC 频道邮件列表或者非官方 Telegram 群组寻求帮助。

最后,本文档的配置示例以 IPv6 为主,契合 DN42 社区对下一代协议的倡导。期待通过这份译文,能为国内技术爱好者搭建更健壮的虚拟骨干网提供参考,也欢迎读者在评论区分享你的多节点部署经验。

延伸阅读

  1. AS4242420263 configuration - Multi-{pop,homing}
  2. 如何引爆 DN42 网络
  3. 看懂 IPv4+IPv6 的子网掩码和子网划分
  4. DN42 实验网络介绍及注册教程
  5. BIRD 与 BGP 的新手开场

分享这篇文章:

上一篇
RIPE Atlas 不完全入门指南
下一篇
记一次 QQ 邮箱「他域互通」折腾日记

人机验证:请刷新页面以加载评论区