fib_unmerge 是内核路由子系统中一个精巧的按需优化设计:
- 默认状态下,local 表只是 main 表的别名,所有路由合并在一张表里,
fib_lookup只需查一次表,性能最优 - 添加策略路由时,
fib_unmerge将 local 路由从 main 表中切割出来,放入独立的 local 表,确保策略路由能正确匹配 - 切割四步走:创建新 local 表 → 复制 local 路由 → 替换旧表 → 清理 main 表中的残留条目
- 幂等设计:只在首次添加策略路由时执行分离,后续再添加时
fib_trie_unmerge检测到已分离,直接返回
本文将从调用点入手,逐步分析 fib_unmerge 的实现细节。
背景
前面文章提到,local 路由默认情况下是插入到 main 表里的。
在 ping本机网口的IP地址,tcpdump在lo口才能抓到对应报文 中我们分析了 fib_lookup 函数:
1 | 374 static inline int fib_lookup(struct net *net, struct flowi4 *flp, |
没有自定义策略路由规则时(fib_has_custom_rules 为 false),fib_lookup 只查找 main 和 default 两张表,并没有查找 local 表。
这是因为在初始化时,local 表被创建为 main 表的别名:
1 | local_table = fib_trie_table(RT_TABLE_LOCAL, main_table); |
所有 local 路由实际上都被插入到了 main 表里。这样做的好处是减少一次路由表查找,提升性能。
那问题来了:当用户添加了自定义的策略路由规则后,路由查找走的是 __fib_lookup → fib_rules_lookup,会按策略路由顺序依次查找 local/main/default 三张表。如果 local 路由还在 main 表里,那 local 表就是空的,查找 local 表就没有意义了。
所以,只有在用户添加了自定义策略路由规则后,local 路由才需要从 main 表里分离出来,放到独立的 local 表里。
函数 fib_unmerge 就是做这个事情的。
调用点
fib_unmerge 的调用点有两个:
fib4_rule_configure:添加策略路由fib4_rule_delete:删除策略路由
这两个函数是 IPv4 策略路由模版 fib4_rules_ops_template 里的 configure 和 delete 函数定义。
1 | 470 static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = { |
以函数 fib4_rule_configure 为例:
1 | 269 static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, |
关键在第 300-303 行:fib_unmerge(net) —— 在配置新的策略路由规则时,先把 local 和 main 表分离开。
fib_unmerge 实现
1 | int fib_unmerge(struct net *net) |
fib_unmerge 的逻辑可以分为四步:
第一步:获取旧的 local 表
1 | old = fib_get_table(net, RT_TABLE_LOCAL); |
获取当前的 local 路由表。如果不存在,说明不需要分离,直接返回。
第二步:创建新的 local 表
1 | new = fib_trie_unmerge(old); |
fib_trie_unmerge 从旧的(合并在 main 表里的)local 表中,把 local 路由复制到一张新的独立 local 表里。
如果返回的 new 和 old 相同,说明已经分离过了(不是第一次添加策略路由),直接返回。
第三步:替换旧表
1 | fib_replace_table(net, old, new); |
用新创建的 local 表替换掉旧的(别名到 main 的)local 表,并释放旧表。
第四步:清理 main 表
1 | main_table = fib_get_table(net, RT_TABLE_MAIN); |
由于之前 local 路由都在 main 表里,现在已经复制到了独立的 local 表,需要把 main 表里残留的 local 路由条目清理掉。fib_table_flush_external 负责刷新 main 表中的外部引用。
流程总结
1 | 初始状态: |
与 fib_has_custom_rules 的关系
回顾 fib_lookup 的入口:
1 | if (net->ipv4.fib_has_custom_rules) |
当 fib_has_custom_rules 为 true 时,走 __fib_lookup 路径,会遍历策略路由链表,依次查找 local/main/default 三张表。此时 local 表已经通过 fib_unmerge 分离出来,包含了独立的 local 路由。
当 fib_has_custom_rules 为 false 时,直接查找 main 表(其中包含了 local 路由)和 default 表。此时不需要分离。
这两条路径保持了一致的路由查找语义,同时在没有自定义规则时通过合并表来优化性能。
相关文章:
这个设计体现了内核”按需分离”的思想——在不需要策略路由时保持简单高效,在需要时才承担额外的开销。
相关文章: