在 IP
层内核实现 一文中,我们看到 ip_rcv_finish()
调用 fib_lookup() 做路由决策——找到下一跳
IP、出接口和路由类型。那篇文章给出了全景概览,本文则深入拆解路由子系统的内部实现。
核心问题:一个 IP 包从”知道目的 IP”到”知道从哪个网卡、经过哪个网关发出”,中间经历了什么?
发包路径:
ip_route_output_flow(net, flowi4, sk)
→ __ip_route_output_key(net, flowi4)
→ fib_lookup(net, flowi4, &res, flags)
→ 策略路由:遍历 fib_rules 规则链
→ 匹配规则:fib_rule->table
→ FIB 查找:fib_table_lookup(tb, flowi4, &res, flags)
→ LC-trie 最长前缀匹配
→ fib_result{fi, nhc, type, scope}
→ ECMP:fib_select_path()
→ 哈希选路 → 选定 fib_nh
→ 构造 rtable(dst_entry 的 IPv4 封装)
→ 设置 output = ip_output / ip_mc_output
→ 设置 input = ip_local_deliver / ip_forward
→ xfrm_lookup_route() IPsec 策略检查
→ 返回 &rtable->dst
收包路径:
ip_route_input_noref(skb, dst, src, tos, dev)
→ fib_lookup() 同上
→ skb_dst_set_noref(skb, &rtable->dst)
一、FIB 表:路由信息的容器
fib_table 结构
struct fib_table(include/net/ip_fib.h:253)是路由表的顶层容器:
struct fib_table {
struct hlist_node tb_hlist; /* 哈希链中的节点 */
u32 tb_id; /* 表 ID:254=main, 255=local */
int tb_num_default;/* 默认路由计数 */
struct rcu_head rcu;
unsigned long *tb_data; /* 指向 LC-trie 根节点 */
unsigned long __data[]; /* 内联 trie 数据 */
};Linux 默认维护三张路由表:
| 表 ID | 名称 | 用途 |
|---|---|---|
| 255 | local | 本机地址、广播地址(内核自动维护) |
| 254 | main | 用户配置的路由(ip route
操作的默认表) |
| 253 | default | 默认路由(通常为空) |
开启 CONFIG_IP_MULTIPLE_TABLES
后(几乎所有发行版都开启),可以创建 1-252
号自定义表,用于策略路由。
LC-trie:最长前缀匹配
fib_table_lookup()(ip_fib.h:274)在
LC-trie(Level-Compressed trie)中执行最长前缀匹配。LC-trie
是 trie
的压缩变体——把只有一个子节点的路径压缩为单个节点,大幅减少内存和查找深度。
对于典型的互联网全表(约 100 万条路由),LC-trie 的查找时间复杂度约 O(W),其中 W = 32(IPv4 地址位数),实际跳数通常不超过 4-6 层。
tb_data 指向 trie 根节点。trie
的内部节点记录”在第几个比特分叉”,叶节点指向
fib_alias → fib_info 链。
二、fib_info 与 fib_nh:路由条目的内核表示
fib_info:一条路由的全部信息
struct fib_info(ip_fib.h:134)存储一条路由的完整信息——协议来源、度量值、下一跳列表:
struct fib_info {
struct hlist_node fib_hash;
struct hlist_node fib_lhash;
struct list_head nh_list;
struct net *fib_net;
refcount_t fib_treeref; /* trie 引用计数 */
refcount_t fib_clntref; /* 客户端引用计数 */
unsigned int fib_flags; /* RTNH_F_* */
unsigned char fib_dead;
unsigned char fib_protocol; /* RTPROT_BOOT/STATIC/BIRD/... */
unsigned char fib_scope; /* RT_SCOPE_UNIVERSE/LINK/HOST */
unsigned char fib_type; /* RTN_UNICAST/LOCAL/BROADCAST/... */
__be32 fib_prefsrc; /* 首选源 IP */
u32 fib_tb_id; /* 所属表 ID */
u32 fib_priority; /* 度量值(metric) */
struct dst_metrics *fib_metrics; /* MTU, RTT 等度量 */
int fib_nhs; /* 下一跳数量(ECMP) */
struct nexthop *nh; /* 新式 nexthop 对象 */
struct fib_nh fib_nh[] __counted_by(fib_nhs); /* 柔性数组 */
};关键设计:fib_nh[]
是变长数组——单路径路由只有 1 个元素,ECMP
路由有多个。fib_nhs 记录数量。
fib_nh:下一跳
struct fib_nh(ip_fib.h:105)继承
fib_nh_common(ip_fib.h:81),存储单个下一跳的信息:
struct fib_nh_common {
struct net_device *nhc_dev; /* 出接口 */
int nhc_oif; /* 出接口 index */
unsigned char nhc_scope; /* 路由范围 */
u8 nhc_family; /* AF_INET / AF_INET6 */
u8 nhc_gw_family; /* 网关地址族 */
unsigned char nhc_flags; /* RTNH_F_DEAD/ONLINK/... */
struct lwtunnel_state *nhc_lwtstate; /* 轻量级隧道状态 */
union {
__be32 ipv4; /* IPv4 网关 */
struct in6_addr ipv6; /* IPv6 网关 */
} nhc_gw;
int nhc_weight; /* ECMP 权重 */
atomic_t nhc_upper_bound; /* 加权 ECMP 上界 */
/* 路由缓存 */
struct rtable __rcu * __percpu *nhc_pcpu_rth_output; /* per-CPU 输出缓存 */
struct rtable __rcu *nhc_rth_input; /* 输入路由缓存 */
struct fnhe_hash_bucket __rcu *nhc_exceptions; /* FNHE 异常表 */
};per-CPU
路由缓存(nhc_pcpu_rth_output)是路由快路径的关键——每个
CPU 缓存自己最近使用的
rtable,避免每次都重新构造。
三、fib_result:路由查找的输出
fib_table_lookup() 的结果写入
struct fib_result(ip_fib.h:169):
struct fib_result {
__be32 prefix; /* 匹配的网络前缀 */
unsigned char prefixlen; /* 前缀长度 */
unsigned char nh_sel; /* 选中的下一跳索引(ECMP) */
unsigned char type; /* RTN_UNICAST/LOCAL/... */
unsigned char scope; /* RT_SCOPE_* */
u32 tclassid; /* TC 分类 ID */
dscp_t dscp;
struct fib_nh_common *nhc; /* 选中的下一跳 */
struct fib_info *fi; /* 路由信息 */
struct fib_table *table; /* 来源表 */
};type 字段决定包的命运:
| RTN 类型 | 值 | 含义 | 对应 dst->input |
|---|---|---|---|
RTN_LOCAL |
1 | 本机地址 | ip_local_deliver |
RTN_UNICAST |
2 | 单播转发 | ip_forward |
RTN_BROADCAST |
3 | 广播 | ip_local_deliver |
RTN_MULTICAST |
5 | 组播 | 组播路由 |
RTN_BLACKHOLE |
6 | 静默丢弃 | dst_discard |
RTN_UNREACHABLE |
7 | 不可达 | ip_error(发 ICMP) |
RTN_PROHIBIT |
8 | 管理禁止 | ip_error(发 ICMP) |
四、策略路由:ip rule
fib_rule 结构
当开启 CONFIG_IP_MULTIPLE_TABLES
时,路由查找不是直接查单张表,而是遍历规则链。每条规则是一个
struct fib_rule(include/net/fib_rules.h:20):
struct fib_rule {
struct list_head list;
int iifindex; /* 入接口匹配 */
int oifindex; /* 出接口匹配 */
u32 mark; /* fwmark 匹配 */
u32 mark_mask; /* fwmark 掩码 */
u32 table; /* 目标表 ID */
u8 action; /* FR_ACT_TO_TBL/BLACKHOLE/... */
u8 ip_proto; /* 协议匹配 */
u32 pref; /* 优先级(数字越小越先匹配) */
char iifname[IFNAMSIZ];
char oifname[IFNAMSIZ];
struct fib_kuid_range uid_range; /* UID 范围匹配 */
struct fib_rule_port_range sport_range; /* 源端口范围 */
struct fib_rule_port_range dport_range; /* 目的端口范围 */
};默认规则链
$ ip rule list
0: from all lookup local # 优先级 0:先查 local 表
32766: from all lookup main # 优先级 32766:再查 main 表
32767: from all lookup default # 优先级 32767:最后查 default 表查找流程
fib_lookup()(ip_fib.h:311)的策略路由路径:
/* 快路径:没有自定义规则 */
if (!net->ipv4.fib_has_custom_rules) {
/* 直接查 local → main,跳过规则遍历 */
tb = rcu_dereference(net->ipv4.fib_local);
err = fib_table_lookup(tb, flp, res, flags);
if (!err) goto out;
tb = rcu_dereference(net->ipv4.fib_main);
err = fib_table_lookup(tb, flp, res, flags);
goto out;
}
/* 慢路径:遍历规则链 */
return __fib_lookup(net, flp, res, flags);
→ fib_rules_lookup(ops, flowi, flags, arg)
for_each_rule(rule) {
if (!fib_rule_match(rule, flowi))
continue;
switch (rule->action) {
case FR_ACT_TO_TBL:
err = fib_table_lookup(table, flp, res, flags);
break;
case FR_ACT_BLACKHOLE:
return -EINVAL;
...
}
}fib_has_custom_rules
是关键优化——大多数服务器只用默认三条规则,直接走快路径。只有执行
ip rule add
添加自定义规则后才切换到慢路径。
策略路由示例
# 基于源 IP 选路:来自 10.0.1.0/24 的包走表 100
ip rule add from 10.0.1.0/24 table 100
ip route add default via 10.0.1.1 table 100
# 基于 fwmark 选路:标记为 0x1 的包走表 200
ip rule add fwmark 0x1 table 200
iptables -t mangle -A OUTPUT -p tcp --dport 443 -j MARK --set-mark 0x1
# 基于入接口选路
ip rule add iif eth1 table 300五、ECMP:多路径负载均衡
哈希选路
当 fib_info->fib_nhs > 1
时,fib_select_path()(ip_fib.h:527)执行
ECMP 选路:
fib_select_path(net, res, fl4, skb)
├── fib_multipath_hash(net, fl4, skb, &flkeys)
│ └── 计算流哈希(基于五元组)
└── fib_select_multipath(res, hash)
└── 用哈希值在 fib_nh[] 中选路
哈希字段
fib_multipath_hash()(ip_fib.h:521)的哈希输入由
sysctl 控制:
# 哈希策略:0=L3, 1=L3+L4, 2=L3+L4+inner(隧道), 3=自定义
sysctl net.ipv4.fib_multipath_hash_policy
# 自定义哈希字段(policy=3 时生效)
sysctl net.ipv4.fib_multipath_hash_fields默认哈希字段:
#define FIB_MULTIPATH_HASH_FIELD_SRC_IP BIT(0)
#define FIB_MULTIPATH_HASH_FIELD_DST_IP BIT(1)
#define FIB_MULTIPATH_HASH_FIELD_IP_PROTO BIT(2)
#define FIB_MULTIPATH_HASH_FIELD_SRC_PORT BIT(4)
#define FIB_MULTIPATH_HASH_FIELD_DST_PORT BIT(5)同一条流(五元组相同)的所有包始终走同一个下一跳——避免乱序。
加权 ECMP
每个 fib_nh_common 有
nhc_weight
字段(1-256)。nhc_upper_bound
是归一化后的上界,选路时用哈希值与上界比较:
nh[0].weight=2, nh[1].weight=1, nh[2].weight=1
→ 总权重 4
→ nh[0].upper_bound = 2/4 = 0.5
→ nh[1].upper_bound = 3/4 = 0.75
→ nh[2].upper_bound = 4/4 = 1.0
→ hash < 0.5 选 nh[0],0.5 ≤ hash < 0.75 选 nh[1],hash ≥ 0.75 选 nh[2]
六、Nexthop 对象:现代路由 API
传统模型的问题
传统路由中,下一跳信息嵌入在
fib_info->fib_nh[]
里——修改一个下一跳需要替换整个 fib_info
对象,影响所有引用它的路由。
Linux 5.3 引入了 nexthop
对象(include/net/nexthop.h:132),将下一跳解耦为独立管理的资源:
struct nexthop {
struct rb_node rb_node; /* 红黑树节点(按 ID 查找) */
struct list_head fi_list; /* 引用此 NH 的 IPv4 路由列表 */
struct list_head f6i_list; /* 引用此 NH 的 IPv6 路由列表 */
struct net *net;
u32 id; /* 唯一 ID(用户指定) */
u8 protocol; /* 管理此 NH 的应用 */
u8 nh_flags;
bool is_group; /* 是否为组 */
union {
struct nh_info __rcu *nh_info; /* 单个下一跳 */
struct nh_group __rcu *nh_grp; /* 下一跳组 */
};
};下一跳组
struct nh_group(nexthop.h:119)管理
ECMP 组:
struct nh_group {
struct nh_group *spare; /* 原子替换用 */
u16 num_nh; /* 成员数量 */
bool is_multipath;
bool hash_threshold; /* 哈希阈值模式 */
bool resilient; /* 弹性哈希组 */
struct nh_res_table __rcu *res_table; /* 弹性哈希表 */
struct nh_grp_entry nh_entries[]; /* 成员列表 */
};弹性哈希(Resilient Hash)
传统 ECMP 的问题:增删下一跳时,哈希空间重新分配,大量已有流被迁移到不同路径——导致 TCP 连接跨路径、乱序甚至中断。
弹性哈希(nh_res_table,nexthop.h:80)用固定数量的桶(bucket)映射到下一跳。增删下一跳时,只重新分配受影响桶的映射,最小化流迁移:
# 创建弹性哈希组
ip nexthop add id 1 via 10.0.0.1 dev eth0
ip nexthop add id 2 via 10.0.0.2 dev eth0
ip nexthop add id 10 group 1/2 type resilient buckets 32
# 添加新成员——只影响 ~1/3 的桶
ip nexthop add id 3 via 10.0.0.3 dev eth0
ip nexthop replace id 10 group 1/2/3 type resilient buckets 32iproute2 操作
# 创建 nexthop 对象
ip nexthop add id 100 via 10.0.0.1 dev eth0
ip nexthop add id 101 via 10.0.0.2 dev eth0
# 创建 nexthop 组(ECMP)
ip nexthop add id 200 group 100/101
# 路由引用 nexthop 对象
ip route add 192.168.0.0/24 nhid 200
# 修改下一跳——所有引用它的路由自动更新
ip nexthop replace id 100 via 10.0.0.3 dev eth0七、FNHE:路由缓存的替代品
路由缓存的废弃
Linux 3.5
及之前版本有一个全局路由缓存(rt_cache)——每个目的
IP 对应一条缓存条目,存储完整的路由查找结果。问题:
- 内存爆炸:互联网服务器可能缓存数百万条目
- 无效化代价大:拓扑变化时需要遍历整个缓存
- DDoS 放大:攻击者发送大量不同目的 IP 的包,撑爆缓存
Linux 3.6 移除了全局路由缓存,替换为两种机制。
per-CPU rtable 缓存
fib_nh_common->nhc_pcpu_rth_output 是
per-CPU 的 rtable 指针。对于同一个下一跳,每个
CPU 缓存自己最近构造的
rtable——复用它避免重复分配:
/* 简化的查找逻辑 */
rth = rcu_dereference(*this_cpu_ptr(nhc->nhc_pcpu_rth_output));
if (rt_cache_valid(rth))
return rth; /* 命中 per-CPU 缓存 */
/* 未命中:构造新 rtable */
rth = rt_dst_alloc(dev, flags, type, ...);
rcu_assign_pointer(*this_cpu_ptr(nhc->nhc_pcpu_rth_output), rth);FNHE(Forwarding Nexthop Exception)
struct fib_nh_exception(ip_fib.h:59)缓存例外信息——PMTU
发现结果和 ICMP Redirect:
struct fib_nh_exception {
struct fib_nh_exception __rcu *fnhe_next; /* 哈希链 */
__be32 fnhe_daddr; /* 目的 IP */
u32 fnhe_pmtu; /* 缓存的 PMTU */
bool fnhe_mtu_locked;/* MTU 锁定标志 */
__be32 fnhe_gw; /* 重定向网关 */
unsigned long fnhe_expires; /* 过期时间 */
struct rtable __rcu *fnhe_rth_input; /* 输入 rtable */
struct rtable __rcu *fnhe_rth_output; /* 输出 rtable */
};每个 fib_nh_common 有独立的 FNHE
哈希表(nhc_exceptions),大小 2048 桶。
FNHE 生命周期:
1. 收到 ICMP "Fragmentation Needed"
→ fnhe_pmtu = 对端建议的 MTU
→ fnhe_expires = jiffies + 10min
2. 后续发包检查 FNHE
→ 如果 fnhe_pmtu < 路由 MTU → 使用 fnhe_pmtu
→ 如果已过期 → 恢复原始 MTU,重新探测
3. 收到 ICMP Redirect
→ fnhe_gw = 重定向的新网关
→ 后续包走新网关
八、dst_entry 与 rtable:路由缓存的载体
dst_entry
struct dst_entry(include/net/dst.h:26)是协议无关的路由缓存条目,所有协议栈的路由信息都继承它:
struct dst_entry {
struct net_device *dev; /* 出接口 */
struct dst_ops *ops; /* 操作表 */
unsigned long _metrics; /* 度量值 */
unsigned long expires; /* 过期时间 */
int (*input)(struct sk_buff *); /* 收包处理 */
int (*output)(struct net *, struct sock *, struct sk_buff *); /* 发包处理 */
unsigned short flags;
short obsolete;
unsigned short header_len; /* 封装头长度 */
unsigned short trailer_len;
rcuref_t __rcuref; /* RCU 引用计数 */
int __use; /* 使用计数 */
unsigned long lastuse; /* 最后使用时间 */
short error; /* 错误码 */
};关键函数指针:
| 场景 | input | output |
|---|---|---|
| 本机收包 | ip_local_deliver |
— |
| 转发 | ip_forward |
ip_output |
| 本机发包 | — | ip_output |
| 黑洞 | dst_discard |
dst_discard_out |
skb_dst(skb) 返回 skb 关联的
dst_entry——整个协议栈通过它判断”这个包往哪走”。
rtable
struct rtable(include/net/route.h:60)是
IPv4 对 dst_entry 的扩展:
struct rtable {
struct dst_entry dst; /* 基类 */
int rt_genid; /* 代际 ID(路由表变更时递增) */
unsigned int rt_flags; /* RTCF_* 标志 */
__u16 rt_type; /* RTN_* */
__u8 rt_is_input; /* 1=输入路由, 0=输出路由 */
__u8 rt_uses_gateway; /* 是否经过网关 */
int rt_iif; /* 入接口 index */
u8 rt_gw_family;
union {
__be32 rt_gw4; /* IPv4 网关 */
struct in6_addr rt_gw6; /* IPv6 网关(IPv4-mapped) */
};
u32 rt_mtu_locked:1;
u32 rt_pmtu:31; /* 缓存的 PMTU */
};rt_genid 用于失效检测——每次路由表变更,全局
rt_genid 递增。查找时如果
rtable->rt_genid != net->ipv4.rt_genid,说明缓存过期,需要重新查找。
九、IPv6 路由差异
fib6_table 与 fib6_node
IPv6 使用 radix tree(不是
LC-trie)实现路由查找。struct fib6_table(include/net/ip6_fib.h:385):
struct fib6_table {
struct hlist_node tb6_hlist;
u32 tb6_id;
spinlock_t tb6_lock; /* IPv6 路由表需要自旋锁 */
struct fib6_node tb6_root; /* 内嵌根节点 */
unsigned int fib_seq; /* 序列号 */
};struct fib6_node(ip6_fib.h:73)是
radix tree 节点:
struct fib6_node {
struct fib6_node __rcu *parent, *left, *right;
struct fib6_node __rcu *subtree; /* 源地址子树(源路由) */
struct fib6_info __rcu *leaf; /* 叶子路由 */
__u16 fn_bit; /* 分叉比特位 */
__u16 fn_flags;
int fn_sernum;
struct fib6_info __rcu *rr_ptr; /* 轮询指针 */
};IPv4 与 IPv6 路由的关键区别
| 特性 | IPv4 (FIB) | IPv6 (FIB6) |
|---|---|---|
| 数据结构 | LC-trie | Radix tree |
| 锁保护 | RCU 无锁读 | spinlock + RCU |
| 源路由 | 无原生支持 | subtree 支持源地址路由 |
| 路由条目 | fib_info + fib_nh[] |
fib6_info + fib6_nh[] |
| 路由缓存 | per-CPU rtable + FNHE | per-CPU rt6_info + rt6_exception |
| ECMP | fib_select_multipath() |
fib6_select_path() 含轮询 |
| 地址长度 | 32 位 | 128 位(trie 更深) |
IPv6 的 fib6_select_path()
支持轮询(round-robin)模式——rr_ptr
记录上次选中的路由,下次从下一个开始。这与 IPv4
纯哈希选路不同。
十、可观测性实战
路由查找追踪
# 追踪 fib_table_lookup——每次路由查找的表和结果
bpftrace -e '
kretprobe:fib_table_lookup {
printf("fib_table_lookup: ret=%d\n", retval);
}'
# 追踪策略路由命中——哪条规则匹配了
bpftrace -e '
kprobe:fib_rules_lookup {
printf("fib_rules_lookup family=%d\n", arg2);
}'ECMP 分布统计
# 统计每个下一跳被选中的次数
bpftrace -e '
kprobe:fib_select_multipath {
@selected = lhist(arg1, 0, 8, 1);
}'FNHE 异常监控
# 追踪 PMTU 更新——频繁更新说明路径 MTU 不稳定
bpftrace -e '
kprobe:__ip_rt_update_pmtu {
$mtu = arg2;
printf("pmtu_update: mtu=%u\n", $mtu);
@pmtu_updates = count();
}'路由缓存失效检测
# rt_genid 变化频率——频繁变化说明路由表不稳定
bpftrace -e '
kprobe:rt_genid_bump_ipv4 {
printf("rt_genid bumped! time=%lu\n", nsecs);
@genid_bumps = count();
}'路由查找延迟
# 测量 fib_lookup 耗时
bpftrace -e '
kprobe:fib_lookup { @start[tid] = nsecs; }
kretprobe:fib_lookup /@start[tid]/ {
@fib_lookup_ns = hist(nsecs - @start[tid]);
delete(@start[tid]);
}'十一、关键参数速查
| 参数 | 默认值 | 含义 | 调优建议 |
|---|---|---|---|
fib_multipath_hash_policy |
0 | ECMP 哈希策略 | L4 负载均衡设为 1 |
fib_multipath_hash_fields |
0x7 | 自定义哈希字段 | policy=3 时配置 |
fib_multipath_use_neigh |
0 | ECMP 避开不可达邻居 | 建议开启 |
ip_forward_use_pmtu |
0 | 转发时使用 PMTU | 特定场景开启 |
ip_forward_update_priority |
1 | 转发时更新 TOS | 通常保持默认 |
fib_sync_mem |
524288 | FIB 同步内存阈值 | 大路由表增大 |
conf.{iface}.rp_filter |
0/1 | 反向路径过滤 | 严格模式设为 1 |
conf.{iface}.accept_redirects |
1 | 接受 ICMP Redirect | 路由器建议关闭 |
conf.{iface}.send_redirects |
1 | 发送 ICMP Redirect | 非路由器关闭 |
conf.{iface}.forwarding |
0 | IP 转发开关 | 路由器开启 |
参考文献
- Linux
内核源码,
include/net/ip_fib.h,6.8(fib_table 于第 253 行,fib_info 于第 134 行,fib_nh_common 于第 81 行) - Linux
内核源码,
include/net/fib_rules.h,6.8(fib_rule 于第 20 行,fib_rules_ops 于第 60 行) - Linux
内核源码,
include/net/nexthop.h,6.8(nexthop 于第 132 行,nh_group 于第 119 行) - Linux
内核源码,
include/net/route.h,6.8(rtable 于第 60 行,ip_route_output_flow 于第 148 行) - Linux
内核源码,
include/net/dst.h,6.8(dst_entry 于第 26 行) - Linux
内核源码,
include/net/ip6_fib.h,6.8(fib6_table 于第 385 行,fib6_node 于第 73 行) - David S. Miller, “Removal of the route cache”, LWN.net, 2012
- Jakub Kicinski, “Resilient nexthop groups”, LWN.net, 2021
下一篇:Netfilter 内核实现:钩子、conntrack 与 NAT
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Linux 网络子系统深度拆解】IP 层内核实现:路由查找、分片与转发
IP 层是 Linux 网络栈的中枢——收包时决定本地投递还是转发,发包时查路由、过 Netfilter、做分片。本文从 Linux 6.6 内核源码出发,拆解 ip_rcv → 路由决策 → ip_local_deliver / ip_forward 的完整路径,深入 FIB 表的 LC-trie 实现、策略路由 ip rule 选表机制、IP 分片/重组状态机、PMTU 发现与 FNHE 缓存,以及 Netfilter 五个钩子点的实际调用时机。
【Kubernetes 网络深度系列】路由与隧道:Linux 怎么决定一个包往哪走
Linux 路由表、FIB 查找、策略路由,以及 VXLAN/Geneve/WireGuard 隧道技术深度拆解
【Linux 网络子系统深度拆解】网络丢包定位:从 drop_monitor 到 kfree_skb 追踪
从内核源码拆解 Linux 网络丢包追踪的完整体系:kfree_skb tracepoint 与 80+ 种 drop_reason 枚举、drop_monitor netlink 子系统、dropwatch 工具、perf 丢包记录、bpftrace 丢包聚合脚本,以及生产环境常见丢包点速查表。
【Linux 网络子系统深度拆解】内核网络追踪工具箱:bpftrace/perf/ftrace 实战
从内核 tracepoint 定义出发,系统讲解 bpftrace、perf、ftrace 三大工具在网络诊断中的实战用法:TCP 重传根因分析、softirq 延迟定位、收发包路径延迟剖析、conntrack 表满监控、per-function 火焰图,以及各工具的适用场景与性能开销对比。