Ovear's Blog

I'am Ovear,Ovear is me.

Theme Refrain made by Eiko

Proudly powered by WordPress

OpenWrt中降低preferred_time和valid_time增加IPv6的稳定性

前言

2024年的今天,在各方积极的推动下,大陆IPv6的覆盖率已经非常高了;三大运营商新开的家庭宽带已经默认开通了IPv6,现存的宽带也通过改造、下发配置、设备更新的方式在迅速的推进中;而移动端则更是激进,在4G部署时就已经是按照双栈网路配置,可以说是完全覆盖,连二级热点都已经做足了充分的兼容。

就在几天前,工信部还发了一份《两部门关于开展“网络去NAT”专项工作 进一步深化IPv6部署应用的通知》的文件,其中的目标是在进一步的推进IPv6的积极应用的同时,开始为IPv4的NAT网路去除做准备(目前阶段主要关注NAT44设备规模停止增长),同时也提出了IPv6子网标准化要求,要求对IPv6网路的二次分发功能做好兼容。

Ovear自己家的网络在大约五六年前,三大运营商开通IPv6基本厘清大陆内的交换路由后,就实装进来了,也算是见过IPv6早期混乱路由的资深用户了(笑)。

PS:目前IPv6的国内路由已经完全可以满足日常使用了,可以说IPv6在移动端更是主要承载网络;除此之外,得益于政策支撑,IPv6跨境路由方面已经不逊于IPv4了,有些情况下因为使用的人稍微少一些,更是超过了IPv4。

问题描述

废话扯了这么久,还是快点进入正题吧。首先要了解这个问题,必须得知道两个前提;IPv6的SLAAC最小子网大小要求是/64
问:这个子网大小是什么概念呢?
答:这个大小比目前整个IPv4的所有地址加起来还要大。

那么这么大的子网,系统为了保护用户隐私,就会利用SLAAC为系统分配该子网下的多个IPv6地址作为临时地址,每段时间就会变更一次。同时因为IPv6极力不推荐使用NAT,就有可能出现一个内网广播域中有多个IPv6子网。

为了更好的管理这些IPv6地址,IPv6引入了两个概念:preferred_timevalid_time。在Linux中可以使用ip -6 ad来确认对应IPv6地址对应的两个时间。其中preferred_lft就是剩余的preferred_time,而valid_lft对应的就是valid_time,如下面的示例所示。

2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
    inet6 fdbd:ffff:ffff::fff/128 scope global dynamic noprefixroute 
       valid_lft 42525sec preferred_lft 1533sec

那么具体而言,IPv6地址的生命周期可大概分为两部分,其中:

  • 0 - preferred_time的这段时间为IPv6的有效使用时间:即该地址有效,且系统会积极使用该地址
  • preferred_time - valid_time则是该IPv6的非积极有效时间:即该地址有效,但是系统会极力避免使用该地址。
  • 通常preferred_timevalid_time的一半。

需要注意:preferred_timevalid_time可以通过RA更新。即和IPv4一样,IPv6地址也可以进行“续租”。其具体时间应该是从preferred_leftpreferred_time一半的时间开始续约(具体记不清了,有一个T0的时间点;如果我记错了还劳烦大家纠正;除此之外还有一个隐私地址机制IPv6 privacy extension,这个机制开启后系统会定期分配新的地址使用),即在时间到达valid_time的1/4时系统就会开始积极续约。

对于Linux系统来说每当preferred_lft到达0以后,在ip -6 ad中可以观察到地址被标记为deprecated,意味着对于新发起的连接,系统会尽量避免使用该地址;而当preferred_lft为0后,则会将该地址从系统中删除。

虽迟但到

Design is prefect, 一切都是这么的完美,会有什么意外呢?不出意外的话,就要出现意外了。

确实在大多数情况下,这套机制能非常完美的工作,但是现实总是没那么完美。当以下问题出现时,这套机制就可能发生问题:

  • 路由器[异常/非异常]重启:对于OpenWRT 21.02来说,只要路由器重启了,旧的IPv6地址就不能正常弃用;其实也很好理解,重启后的路由就不知道之前的老的IPv6地址了,应当在重启之前将所有IPv6地址弃用,希望后续版本改进了这个问题。
  • Linux在使用Network Manager时,调整了关于temp_addrkernel sysctl:理论上temp_addr应当由内核协议栈处理,但是使用Network Manager之后,这部分逻辑似乎会有不同步的情况发生。解决该问题需要统一在Network Manager中配置,而不是系统内核。目前在Arch Linux中遇到过。
  • 局域网内丢包:该问题常见于移动设备设备休眠,感觉理论上还是内核处理不当。当系统未收到路由RA时对于某些IPv6段的弃用申明时,就会发生。或者中间交换机重启的时间,刚好和路由器拨号时间重叠,也会遇到这个问题,只能等待下次路由器RA广播时希望系统收到后能解决。
  • 其他BUG:在Network Manager已经设定正确的上述情况下,偶发也会出现只有部分地址deprecated的问题。目前也仅在Arch Linux+Network Manager中遇到,其中同网段的一个地址被正确弃用了,但是另外一个地址却没有被弃用,非常奇怪;只能怀疑不是Kernel的问题就是Network Manager的问题。Network Manager出现问题的可能性会大一些,局域网内其他Linux(Debian/Ubuntu)设备都没遇到这样的问题;Debian默认用的ifupdown,Ubuntu则是自己的私货netplan。临时解决方法当然就是重启网络。
  • 启动DHCPv6 Statful以后:DHCPv6获取的地址是由DHCP客户端管理的,所以大概率要看DHCP客户端对RA的响应了。目前来看是会慢好几拍才会变,可能还是得看看DHCPv6的租约设定。不过由于系统一般都是会优先使用SLAAC的地址,所以问题倒是还不大。

以上仅仅是Ovear个人遇到的一部分情况,可能还有更多的情况没有列出,欢迎大家一起补充。

解决方案

经过抓包诊断,发现Ovear家电信的宽带设定的时间较长,preferred_time设置为2天,valid_time设置为3天;而为了避免运营商强制断线影响到网路正常使用,目前设定的都是每隔24小时自动重播一次,所以可能加剧了这个问题;移动也是类似情况;而联通则都设定为1个小时,联通宽带也基本没有没有观测到这个问题;因此可以初步考虑将电信和移动宽带的设置统一为和联通一样。

明确了原因之后,问题就很好解决了:

  • 丢包问题:降低非请求RA广播间隔,降低RA有效时间
  • IPv6有效期时间过长,降低最大preferred_timevalid_time

通过查询OpenWrt官方文档[1]可知,相关的参数为为:

  • ra_minintervalra_maxinterval:控制RA广播间隔
  • preferred_lifetime:控制最大的preferred_time
  • ra_useleasetime:是否设定valid_timeDHCP租约时间
  • ra_lifetime:路由器发送的RA有效期

相关代码

# https://github.com/openwrt/odhcpd/blob/a29882318a4ccb3ae26f7cc0145e06ad4ead224b/src/router.c#L600-L614

        if (addr->preferred_lt > (uint32_t)now) {
            preferred_lt = TIME_LEFT(addr->preferred_lt, now);

            if (preferred_lt > iface->preferred_lifetime) {
                /* set to possibly user mandated preferred_lt */
                preferred_lt = iface->preferred_lifetime;
            }
        }

        if (addr->valid_lt > (uint32_t)now) {
            valid_lt = TIME_LEFT(addr->valid_lt, now);

            if (iface->ra_useleasetime && valid_lt > iface->dhcp_leasetime)
                valid_lt = iface->dhcp_leasetime;
        }

其中ra_minintervalra_maxinterval支持在luci界面中设定,设定位置为Network=>Interfaces=>lan=>IPv6 RA Settings。

为了方便统一设定,也可以使用编辑文件的方式设定,编辑OpenWrt中的/etc/config/dhcp,找到对应配置文件段落,编辑为所需要的内容。

# /etc/config/dhcp
config dhcp 'lan'
    option ra_mininterval 60
    option ra_maxinterval 300
    option leasetime '1h'  # DHCPv4租约时间
    option ra_lifetime 600 # 路由RA有效期
    option ra_useleasetime 1 # 使用DHCPv4租约时间作为IPv6 valid_time
    option preferred_lifetime '30m' # IPv6 preferred_time

设定完成并重启后可以使用tcpdump/wireshark等工具抓包查看是否生效。

# tcpdump -i br-lan -n -vv "icmp6 && ip6[40] == 134"

这样理论上应该能将无效的IPv6地址生命周期控制在可接收范围内。

参考文章

[1] [OpenWrt Wiki] odhcpd
[2] openwrt/immortalwrt修改odhcpd ipv6 preferred_lifetime和valid_lifetime - 海运的博客

OpenWrt中降低preferred_time和valid_time增加IPv6的稳定性没有评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

© 2024 Ovear's Blog All rights reserved.