ipv6源地址选择原理
本文最后更新于:2025年11月19日 下午
引言
一个接口通常有多个ipv6地址(比如可能存在多个全球地址,多个链路本地地址)。那么设备从接口发包时它会使用哪个地址作为源地址呢?
假设场景如下:
我们设备是路由器,br0有两个链路本地地址:fe80::1/64, fe80::2/64。
路由器下的PC发送dns查询,目的地址为fe80::1,路由器的dns server回包时会使用哪个源地址,fe80::1,还是fe80::2呢?
dns server发包时使用的sendto函数,而且socket也没有bind到一个特定的地址上,选择哪个源地址取决于内核的选择。
如果使用了fe80::2,那么糟糕,PC可能认为源地址与自己发送的目的地址不一样,丢包,导致无法上网。
本文主要围绕上面问题来分析地址选择,而略过其他场景下的选择。
内核选择算法
选择源地址的函数从ipv6_dev_get_saddr开始。
use_oif_addrs_only
如果目的地址是组播、链路本地地址、或者接口的use_oif_addrs_only配置为1,那么源地址只会从发包接口选择。
1 | |
use_oif_addrs_only默认为0,即使用全球地址通信时,源地址不一定是出接口上的IP地址。
__ipv6_dev_get_saddr
评选出接口上评分最高的地址。这是选择地址的核心。
它通过以下一系列的标准,从上往下比较两个地址的评分。
1 | |
IPV6_SADDR_RULE_LOCAL
如果目的地址和接口地址相等,那么优先
IPV6_SADDR_RULE_SCOPE
使用范围的比较,这儿实际判断比较复杂,因为地址范围有好几个。
可以先简单理解为:在源地址范围大于等于目的地址范围的情况下,地址的使用范围越小,越优先。如果源地址范围小于目的地址范围,那么分值一样。
IPV6_SADDR_RULE_PREFERRED
地址优选期没到期的高于到期的,优选期到期的地址在ip命令里面能看到deprecated标识
IPV6_SADDR_RULE_OIF
优先使用出接口的地址
IPV6_SADDR_RULE_LABEL
如果label与目的地址相同,那么优选这个地址。
IPV6_SADDR_RULE_PRIVACY
优先使用隐私地址(临时地址)。可通过proc配置接口的use_tempaddr,默认为2,默认使用隐私地址。
也可通过sockopt配置。
IPV6_SADDR_RULE_ORCHID
不清楚
IPV6_SADDR_RULE_PREFIX
前缀长度,长度越长,越优先。
1 | |
选谁呢
fe80::1/64, fe80::2/64这两个地址,用上面的选择算法走一遍,发现都是一样的。比较不出来。。
那么用哪个呢?用第一个,看哪个在链表最前面。
可以简单的理解为使用最近时间添加的一个,如果fe80::2/64是最近添加的,那么就用它。
接口上的地址顺序
接口上添加地址的函数如下。
地址列表是按照scope排序的。scope表示地址的使用范围,比如常见的全球地址(0x0e)和链路本地地址(0x02)。scope高的在前面,同一scope则是后填加的地址在前面。
1 | |
我们通过ip命令查看到地址列表,和上面的顺序基本吻合
1 | |
dadfailed
添加一个ipv6地址时,默认会启动地址重复检查。如果dad检查失败,会有一条相关打印,ip命令看到的地址状态如下。
1 | |
dadfailed的地址是无法使用的。
左右源地址优先级
还是最初的场景,要是我们无论如何都想系统使用fe80::2,而不使用fe80::1。要怎么做呢?
从上面的分析看,有两种方法:
(1)添加fe80::1时,设置它的优选期为0就行了。
1 | |
(2)缩短地址的前缀长度
1 | |