OSPF作为链路状态协议,除了维护域内路由,还需要处理从其他协议引入的外部路由。当ASBR把外部路由重分发进OSPF时,每条路由都会附带一个外部度量类型:Type 1(E1)或Type 2(E2)。两者选路逻辑差别很大,理解这一点对排查次优路径问题非常关键。与其死记结论,不如用Ruby写一个简化模型,亲手算一遍开销,概念会清晰得多。

Type 1与Type 2的核心差异
Type 1外部路由的代价等于外部开销 + 从本路由器到ASBR的内部开销之和。也就是说,路由器计算E1路由时,必须先用SPF算出自己到ASBR的成本,再叠加外部度量值。这种方式适合内部链路开销差异较大的场景,比如出口带宽和内网带宽接近时,去往ASBR的路径长短会显著影响总代价。
Type 2外部路由则只看外部度量值,内部到ASBR的开销被完全忽略。无论路由器离ASBR有多远,只要外部度量相同,这些路由就被视为等价。只有当E2的外部度量完全相同时,OSPF才会退而比较内部路径开销。E2是OSPF重分发的默认类型,适合内网带宽远大于出口带宽、内部路径开销无关紧要的环境。
此外要记住一条排序规则:任何E1路由的优先级都高于E2路由。也就是说,即使E2的外部度量是1,E1的外部度量是100,路由器也会优先选择E1路由,两种类型不会直接比较数值大小。
用Ruby建模路由开销计算
下面用Ruby定义一个简单的模型。网络抽象成若干节点和带权边,ASBR节点负责注入外部路由,每条外部路由记录类型和外部度量。计算总开销时,E1需要先求最短路径(这里用Dijkstra算法简化实现),E2则直接返回外部度量。
# 表示OSPF区域的简单拓扑
class OspfArea
attr_reader :asbrs
def initialize
@links = Hash.new { |h, k| h[k] = {} }
@asbrs = {}
end
def add_link(node_a, node_b, cost)
@links[node_a][node_b] = cost
@links[node_b][node_a] = cost
end
# 将某节点标记为ASBR并注入外部路由
def inject_route(node, prefix, type:, metric:)
@asbrs[node] ||= []
@asbrs[node] << { prefix: prefix, type: type, metric: metric }
end
def links
@links
end
end
area = OspfArea.new
area.add_link("R1", "R2", 10)
area.add_link("R2", "R3", 20)
area.add_link("R1", "R3", 45)
# R3是ASBR,注入一条E1和一条E2路由
area.inject_route("R3", "10.0.0.0/8", type: :e1, metric: 5)
area.inject_route("R3", "192.168.0.0/16", type: :e2, metric: 5)
上面定义了三台路由器:R1到R3有直连链路开销45,也可以经R2中转,总开销30。R3作为ASBR同时注入了外部度量相同的E1和E2路由各一条。接下来实现Dijkstra计算内部最短路径,并根据度量类型求最终开销。
require "set"
def dijkstra(links, source)
dist = { source => 0 }
visited = Set.new
queue = [[0, source]]
until queue.empty?
d, node = queue.min_by { |cost, _| cost }
queue.delete_at(queue.index([d, node]))
next if visited.include?(node)
visited << node
links[node].each do |neighbor, cost|
new_dist = d + cost
if new_dist < dist.fetch(neighbor, Float::INFINITY)
dist[neighbor] = new_dist
queue << [new_dist, neighbor]
end
end
end
dist
end
# 计算从某路由器看一条外部路由的总开销
def total_cost(area, from, route_entry)
internal = dijkstra(area.links, from)
asbr_cost = internal[route_entry[:asbr]]
case route_entry[:type]
when :e1 then asbr_cost + route_entry[:metric] # 内部开销 + 外部开销
when :e2 then route_entry[:metric] # 只取外部开销
end
end
# 从R1视角计算两条路由
routes = [
{ prefix: "10.0.0.0/8", type: :e1, metric: 5, asbr: "R3" },
{ prefix: "192.168.0.0/16", type: :e2, metric: 5, asbr: "R3" }
]
routes.each do |r|
puts "#{r[:prefix]} (#{r[:type].upcase}) total cost = #{total_cost(area, "R1", r)}"
end
# 输出:
# 10.0.0.0/8 (E1) total cost = 35
# 192.168.0.0/16 (E2) total cost = 5
结果很直观:E1路由的总开销是30(R1到R3的内部最短路径)加上外部度量5,得到35;E2路由无论内部路径多长,总开销始终是外部度量5。如果R1和R3之间还有另一条开销40的路径,E1会明显感知差异而E2毫无反应,这正是两类路由在多出口场景下表现不同的根源。
路由比较与选路优先级的实现
有了总开销还不够,真实路由器在选路时要遵循完整的比较顺序。用Ruby实现一个compare_routes方法,模拟OSPF的决策逻辑:先比较度量类型,E1永远优于E2;同为E2且外部度量相等时,再比较到ASBR的内部开销。
def compare_routes(links, from, a, b)
internal = dijkstra(links, from)
# 第一优先级:E1 优于 E2
type_rank = { e1: 0, e2: 1 }
return a if type_rank[a[:type]] != type_rank[b[:type]] &&
type_rank[a[:type]] < type_rank[b[:type]]
return b if type_rank[a[:type]] > type_rank[b[:type]]
if a[:type] == :e1
# E1直接比较总开销
ca = internal[a[:asbr]] + a[:metric]
cb = internal[b[:asbr]] + b[:metric]
return ca <= cb ? a : b
else
# E2先比外部度量,相同再比内部开销
return a if a[:metric] != b[:metric]
ia = internal[a[:asbr]]
ib = internal[b[:asbr]]
return ia <= ib ? a : b
end
end
winner = compare_routes(area.links, "R1",
{ prefix: "10.0.0.0/8", type: :e1, metric: 100, asbr: "R3" },
{ prefix: "10.0.0.0/8", type: :e2, metric: 1, asbr: "R3" })
puts "选中:#{winner[:type].upcase} 路由"
# 输出:选中:E1 路由(即使E1度量100远大于E2的1)
这段代码验证了前文提到的关键结论:度量类型的比较发生在数值比较之前。很多初学者以为开销小的路由一定被选中,实际上E1与E2之间根本不做数值比较,类型本身就是第一道门槛。这个细节在双出口互备的网络中尤其重要——如果两个ASBR分别注入E1和E2路由,E2那条可能永远不会被选中,排查时需要格外留意。
适用场景与实践建议
什么时候该用E1?当存在多个ASBR且内部路径开销不可忽略时,E1能让路由器选出真实代价最低的完整路径。典型例子是双出口企业网,两个出口分别连接不同的运营商,内部链路质量差异明显,此时对外部目的地址使用E1可以避免流量绕行劣质内网路径。
E2适合什么情况?当内网带宽充足、所有到ASBR的路径代价差异对业务无感知时,E2简化了计算且行为稳定。比如典型的企业出口场景,内网全是千兆万兆链路,出口只有百兆,内部路径开销加不加意义不大,默认的E2反而是更合理的选择。
在真实设备上配置时,Cisco的redistribute subnets metric-type 1、华为的import-route metric-type 1等命令都可以指定类型。建议先用本文的Ruby模型做推演,再到实验室环境抓取LSA Type 5验证:show ip ospf database external输出的Forward Address和Metric字段,正好对应模型中asbr与metric两个属性。代码与协议对照着看,理解会深刻许多。