综述
fib tie是一个多叉树。其原理可以简化为:
根据前缀每一位是0还是1, 决定走左子树还是右子树。
绝大部分路由树,都没有那么多条目路由,所以再优化下,
- 多个联系节点只有一个子树,如
仅有两条路由:8.8.2.0/24和8.8.3.0/24, 前面16+6位都完全一样。
- 多级子树转换成一个节点下多个孩子。
仅有5条路由:
1 2 3 4 5
| 8.8.4.0/24 8.8.5.0/24 8.8.6.0/24 8.8.7.0/24 8.8.8.0/24
|
这就催生了Fib compress tree的产生。

fib Trie 节点类型
- Trie:是个多叉树。
- Tnode/key vector: 每个树节点是个,包含两个关键信息
- 前缀: 前缀相同继续找孩子。前缀不同,则回溯。
- 孩子: 前缀相同,根据指定区域位的值,继续在子树里查找。
- Leaf:
- 每个叶子节点是路由终点,但不是对应路由条目。
- 每个叶子节点有一个FA节点链在一起的list链。
- Fib alias:
- Fib info: 与fib alias一一对应?
- Fib nh:
- 对应每条路由下的每个下一跳。
- ECMP场景下,会有多个下一跳。

Fib Trie 查找逻辑:
- 根据目的IP地址,从Trie树根节点开始查找。
- 遍历每个节点
- 如果前缀不相同,回溯上级节点,查找其他膝兄弟节点
- 如果前缀相同, 查找孩子节点(子树)
- 到达叶子节点:
遍历FA, 查找最长路径匹配。
TODO:
节点类型
trie
1 2 3 4 5 6
| 167 struct trie { 168 struct key_vector kv[1]; 169 #ifdef CONFIG_IP_FIB_TRIE_STATS 170 struct trie_use_stats __percpu *stats; 171 #endif 172 };
|
fib trie的入口节点,自带一个根节点kv[1]。
1
| struct key_vector kv[1];
|
tnode
1 2 3 4 5 6 7 8
| 134 struct tnode { 135 struct rcu_head rcu; 136 t_key empty_children; /* KEYLENGTH bits needed */ 137 t_key full_children; /* KEYLENGTH bits needed */ 138 struct key_vector __rcu *parent; 139 struct key_vector kv[1]; 140 #define tn_bits kv[0].bits 141 };
|
【AI总结】tnode 是 key_vector 的”管理外壳”。
empty_children 和 full_children 这两个计数器实时追踪子节点的填充状态,
让 resize() 能快速判断是否需要 inflate(扩大扇出)、halve(缩小扇出)或 collapse(合并节点)。
查找热路径完全绕开 tnode,只在 key_vector 层面操作。
注:只有中间节点有tnode, Leaf节点是纯key_vector.
key_vector
1 2 3 4 5 6 7 8 9 10 11 12
| 121 struct key_vector { 122 t_key key; 123 unsigned char pos; /* 2log(KEYLENGTH) bits needed */ 124 unsigned char bits; /* 2log(KEYLENGTH) bits needed */ 125 unsigned char slen; 126 union { 127 /* This list pointer if valid if (pos | bits) == 0 (LEAF) */ 128 struct hlist_head leaf; 129 /* This array is valid if (pos | bits) > 0 (TNODE) */ 130 DECLARE_FLEX_ARRAY(struct key_vector __rcu *, tnode); 131 }; 132 };
|
key_vector 是fib路由的核心数据结构。
【中间节点】
- key:路由前缀
- pos+bits:从pos开始的bits, 根据数值/下标,去key_vector数组里去找child/子树
- slen:子树中最大的后缀长度 (32-plen) max(pos, 子树中最大的 slen)
- struct key_vector *tnode:

【叶子】
- Leaf链接相同前缀的所有路由Fib alias。
- pos = 0
- bit = 0
- slen:挂载路由的最大(fa_slen)
fib_alias
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| struct fib_alias { struct hlist_node fa_list; // 链表节点 struct fib_info *fa_info; // 指向路由详细信息 dscp_t fa_dscp; // DSCP 匹配条件 u8 fa_type; // 路由类型, local,broadcast等 u8 fa_state; // 状态标记 u8 fa_slen; // 后缀长度 = 32 - prefix_len u32 tb_id; // 路由表 ID s16 fa_default; // 默认路由选择索引 u8 offload; // 硬件卸载标记 u8 trap; // 硬件 trap 标记 u8 offload_failed; // 硬件卸载失败标记 struct rcu_head rcu; // RCU 延迟释放 };
|
例:一个Leaf下有三条不同掩码和table的路由。
1 2 3
| 192.168.1.1/32, local表 192.168.1.0/24, main表 192.168.0.0/16, main表
|
fa_slen从小到大排序(前缀越长越靠前),查找时优先匹配最长前缀。
1 2 3 4 5 6 7 8 9 10 11 12
| Leaf (key_vector, bits=0) │ └── leaf (hlist_head) │ ├── fib_alias [fa_slen=0, tb_id=255] ──→ fib_info ──→ fib_nh[] │ (192.168.1.1/32, local表) │ ├── fib_alias [fa_slen=8, tb_id=254] ──→ fib_info ──→ fib_nh[] │ (192.168.1.0/24, main表) │ └── fib_alias [fa_slen=16, tb_id=254] ──→ fib_info ──→ fib_nh[] (192.168.0.0/16, main表)
|
fa_state:
目前就是一个 1-bit 的 “脏标记”.
核心目的是:在路由变更时,避免对从未使用过的路由执行不必要的缓存刷新。
目前只用了 bit 0(FA_S_ACCESSED)。
fib_info
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| struct fib_info { struct hlist_node fib_hash; // 全局 fib_info 哈希表节点(按路由属性去重) struct hlist_node fib_lhash; // local地址哈希表节点(按 fib_prefsrc 索引) struct list_head nh_list; // 挂载到 nexthop->fi_list,用于 nexthop 更新时遍历 struct net *fib_net; // 所属 netname space refcount_t fib_treeref; // Trie 树引用计数(fib_alias 引用) refcount_t fib_clntref; // 外部引用计数(路由缓存等) unsigned int fib_flags; // 路由标志, 如 RTNH_F_DEAD, RTNH_F_ONLINK unsigned char fib_dead; // 标记路由已删除,阻止新引用 unsigned char fib_protocol; // 路由来源协议, 如 RTPROT_STATIC, RTPROT_BOOT unsigned char fib_scope; // 路由作用域, 如 RT_SCOPE_LINK, RT_SCOPE_UNIVERSE unsigned char fib_type; // 路由类型, 如 RTN_UNICAST, RTN_LOCAL __be32 fib_prefsrc; // 首选源地址(src hint) u32 fib_tb_id; // 路由表 ID, main,local or 其他 u32 fib_priority; // 路由优先级(metric) struct dst_metrics *fib_metrics; // 路由度量指标(MTU, MSS, RTT等) #define fib_mtu fib_metrics->metrics[RTAX_MTU-1] #define fib_window fib_metrics->metrics[RTAX_WINDOW-1] #define fib_rtt fib_metrics->metrics[RTAX_RTT-1] #define fib_advmss fib_metrics->metrics[RTAX_ADVMSS-1] int fib_nhs; // 下一跳数量(ECMP 场景 >1) bool fib_nh_is_v6; // 下一跳是否为 IPv6 地址 bool nh_updated; // nexthop 对象更新标记,触发路由重新评估 bool pfsrc_removed; // 首选源地址被删除标记 struct nexthop *nh; // 新版 nexthop 对象指针(与 fib_nh[] 互斥) struct rcu_head rcu; // RCU 延迟释放 struct fib_nh fib_nh[]; // 下一跳数组(传统模式, 长度 = fib_nhs) };
|
fib_nh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 107 struct fib_nh { 108 struct fib_nh_common nh_common; 109 struct hlist_node nh_hash; 110 struct fib_info *nh_parent; 111 #ifdef CONFIG_IP_ROUTE_CLASSID 112 __u32 nh_tclassid; 113 #endif 114 __be32 nh_saddr; 115 int nh_saddr_genid; 116 #define fib_nh_family nh_common.nhc_family 117 #define fib_nh_dev nh_common.nhc_dev 118 #define fib_nh_dev_tracker nh_common.nhc_dev_tracker 119 #define fib_nh_oif nh_common.nhc_oif 120 #define fib_nh_flags nh_common.nhc_flags 121 #define fib_nh_lws nh_common.nhc_lwtstate 122 #define fib_nh_scope nh_common.nhc_scope 123 #define fib_nh_gw_family nh_common.nhc_gw_family 124 #define fib_nh_gw4 nh_common.nhc_gw.ipv4 125 #define fib_nh_gw6 nh_common.nhc_gw.ipv6 126 #define fib_nh_weight nh_common.nhc_weight 127 #define fib_nh_upper_bound nh_common.nhc_upper_bound 128 };
|