第337篇:OSPF 与 BGP 路由重分发故障案例

关键词

OSPF、BGP、路由重分发、路由环路、次优路径、路由反馈、双点重分发、路由策略


一、案例背景

1.1 网络拓扑

某企业网络拓扑:

  ┌──────────────────────────────────────────┐
  │  核心层(OSPF Area 0)                    │
  │  ┌────────────────────────────────────┐  │
  │  │  Core-1 ───── OSPF ───── Core-2  │  │
  │  │     │                    │          │  │
  │  │  重分发               重分发        │  │
  │  │     │                    │          │  │
  │  │  Border-1 ──── BGP ─── Border-2 │  │
  │  │     │                    │          │  │
  │  │  [ISP-1]              [ISP-2]      │  │
  │  └────────────────────────────────────┘  │
  │                                           │
  │  BGP AS: 65001                           │
  │  OSPF Process: 1, Area 0                 │
  │                                           │
  │  需求:                                    │
  │  └─ 内部使用 OSPF 路由                    │
  │  └─ 外部通过 BGP 从 ISP 学习              │
  │  └─ BGP 路由重分发到 OSPF(给内部设备)   │
  │  └─ OSPF 默认路由重分发到 BGP(缺省路由) │
  └──────────────────────────────────────────┘

1.2 故障现象

故障现象:

  时间:周三上午 10:00
  影响:内部网络访问 Internet 延迟高、不稳定

  现象:
  ┌──────────────────────────────────────────┐
  │  ping 外网(114.114.114.114):           │
  │  └─ Core-1 上 ping:延迟 5ms(正常)    │
  │  └─ Core-2 上 ping:延迟 5ms(正常)    │
  │  └─ 内部接入交换机 ping:延迟 200ms+    │
  │  └─ 内部接入交换机 ping:间歇性超时      │
  │                                           │
  │  traceroute 外网:                         │
  │  └─ 内部接入 → Border-1 → ISP-1           │
  │  └─ 但 ping 的流量却走了:                 │
  │     内部接入 → Core-1 → Core-2 →         │
  │     Border-2 → ISP-2 → ISP-1             │
  │  └─ 走了 5 跳,多绕了 4 跳!              │
  │                                           │
  │  路由表检查:                              │
  │  └─ 内部接入交换机上:                     │
  │     0.0.0.0/0 via 10.0.1.254 (Core-1)    │
  │     但 BGP 路由从 Core-2 学到的           │
  │     实际上 10.0.1.254 不是最优路径        │
  └──────────────────────────────────────────┘

二、根因分析

2.1 配置回顾

问题配置:

  Border-1 的重分发配置:
  ┌──────────────────────────────────────────┐
  │  ospf 1                                   │
  │   import-route bgp                        │
  │   ↑ 将 BGP 路由全部重分发到 OSPF          │
  │                                           │
  │  bgp 65001                                │
  │   import-route ospf 1                     │
  │   ↑ 将 OSPF 路由全部重分发到 BGP          │
  └──────────────────────────────────────────┘

  Border-2 的重分发配置(完全一样):
  ┌──────────────────────────────────────────┐
  │  ospf 1                                   │
  │   import-route bgp                        │
  │                                           │
  │  bgp 65001                                │
  │   import-route ospf 1                     │
  └──────────────────────────────────────────┘

2.2 问题分析

路由反馈环路(Route Feedback Loop):

  步骤 1:初始状态
  ┌──────────────────────────────────────────┐
  │  ISP-1 发布 0.0.0.0/0 → Border-1 BGP   │
  │  ISP-2 发布 0.0.0.0/0 → Border-2 BGP   │
  │                                           │
  │  Border-1:                                │
  │  └─ BGP 路由:0.0.0.0/0 via ISP-1        │
  │  └─ OSPF 路由:0.0.0.0/0 via ISP-1      │
  │     (import-route bgp 注入到 OSPF)     │
  └──────────────────────────────────────────┘

  步骤 2:Border-2 收到 OSPF 默认路由
  ┌──────────────────────────────────────────┐
  │  Border-2 从 OSPF 学到:                  │
  │  └─ 0.0.0.0/0 via Border-1 (OSPF)       │
  │                                           │
  │  Border-2 的 BGP 从 ISP-2 也学到:         │
  │  └─ 0.0.0.0/0 via ISP-2 (BGP)            │
  │                                           │
  │  Border-2 将 OSPF 路由重分发到 BGP:      │
  │  └─ import-route ospf                    │
  │  └─ 0.0.0.0/0 via Border-1 注入到 BGP   │
  │  └─ 这就是"路由反馈"                      │
  └──────────────────────────────────────────┘

  步骤 3:Border-1 收到"被污染"的 BGP 路由
  ┌──────────────────────────────────────────┐
  │  Border-1 从 BGP 学到:                    │
  │  └─ 0.0.0.0/0 via ISP-1 (原始)          │
  │  └─ 0.0.0.0/0 via Border-2 (反馈)       │
  │                                           │
  │  比较:                                    │
  │  ┌─ ISP-1: AS_PATH = [100]               │
  │  ├─ Border-2: AS_PATH = [65001 100]      │
  │  └─ ISP-1 路径更短,优选 ISP-1           │
  │                                           │
  │  但问题是:内部设备看到多条默认路由!      │
  └──────────────────────────────────────────┘

  最终效果(次优路径):
  ┌──────────────────────────────────────────┐
  │  内部接入交换机看到 OSPF 默认路由:        │
  │  ┌─ 0.0.0.0/0 via Border-1 (Cost: 10)   │
  │  ├─ 0.0.0.0/0 via Border-2 (Cost: 8)    │
  │  └─ 选择 Cost 更小的 Border-2            │
  │                                           │
  │  流量路径:                                │
  │  └─ 接入交换机 → Core-2 → Border-2       │
  │  └─ Border-2 有两条默认路由:              │
  │     └─ 优选 BGP 路由(AD 20 vs OSPF 110)│
  │     └─ 通过 ISP-2 出网                    │
  │                                           │
  │  这不是次优路径,而是正确路径!            │
  │                                           │
  │  真正的问题在更复杂的场景:                 │
  │  当 ISP-1 的链路是 1G,ISP-2 是 100M      │
  │  但内部设备选择了 ISP-2 路径                │
  │  导致出口带宽受限,延迟增大                │
  └──────────────────────────────────────────┘

2.3 真正的问题

本案例的"真正故障"是路由环路风险:

潜在的路由环路场景:

当 ISP-1 链路断开时: 1. Border-1 的 BGP 默认路由消失 2. 但 Border-1 的 OSPF 默认路由还在 (从 Border-2 通过 OSPF 学到的) 3. Border-1 将 OSPF 默认路由再注入 BGP 4. Border-1 的 BGP 默认路由 via Border-2 5. Border-1 访问外网 → 发给 Border-2 6. Border-2 默认路由 via Border-1 (从 OSPF 学到的反馈路由) 7. Border-2 → 发给 Border-1 8. 形成路由环路! TTL 耗尽前,数据包在两个路由器间来回 导致大量丢包和高延迟


三、解决方案

3.1 正确的重分发配置

# correct_redistribution.py — 正确的路由重分发配置

def generate_correct_config():
    """生成正确的重分发配置 - 使用路由策略"""

    border_1_config = """
sysname Border-1

# ========== 使用 Route-Policy 控制重分发 ==========

# 1. BGP → OSPF:只重分发来自 ISP 的外部路由
#    不重分发从 OSPF 学到的路由(防反馈)
route-policy BGP_TO_OSPF permit node 10
 if-match route-type external           # 只匹配 EBGP 路由
 if-match as-path ^100$                 # 只匹配来自 ISP-1 的
 apply cost 10                           # 设置 OSPF Cost

route-policy BGP_TO_OSPF deny node 100  # 拒绝所有其他

# 2. OSPF → BGP:只重分发内部网段(非默认路由)
route-policy OSPF_TO_BGP permit node 10
 if-match ip-prefix INTERNAL_NET        # 只匹配内部网段

route-policy OSPF_TO_BGP deny node 100

# 3. 内部网段前缀列表
ip ip-prefix INTERNAL_NET permit 10.0.0.0 8 greater-equal 8 less-equal 32
ip ip-prefix INTERNAL_NET deny 0.0.0.0 0      # 拒绝默认路由

# ========== 应用 Route-Policy ==========

ospf 1
 import-route bgp route-policy BGP_TO_OSPF   # 控制 BGP→OSPF

bgp 65001
 import-route ospf 1 route-policy OSPF_TO_BGP  # 控制 OSPF→BGP
"""

    return border_1_config


def generate_tag_based_config():
    """使用 Tag 标记防止路由反馈"""
    config = """
sysname Border-1

# ========== 使用 Tag 防止环路 ==========

route-policy SET_TAG permit node 10
 apply tag 100           # 从 BGP 注入 OSPF 的路由打 Tag 100

route-policy FILTER_TAG deny node 10
 if-match tag 100        # 拒绝带回 Tag 100 的路由

route-policy FILTER_TAG permit node 20   # 允许其他所有

# ========== 应用 ==========

ospf 1
 import-route bgp route-policy SET_TAG    # BGP→OSPF 打 Tag 100

 import-route bgp route-policy FILTER_TAG  # 但是这样不行

# 正确的做法:在 OSPF → BGP 时过滤
bgp 65001
 import-route ospf 1 route-policy FILTER_TAG  # 拒绝 OSPF 中 Tag=100 的路由
"""
    return config

3.2 最佳实践方案

OSPF ↔ BGP 双点重分发最佳实践:

方案一:单向重分发 + 静态默认路由(推荐)

  1. BGP → OSPF:重分发默认路由 用 route-policy 严格控制 只重分发 0.0.0.0/0 不重分发其他 BGP 路由
  2. OSPF → BGP:不重分发 内部 OSPF 路由不需要进入 BGP BGP 只和 ISP 交换路由 防止路由反馈的根本方法
  3. 静态默认路由 + BFD 检测 如果 ISP 链路中断,BFD 检测到后 静态路由自动消失 不会产生黑洞

方案二:双向重分发 + 严格策略(如果必须)

  1. BGP → OSPF: └─ 使用 prefix-list 只放行 EBGP 路由 └─ 使用 as-path 过滤内部 AS 路由 └─ 设置管理距离(Distance)
  2. OSPF → BGP: └─ 使用 tag 标记 BGP 注入的路由 └─ OSPF→BGP 时过滤有 tag 的路由 └─ 只重分发内部接口路由(非 OSPF)
  3. 双点使用相同的策略 └─ Border-1 和 Border-2 配置一致

3.3 自动检测脚本

# detect_route_feedback.py — 路由反馈检测

from netmiko import ConnectHandler
import re


class RouteFeedbackDetector:
    """路由反馈检测器"""

    def __init__(self, device_info: dict):
        self.conn = ConnectHandler(**device_info)
        self.conn.enable()

    def check_dual_redistribution(self) -> dict:
        """检测是否存在双向重分发"""
        config = self.conn.send_command(
            "display current-configuration |"
            " section ospf"
        )
        bgp_to_ospf = "import-route bgp" in config

        config = self.conn.send_command(
            "display current-configuration |"
            " section bgp"
        )
        ospf_to_bgp = "import-route ospf" in config

        return {
            "bgp_to_ospf": bgp_to_ospf,
            "ospf_to_bgp": ospf_to_bgp,
            "dual_redistribution": bgp_to_ospf and ospf_to_bgp,
        }

    def check_route_feedback(self) -> list:
        """检测路由反馈"""
        # 检查 OSPF 路由表中是否有 BGP 来源的路由
        ospf_routes = self.conn.send_command(
            "display ospf routing"
        )

        # 检查 BGP 路由表中是否有 OSPF 来源的路由
        bgp_routes = self.conn.send_command(
            "display bgp routing-table"
        )

        issues = []

        # 关键:检查默认路由的来源
        default_bgp = self.conn.send_command(
            "display bgp routing-table 0.0.0.0/0"
        )
        default_ospf = self.conn.send_command(
            "display ospf routing 0.0.0.0"
        )

        # 检查默认路由在 BGP 中的 AS_PATH
        for line in default_bgp.splitlines():
            if "0.0.0.0/0" in line and "65001" in line:
                # 默认路由的 AS_PATH 中包含本 AS → 路由反馈!
                issues.append({
                    "type": "ROUTE_FEEDBACK",
                    "detail": "默认路由的 AS_PATH 中包含本 AS 号",
                    "severity": "CRITICAL",
                    "evidence": line.strip(),
                })

        self.conn.disconnect()
        return issues

    def fix_redistribution(self) -> str:
        """修复重分发配置 - 添加过滤策略"""
        config_changes = []

        # 添加 prefix-list 过滤
        config_changes.append(
            "ip ip-prefix BGP_TO_OSPF permit 0.0.0.0/0"
        )
        config_changes.append(
            "ip ip-prefix BGP_TO_OSPF deny 0.0.0.0 0 less-equal 32"
        )

        # 修改 OSPF 配置
        config_changes.extend([
            "ospf 1",
            "import-route bgp route-policy BGP_TO_OSPF_ONLY_DEFAULT",
        ])

        # 创建 route-policy
        route_policy = [
            "route-policy BGP_TO_OSPF_ONLY_DEFAULT permit node 10",
            " if-match ip-prefix BGP_TO_OSPF",
            " apply cost 10",
            "route-policy BGP_TO_OSPF_ONLY_DEFAULT deny node 100",
        ]
        config_changes.extend(route_policy)

        return "\n".join(config_changes)

    def close(self):
        self.conn.disconnect()

四、验证与监控

4.1 验证方法

修复后的验证:

  1. 确认 OSPF 路由表
  ┌──────────────────────────────────────────┐
  │  display ospf routing                    │
  │                                           │
  │  确认:                                    │
  │  └─ OSPF 只有一条默认路由(来自 Border-1)│
  │  └─ 没有来自 Border-2 的重复默认路由     │
  │  └─ 没有来自 BGP 重分发产生的内部路由    │
  └──────────────────────────────────────────┘

  2. 确认 BGP 路由表
  ┌──────────────────────────────────────────┐
  │  display bgp routing-table 0.0.0.0/0     │
  │                                           │
  │  确认:                                    │
  │  └─ 默认路由的 AS_PATH 不含本 AS 号      │
  │  └─ BGP 路由数没有异常增长               │
  └──────────────────────────────────────────┘

  3. 环路测试
  ┌──────────────────────────────────────────┐
  │  traceroute 外网 IP                       │
  │                                           │
  │  确认:                                    │
  │  └─ 每跳 TTL 正常递增                     │
  │  └─ 路径不超过 5 跳                       │
  │  └─ 没有出现来回跳的现象                   │
  │                                           │
  │  模拟 ISP 链路断开:                       │
  │  └─ 手动 shutdown 主 ISP 接口             │
  │  └─ 确认不会产生路由环路                   │
  │  └─ 确认正确切换到备用链路                  │
  └──────────────────────────────────────────┘

五、经验总结

OSPF + BGP 双点重分发关键经验:

  1. 避免双点双向重分发
  ┌─ 双点重分发 = 路由反馈风险
  ├─ 如果要双向重分发,必须用 route-policy 严格控制
  ├─ 使用 tag 标记 BGP 注入的路由
  └─ 在 OSPF→BGP 时过滤带 tag 的路由

  2. 推荐设计
  ┌─ BGP → OSPF:只注入默认路由
  ├─ OSPF → BGP:不重分发(或只重分内部接口路由)
  ├─ 内部路由通过 IGP 传播
  └─ 外部路由通过 BGP + 默认路由访问

  3. 路由策略检查清单
  ┌─ 是否配置了 route-policy 控制重分发?
  ├─ 是否使用了 prefix-list 限定路由范围?
  ├─ 是否使用了 tag 标记防反馈?
  ├─ 是否双点配置一致?
  └─ 是否验证了 ISP 链路断开场景?

  4. 监控建议
  ┌─ 监控 BGP 路由数量变化
  ├─ 监控 OSPF 重分发的外部路由数量
  ├─ 定期检查 AS_PATH 中是否出现本 AS
  └─ 定期做故障模拟测试

下篇预告:第338篇《IS-IS微环路分析与解决方案》——深入IS-IS微环路的产生原理、检测方法和防制方案。


下篇预告:第338篇《IS-IS微环路分析与解决方案》——深入IS-IS微环路的产生原理、检测方法和防制方案。