文章目录
-
- Linux 路由子系统深度分析:框架、实现与代码路径
-
- 一、路由子系统核心框架
- 二、 路由子系统核心框架与组件
- 三、 路由查找流程详解(以 IPv4 接收转发为例)
- 四、核心组件实现机制
-
- 1. 路由策略数据库 (RPDB)
- 2. 转发信息库 (FIB)
- 3. 目的地缓存 (dst_entry)
- 4. 邻居子系统集成
- 五、核心路径代码分析
-
- 1. 输入路径路由 (ip_route_input)
- 2. 输出路径路由 (__ip_route_output_key)
- 3. FIB更新操作 (fib_table_insert)
- 六、关键机制实现细节
-
- 1. 多路径路由 (ECMP)
- 2. 邻居子系统集成
- 七 、性能优化机制
-
- 1. RCU锁优化
- 2. 路由缓存优化(历史)
- 八、诊断工具与调试接口
-
- 1. /proc 文件系统接口
- 2. ftrace 跟踪点
- 九、总结架构图
- 关键实现要点:
Linux 路由子系统深度分析:框架、实现与代码路径
一、路由子系统核心框架
+-----------------------+
| 用户空间工具 |
| (iproute2, net-tools) |
+----------+------------+
| Netlink (RTM)
v
+-----------------------+
| Netlink 接口 |<---------------------------------+
| (rtnetlink, fib_netns_ops) | |
+----------+------------+ |
| |
| 路由配置管理 |
v |
+----------------------+ |
| 路由表管理子系统 | |
| (fib_frontend.c) |--+ |
| - fib_table_insert | | |
| - fib_table_delete | | |
+----------+-----------+ | |
| | 路由规则通知 |
| 路由规则操作 v |
+----------+-----------+ +----------------------+ |
| 路由策略数据库 (RPDB) |<->| FIB通知机制 (fib_notifier) |
| (fib_rules.c) | +----------------------+ |
| - fib_rules_lookup | |
| - fib_default_rule_add| |
+----------+------------+ |
| 策略匹配 |
v |
+----------------------+ |
| 转发信息库 (FIB) |<-----------------------------+
| (fib_trie.c) |
| - fib_find_node |
| - fib_table_lookup |
+----------+-----------+
| LPM查找
v
+----------------------+
| 路由条目 (fib_info) |
| (fib_semantics.c) |
| - fib_create_info |
| - fib_release_info |
+----------+-----------+
| 下一跳信息
v
+----------------------+ +----------------------+
| 下一跳 (fib_nh) +--->| 目的地缓存 (dst_entry) |
| - nh_dev, nh_gw | | - input, output |
+----------------------+ +----------+-----------+
| 关联
v
+----------------------+
| 邻居子系统 (neighbour) |
| - neigh_output |
| - neigh_resolve_output|
+----------------------+
好的,我们来对 Linux 网络子系统中极其关键的 路由子系统 进行一个详细深入的分析,涵盖其框架设计和核心实现机制。
路由子系统核心任务: 确定进入内核网络栈的数据包(无论是本地生成还是从网络接口接收)的下一跳(Next Hop),即决定数据包应该从哪个网络接口发出、发往哪个网关(或直接发给目标主机)以及使用哪个源 IP 地址。它是实现 IP 协议“无连接”、“尽力而为”转发的基础。
二、 路由子系统核心框架与组件
路由子系统采用了分层和模块化的设计,主要包含以下几个核心部分:
路由缓存 (Route Cache) - 历史组件 (已移除):
- 历史作用: 早期内核(2.6 时代及之前)为了提高性能,会将最近成功查找的路由结果(
struct rtable
)缓存起来。缓存基于源/目的 IP、TOS、入接口等键值进行哈希查找,速度极快。 - 问题与移除: 随着连接跟踪(Netfilter Conntrack)的成熟和广泛应用,以及路由查找算法本身的优化(如 LPC-trie),路由缓存的维护开销(失效、同步)超过了其带来的性能收益,且在 DoS 攻击下表现不佳。现代内核(3.6+)已完全移除了路由缓存。查找现在总是基于 Forwarding Information Base (FIB)。
- 历史作用: 早期内核(2.6 时代及之前)为了提高性能,会将最近成功查找的路由结果(
转发信息库 (FIB - Forwarding Information Base):
- 核心数据平面: FIB 是路由子系统的核心数据结构,它存储了内核当前使用的所有有效路由规则。它代表了内核的“路由表”。
- 数据结构演进: 早期使用简单的哈希表。现代内核(2.6+)主要使用基于 LC-trie (Level Compressed trie) 算法的数据结构
struct fib_table
。这是一种高度优化的前缀树(trie),专门为快速 最长前缀匹配 (Longest Prefix Match - LPM) 设计,这是 IP 路由查找的核心算法。 - 多 FIB 表支持: Linux 支持多个路由表(默认最多 255 个)。每个表由唯一的数字 ID 标识。最常用的是:
RT_TABLE_LOCAL
(255): 本地接口地址和广播地址(内核自动管理)。RT_TABLE_MAIN
(254): 默认表,用户通过ip route
添加的路由通常放在这里。RT_TABLE_DEFAULT
(253): 通常为空。- 其他表(0-252)可用于策略路由。
fib_lookup
: 这是执行 LPM 查找的核心函数。给定目标 IP 地址,它在指定的 FIB 表中查找最具体的匹配路由条目。
路由策略数据库 (RPDB - Routing Policy Database):
- 控制平面决策器: RPDB 定义了如何选择使用哪个路由表来进行查找。它提供了基于策略的路由功能。
struct fib_rule
: 策略规则的核心结构。每条规则定义了:- 优先级 (
r_priority
): 数字越小,优先级越高。 - 选择器 (
r_prefsrc
,r_src
,r_dst
,r_tos
,r_iif
,r_oif
,r_fwmark
,r_table
等): 基于源 IP、目的 IP、TOS、入接口、出接口、防火墙标记(fwmark)等条件匹配数据包。 - 动作 (
r_action
): 最常见的是FR_ACT_TO_TBL
,表示如果规则匹配,则使用规则指定的路由表(r_table
)进行 FIB 查找。其他动作包括FR_ACT_UNREACHABLE
,FR_ACT_BLACKHOLE
,FR_ACT_PROHIBIT
等。 r_src_len
,r_dst_len
: 源/目的地址的网络掩码长度。
- 优先级 (
- 查找过程 (
fib_rules_lookup
): 当需要进行路由决策时,内核按优先级顺序遍历 RPDB 中的所有规则。一旦找到第一条匹配当前数据包属性(源 IP、目的 IP、入接口、fwmark 等)的规则,就执行该规则指定的动作(通常是去指定的路由表中进行 FIB 查找)。如果没有任何规则匹配,通常使用RT_TABLE_MAIN
。
路由条目 (
struct fib_info
):- 这是 FIB 查找结果的具体表示。它包含了执行路由决策所需的所有关键信息:
- 前缀 (
fib_dst
,fib_prefsrc
): 路由的目标网络和掩码长度,以及建议使用的源 IP 地址(通常用于多宿主主机)。 - 类型 (
fib_type
): 路由类型(RTN_UNICAST
- 单播,RTN_LOCAL
- 本地,RTN_BROADCAST
,RTN_MULTICAST
,RTN_BLACKHOLE
,RTN_UNREACHABLE
,RTN_PROHIBIT
等)。 - 范围 (
fib_scope
): 路由的有效范围(RT_SCOPE_UNIVERSE
- 全局有效,RT_SCOPE_LINK
- 仅在同一链路上有效,RT_SCOPE_HOST
- 仅本机有效)。 - 下一跳信息 (
struct fib_nh
): 这是最关键的部分! 一个fib_info
可能包含多个fib_nh
(下一跳)结构,用于支持多路径路由 (ECMP)。每个fib_nh
包含:nh_dev
: 指向输出网络设备 (struct net_device
) 的指针。nh_gw
: 网关的 IP 地址(如果路由是间接的)。对于直连路由,这个字段为 0。nh_weight
: 权重(用于加权 ECMP)。nh_flags
: 标志位(如RTNH_F_ONLINK
- 网关在本地链路上)。
- 关联的邻居条目 (
nh_pcpu_rth_output
): 指向下一跳对应的邻居子系统条目(struct neighbour
或struct dst_entry
),该条目存储了网关(或目的主机)的链路层地址(MAC 地址)以及输出函数指针。这建立了路由子系统与邻居子系统之间的桥梁。 - 引用计数 (
fib_clntref
): 管理该结构生命周期的引用计数。 - 度量值 (
fib_priority
): 路由优先级(在相同前缀长度下,值越小优先级越高)。
- 前缀 (
struct fib_result
: FIB 查找函数 (fib_lookup
) 的返回值。它包含了指向匹配的fib_info
的指针以及一些额外的查找信息(如路由表 ID、规则类型等)。
- 这是 FIB 查找结果的具体表示。它包含了执行路由决策所需的所有关键信息:
目的地缓存 (
struct dst_entry
):- 路由结果的封装与输出接口: 这是路由查找的最终产物,也是网络栈后续处理(特别是输出路径)的关键对象。
- 关键成员:
__refcnt
: 引用计数。dev
: 输出网络设备。_metrics
: 路由度量值(如 MTU、RTT 等)。flags
: 标志位。lastuse
: 上次使用时间戳。input
,output
: 关键函数指针! 定义了如何处理指向该目的地的数据包。input
: 通常用于输入路径,指向处理本地投递或转发的函数(如ip_local_deliver
或ip_forward
)。output
: 用于输出路径,指向实际执行数据包发送的函数。这个函数通常是通过邻居子系统解析链路层地址后设置的(如neigh_output
)。在路由查找刚完成时,它可能指向一个通用的输出函数(如ip_output
),该函数会触发邻居子系统的处理。
neighbour
: 指向对应的邻居条目 (struct neighbour
)。这是路由子系统与邻居子系统紧密协作的关键点。output
函数最终会调用neighbour->output
(例如neigh_resolve_output
或dev_queue_xmit
)来发送数据包。
struct rtable
: 这是 IPv4 特定的dst_entry
子类型,包含了 IPv4 特有的信息,如源/目的 IP 地址、TOS、路由标记等。
邻居子系统 (Neighbour Subsystem - ARP/ND):
- 紧密集成: 路由子系统确定了下一跳的 IP 地址(网关或目标主机)和输出接口。邻居子系统负责将该 IP 地址解析为对应的链路层地址(如以太网 MAC 地址)。
struct neighbour
: 代表一个邻居(同一链路上的主机或路由器)。它存储了链路层地址、状态(NUD_NONE
,NUD_INCOMPLETE
,NUD_REACHABLE
,NUD_STALE
等)以及输出函数指针 (output
)。- 交互流程:
- 路由查找得到
dst_entry
(如rtable
)。 - 内核通过
dst->output
(初始可能是ip_output
或ip6_output
) 尝试发送数据包。 ip_output
会调用dst_neigh_output
。dst_neigh_output
会获取或创建与dst_entry
关联的neighbour
条目。- 根据
neighbour
的状态,调用其output
方法:- 如果状态是
NUD_REACHABLE
,直接调用neigh->ops->queue_xmit
(最终通常是dev_queue_xmit
将包放入设备队列)。 - 如果状态是
NUD_NONE
或NUD_STALE
,则调用neigh_resolve_output
。该函数会检查是否需要发送 ARP 请求(IPv4)或 Neighbor Solicitation(IPv6)。如果需要,它会缓存数据包(到neigh->arp_queue
)并启动地址解析过程。解析成功后,状态变为NUD_REACHABLE
,缓存的包会被发送。 - 如果状态是
NUD_FAILED
,通常会导致发送失败。
- 如果状态是
- 路由查找得到
三、 路由查找流程详解(以 IPv4 接收转发为例)
- 数据包接收: 网卡驱动通过 NAPI 将数据包放入接收队列,软中断
NET_RX_SOFTIRQ
触发net_rx_action
。 - 协议处理 (
ip_rcv
): 网络层处理函数ip_rcv
被调用,进行基本的 IP 头校验。 - 路由决策入口 (
ip_route_input_noref
/ip_route_input_rcu
):- 这是输入路径路由查找的入口函数。主要任务:确定包是发给本机的 (
INPUT
)、需要转发的 (FORWARD
)、还是广播/组播的。 - 关键步骤:
- 检查目的地址是否是本地地址? 查
local
表 (RT_TABLE_LOCAL
)。如果找到类型为RTN_LOCAL
的路由,则包是发给本机的。 - 检查是否开启转发 (
net->ipv4.ip_forward
)? 如果没开启,且包不是发给本机的,则丢弃并回复 ICMP 目的不可达。 - 执行 RPDB 查找 (
fib_rules_lookup
): 使用数据包的属性(源 IP、目的 IP、入接口、TOS、fwmark 等)遍历路由策略规则。 - FIB 查找 (
fib_lookup
): 根据 RPDB 规则选定的路由表(通常是main
表)进行最长前缀匹配查找,得到fib_result
。 - 构建
dst_entry
(rt_dst_alloc
): 基于fib_result
创建一个struct rtable
(IPv4 的dst_entry
)。设置关键字段:rtable->dst.input
: 设置为ip_forward
(用于转发)或ip_local_deliver
(用于本机,但这一步通常在查local
表时已确定)。rtable->dst.output
: 初始通常设置为ip_output
(对于本机生成或转发的包,后续邻居子系统会替换它)。rtable->rt_gateway
: 下一跳 IP(网关地址或目的地址)。rtable->rt_dev
: 输出设备(来自fib_nh
)。rtable->rt_route_iif
: 输入设备。
- 关联邻居: 隐含地为这个
rtable
初始化了邻居关联(后续输出时具体解析)。
- 检查目的地址是否是本地地址? 查
- 这是输入路径路由查找的入口函数。主要任务:确定包是发给本机的 (
- 设置 skb 目的地 (
skb_dst_set
): 将创建的rtable
(作为dst_entry
)关联到sk_buff
(skb->dst
)。 - 后续处理: 根据
dst->input
函数指针调用相应的处理函数:- 如果是
ip_local_deliver
: 数据包进入本机上层协议栈(TCP/UDP/ICMP 等)。 - 如果是
ip_forward
: 执行转发逻辑(TTL 减 1,处理选项,分片等),然后调用dst_output(skb)
。
- 如果是
- 输出路径 (
dst_output
->skb_dst(skb)->output(skb)
):- 这会调用
rtable->dst.output
,通常是ip_output
。 ip_output
处理可能的 IP 选项,然后调用ip_finish_output
。ip_finish_output
处理可能的分片,然后调用ip_finish_output2
。ip_finish_output2
调用dst_neigh_output
。dst_neigh_output
获取或创建与rtable->rt_gateway
对应的邻居条目 (struct neighbour
),并调用neigh_output
。neigh_output
根据邻居条目的状态 (neigh->nud_state
) 调用其output
方法:NUD_CONNECTED
/NUD_REACHABLE
: 直接调用neigh->ops->queue_xmit
(最终调用dev_queue_xmit
将包放入网卡发送队列)。- 其他状态 (
NUD_INCOMPLETE
,NUD_STALE
,NUD_DELAY
,NUD_PROBE
): 调用neigh_resolve_output
,该函数负责处理 ARP/ND 解析(可能发送请求并缓存数据包),解析成功后发送包。
- 这会调用
四、核心组件实现机制
1. 路由策略数据库 (RPDB)
数据结构:
// 策略规则定义
struct fib_rule {
u32 priority; // 优先级(值越小优先级越高)
u32 table; // 目标路由表ID
u8 action; // 动作类型
u8 l3mdev; // L3主设备标志
u32 flags; // 规则标志
u32 fwmark; // 防火墙标记
u32 fwmask; // 防火墙掩码
__be32 src; // 源地址
__be32 dst; // 目的地址
u8 src_len; // 源地址长度
u8 dst_len; // 目的地址长度
};
策略匹配流程:
// net/ipv4/fib_rules.c
int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl, int flags,
struct fib_lookup_arg *arg)
{
struct fib_rule *rule;
list_for_each_entry(rule, &ops->rules_list, list) {
if (!fib_rule_match(rule, ops, fl, flags))
continue;
if (rule->action == FR_ACT_GOTO) {
// 跳转到其他规则链
} else if (rule->action == FR_ACT_TO_TBL) {
// 使用指定路由表
err = ops->action(rule, fl, flags, arg);
if (err != -EAGAIN)
return err;
}
}
return -ESRCH; // 未匹配
}
2. 转发信息库 (FIB)
数据结构:
// 路由表结构
struct fib_table {
struct hlist_node tb_hlist; // 哈希链表
u32 tb_id; // 表ID
unsigned tb_num_default; // 默认路由数量
struct rcu_head rcu; // RCU保护
struct trie __rcu *tb_trie; // LC-trie树根
};
// LC-trie节点结构
struct tnode {
t_key key; // 键值
unsigned long pos; // 位位置
struct tnode __rcu *parent; // 父节点
struct tnode __rcu *child[0]; // 子节点
};
LPM查找算法:
// net/ipv4/fib_trie.c
static struct key_vector *fib_find_node(struct trie *t, struct key_vector **tp,
u32 key)
{
struct key_vector *n, *pn;
unsigned long index;
pn = t->kv;
n = get_child(pn, 1);
while (n) {
if (index >= n->key && index <= n->key + n->pos) {
*tp = pn;
return n;
}
pn = n;
n = get_child_rcu(n, get_index(key, n));
}
return NULL;
}
3. 目的地缓存 (dst_entry)
核心结构:
struct dst_entry {
struct net_device *dev; // 输出设备
unsigned long _metrics; // 路由度量
unsigned long expires; // 过期时间
struct neighbour __rcu *neighbour; // 邻居条目
int (*input)(struct sk_buff *); // 输入处理函数
int (*output)(struct net *, struct sock *, struct sk_buff *); // 输出处理函数
};
// IPv4专用路由缓存
struct rtable {
struct dst_entry dst;
__be32 rt_key_dst; // 目的地址
__be32 rt_key_src; // 源地址
__be32 rt_gateway; // 网关地址
u8 rt_type; // 路由类型
};
4. 邻居子系统集成
路由到邻居的转换:
// net/ipv4/ip_output.c
static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct rtable *rt = (struct rtable *)dst;
struct neighbour *neigh;
// 获取邻居条目
neigh = __ipv4_neigh_lookup_noref(dst->dev, rt->rt_gateway);
if (unlikely(!neigh))
neigh = __neigh_create(&arp_tbl, &rt->rt_gateway, dst->dev, false);
// 通过邻居子系统发送
return neigh_output(neigh, skb);
}
五、核心路径代码分析
1. 输入路径路由 (ip_route_input)
// net/ipv4/route.c
int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,
u8 tos, struct net_device *dev)
{
struct fib_result res;
struct flowi4 fl4;
// 初始化流信息
memset(&fl4, 0, sizeof(fl4));
fl4.flowi4_iif = dev->ifindex;
fl4.daddr = daddr;
fl4.saddr = saddr;
fl4.flowi4_tos = tos;
// 策略路由查找
if (fib_rules_lookup(net->ipv4.rules_ops, flowi4_to_flowi(&fl4), 0, &res))
goto no_route;
// 创建路由缓存
rth = rt_dst_alloc(dev_net(dev), flags);
if (!rth)
goto e_nobufs;
// 设置路由属性
rth->dst.input = ip_forward;
rth->dst.output = ip_output;
rth->rt_key_dst = daddr;
rth->rt_gateway = res.fi->fib_nh[0].nh_gw;
// 关联到skb
skb_dst_set(skb, &rth->dst);
return 0;
}
2. 输出路径路由 (__ip_route_output_key)
// net/ipv4/route.c
struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
{
struct fib_result res;
// 策略路由查找
if (fib_rules_lookup(net->ipv4.rules_ops, flowi4_to_flowi(fl4), 0, &res))
return ERR_PTR(-ENETUNREACH);
// 创建路由缓存
rth = rt_dst_alloc(out_dev, flags);
rth->rt_key_dst = fl4->daddr;
rth->rt_key_src = fl4->saddr;
// 设置输出函数
rth->dst.output = ip_output;
// 多路径路由处理
if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0)
fib_select_multipath(&res, hash);
return rth;
}
3. FIB更新操作 (fib_table_insert)
// net/ipv4/fib_trie.c
int fib_table_insert(struct net *net, struct fib_table *tb,
struct fib_config *cfg, struct netlink_ext_ack *extack)
{
struct trie *t = (struct trie *)tb->tb_data;
struct fib_alias *fa, *new_fa;
// 查找插入位置
l = fib_find_node(t, &tp, key);
// 创建新条目
new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
new_fa->fa_info = fi;
new_fa->fa_tos = cfg->fc_tos;
new_fa->fa_type = cfg->fc_type;
// 插入到trie树
if (!l) {
// 创建新叶子节点
l = leaf_new(key);
insert_leaf(t, tp, l);
}
// 添加到别名列表
hlist_add_head_rcu(&new_fa->fa_list, &l->leaf);
// 通知路由变更
rtmsg_fib(RTM_NEWROUTE, key, new_fa, cfg->fc_dst_len, tb->tb_id, cfg, NLM_F_CREATE);
}
六、关键机制实现细节
1. 多路径路由 (ECMP)
// net/ipv4/fib_semantics.c
void fib_select_multipath(struct fib_result *res, int hash)
{
struct fib_info *fi = res->fi;
u32 w;
for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
struct fib_nh *nh = &fi->fib_nh[nhsel];
// 计算权重
w = nh->nh_weight;
if (w == 0)
continue;
// 哈希选择路径
if (hash <= atomic_read(&nh->nh_upper_bound)) {
res->nh_sel = nhsel;
return;
}
hash -= atomic_read(&nh->nh_upper_bound);
}
}
2. 邻居子系统集成
// net/core/neighbour.c
int neigh_output(struct neighbour *n, struct sk_buff *skb)
{
// 根据邻居状态选择输出函数
if (n->nud_state & NUD_CONNECTED)
return n->output(n, skb); // 直接发送
else
return neigh_resolve_output(n, skb); // 需要解析
}
int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
{
// 发送ARP请求
if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
neigh->ops->solicit(neigh, skb);
__skb_queue_tail(&neigh->arp_queue, skb);
return 0;
}
// 已解析,直接发送
return neigh->ops->output(neigh, skb);
}
七 、性能优化机制
1. RCU锁优化
// FIB查找使用RCU保护
static struct key_vector *fib_find_node(struct trie *t, struct key_vector **tp, u32 key)
{
struct key_vector *pn, *n;
rcu_read_lock();
pn = t->kv;
n = rcu_dereference(pn->tnode[0]);
...
rcu_read_unlock();
}
2. 路由缓存优化(历史)
// 旧版路由缓存(3.6之前)
struct rt_hash_bucket {
struct rtable *chain;
};
// 缓存查找(已移除)
static inline struct rtable *rt_hash_result(u32 saddr, u32 daddr, int iif)
{
unsigned int hash = rt_hash_code(saddr, daddr, iif);
struct rtable *rth = rt_hash_table[hash].chain;
while (rth) {
if (rth->fl.fl4_src == saddr &&
rth->fl.fl4_dst == daddr &&
rth->fl.iif == iif)
return rth;
rth = rth->u.dst.rt_next;
}
return NULL;
}
八、诊断工具与调试接口
1. /proc 文件系统接口
# 查看路由表
cat /proc/net/route
# 查看FIB统计
cat /proc/net/stat/rt_cache
# 查看路由策略
cat /proc/net/rt_acct
2. ftrace 跟踪点
# 启用路由跟踪点
echo 1 > /sys/kernel/debug/tracing/events/fib/enable
echo 1 > /sys/kernel/debug/tracing/events/neigh/enable
# 查看跟踪结果
cat /sys/kernel/debug/tracing/trace_pipe
九、总结架构图
+-------------------+ +-------------------+ +-------------------+
| 用户空间配置工具 | | 网络协议栈 | | 网络设备驱动 |
| (ip route add/del) | | (TCP/UDP/ICMP) | | (驱动收发包队列) |
+---------+---------+ +---------+---------+ +---------+---------+
| Netlink | |
v v v
+-------------------+ +-------------------+ +-------------------+
| Netlink 子系统 | | IP 路由入口 | | 邻居子系统ARP/ND |
| (处理RTM_NEWROUTE) | | (ip_route_input) | | (MAC地址解析) |
+---------+---------+ +---------+---------+ +---------+---------+
| | |
v v |
+-------------------+ +-------------------+ |
| 路由策略数据库 RPDB | | 路由缓存 dst_entry |<----------+
| (策略路由选择) | | (输入/输出函数指针) |
+---------+---------+ +---------+---------+
| |
v v
+-------------------+ +-------------------+
| 转发信息库 FIB | | 多路径路由ECMP |
| (LC-trie实现LPM) | | (流量负载均衡) |
+-------------------+ +-------------------+
关键实现要点:
- 分层架构:用户空间→Netlink→RPDB→FIB→dst_entry→邻居子系统
- 高性能查找:LC-trie实现O(log n)复杂度LPM查找
- 策略路由:RPDB支持基于源/目的地址、防火墙标记等多维路由
- 无缝集成:dst_entry通过input/output函数指针连接网络栈各层
- 动态更新:RCU保护实现路由表无锁更新
- 多路径支持:ECMP实现流量负载均衡和高可用
路由子系统通过高度模块化的设计和精密的算法实现,在保证功能灵活性的同时,提供了接近线速的转发性能,是现代Linux网络栈的核心基础设施。