资讯动态

VPP FIB表项内部结构、写入路径与排查实践

发布时间:2026/10/27 4:25:03 来源:尧图企业网站定制
1. VPP FIB 表项到底是什么为什么值得单独拆开看刚接手 VPP 转发面的人十有八九会在show ip fib的输出里迷路过一个前缀下面挂着unicast-ip4-chain再往下是dpo-load-balance再往下是一串ipv4 via ...中间还夹着locks:[API:1, ]、flags:[drop]、fib_index:0这些看着眼熟但说不清用途的字段。等到线上真出问题比如路由明明配了却不通、表项数一夜之间涨了几十万、删路由提示成功但表里还在能救命的恰恰是对 VPP FIB 表项内部结构的那点理解。这篇就把这条表项从数据结构、字段含义、写入路径到排查手法完整拆一遍。适合两类人一类是刚开始用 VPP 做转发面、需要读懂show输出的新手另一类是已经在跑现网、需要定位表项异常和规模瓶颈的老手。文中涉及的命令和代码都可以直接拿去复现涉及参数的地方我会把估算过程写清楚而不是甩一个数字让你照抄。1.1 别把 FIB 当成一张哈希表很多人第一反应是转发表嘛不就是“前缀 → 下一跳”的映射用哈希表存一下查的时候最长前缀匹配就完事。这个思路在表项规模小、前缀长度固定的时候能跑但放到 VPP 的场景里立刻塌掉。原因有三个第一最长前缀匹配本身不是等值查询10.0.0.0/8、10.1.0.0/16、10.1.1.0/24三条表项存在覆盖关系哈希表要么退化成线性扫描要么得为每个前缀长度建一张表再逐级回退查一次包要打好几次表第二VPP 的设计目标是把一次查表压缩进几十个时钟周期任何“查不到就回退一层”的分支都是灾难第三真实网络里表项不是孤立的一条路由背后可能挂着一个邻接、一个多路径组、甚至一个递归解析链这些东西需要被共享、被引用计数、被增量更新扁平哈希表做不到。所以 VPP 的 FIB 是一套“对象图”。前缀是对象路径列表是对象单条路径是对象解析出来的转发动作DPO也是对象它们之间用索引互相引用用引用计数管理生命周期。你看到的show ip fib输出本质是把这张对象图按前缀维度重新排版打印出来。理解这一点非常关键因为它决定了后面所有的排查思路当你说“表项不对”的时候要先确认是前缀对象错了、路径对象错了、还是 DPO 对象错了。这三层的排查手段完全不同。1.2 四个必须分清的索引table_id、fib_index、node index、DPO index新手最容易踩的坑是把table_id和fib_index当成一个东西。table_id是你配置时写的那个路由表编号比如ip table add 100fib_index是 VPP 内部为这张表分配的连续索引从 0 开始按创建顺序递增。绝大多数情况下两者不相等除非你只建了表 0。表 0 的fib_index是 0表 100 的fib_index可能是 1也可能是 7取决于中间建过多少张表又删过多少张。这个区别带来的直接后果是数据平面查表用的是fib_index不是table_id。你在 API 里下发路由写table_id: 100框架内部会先把它翻译成fib_index再往 mtrie 里写。如果翻译这一步出了问题比如表还没建就下发路由或者表删了重建导致fib_index变了数据平面就会查到一张空表表现出来就是“路由配了但不通”而show命令按table_id查又能看到表项特别迷惑人。node index是 FIB 对象图里的通用节点编号fib_entry、fib_path_list、fib_path都挂在这套编号体系下类型由fn_type区分。DPO index则是另一个独立的编号空间专门给转发动作对象用。这三套编号互不相通输出里出现的[0]、[5]这种方括号数字就是 DPO index。看到两个不同的前缀都指向[5]说明它们共享同一个转发动作这是正常且被鼓励的不是 bug。1.3 一条表项从配置到转发要经过几道手完整链路大致是这样你通过 CLI 或 API 下发一条路由框架先根据table_id找到或创建对应的 FIB 表再为这个前缀创建或复用一个fib_entry对象。接着来源信息被记录成一份fib_entry_src挂到这个前缀的源列表里同时根据你给的下一跳组合去全局路径列表池里查有没有现成的组合有就复用没有就新建一个fib_path_list里面每条fib_path记录一个下一跳。路径确定后进入解析阶段如果下一跳是直连的就会去找对应的邻接adjacency找到后生成一个终结型 DPO如果下一跳需要再查一次路由递归场景就会往下递归生成一个带协议的聚合 DPO。DPO 确定后表项被“编码”成一个 32 位整数写进 mtrie 对应前缀位置的桶里。到这一步数据平面才真正能查到它。这条链路上任何一环卡住表项都会停在中间状态。比如邻接还没建立DPO 会是dpo-adjacency-incomplete表项本身是存在的show也能看到但包过来直接丢。这就是后面排查章节要反复用到的判断依据。2. 一条 FIB 表项的内部结构逐个字段拆解要把排查做扎实光会看输出不够得知道每个字段是从哪来的。这一节按“键 → 来源 → 路径 → DPO”的顺序拆配合真实的字段名方便你回头翻代码或者对 API 结构。2.1 键的三件套前缀地址、前缀长度、表索引fib_entry的键类型叫fib_entry_key_t里面就三样东西前缀地址IPv4 是 32 位IPv6 是 128 位、前缀长度、所属表的fib_index。三者组合才能唯一确定一条表项。这意味着10.0.0.0/8和10.0.0.0/16是两条完全独立的表项跟“同一个地址”没有任何关系前缀长度是键的一部分不是附属属性。这里有个细节值得留意默认路由0.0.0.0/0的长度是 0它在 mtrie 里落在根节点上是所有前缀的最终兜底。很多人排查“为什么流量走了默认路由”时会去翻具体前缀其实只要看根节点的桶里有没有 DPO 就知道了。另外长度 0 和长度 32主机路由是两个极端前者最容易被忽略后者最容易被误建。相邻前缀的覆盖关系全部由长度决定跟配置顺序无关——后配的短前缀不会“盖掉”先配的长前缀数据平面永远选最长匹配的那条。顺便说一句IPv4 和 IPv6 用的是两套独立的 FIB 表结构和两套独立的 mtrietable_id相同的 IPv4 表和 IPv6 表在内部是两个不同的对象。排查双栈问题时一定要确认你查的是哪个协议族命令里的ip和ip6不能混。2.2 一个前缀挂多个来源best source 是怎么选出来的一条表项可以同时被多个来源“持有”。典型场景接口上配了地址框架自动生成直连前缀来源interface你又手工加了一条同前缀的静态路由来源API或CLI如果跑着动态路由协议还可能有一条协议学来的来源protocol。这三份来源不会互相覆盖而是作为三个fib_entry_src同时挂在表项的源列表里每份都带自己的路径列表。真正决定转发行为的是“最佳来源”best source。框架按来源类型的优先级排一遍优先级高的那份路径列表胜出它的 DPO 被装进表项。输出里的locks:[API:1, ]就是在告诉你当前有哪些来源持有这条表项、各持有了几份引用。如果某份来源被撤销框架会把它从列表里摘掉然后重新选一次最佳来源——这个动作叫重新解析re-resolve是 FIB 里最频繁也最容易出问题的操作之一。来源优先级的顺序由枚举定义序号靠前的优先。实际用的时候我不建议你死记这个顺序因为它在不同版本之间调整过。更靠谱的做法是show输出里会直接打出当前生效的来源名你看到什么就是什么如果怀疑被覆盖把可疑来源的那条路由删掉再show一次看 DPO 有没有变比记顺序可靠得多。注意删掉高优先级来源后表项不一定消失可能只是“降级”到次优来源。如果你删路由的目的是让流量断掉务必确认降级后没有别的来源在兜底。2.3 路径列表与 fib_path控制平面的“路”怎么变成数据平面的“动作”fib_path_list的存在是为了共享。假设你有 500 条路由下一跳都是“出接口 A 网关 B”那么这 500 条表项会指向同一个路径列表对象而不是各存一份。这个设计在表项规模大、下一跳重复度高的场景下能省下非常可观的内存也是 VPP 在百万级表项下还能保持可控占用的关键。每条fib_path记录的信息包括出接口索引、下一跳地址、路径类型普通、直连、本地、丢弃、递归、权重、优先级。权重和优先级是多路径场景的核心——权重决定流量分配比例优先级决定哪一组路径先被选中。很多人配了多路径但流量全走一条八成是优先级没给对或者权重给了但路径列表被判定为“不等价”而退化成了单路径。路径类型里最容易搞混的是“直连”和“递归”。直连路径的下一跳在同一个二层域内可以直接解析到邻接递归路径的下一跳需要再查一次路由表才能确定出接口。递归层数在 VPP 里是有上限的超过上限的递归会被判定为不可解析表项降级成丢弃。这个上限不是拍脑袋定的因为每多一层递归就多一次查表转发性能会线性下降而且递归环路会导致解析永远不收敛。2.4 DPO真正干活的转发动作对象路径解析完之后得到的是 DPOData Plane Object。常见类型有这么几种dpo-drop丢弃、dpo-receive上送本机、dpo-ip-null空接口静默丢、dpo-load-balance多路径组、dpo-adjacency邻接已完成重写信息、dpo-adjacency-incomplete邻接未完成通常是 ARP 或邻居发现没解析、dpo-punt上送控制面。show输出里那个[N]就是 DPO 的索引。判断表项健不健康最快的方法就是看它的 DPO 类型。看到dpo-adjacency且带完整的重写信息说明这条路是通的看到dpo-adjacency-incomplete说明二层解析还没完成包过来会丢看到dpo-drop或者dpo-ip-null说明这是一条黑洞路由配的时候可能就是这个意图也可能是某个来源覆盖导致的意外得结合上面说的 best source 一起看。dpo-load-balance是个容器它自己不做转发而是把包分给某个桶bucket每个桶里又是一个 DPO。输出里的buckets:N就是桶的数量。桶的数量和路径的权重是关联的权重 1:2 的两条路径通常会展开成 3 个桶或者按更大公约数展开成更多桶以获得更细的分配粒度。桶多了分配更均匀但内存和构建时间也会涨这是个需要权衡的参数。2.5 表项编码那个 32 位整数到底是什么mtrie 的叶子桶里存的不是指针而是一个 32 位整数由表项编码函数生成。这个整数里塞了表索引、前缀长度、前缀的一部分比特目的是让数据平面在命中前缀之后直接拿这个值去索引后续结构不用再回查一层映射表。这是一次典型的“用编码换访存”的取舍多花一点编码逻辑省掉一次内存访问。具体位域怎么分各版本之间有过调整我不建议你在业务代码里硬解这个整数。真要做关联分析老老实实用show输出里的前缀文本或者走 API 拿结构化的前缀字段。硬解编码的代码在升级之后会静默出错而且错得很隐蔽——值还能用只是指向了别的表项排查起来非常痛苦。还有一个相关问题mtrie 的桶里除了当前前缀的 DPO还可能有“指向父节点”的链接。当一条更长前缀没有匹配时查表会沿着这个链接回退到更短的前缀直到找到有 DPO 的节点。这就是覆盖关系的实现方式。它意味着删除一条前缀时不只是把桶清空还要处理指向它的那些子节点的回退行为这也是删除操作比添加操作更容易出问题的原因。3. 实操把 FIB 表项信息真正读出来理论说完了进实操。这一节给的是能直接粘贴执行的命令和代码场景我按“先看全局、再看单条、最后批量拉”的顺序排跟实际排查的节奏一致。3.1 CLI 的几组常用入口最基础的是看整张表vppctl show ip fib输出开头会有一行表头类似ipv4-VRF:0, fib_index:0, flow hash:[src dst sport dport proto ] epoch:0 flags:none locks:[]。fib_index前面说过是内部索引flow hash是这张表做多路径分流时用的哈希字段组合改了它会影响同一条流的走向epoch是个自增计数器每次表项集合发生变化它就加一适合做增量同步。看指定表vppctl show ip fib table 100看单条表项这个命令在排查时最好用输出集中不含干扰vppctl show fib entry 10.1.0.0/24看 IPv6 就把ip换成ip6。另外show ip fib summary之类带 summary 的子命令在不同版本里支持程度不一样如果没有用show ip fib | grep -c之类的组合也能凑合统计。3.2 逐字段解读一段典型输出拿一段简化过的输出来说10.1.0.0/24 unicast-ip4-chain [0]: dpo-load-balance: [proto:ip4 index:1 buckets:2 uRPF:0 to:[0:2]] [0] [5]: ipv4 via 192.168.1.1 GigabitEthernet0/0/0: mtu:1500 [1] [6]: ipv4 via 192.168.1.2 GigabitEthernet0/0/1: mtu:1500unicast-ip4-chain表示这条表项走的是单播 IPv4 转发链是正常的。[0]是顶层 DPO 的索引类型是dpo-load-balance说明这是多路径。buckets:2表示两个桶to:[0:2]大致的含义是这个负载均衡组包含 2 个成员具体数字格式各版本略有差异看桶的数量更直接。下面两行分别是两个桶的内容[5]、[6]是桶内 DPO 的索引ipv4 via ...后面跟着下一跳地址、出接口、MTU。如果这里出现的是dpo-adjacency-incomplete那基本可以断定是邻接没解析。查一下 ARP 或者 IPv6 邻居表show ip arp看看对应网关有没有条目没有就手工加一条或者检查接口是不是 down 的。如果出现dpo-drop ip4说明这条前缀当前的最佳来源给出的动作是丢弃要么你配的就是黑洞路由要么有什么东西把它覆盖了。3.3 走 API 批量拉表项CLI 适合看单条表项上万条的时候就得走 API 了。Python 绑定的用法大概是from vpp_papi import VPPApiClient vpp VPPApiClient(apidir/usr/share/vpp/api/, apifiles[ip.api.json]) vpp.connect(fib-dump) entries [] for dump in vpp.ip_route_dump(table{table_id: 0, af: 0}): entries.append(dump) for e in entries[:5]: print(e.route.prefix.address, e.route.prefix.len, e.route.n_paths) for p in e.route.paths: print( nh:, p.nh.address, sw_if_index:, p.sw_if_index) vpp.disconnect()af字段 0 是 IPv41 是 IPv6。n_paths是路径数paths里每条带下一跳、出接口、权重、优先级。注意apidir和apifiles要指向实际安装路径不同打包方式的路径不一样如果连的是运行中的实例客户端会尝试读取运行时的 API 定义但指定文件更稳。不想折腾 API 又想要结构化数据还有个偷懒的办法走命令透传拿文本再解析。reply vpp.cli_inband(cmdshow ip fib).reply这个方法胜在兼容性极强任何版本都能用缺点是输出格式可能随版本变解析脚本要跟着维护。表项规模不大的时候够用。3.4 多路径、递归、覆盖关系的识别技巧多路径看dpo-load-balance和buckets这个前面说过。要验证流量分配是否符合预期别只看桶数量要看桶里各路径的权重。权重不写在桶里得回到路径列表那一层看API 里的paths[].weight更清楚。递归的识别方法是看路径的下一跳地址是不是不在直连网段内。如果下一跳需要再过一跳才能到框架会做递归解析输出里可能显示成一个中间 DPO 再指向最终 DPO。递归层级深的时候输出会拉得很长这时候用show fib entry看单条反而更清爽。要确认递归有没有收敛直接看最终 DPO 是不是 adjacency如果不是说明递归链上有一环断了。覆盖关系的识别靠看长度。同一个地址段下不同长度的表项在输出里是平铺的不会有缩进层级关系得自己按长度排序判断谁覆盖谁。有个实用技巧想看某个前缀实际生效的是哪条直接拿一个落在里面的具体地址去show fib entry 10.1.1.5/32如果这条针对于具体地址的表项不存在输出会告诉你没有表项而不是回退显示所以这个办法不太灵。更可靠的是构造一个真实流量或者在测试环境里用抓包确认实际走的出接口。4. 表项写入、覆盖与回收的实操细节会看了之后下一步是理解“为什么表项会变成这个样子”。这一节讲写入路径上的选择、优先级冲突导致的覆盖、以及删除时的引用计数问题这三块是实操中出问题最多的地方。4.1 加表项的两种路径参数怎么选CLI 和 API 是两条独立入口走的是不同的来源类型。CLI 上加静态路由大致是vppctl ip route add 10.1.0.0/24 via 192.168.1.1 GigabitEthernet0/0/0接口名能不能直接写取决于版本老版本可能要写sw_if_index用show interface查索引。API 上则用ip_route_add_del参数结构里最关键的是table_id、prefix、paths三块paths里每条要写sw_if_index、nh、weight、preference。选哪条路径取决于你的场景。CLI 适合临时验证和调试改完立刻能看到效果API 适合程序化下发尤其是需要批量同步或者和外部路由源对接的时候。但要注意CLI 和 API 下发的表项来源类型不同优先级也不同同一前缀两边都配会触发 best source 竞争最终生效的可能是你不想要的那条。生产环境我建议统一走 APICLI 只用于临时排查。还有个参数容易被忽略is_multipath。这个标志决定框架是把它当成一组等价路径还是只取第一条。配了多条路径但没开这个标志只有一条会生效输出里buckets:1看着像配错了其实只是标志没给。4.2 优先级冲突导致的覆盖怎么验证前面说过 best source 的机制实操里验证的办法就三招。第一招加之前先看。下发之前show fib entry看一眼当前状态记下 DPO 索引下发之后再show一次对比 DPO 有没有变。变了说明生效没变说明被更高优先级来源挡住了。第二招删掉验证。把怀疑的更高优先级来源删掉再show看 DPO 是否切到你要的那条。这个方法破坏性强只在测试环境用。第三招看locks。输出里的locks:[...]会列出当前持有表项的来源如果列表里出现了你没预期的来源名那就是它在捣鬼。比如你只配了静态路由但locks里有interface说明接口上自动生成的直连前缀和你配的前缀撞了。注意直连前缀attached是自动生成的接口配了地址、状态 up 之后就会出现。它和手工配的同前缀静态路由会同时存在谁生效取决于优先级。如果你配的静态路由“不生效”先怀疑是不是被直连前缀挡住了。4.3 删表项为什么删不掉“删了但还在”是最高频的疑问之一原因通常有三类。第一类是引用计数没归零。表项被别的东西持有比如还有一条路由的递归解析依赖它或者有个邻接对象挂在它上面。这时候删除请求会被记录成“标记删除”等引用归零才真正回收。输出里的flags可能会出现对应的状态标记locks里也能看到还有谁在持有。第二类是删除来源不对。用 API 加的路由用 CLI 删不掉因为来源类型不匹配删除操作只会去摘对应来源的那份摘不到就什么都不做。这种情况得用同一条路径删或者干脆把整张表清掉重建。第三类是表本身还在被引用。删表之前必须先把表里的表项清干净或者确认没有接口绑在这张表上否则表删不掉表项自然也就跟着留着。排查顺序我一般是先show fib entry看locks还有谁再查有没有接口或路由表绑着这张表最后才怀疑框架状态不对。绝大多数情况在前两步就解决了。4.4 百万级表项的内存账表项规模上来之后内存估算必须提前做。VPP 默认的堆大小通常不够撑百万级 IPv4 表项得在启动参数里调。粗略的估算方式如下对象单条大致开销说明fib_entry约 120 到 200 字节含键、标志、来源列表头fib_entry_src约 40 到 60 字节每个来源一份fib_path_list约 40 字节加共享相同下一跳组合可共享fib_path约 60 到 80 字节每条下一跳一份adjacency约 200 到 300 字节含二层重写信息DPO约 32 到 64 字节常与其他对象复用mtrie 节点随前缀分布变化每层固定槽位按单路径、无多路径复用的最坏情况估算100 万条 IPv4 表项大约需要 300 到 600 MB 堆空间具体落点取决于前缀分布前缀越分散mtrie 节点越多和邻接复用程度。IPv6 表项单条更大因为键是 128 位同样规模下占用通常是 IPv4 的两倍往上。这个数字的意义在于部署之前先按表项规模算一遍把堆大小留出 1.5 倍余量别等跑起来 OOM 再回头调那时候重启代价很大。5. 常见问题排查实录这一节按症状整理都是实打实遇到过的情况每条给出判断依据和处理方式最后附一张速查表。5.1 表项在、命令能查到、但流量不通这是最典型的一组症状判断顺序按下面走。先看 DPO 类型。dpo-adjacency-incomplete说明二层解析没完成查 ARP 或 IPv6 邻居表确认网关有没有学到。学不到的原因可能是接口 down、VLAN 配置不对、对端没响应逐个排。再看接口状态。show interface确认出接口是 up 的show hardware-interfaces看底层链路。接口 down 但表项还在是正常的FIB 不会因为接口 down 就删表项只会让邻接失效。再看表索引。确认你配的路由进的是数据平面查的那张表。方法是对比show ip fib表头里的fib_index和接口上绑定的fib_index两者不一致就是表配错了地方。这个坑在多 VRF 场景下特别常见。最后看 uRPF。如果开了反向路径检查源地址在 FIB 里查不到对应路径的包会被直接丢而且丢得很安静抓包只能看到单向流量。show输出里的uRPF字段可以辅助判断。5.2 表项显示不对疑似被覆盖先对比locks里的来源列表和你预期的是否一致。多出来的来源就是嫌疑人。然后看 DPO 是不是你想要的动作类型如果是dpo-drop但你配的是正常转发那肯定有东西覆盖了。处理方式是二选一把冲突来源删掉或者把你这条路由的来源优先级提到更高。前者直接后者需要确认框架支持你调整来源优先级通常 CLI 和 API 两条路都能给到比较高的优先级。还有一种隐蔽情况前缀写错了。10.1.0.0/24写成10.1.0.0/16输出里看着有表项但和你以为的不是同一条而且长前缀可能被短前缀覆盖。查的时候一定要把长度带上只看地址会误判。5.3 表项数量异常增长突然涨几十万条表项通常是三个原因之一。一是路由抖动导致的重复下发。外部路由源反复增删同一条路由每次删除没清干净新的又加上累积起来数量就爆了。判断方法是看locks里同一个来源的引用数是不是大于 1大于 1 就是重复持有。二是递归解析产生的中间表项。递归路由会在解析过程中生成辅助表项正常情况下解析完成后会被回收如果递归链断在半路这些中间表项会残留下来。判断方法是看这些表项的前缀长度和地址是不是都很奇怪、不成规律。三是邻接对象泄漏。每条新邻接都对应一份内存邻接不回收表项引用也就释放不掉。这个要看邻接表规模是不是也在同步涨。处理上短期的办法是清表重建把异常表项一次性清掉长期得找到重复下发的源头把下发逻辑里的幂等性补上——同一条路由重复下发应该是“更新”而不是“新增一份”。5.4 常见问题速查表症状优先检查常见原因处理方式表项在但 ping 不通DPO 类型邻接未完成、接口 down查 ARP、查接口状态路由配了查不到table_id 与 fib_index表未创建、表索引不匹配先建表再下发核对 fib_index表项被意外覆盖locks 列表直连前缀或更高优先级来源删冲突来源或调整优先级删路由提示成功但表项还在引用计数被其他对象持有找到持有者先释放多路径只走一条buckets 数量权重或优先级配置问题核对 paths 里的 weight表项数暴涨locks 引用数重复下发、递归残留清表重建补幂等逻辑递归路由不收敛最终 DPO 类型递归链断裂或有环检查下一跳可达性这张表我平时就贴在工位旁边遇到问题先对一遍能省掉大部分瞎猜的时间。表里的“优先检查”那一列是有顺序的别跳着看跳着看容易漏。6. 规模上去之后才暴露的几个坑前面讲的都是单条表项层面的东西这一段讲规模效应。表项从一万涨到一百万很多在实验室里完全正常的行为会突然变得很糟下面这几条都是踩出来的。6.1 mtrie 的槽位密度和前级分布的关系mtrie 每一层固定槽位数量前缀分布越稀疏中间节点越多内存占用越大查表深度也可能增加。同样是 100 万条表项全是 /24 的规整分布和随机长度的散乱分布占用的内存能差出一大截。这也意味着你在测试环境用小规模规整前缀压出来的内存数字不能直接线性外推到生产。判断方法是在接近真实的分布下测。如果拿不到真实前缀分布至少构造几组不同长度比例的集合分别测一遍取最差的那组做容量规划。另外前缀分布还会影响表项编码之后的桶填充率桶太稀疏意味着查表时要多走几层虽然不至于错但性能会掉。6.2 邻接和表项的耦合带来的连锁反应邻接对象不是孤立存在的它被表项引用。这意味着邻接的一次失效会连带影响一批表项。典型场景某个出接口的链路闪断这条链路上的所有邻接失效引用这些邻接的几千条表项同时降级成dpo-adjacency-incomplete如果这时候正好在批量下发路由两件事叠在一起就会出现“表项数剧烈波动 转发大面积中断”的复合故障比单一故障难定位得多。我的做法是在接口状态变化和路由批量下发之间加个限流别让两件事同时发生。具体实现方式看你的控制面架构核心思路就是给邻接重建留出时间窗口别让 FIB 在高频变更期间还要处理大量新表项。6.3 怎么盯住表项变化线上环境必须能感知表项变化不然出了问题只能事后翻日志。三个层次的做法按投入从小到大排。最轻的是定时采样。每分钟show ip fib统计一次条数画出曲线数量突变立刻能看出来。缺点是只能看总数看不出是哪些前缀变了而且采样间隔内发生的抖动会被漏掉。中间一档是走 API 的全量对比。定期拉一次全量表项和上一份做差异比对输出新增和删除的列表。这个方法能定位到具体前缀代价是每次全量拉取对控制面有压力表项大的时候要控制频率。最重的是订阅变化事件。VPP 有一些机制可以在表项变化时发出通知具体可用性取决于版本和编译选项我这里不展开因为这部分的接口在不同版本之间稳定性一般。如果要做严格实时的监控建议在控制面自己的下发逻辑里埋点而不是完全依赖数据面的通知。6.4 我自己的几条经验第一条任何改 FIB 的操作改之前先show fib entry存一份改完再存一份对比着看。养成这个习惯之后很多“莫名其妙变了”的问题会自己浮出水面不用再去猜。第二条别迷信“删除成功”的返回。删除接口返回成功只代表请求被接受不代表表项真的没了。凡是要确认删除效果的场景一律以show的结果为准。第三条表项规模大的环境show ip fib全量输出慎用。这个命令会遍历整张表并格式化打印百万级表项下执行一次可能卡住几秒甚至更久期间转发面虽然不受影响但控制面其他操作会被拖慢。要看单条就用show fib entry要看统计就用计数别为了看一眼总数把整张表打出来。第四条前缀长度一定要带上。我见过太多次因为省略长度导致的误判10.1.0.0和10.1.0.0/24在 FIB 里是完全不同的东西敲命令的时候手快漏掉一个斜杠排查方向就全偏了。这条听起来最基础但真到了着急的时候出错的往往就是最基础的地方。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑