IPv6路由信息序号保存在网络命名空间结构成员fib6_sernum中,其为递增的原子变量,到最大值时,再次由初始值1开始。

static int fib6_new_sernum(struct net *net)
{int new, old;do {old = atomic_read(&net->ipv6.fib6_sernum);new = old < INT_MAX ? old + 1 : 1;} while (atomic_cmpxchg(&net->ipv6.fib6_sernum, old, new) != old);return new;
}static int __net_init inet6_net_init(struct net *net)
{atomic_set(&net->ipv6.fib6_sernum, 1);

内核依据此值判断路由缓存的有效性,如下此判断是按照相等进行,不相等即认为路由缓存无效。

static inline int rt_genid_ipv6(const struct net *net)
{return atomic_read(&net->ipv6.fib6_sernum);
}
static bool rt6_is_valid(const struct rt6_info *rt6)
{return rt6->sernum == rt_genid_ipv6(dev_net(rt6->dst.dev));
}

路由树节点中序号

函数fib6_update_sernum更新路由节点结构中的序号。

void fib6_update_sernum(struct net *net, struct fib6_info *f6i)
{struct fib6_node *fn;fn = rcu_dereference_protected(f6i->fib6_node,lockdep_is_held(&f6i->fib6_table->tb6_lock));if (fn)fn->fn_sernum = fib6_new_sernum(net);
}

以下函数由当前节点开始,向路由树根遍历,更新每个路由节点的序号。

static void __fib6_update_sernum_upto_root(struct fib6_info *rt, int sernum)
{struct fib6_node *fn = rcu_dereference_protected(rt->fib6_node,lockdep_is_held(&rt->fib6_table->tb6_lock));/* paired with smp_rmb() in rt6_get_cookie_safe() */smp_wmb();while (fn) {fn->fn_sernum = sernum;fn = rcu_dereference_protected(fn->parent,lockdep_is_held(&rt->fib6_table->tb6_lock));}
}

以下两个函数为变体,其中fib6_update_sernum_stub在nexthop操作时使用。

void fib6_update_sernum_upto_root(struct net *net, struct fib6_info *rt)
{__fib6_update_sernum_upto_root(rt, fib6_new_sernum(net));
}
/* allow ipv4 to update sernum via ipv6_stub */
void fib6_update_sernum_stub(struct net *net, struct fib6_info *f6i)
{spin_lock_bh(&f6i->fib6_table->tb6_lock);fib6_update_sernum_upto_root(net, f6i);spin_unlock_bh(&f6i->fib6_table->tb6_lock);
}

路由变化序号更新

在路由信息改变时,序号增加序号值。如增加路由项信息时,成功完成之后,更新此新节点及其向路由树根方向的节点的序号值。

int fib6_add(struct fib6_node *root, struct fib6_info *rt,struct nl_info *info, struct netlink_ext_ack *extack)
{struct fib6_table *table = rt->fib6_table;struct fib6_node *fn, *pn = NULL;int sernum = fib6_new_sernum(info->nl_net);err = fib6_add_rt2node(fn, rt, info, extack);if (!err) {if (rt->nh)list_add(&rt->nh_list, &rt->nh->f6i_list);__fib6_update_sernum_upto_root(rt, sernum);fib6_start_gc(info->nl_net, rt);}

更新路由树全部序号

函数fib6_flush_trees仅用于更新所有树节点的序号。

static void fib6_flush_trees(struct net *net)
{int new_sernum = fib6_new_sernum(net);__fib6_clean_all(net, NULL, new_sernum, NULL, false);
}

如在删除接口地址时,更新路由树序号。

static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
{switch (event) {case RTM_DELADDR:...rt_genid_bump_ipv6(net);break;

另外,在用户层添加接口地址时,对于新加入的地址如果其设置了IFA_F_TENTATIVE标志,更新路由树的序号。

static int inet6_addr_add(struct net *net, int ifindex, struct ifa6_config *cfg, struct netlink_ext_ack *extack)
{ifp = ipv6_add_addr(idev, cfg, true, extack);if (!IS_ERR(ifp)) {...addrconf_dad_start(ifp);static void addrconf_dad_begin(struct inet6_ifaddr *ifp)
{if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||(net->ipv6.devconf_all->accept_dad < 1 && idev->cnf.accept_dad < 1) ||!(ifp->flags&IFA_F_TENTATIVE) || ifp->flags & IFA_F_NODAD) {bump_id = ifp->flags & IFA_F_TENTATIVE;addrconf_dad_completed(ifp, bump_id, send_na);static void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id, bool send_na)
{if (bump_id)rt_genid_bump_ipv6(dev_net(dev));static void addrconf_dad_work(struct work_struct *w)
{if (ifp->dad_probes == 0) {/* DAD was successful*/bump_id = ifp->flags & IFA_F_TENTATIVE;addrconf_dad_completed(ifp, bump_id, send_na);

接口变化更新路由序号

函数addrconf_notify监听设备状态的变化,由rt6_sync_up和rt6_sync_down_dev处理UP和DOWN。

static int addrconf_notify(struct notifier_block *this, unsigned long event, void *ptr)
{switch (event) {case NETDEV_UP:case NETDEV_CHANGE:if (event == NETDEV_UP) {} else if (event == NETDEV_CHANGE) {if (!addrconf_link_ready(dev)) {/* device is still not ready. */rt6_sync_down_dev(dev, event);break;}if (!IS_ERR_OR_NULL(idev)) {if (idev->if_flags & IF_READY) {rt6_sync_up(dev, RTNH_F_LINKDOWN);

接口UP时,遍历路由树中的路由信息,如果其不等于空,不是具有nexthop属性的路由,并且其下一跳出接口等于状态发生变化的接口,更新此路由信息以及由其到树根的路由节点的序号值。

void rt6_sync_up(struct net_device *dev, unsigned char nh_flags)
{   if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))arg.nh_flags |= RTNH_F_LINKDOWN;fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
}
static int fib6_ifup(struct fib6_info *rt, void *p_arg)
{const struct arg_netdev_event *arg = p_arg;struct net *net = dev_net(arg->dev);if (rt != net->ipv6.fib6_null_entry && !rt->nh &&rt->fib6_nh->fib_nh_dev == arg->dev) {rt->fib6_nh->fib_nh_flags &= ~arg->nh_flags;fib6_update_sernum_upto_root(net, rt);rt6_multipath_rebalance(rt);}return 0;
}

在处理DOWN事件时,遍历路由树中的路由信息,如果其为多径路由中的一员,并且多径路由中的路径没有全部失效,更新其所在节点的序号。

void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
{if (net->ipv6.sysctl.skip_notify_on_dev_down)fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);elsefib6_clean_all(net, fib6_ifdown, &arg);
}
static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
{if (rt == net->ipv6.fib6_null_entry || rt->nh)return 0;switch (arg->event) {case NETDEV_DOWN:if (rt6_multipath_uses_dev(rt, dev)) {unsigned int count;count = rt6_multipath_dead_count(rt, dev);if (rt->fib6_nsiblings + 1 == count) {rt6_multipath_flush(rt);return -1;}rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);fib6_update_sernum(net, rt);

路由缓存

对于重定向或者PMTU更新创建的exception缓存,更新对应的路由信息的路由节点的序号,表示其发生变化。

static int rt6_insert_exception(struct rt6_info *nrt, const struct fib6_result *res)
{struct rt6_exception_bucket *bucket;struct fib6_info *f6i = res->f6i;struct rt6_exception *rt6_ex;rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);rt6_ex->rt6i = nrt;rt6_ex->stamp = jiffies;hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);out:spin_unlock_bh(&rt6_exception_lock);/* Update fn->fn_sernum to invalidate all cached dst */if (!err) {spin_lock_bh(&f6i->fib6_table->tb6_lock);fib6_update_sernum(net, f6i);

nexthop属性路由

如果nexthop进行替换操作(ip nexthop replace),在完成之后,由nh_rt_cache_flush清除相关缓存。

static int replace_nexthop(struct net *net, struct nexthop *old,struct nexthop *new, struct netlink_ext_ack *extack)
{if (old->is_group)err = replace_nexthop_grp(net, old, new, extack);elseerr = replace_nexthop_single(net, old, new, extack);if (!err) {nh_rt_cache_flush(net, old);

遍历使用此nexthop的路由信息链表,更新其对应路由节点的序号,以及其到路由树根节点路径上的所有节点的序号,达到清空路由缓存的目的。

/* if any FIB entries reference this nexthop, any dst entries need to be regenerated*/
static void nh_rt_cache_flush(struct net *net, struct nexthop *nh)
{struct fib6_info *f6i;if (!list_empty(&nh->fi_list))rt_cache_flush(net);list_for_each_entry(f6i, &nh->f6i_list, nh_list)ipv6_stub->fib6_update_sernum(net, f6i);
}static const struct ipv6_stub ipv6_stub_impl = {....fib6_update_sernum = fib6_update_sernum_stub,

缓存有效性检测

IPv6在缓存操作结构中注册了缓存检测函数ip6_dst_check。

static struct dst_ops ip6_dst_ops_template = {.family         =   AF_INET6,.gc         =   ip6_dst_gc,.gc_thresh      =   1024,.check          =   ip6_dst_check,

如下函数,如果路由缓存的序号不为空,直接由函数rt6_is_valid进行检查,参见以上的介绍,即对比其序号和命名空间中的序号是否相等。在缓存分配函数ip6_rt_pcpu_alloc中可见,配置了nexthop属性的路由信息,生成的路由缓存将初始化序号sernum字段。参见 每处理器路由缓存。

对于路由信息(from)不为空,并且为每处理器缓存,或者位于rt6_uncached_list链表上的路由缓存,由函数rt6_dst_from_check校验缓存。否则,函数rt6_check进行校验。

static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
{struct dst_entry *dst_ret;struct fib6_info *from;struct rt6_info *rt;rt = container_of(dst, struct rt6_info, dst);if (rt->sernum)return rt6_is_valid(rt) ? dst : NULL;rcu_read_lock();/* All IPV6 dsts are created with ->obsolete set to the value* DST_OBSOLETE_FORCE_CHK which forces validation calls down into this function always.*/from = rcu_dereference(rt->from);if (from && (rt->rt6i_flags & RTF_PCPU || unlikely(!list_empty(&rt->rt6i_uncached))))dst_ret = rt6_dst_from_check(rt, from, cookie);elsedst_ret = rt6_check(rt, from, cookie);rcu_read_unlock();return dst_ret;

对于函数rt6_dst_from_check,其主要由fib6_check进行处理。

static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, struct fib6_info *from, u32 cookie)
{if (!__rt6_check_expired(rt) && rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&fib6_check(from, cookie))return &rt->dst;elsereturn NULL;
}

由路由缓存依据的路由信息中取出其路由节点中的序号(fn_sernum),其赋值给rt_cookie,比较其与参数cookie是否相等,为真表明是我们要的结果。

static bool fib6_check(struct fib6_info *f6i, u32 cookie)
{u32 rt_cookie = 0;if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)return false;if (fib6_check_expired(f6i)) return false;return true;
}
static inline bool fib6_get_cookie_safe(const struct fib6_info *f6i, u32 *cookie)
{       struct fib6_node *fn;bool status = false;fn = rcu_dereference(f6i->fib6_node);if (fn) {*cookie = fn->fn_sernum;/* pairs with smp_wmb() in fib6_update_sernum_upto_root() */smp_rmb();status = true;}return status;

再来看一下rt6_check函数,其在缓存的from为空,或者非每处理器路由缓存,非rt6i_uncached链表缓存时使用。对于from为空的情况,说明缓存对应的路由信息已经释放,此缓存不在有效。之后的处理逻辑与以上函数fib6_check完全相同,区别仅在于返回值类型。

static struct dst_entry *rt6_check(struct rt6_info *rt, struct fib6_info *from, u32 cookie)
{u32 rt_cookie = 0;if (!from || !fib6_get_cookie_safe(from, &rt_cookie) || rt_cookie != cookie)return NULL;if (rt6_check_expired(rt))return NULL;return &rt->dst;
}

内核版本 5.10

IPv6路由信息的序号相关推荐

  1. IPv6 路由信息查看命令

    IPv6 路由信息查看命令 1.route -A inet6 2.ip -6 route show

  2. IPv6路由(学习日记)

    一.IPV6静态路由的配置 在下面拓扑图的网络中使用IPV6时实现主机间的通信 拓扑图 配置过程 PC1主机的配置 PC2主机的配置 路由器R1的配置 <Huawei>system-vie ...

  3. IPv6基础介绍--IPv6路由基础--DHCPv6原理与配置——总结

    一.IPv6基础介绍 1.IPv6是Internet工程任务组(IETF)设计的一套规范,它是网络层协议的第二代标准协议,也是IPv4(Internet Protocol Version 4)的升级版 ...

  4. Linux 3.10内核锁瓶颈描述以及解决-IPv6路由cache的性能缺陷

    大量线程争抢锁导致CPU自旋乃至内核hang住的例子层出不穷. 我曾经解过很多关于这方面的内核bug: nat模块复制tso结构不完全导致SSL握手弹证书慢. IP路由neighbour系统对poin ...

  5. windows设置路由信息

    用法 ROUTE [-f] [-p] [-4|-6] command [destination] [MASK netmask] [gateway] [METRIC metric] [IF interf ...

  6. linux 路由转发 ipv6,IPv6路由

    路由概念保持在相同情况下的IPv6,但几乎所有的路由协议已据此重新定义.我们已经看到在通信中的IPv6段,主机如何说,以它的网关.路由是一个过程,路由转发数据选择几个可用的路由或路径到目的地之间的最佳 ...

  7. IPv6路由FIB通知链

    在网络命名空间初始化时,初始化fib通知链操作链表.此函数位于文件net/core/fib_notifier.c中,也就是IPv4和IPv6共用. static int __net_init fib_ ...

  8. 互联网协议 — BGP 边界网关协议 — Route(路由信息)

    目录 文章目录 目录 BGP Route(路由信息) BGP Msg Header BGP Msg Type Open Msg Update Msg Keepalive Msg Notificatio ...

  9. 【Android 组件化】路由组件 ( 构造路由表中的路由信息 )

    文章目录 一.封装路由信息 二.注解处理器 生成路由表信息 1.Activity 中使用 @Route 注解 2.注解处理器中判定注解是否检测出来 3.获取被 @Route 标注的 注解节点 4.判断 ...

  10. rip协议中周期性广播路由信息的报文_距离矢量路由协议(RIP)

    路由信息协议RIP(Routing Information Protocol)的简称,它是一种基于距离矢量算法的协议,使用跳数作为度量来衡量到达目的网络的距离.RIP主要应用于规模较小的网络中. 基本 ...

最新文章

  1. 菜鸟建站别发愁,省钱建站新攻略!
  2. phpstudy搭建网站使用php,教你用phpstudy搭建本地服务并建dedecms网站
  3. 基于PyMC的贝叶斯建模实战
  4. 【C语言】将两个字符串连接起来
  5. (33)FPGA分频设计-偶数分频(第7天)
  6. 佳能9100cdn故障_佳能 打印机故障代码大全
  7. unity, 判断可见性
  8. CS61B+CS170
  9. 银行争夺又一万亿市场:汽车金融
  10. Entrez Direct
  11. linux cp并打包目录,【linux】【qt5】【将linux下的qt程序打包发布(完全适用于中标麒麟)】...
  12. uniApp 生命周期【应用生命周期 和 页面生命周期】
  13. 如何看笔记本电脑配置?
  14. bash: node: command not found
  15. flex effect
  16. keil(MDK)中出现error: #513:
  17. 视觉检测零件同轴度 测试零件同轴度,检测是否同心圆
  18. Elliptic Labs推出虚拟智能存在检测传感解决方案,为低功耗笔记本电脑的安全性保驾护航
  19. 【海子诗全编】序及后记
  20. 172. 阶乘后的零 尾随零

热门文章

  1. 十年前开发的平板游戏:HyllCube 三维四子棋游戏,获得了全国一等奖
  2. 思维导图工具之Freeplane(上篇)
  3. 中国女篮姐妹花杨舒予、杨力维成为护肤品牌佰草集太极首组代言人
  4. 用Matlab把SMAP vegetation optical depth (VOD)数据转换成带投影信息EASE GRID2的tif文件
  5. 图像灰度共生矩阵cooc_feature_image.hdev
  6. 百度 LBS 开放平台,开发人员众測计划正式启动
  7. at/atq/atrm
  8. 迅雷欲缔造互联网“视频梦工厂”
  9. JS的IE和Firefox兼容性汇编(原作:hotman_x)
  10. Ionic4.x ion-refresher 下拉更新