Linux 内核 Nexthop 对象化: Patch 4c7e8084 深度解析

一、问题起源 (Problem Origin)

2.1 性能瓶颈 (Performance Bottleneck)

David Ahern 在 2017 年左右开始构思 nexthop 对象化 (nexthop objectification),核心动机来自大规模路由注入的性能瓶颈 (large-scale route injection performance bottleneck)。当时的状况:

场景 (Scenario) 耗时 (Duration)
注入 70 万+ IPv4 路由,单路径 (single path) 18 秒
注入 70 万+ IPv4 路由,4 路径 ECMP 28 秒
注入 70 万+ IPv4 路由,16 路径 ECMP 72+ 秒

时间随 ECMP (Equal-Cost Multi-Path,等价多路径) 路径数急剧膨胀,这对 BGP 大表 (full internet table,完整互联网路由表) 场景完全不可接受。

Read More

ECMP选路: fib_select_multipath Hash 机制深度解析

一、概述 (Overview)

Linux 内核中的 ECMP(Equal-Cost Multi-Path)选路机制允许将流量分布到多条等价路径上。

核心实现主要分为两个阶段, 分别对应两个函数。

  1. hash计算 : fib_multipath_hash()net/ipv4/route.c)—— 计算 hash 值
    如根据skb或flow的五元组、三元组等策略,计算出hash值。
  2. 根据hash选择nexthop: fib_select_multipath()net/ipv4/fib_semantics.c)—— 用 hash 选 nexthop

本文基于 Linux v6.16 源码,逐层剖析 ECMP 的 hash 计算与路径选择全流程。
使用coebuddy+claude-4.6-ops辅助生成。

二、整体架构 (Architecture)

2.1 内部流程

Read More

fib data struct

综述

fib tie是一个多叉树。其原理可以简化为:
根据前缀每一位是0还是1, 决定走左子树还是右子树。
绝大部分路由树,都没有那么多条目路由,所以再优化下,

  1. 多个联系节点只有一个子树,如
    仅有两条路由:8.8.2.0/248.8.3.0/24, 前面16+6位都完全一样。
  2. 多级子树转换成一个节点下多个孩子。
    仅有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 data struct overview

Read More

linux内核多路由表与策略路由的实现

使用场景

接上篇文章,我们提到在网口eth1 上增加一个 ip 地址,内核会生成 3 个路由。

  • local 路由表:增加/32 主机路由
  • main 路由表:增加本网段前缀的网络路由

为什么需要使用local/main 两个路由表呢? ip地址引发的路由变化或者手动添加一个目的网段的路由,这些场景看起来,完全可以使用一个路由表就能满足场景需求。那内核为什么要支持多个路由表?

其中一个场景就是 策略路由
比如公司有两个网关, 其中一个网关GW1,网络质量比较高。我们希望对网络质量有较高要求的个别网段的外访流量,走GW1.其他网段都用 GW2.
这时,我们就不能仅仅依赖目前 IP 去做路由。我们需要:

  • 先根据源 IP 地址做一次筛选,选中的这部分流量,缺省路由到GW1.
  • 没有选中的走我们的正常路由查找,其中缺省路由到GW2。
  • 这两个缺省路由只能放到两个单独的路由表里。

那下面详细展开介绍下策略路由及内核实现。

策略路由

策略路由是一个按优先级排列的规则链表。路由查找时,按优先级顺序遍历链。

每个策略路由规则,由match+action两部分组成。 match 也被称为SELECTOR

  1. 每个规则的match条件,可以支持多种字段,如源IP/协议/协议源端口/目的端口等,也可以这多个字段的组合。如果满足规则匹配条件就执行规则指定的动作。
  2. 每个策略路由的action有下面几种:
    • table X:查找对应的路由表 table X。X 是 table ID 或者 table 名字。
    • goto xx:跳转到更低优先级到规则。
    • nat …:ip地址替换
  3. 所有的策略路由通过 IP rule 命令添加。
  4. 策略路由按照优先级插入到一个链表里。
  5. 按照优先级从高(数值最小)到低(数值大)按顺序排列。
  6. 通过 ip rule show命令我们可以看到每条rule对应的优先级。添加rule时候,默认的是当前除0以外最高优先级的值-1, 即默认新建的rule优先级高。

这部分用法详见ip rule命令帮助手册,
https://man7.org/linux/man-pages/man8/ip-rule.8.html

Read More

ping本机网口的IP地址,tcpdump在lo口才能抓到对应报文

问题背景

Q1:ping 本机eth1口的ip地址时,对应的ICMP报文会被发送到eth1口上吗?

实验: ping本机网口地址

ping本机网口地址实验

通过tcpdump抓包命令,我们发现报文被发送到lo上,而不是对应的物理口 eth1。
虽然ip 地址被配在本机的物理口上,但是报文并没有被发送到物理口。为了理解这个问题,我们需要先看下下面这个问题。

Q2: 当一个IP地址被添加到一个网口后,会引起哪些路由变化呢?
为一个网口增加一个 IP 地址是个很常见的网络操作。
具体添加方式有多种,包括命令行手动添加、通过配置文件在系统启动时候添加,还是通过dhcp自动获取并添加等。
无论哪种场景,添加完IP地址后,系统路由有什么变化。
场景:在本机物理口 eth1上配置192.168.8.8/24,网口状态正常(UP).

Read More

fib_unmerge:把local路由从main表里切割出来

fib_unmerge 是内核路由子系统中一个精巧的按需优化设计:

  • 默认状态下,local 表只是 main 表的别名,所有路由合并在一张表里,fib_lookup 只需查一次表,性能最优
  • 添加策略路由时fib_unmerge 将 local 路由从 main 表中切割出来,放入独立的 local 表,确保策略路由能正确匹配
  • 切割四步走:创建新 local 表 → 复制 local 路由 → 替换旧表 → 清理 main 表中的残留条目
  • 幂等设计:只在首次添加策略路由时执行分离,后续再添加时 fib_trie_unmerge 检测到已分离,直接返回

本文将从调用点入手,逐步分析 fib_unmerge 的实现细节。

Read More

lo口与local路由表

小实验

接上篇文章 ping本机网口的IP地址,tcpdump在lo口才能抓到对应报文,我们知道在网口eth1上增加一个IP地址192.168.8.8/24后,内核会生成3条路由:

  • local表:增加192.168.8.8/32的主机路由(RTN_LOCAL
  • main表:增加192.168.8.8/24的网段路由(RTN_UNICAST
  • local表:增加广播路由

其中主机路由类型是RTN_LOCAL,网段路由类型是RTN_UNICAST
那如果把IP地址配到lo口上,会有什么不同呢?

实验题目

  1. ping 本机 lo 口的IP地址 127.0.0.1 能ping通吗?
  2. ping 127.0.0.0/8 网段下的其他地址能ping通吗?
  3. 把一个任意IP地址配置到 lo 口上,又会有什么实验结果呢?

Read More