今天遇到一个有意思的问题:
Q:Linux系统在内核初始化完成后,默认创建了多少个路由表?
这个问题在技术社区中存在不少争议,无论是询问AI、Google搜索还是查阅博客,都能得到各种不同的答案。
问题的核心争议点在于:
内核如何管理主机路由(local路由)对应的本地路由表。
下面让我们逐步分析不同的观点:
step 1:咨询deepseek,系统创建了 4 个路由表
DeepSeek不仅给出了结论,还详细说明了分析过程,并提供了验证依据。
同时提供了验证方法。
1
| cat /etc/iproute2/rt_tables
|
step 2. 手动测试发现,系统只创建了 local/main两个路由表
基于Deepseek的答案进行进一步验证,通过IP命令检查指定路由表可发现:
- 实际仅存在local(255)和main(254)两个路由表
- unspec(253)表实际不存在,该ID仅为保留编号
- 表0是IPv4和IPv6所有路由的汇总视图,并非独立路由表
最终结论:系统仅创建了local和main两个路由表
step 3. 分析内核源代码
这两种观点都很有道理,论据充分。真相最终还是要看代码实现,让我们深入内核源码一探究竟。
以路由查找函数fib_lookup为例,内核实际上维护了两套不同的函数实现。
具体采用哪套方案,取决于编译时是否启用了CONFIG_IP_MULTIPLE_TABLES选项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 292 #ifndef CONFIG_IP_MULTIPLE_TABLES ... 316 static inline int fib_lookup(struct net *net, const struct flowi4 *flp, 317 struct fib_result *res, unsigned int flags) 318 { ... 364 #else ... 371 int __fib_lookup(struct net *net, struct flowi4 *flp, 372 struct fib_result *res, unsigned int flags); 373 374 static inline int fib_lookup(struct net *net, struct flowi4 *flp, 375 struct fib_result *res, unsigned int flags) 376 {
|
不支持多路由表
#ifndef CONFIG_IP_MULTIPLE_TABLES
代码分析显示,内核路由查找函数fib_llookup的实现取决于编译选项CONFIG_IP_MULTIPLE_TABLES。当该选项未启用时,内核仅查询RT_TABLE_MAIN路由表。
由此可以确定:在此配置下,系统仅维护一个主路由main表。
初始化
1 2 3 4 5
| ==> static int __net_init fib_net_init(struct net *net) ==> ==> ip_fib_net_init(net); ==> ==> ==> fib4_rules_init(net); ==> ==> ==> ==> main_table = fib_trie_table(RT_TABLE_MAIN, NULL); ==> ==> ==> ==> local_table = fib_trie_table(RT_TABLE_LOCAL, main_table);
|
这里重点关注函数fib4_rules_init。
从代码调用看,确实创建了两个路由表:local和main。
但在创建main表时候,我们使用的一个alias的参数,并把main表当做参数传递过去的。
1 2 3 4 5 6 7 8 9 10
| 50 #ifndef CONFIG_IP_MULTIPLE_TABLES 51 52 static int __net_init fib4_rules_init(struct net *net) 53 { 54 struct fib_table *local_table, *main_table; 55 56 main_table = fib_trie_table(RT_TABLE_MAIN, NULL); 57 if (!main_table) 58 return -ENOMEM; 59
|
路由表初始化细节
在fib4_rules_init函数中,我们可以看到路由表的创建过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static int __net_init fib4_rules_init(struct net *net) { struct fib_table *local_table, *main_table;
main_table = fib_trie_table(RT_TABLE_MAIN, NULL); if (!main_table) return -ENOMEM;
local_table = fib_trie_table(RT_TABLE_LOCAL, main_table); if (!local_table) { fib_trie_table_free(main_table); return -ENOMEM; }
rcu_assign_pointer(net->ipv4.fib_main, main_table); rcu_assign_pointer(net->ipv4.fib_local, local_table); return 0; }
|
关键发现
- 两个独立的路由表:系统确实创建了local(255)和main(254)两个路由表
- 表关系:local表基于main表创建,形成层级关系
- 表0:不是独立路由表,而是所有路由的汇总视图
- 表253:unspec表只是保留编号,实际不存在
路由表功能解析
local路由表 (RT_TABLE_LOCAL = 255)
- 管理主机本地路由(loopback、设备地址等)
- 优先级最高,用于本地通信
- 包含127.0.0.1/8、设备IP地址等
main路由表 (RT_TABLE_MAIN = 254)
- 管理常规路由规则
- 包含默认路由、静态路由等
- 优先级次于local表
验证方法
查看路由表配置
1
| cat /etc/iproute2/rt_tables
|
检查特定路由表
1 2 3 4 5 6 7 8
| ip route show table local
ip route show table main
ip route show table 0
|
验证表253不存在
1 2 3
| ip route show table 253
|
总结
通过深入分析内核源代码,我们得出了明确的结论:
Linux内核在初始化完成后,默认只创建了两个路由表:
- local表 (255):管理主机本地路由
- main表 (254):管理常规路由规则
其他表编号(如0、253)并非独立的路由表:
- 表0是所有路由的汇总视图
- 表253是保留编号,实际不存在
这个问题的争议源于对路由表编号和功能的误解。正确的理解是:内核通过local和main两个表的协作,实现了完整的路由功能。
技术要点
- 编译选项影响:
CONFIG_IP_MULTIPLE_TABLES选项决定了是否支持多路由表
- 表层级关系:local表基于main表创建,形成路由查找的优先级关系
- 实际验证:通过iproute2工具可以验证路由表的实际存在状态
这个问题的分析过程展示了内核源码分析的重要性,也提醒我们在技术讨论中要基于事实和代码验证,而不是依赖表面的数字统计。