在中小型网络里,OSPF的区域划分往往由不同工程师分批完成,时间一长就容易出现同一个区域号在某些设备上被配成Stub,在另一些设备上却被配成NSSA。这种类型不匹配不会立刻断网,却会让相关邻居停留在ExStart或Exchange状态,路由计算异常。下面我们用Ruby写一套轻量工具,把配置一致性检查变成可重复执行的脚本。
一、Stub与NSSA的核心差异
OSPF的Stub区域通过阻塞五类外部LSA来简化末梢路由表,ABR会自动向区域内注入一条默认路由。NSSA(Not-So-Stubby Area)是Stub的扩展,它允许引入外部路由但以七类LSA表示,再由NSSA ABR转成五类LSA向外发布。从协议角度看,两者在区域参数里的标识位不同,邻居建立时会对区域类型做严格校验。
如果一台路由器在区域0.0.0.1下配置了stub,而邻居在同一区域下配置了nssa,双方hello报文中的E位和N位组合无法吻合,邻居关系就无法进入Full状态。实际排错时,很多人只盯着area编号是否一致,却忽略了类型关键字,这正是Ruby脚本要抓的重点。
二、用Ruby解析配置文本
我们假设每台路由器的配置已导出为纯文本,区域相关行大致形如area 0.0.0.1 stub或area 0.0.0.1 nssa no-summary。Ruby的正则表达式很适合做这种提取。下面的方法接收配置内容,返回区域号到类型的映射。
def parse_ospf_areas(config_text)
areas = {}
config_text.each_line do |line|
# 匹配 area 编号 后接 stub 或 nssa 及可选参数
if line =~ /^s*areas+(d+.d+.d+.d+)s+(stub|nssa)/
area_id = $1
type = $2
areas[area_id] = type
end
end
areas
end
sample = <<~CFG
router ospf 1
area 0.0.0.1 stub
area 0.0.0.2 nssa no-summary
CFG
puts parse_ospf_areas(sample).inspect
上面的代码逐行扫描,用捕获组拿到区域号和类型字符串。注意配置里可能带有no-summary之类的后缀,但类型本质仍是stub或nssa,所以正则只取第二个关键字即可。将多台设备的解析结果放进哈希数组,就能交叉比对。
这种文本解析方式不依赖厂商CLI的JSON输出,老设备也能用。缺点是若有人把area写成了缩写ar 0.0.0.1 stub,正则就需要微调。工程上建议先统一配置规范,再跑脚本。
三、一致性检测与冲突报告
把各设备的区域映射收集起来后,按区域号分组,检查同一区域号对应的类型是否全部相同。Ruby的group_by和uniq让这件事非常直白。
def find_mismatches(device_area_maps)
# device_area_maps: { device_name => { area_id => type } }
conflicts = {}
device_area_maps.each do |dev, areas|
areas.each do |aid, type|
conflicts[aid] ||= {}
conflicts[aid][dev] = type
end
end
conflicts.select do |aid, dev_map|
dev_map.values.uniq.length > 1
end
end
maps = {
'R1' => { '0.0.0.1' => 'stub', '0.0.0.2' => 'nssa' },
'R2' => { '0.0.0.1' => 'nssa', '0.0.0.2' => 'nssa' }
}
puts find_mismatches(maps).inspect
运行后可以看到0.0.0.1区域在R1是stub、在R2是nssa,被准确挑出。实际项目中可把冲突区域名和涉及设备打印成报表,或者直接抛异常阻断发布流程。
相比手动登录每台设备敲show run | section ospf,脚本能在持续集成里每次变更后自动跑,把人为疏忽挡在合并前。它的局限是只查静态配置,不验证运行时邻居状态,因此建议搭配SNMP或SSH实时校验做双层防护。
四、自动生成修复建议
确认冲突后,通常的做法是统一成Stub或统一成NSSA。若业务需要引入外部路由则选NSSA,否则选Stub更省资源。下面函数根据多数派决定目标类型,并生成命令行补丁。
def build_fix_commands(conflicts, device_area_maps, prefer = 'stub')
cmds = []
conflicts.each do |aid, dev_map|
# 统计现有类型,若无明显多数则按偏好
counts = dev_map.values.each_with_object(Hash.new(0)) { |v, h| h[v] += 1 }
target = counts.max_by { |k, v| v }&.first || prefer
dev_map.each do |dev, cur|
next if cur == target
cmds << "#{dev}: no area #{aid} #{cur}"
cmds << "#{dev}: area #{aid} #{target}"
end
end
cmds
end
puts build_fix_commands(find_mismatches(maps), maps).each { |c| puts c }
生成的指令先删掉错误类型再写入目标类型,避免同一区域下出现多条互斥声明。把字符串通过SSH库推送到设备即可完成修复。需要提醒的是,线上改动区域类型会让OSPF进程重置邻居,应选在维护窗口操作。
整体来看,Ruby凭借简洁的语法和强大的文本处理能力,很适合写这类网络配置审计小工具。它不取代协议知识,而是把知识固化成可执行的检查单,让Stub与NSSA的不匹配无处藏身。