RFC 9234 落地观察:BGP Roles 与 OTC 如何自动拦截路由泄漏

2026-08-18 37 预计阅读时间: 1 分钟
来源: blog.cloudflare.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

传统的 BGP 路由泄漏防护高度依赖人工维护的导入、导出策略。一旦运营商误配策略,原本只该发给客户的路由可能被传播给上游或对等网络。RFC 9234 引入 BGP Roles 和 Only to Customer(OTC)属性,让路由器能够根据邻居关系识别并拒绝部分泄漏,而不必等监控系统报警后再人工处置。

针对部署情况的测量表明,这套机制已经进入真实网络,但传播链条并不总是可靠:研究观察到两个 Tier 1 网络意外删除了 OTC。这个现象提醒我们,部署 RFC 9234 不只是打开一个邻居选项,还必须验证 OTC 是否端到端保留。

BGP Roles 把商业关系变成协议输入

BGP 的传统配置知道邻居地址和 ASN,却不天然理解双方是客户、提供商还是对等网络。运营人员通常用路由策略表达这些关系,但策略错误很难从会话本身发现。

RFC 9234 定义了五种角色:

本端 BGP Role 对端通常扮演的角色 关系含义
Provider Customer 本端向客户提供传输能力
Customer Provider 本端购买上游传输能力
Peer Peer 双方进行对等互联
RS RS-Client 本端是路由服务器
RS-Client RS 本端连接路由服务器

角色通过 BGP 能力协商,使双方能够检查关系是否匹配。例如,一端声明自己是 Provider,另一端也声明 Provider,通常意味着配置与实际商业关系不一致。支持严格模式的实现可以因此拒绝建立会话,避免错误策略开始传播路由。

角色并不能替代 prefix-list、RPKI Origin Validation 或 AS_PATH 过滤。它解决的是另一个问题:把“这条会话在拓扑中的方向”交给协议,让设备有条件执行统一的防泄漏规则。

OTC 如何给路由加上“只能向客户传播”的约束

OTC 是一个可传递的 BGP 路径属性。它表达的核心约束是:这条路由已经进入只能继续向客户传播的阶段,不能再被发送给提供商或对等网络。

可以把正常传播路径理解成一个“先上坡、可横向、再下坡”的谷形模型:

Customer -> Provider -> Peer -> Customer
             上坡        横向       下坡

一条从提供商或对等网络学到的路由,通常只能向客户方向继续传播。如果客户错误地把带 OTC 的路由重新通告给另一个上游或对等网络,支持 RFC 9234 的接收方就能识别这种异常,并按实现与策略拒绝该更新。

这里有两个重要边界:

  1. OTC 不是全局真实性证明。 它依赖参与网络正确设置 BGP Role,并按规范生成、保留和检查属性。
  2. 链路中途删除 OTC 会破坏后续判断。 下游路由器看到的将是一条缺少约束标记的普通路由,即使它本来已经跨过不应再次“上坡”的边界。

因此,两个 Tier 1 网络意外剥离 OTC 的发现尤其值得关注。Tier 1 位于大量路径的中间位置,属性被删除后,影响可能延伸到许多并不直接相邻的网络。这不必然说明存在恶意行为,更可能是实现版本、策略模板或未知属性处理方式造成的互操作问题,但结果同样需要排查。

可以这样实践:检测 OTC 是否在传播途中消失

实际环境可以从 BMP、实验室 BGP 邻居、路由器遥测或受控前缀探测中采集更新,再把观测结果归一化。下面这个可直接运行的 Python 脚本模拟两条路径:一条正常保留 OTC,另一条在中间网络之后丢失 OTC。

将以下内容保存为 check_otc.py,然后执行 python3 check_otc.py

#!/usr/bin/env python3
from collections import defaultdict

# 实际使用时,可将这里替换为 BMP、路由器 JSON 输出或实验探针数据。
# 同一 probe 的 before/after 分别表示进入和离开被测网络时的观测。
observations = [
    {
        'probe': 'tier1-a',
        'prefix': '192.0.2.0/24',
        'stage': 'before',
        'as_path': [64510, 64520],
        'otc': 64510,
    },
    {
        'probe': 'tier1-a',
        'prefix': '192.0.2.0/24',
        'stage': 'after',
        'as_path': [64530, 64510, 64520],
        'otc': None,
    },
    {
        'probe': 'transit-b',
        'prefix': '198.51.100.0/24',
        'stage': 'before',
        'as_path': [64540, 64550],
        'otc': 64540,
    },
    {
        'probe': 'transit-b',
        'prefix': '198.51.100.0/24',
        'stage': 'after',
        'as_path': [64560, 64540, 64550],
        'otc': 64540,
    },
]

groups = defaultdict(dict)
for item in observations:
    key = (item['probe'], item['prefix'])
    groups[key][item['stage']] = item

for (probe, prefix), stages in sorted(groups.items()):
    before = stages.get('before')
    after = stages.get('after')

    if not before or not after:
        print(f'INCOMPLETE probe={probe} prefix={prefix}')
        continue

    if before['otc'] is not None and after['otc'] is None:
        print(
            f'STRIPPED probe={probe} prefix={prefix} '
            f'expected_otc={before["otc"]} after_path={after["as_path"]}'
        )
    elif before['otc'] != after['otc']:
        print(
            f'CHANGED probe={probe} prefix={prefix} '
            f'before={before["otc"]} after={after["otc"]}'
        )
    else:
        print(f'PRESERVED probe={probe} prefix={prefix} otc={after["otc"]}')

预期输出为:

PRESERVED probe=transit-b prefix=198.51.100.0/24 otc=64540
STRIPPED probe=tier1-a prefix=192.0.2.0/24 expected_otc=64510 after_path=[64530, 64510, 64520]

这只是最小检测器。接入生产数据时,还需要加入以下约束,避免把观测差异误判成属性剥离:

  • 确认前后观测的是同一个前缀、同一轮探测和可比较的 AS_PATH。
  • 排除路由切换导致的路径变化;最好使用受控前缀和明确的时间窗口。
  • 区分“设备没有生成 OTC”与“设备收到后删除 OTC”。只有同时看到进入和离开观测,才能较有把握地判断剥离发生在哪一段。
  • 检查采集系统是否完整保留未知或可选可传递属性,防止数据管道本身删除 OTC。

上线时不要只检查会话是否建立

部署 RFC 9234 可以从少量明确关系的 eBGP 会话开始,而不是一次修改全部边界路由器。一个稳妥的上线清单包括:

  • 盘点每个外部邻居的商业关系,并为 Provider、Customer、Peer、RS 和 RS-Client 建立单一事实来源。
  • 检查路由器操作系统版本是否支持 BGP Roles、OTC 生成、OTC 保留和基于角色的拒绝逻辑。
  • 先在监控或告警模式观察角色不匹配,再评估是否启用严格拒绝,避免历史配置错误造成大面积会话中断。
  • 用受控测试前缀验证 OTC 的生成、传播和拒绝行为,而不是只检查配置文本。
  • 对升级、路由反射、路由服务器以及跨厂商边界分别做回归测试。
  • 保留现有的导入导出策略、RPKI 验证和前缀过滤;RFC 9234 是附加防线,不是替代品。

BGP Roles 让设备理解邻居关系,OTC 则把传播约束带到后续网络。二者结合后,部分路由泄漏可以在控制平面内直接被拒绝。不过,任何依赖可传递属性的安全机制都要面对同一个现实:路径中最不兼容的一跳,可能决定整条链路的保护效果。部署完成后持续测量 OTC 是否被保留,与启用功能本身同样重要。


相关推荐