默认情况下,配置项autoconf都是1,即开启地址自动配置。

$ cat /proc/sys/net/ipv6/conf/all/autoconf
1
$ cat /proc/sys/net/ipv6/conf/default/autoconf
1
$ cat /proc/sys/net/ipv6/conf/ens33/autoconf
1$ cat /proc/sys/net/ipv6/conf/all/accept_ra_pinfo
1
$ cat /proc/sys/net/ipv6/conf/default/accept_ra_pinfo
1
$ cat /proc/sys/net/ipv6/conf/ens33/accept_ra_pinfo
1

如下代码可见,autoconf都初始化为1。

static struct ipv6_devconf ipv6_devconf __read_mostly = {.autoconf       = 1,.accept_ra_pinfo    = 1,static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {.autoconf       = 1,.accept_ra_pinfo    = 1,

另外,在IPv6模块加载时,可通过参数autoconf指定autoconf的默认值。

struct ipv6_params ipv6_defaults = {.disable_ipv6 = 0,.autoconf = 1,
};module_param_named(autoconf, ipv6_defaults.autoconf, int, 0444);
MODULE_PARM_DESC(autoconf, "Enable IPv6 address autoconfiguration on all interfaces");static int __net_init addrconf_init_net(struct net *net)
{/* these will be inherited by all namespaces */dflt->autoconf = ipv6_defaults.autoconf;dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;

前缀信息处理

接收到RA报文之后,如果其中包含前缀信息,并且当前接口配置项accept_ra_pinfo为真,遍历其中的前缀信息,由函数addrconf_prefix_rcv处理其中的每一项。

static void ndisc_router_discovery(struct sk_buff *skb)
{if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {struct nd_opt_hdr *p;for (p = ndopts.nd_opts_pi;p;p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {addrconf_prefix_rcv(skb->dev, (u8 *)p,(p->nd_opt_len) << 3,ndopts.nd_opts_src_lladdr != NULL);}}

首先,检查前缀地址类型,对于多播或者链路本地类型,不进行地址自动配置。前缀的prefered时长不应大于valid有效时长。

void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
{struct prefix_info *pinfo;pinfo = (struct prefix_info *) opt;/*  Validation checks ([ADDRCONF], page 19)*/addr_type = ipv6_addr_type(&pinfo->prefix);if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL))return;valid_lft = ntohl(pinfo->valid);prefered_lft = ntohl(pinfo->prefered);if (prefered_lft > valid_lft) {net_warn_ratelimited("addrconf: prefix option has invalid lifetime\n");return;}in6_dev = in6_dev_get(dev);if (!in6_dev) {net_dbg_ratelimited("addrconf: device %s not configured\n", dev->name);return;}

对于链路本地前缀信息,增加路由表项。由于前缀信息的有效valid时长单位为秒值,而路由项的计时单位为jiffies,参见路由回收函数fib6_age中的处理,这里需要进行转换。将u32类型的valid_lft秒值,转换为unsigned long类型的rt_expires值。

如果valid_lft值为0xffffffff,表示永不超时。对于32位架构的处理器,将u32秒值转换为同为32位的long型jiffies值,很可能导致溢出,由addrconf_timeout_fixup进行处理。

    if (pinfo->onlink) {struct fib6_info *rt;unsigned long rt_expires;/* Avoid arithmetic overflow. Really, we could save rt_expires in seconds, likely valid_lft,* but it would require division in fib gc, that it not good.*/if (HZ > USER_HZ)rt_expires = addrconf_timeout_fixup(valid_lft, HZ);elsert_expires = addrconf_timeout_fixup(valid_lft, USER_HZ);if (addrconf_finite_timeout(rt_expires))rt_expires *= HZ;

如果前缀对应的路由项已经存在,更新其超时时间,如果valid_lft为零,删除路由表项。如果valid_lft为永不超时,停止路由表项的超时检测。另外,如果表项不存在,并且valid_lft有效时间不为零,为前缀创建新的路由表项。

        rt = addrconf_get_prefix_route(&pinfo->prefix, pinfo->prefix_len, dev,RTF_ADDRCONF | RTF_PREFIX_RT, RTF_DEFAULT, true);if (rt) {/* Autoconf prefix route */if (valid_lft == 0) {ip6_del_rt(net, rt, false);rt = NULL;} else if (addrconf_finite_timeout(rt_expires)) {fib6_set_expires(rt, jiffies + rt_expires); /* not infinity */} else {fib6_clean_expires(rt);}} else if (valid_lft) {clock_t expires = 0;int flags = RTF_ADDRCONF | RTF_PREFIX_RT;if (addrconf_finite_timeout(rt_expires)) {/* not infinity */flags |= RTF_EXPIRES;expires = jiffies_to_clock_t(rt_expires);}addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,0, dev, expires, flags, GFP_ATOMIC);}fib6_info_release(rt);}

如果前缀信息设置了autoconf标志,并且设备的autoconf配置项为真,在前缀长度为64的情况下,根据前缀信息生成接口地址。由以下几种情况:

1) 接口的token有值(ip token set命令),将token地址的后8字节最为接口ID,生成地址;
2) 接口地址生成模式为STABLE,由函数ipv6_generate_stable_address生成地址;
3) 否则,生成EUI64地址;
4) 如果以上都不成立,使用上次上次的EUI64格式的接口ID,生成地址。

   /* Try to figure out our local address for this prefix */if (pinfo->autoconf && in6_dev->cnf.autoconf) {struct in6_addr addr;bool tokenized = false, dev_addr_generated = false;if (pinfo->prefix_len == 64) {memcpy(&addr, &pinfo->prefix, 8);if (!ipv6_addr_any(&in6_dev->token)) {read_lock_bh(&in6_dev->lock);memcpy(addr.s6_addr + 8, in6_dev->token.s6_addr + 8, 8);read_unlock_bh(&in6_dev->lock);tokenized = true;} else if (is_addr_mode_generate_stable(in6_dev) &&!ipv6_generate_stable_address(&addr, 0, in6_dev)) {addr_flags |= IFA_F_STABLE_PRIVACY;goto ok;} else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {goto put;} else {dev_addr_generated = true;}goto ok;}net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n", pinfo->prefix_len);goto put;

以下配置生成的地址。

ok:err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev,&addr, addr_type, addr_flags, sllao,tokenized, valid_lft, prefered_lft);if (err) goto put;/* Ignore error case here because previous prefix add addr was* successful which will be notified.*/ndisc_ops_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr,addr_type, addr_flags, sllao, tokenized, valid_lft,prefered_lft, dev_addr_generated);}

前缀地址配置

在上节生成前缀地址之后,由函数addrconf_prefix_rcv_add_addr进行配置处理。首先,检查是否已经配置了此地址,如果没有,并且要配置地址的有效时间valid_lft不为零,尝试配置此地址。

int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,const struct prefix_info *pinfo, struct inet6_dev *in6_dev,const struct in6_addr *addr, int addr_type,u32 addr_flags, bool sllao, bool tokenized,__u32 valid_lft, u32 prefered_lft)
{struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);int create = 0;if (!ifp && valid_lft) {int max_addresses = in6_dev->cnf.max_addresses;struct ifa6_config cfg = {.pfx = addr,.plen = pinfo->prefix_len,.ifa_flags = addr_flags,.valid_lft = valid_lft,.preferred_lft = prefered_lft,.scope = addr_type & IPV6_ADDR_SCOPE_MASK,};#ifdef CONFIG_IPV6_OPTIMISTIC_DADif ((net->ipv6.devconf_all->optimistic_dad || in6_dev->cnf.optimistic_dad) &&!net->ipv6.devconf_all->forwarding && sllao)cfg.ifa_flags |= IFA_F_OPTIMISTIC;
#endif

只有在接口当前的地址数量小于配置的最大数量max_addresses时,才能新增地址。对于新增地址,需要启动DAD检测。

        /* Do not allow to create too much of autoconfigured* addresses; this would be too easy way to crash kernel.*/if (!max_addresses || ipv6_count_addresses(in6_dev) < max_addresses)ifp = ipv6_add_addr(in6_dev, &cfg, false, NULL);if (IS_ERR_OR_NULL(ifp)) return -1;create = 1;spin_lock_bh(&ifp->lock);ifp->flags |= IFA_F_MANAGETEMPADDR;ifp->cstamp = jiffies;ifp->tokenized = tokenized;spin_unlock_bh(&ifp->lock);addrconf_dad_start(ifp);}

以下,如果地址存在,或者以上操作新建了地址(create等于1),首先看一下是否需要更新以存在地址的valid有效时长。如果地址项自身的valid时长大于已经逝去的时长,地址还在有效期内,将新的valid和prefered时长更新到地址项中。清除DEPRECATED标记,如果地址为非TENTATIVE临时地址,发送RTM_NEWADDR事件通知。

以上的地址valid时间更新操作与RFC4862中表述的不一致,RFC4862为了防止伪造的RA包含过短的valid时长,造成地址的过早失效,定义了以下三种情况:

1) 如果前缀中的valid时长大于2个小时,或者大于接口已有地址中剩余的有效时长,将前缀中valid更新到地址中的valid_lft字段;
2) 如果地址中剩余有效时长小于等于2个小时,忽略前缀中的valid值,不更新。除非前缀所在的RA是经过认证的报文(如SEND);
3) 以上情况都不成立,将地址中的valid时长设置为2个小时。

但是,内核中为了及时响应地址重新配置,尽快去除旧地址,未采用RFC中的做法。也许这里可以设置一个配置项,来决定采用哪种方式。

    if (ifp) {/* Update lifetime (RFC4862 5.5.3 e)* We deviate from RFC4862 by honoring all Valid Lifetimes to* improve the reaction of SLAAC to renumbering events* (draft-gont-6man-slaac-renum-06, Section 4.2)*/now = jiffies;if (ifp->valid_lft > (now - ifp->tstamp) / HZ)stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;elsestored_lft = 0;if (!create && stored_lft) {ifp->valid_lft = valid_lft;ifp->prefered_lft = prefered_lft;ifp->tstamp = now;flags = ifp->flags;ifp->flags &= ~IFA_F_DEPRECATED;if (!(flags&IFA_F_TENTATIVE)) ipv6_ifa_notify(0, ifp);}manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft, create, now);in6_ifa_put(ifp);addrconf_verify();

接口token设置

如下ip命令所示。

# ip token set ::0102:0304 dev ens33
#
# ip token list
token ::1.2.3.4 dev ens33
token :: dev ens34
token :: dev ens35

内核版本 5.10

IPv6地址自动配置相关推荐

  1. 配置无状态IPv6地址自动配置基础实验

    实验描述 两台路由器设备相连,其中AR2的接口GE0/0/0能够自动获取IPv6地址,从而实现设备之间通信. 实验思路 为了能让AR2的接口GE0/0/0自动获取IPv6地址,该AR2的接口GE0/0 ...

  2. 闲谈IPv6-一起玩转IPv6地址自动配置

    昨夜梦里惊魂,1997年,安阳市文峰中路老口腔医院门口那个卖冰糖葫芦的老人,他死了.1997年我刚上初中,他已经是老人了,我上学路上老是碰到他,却从没有买过他一个冰糖葫芦-现在,他死了,那个卖洗衣膏的 ...

  3. ipv6笔记无状态地址自动配置及状态

    当配置一个新的ipv6地址时,会进入到tentative 阶段,会发送以自己IP地址为目的地址的请求报文,一秒后,如果没响应,就没有冲突,就可以用. 生命周期是针对动态获取的IP的,但是tentati ...

  4. IPv6技术精要--第9章 无状态地址自动配置SLACC

    文章目录 本章要点: 9.1 RA消息和SLAAC 9.2 WinPC的EUI-64和随机生成接口ID 9.3 IPv6地址的隐私问题 [学习资料] [01学习笔记] [02课程实验] [03其他资料 ...

  5. IPv6接口自动配置的地址数量

    默认情况下,每个接口最大可自动配置16个地址.如下PROC文件max_addresses中的值,如果设置为0,将不限制地址数量. $ cat /proc/sys/net/ipv6/conf/all/m ...

  6. linux配置端口ipv6地址,linux配置ipv6地址命令

    linux配置ipv6地址命令 模块加载: /etc/module.conf中增加下列行: alias net-pf-10 ipv6 配置ipv6: /etc/sysconfig/network中增加 ...

  7. ipv6地址概述——配置ipv6

    个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能. 个人爱好: 编程,打篮球,计算机知识 个人名言:海不辞水,故能成其大:山不辞石,故能成其高. 个人主 ...

  8. RH358管理DHCP和IP地址分配--配置分配IPv6地址

    RH358管理DHCP和IP地址分配–配置分配IPv6地址 本章节介绍配置DHCP分配IPv6地址,虽然还是有点鸡肋,但可以作为了解和学习用. 专栏地址:https://blog.csdn.net/q ...

  9. IPv6 地址获取方式及工具

    IPv6 地址获取方式及工具 配置 make config ----------> kernel config ------------>networking support ------ ...

最新文章

  1. C++入门教程,全套C++基础教程(已更新完毕)
  2. MongoDB Modifiers(原子操作)
  3. 为什么文件会自动恢复成旧文件? -- windows server 2003
  4. MySQL学习笔记07【事务、用户管理和权限管理】
  5. JEECG v3视频陆续更新
  6. mysql环境变量的配置
  7. java 综合练习_Java第二季6-1综合练习作业
  8. ZOJ 2859 二维RMQ(模板)
  9. ong拼音汉字_汉语拼音ang-ong(教案)
  10. 论文分享(2)MASAD: A Large-Scale Dataset for Multimodal Aspect-Based Sentiment Analysis
  11. RNA-Seq HISAT+ HTSeq + DESeq2流程 及测序深度和质控问题讨论
  12. 数据分析之实战项目——电商用户行为分析【python】
  13. 微信公众号数据2019_2019年微信公众号文章数据报告
  14. 【Java基础】之 哈希值
  15. 「C++ MFC」 “多媒体定时器实例”讲解
  16. 怎么在html中创建单选按钮?
  17. 2022年全球市场筒仓称重系统总体规模、主要生产商、主要地区、产品和应用细分研究报告
  18. vivado下载错误 core_34 is an invalid handle
  19. JQuery插件Validation的使用-遁地龙卷风
  20. 【PHP】`异客塞尔`世界 与 神奇的字符串++

热门文章

  1. 弘辽科技:淘宝新店扶持是人人享有的权益吗?
  2. 3208点阵时钟c语言程序,点阵万年历(带时间、年月日星期调整及闹钟功能)C程序...
  3. linux npm全局安装路径,NPM全局路径的设置及NRM的使用
  4. Single-Shot Object Detection with Enriched Semantics 论文笔记
  5. 预估期刊2021年的影响因子
  6. 计算机音乐文爱图片,CG/贺敬轩《文爱》[FLAC/MP3-320K]
  7. ios代码中的内存泄露,内存检测工具leaks 检测不出来
  8. 一道受用终身的测试题
  9. 基于k-近邻算法的室内WiFi位置指纹定位实验报告
  10. 关于计算机的英语演讲稿三分钟,三分钟电脑的英文演讲稿5篇.doc