锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

IPv6邻居发现协议添加默认路由

时间:2022-10-10 04:00:00 二极管dflt48a

收到邻居发现协议后,RA(Router Advertisement)报文后,由ndisc_router_discovery处理。首先,找出该报纸的源地址是否有默认路由器(rt6_get_dflt_router),并检查是否有可到达的邻居表项来释放路由信息。

如果RA通知的路由时间为零,本机中有以其为默认路由的路由信息,表明该默认路由信息应删除。

static void ndisc_router_discovery(struct sk_buff *skb) {     struct fib6_info *rt = NULL;      lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);      /* routes added from RAs do not use nexthop objects */     rt = rt6_get_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev);     if (rt) {         neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,                      rt->fib6_nh->fib_nh_dev, NULL, &ipv6_hdr(skb)->saddr);         if (!neigh) {             ND_PRINTK(0, err, "RA: %s got default router without neighbour\n", __func__);             fib6_info_release(rt);             return;         }     }     if (rt && lifetime == 0) {         ip6_del_rt(net, rt, false);         rt = NULL;     }     ND_PRINTK(3, info, "RA: rt: %p  lifetime: %d, for dev: %s\n", rt, lifetime, skb->dev->name); 

其次,如果上述路由信息为空,本通知的有效期不为零,则根据报告源地址添加默认路由器,并检查邻居的表格。

    if (!rt && lifetime) {         ND_PRINTK(3, info, "RA: adding default router\n");          rt = rt6_add_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev, pref);         if (!rt) {             ND_PRINTK(0, err, "RA: %s failed to add default route\n", __func__);             return;         }         neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,                      rt->fib6_nh->fib_nh_dev, NULL, &ipv6_hdr(skb)->saddr);         if (!neigh) {             ND_PRINTK(0, err, "RA: %s got default router without neighbour\n",  __func__);             fib6_info_release(rt);             return;         }         neigh->flags |= NTF_ROUTER;     } else if (rt) {         rt->fib6_flags = (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);     }     if (rt)         fib6_set_expires(rt, jiffies   (HZ * lifetime));     if (in6_dev->cnf.accept_ra_min_hop_limit < 256 && ra_msg->icmph.icmp6_hop_limit) {         if (in6_dev->cnf.accept_ra_min_hop_limit <= ra_msg->icmph.icmp6_hop_limit) {             in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;             fib6_metric_set(rt, RTAX_HOPLIMIT, ra_msg->icmph.icmp6_hop_limit);         } else {             ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than minimum\n");         }     } 

查找默认路由器

通过路由表中的所有叶片节点,找到合适的路由信息。默认路由器不得包括在内nexthop属性,跳过此类路由信息。将当前路由信息的出口设备、标志和参数与下一个跳网关进行比较,都表明找到路由信息是一样的。

struct fib6_info *rt6_get_dflt_router(struct net *net, const struct in6_addr *addr, struct net_device *dev) {     u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;     struct fib6_info *rt;     struct fib6_table *table;      table = fib6_get_table(net, tb_id);     if (!table)         return NULL;      rcu_read_lock();     for_each_fib6_node_rt_rcu(&table->tb6_root) {         struct fib6_nh *nh;          /* RA routes do not use nexthops */         if (rt->nh) continue;          nh = rt->fib6_nh;         if (dev == nh->fib_nh_dev &&             ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&             ipv6_addr_equal(&nh->fib_nh_gw6, addr))             break;     } 

添加默认路由器

调用通用函数ip6_route_add在此协议中添加默认路由器RTPROT_RA,表明是根据RA报纸生成。默认路由器成功添加后,增加当前路由表RT6_TABLE_HAS_DFLT_ROUTER在查询默认路由器表项时,将使用标志,以加速搜索。

struct fib6_info *rt6_add_dflt_router(struct net *net,                      const struct in6_addr *gwaddr, struct net_device *dev, unsigned int pref) {     struct fib6_config cfg = {         .fc_table   = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,         .fc_metric  = IP6_RT_PRIO_USER,         .fc_ifindex = dev->ifindex,         .fc_flags   = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |                   RTF_UP | RTF_EXPIRES | RTF_PREF(pref),         .fc_protocol = RTPROT_RA,         .fc_type = RTN_UNICAST,         .fc_nlinfo.portid = 0,         .fc_nlinfo.nlh = NULL,         .fc_nlinfo.nl_net = net,     };     cfg.fc_gateway = *gwaddr;      if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {         struct fib6_table *table;          table = fib6_get_table(dev_net(dev), cfg.fc_table);         if (table)             table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;     }      return rt6_get_dflt_router(net, gwaddr, dev); 

清除默认路由器

当用户修改PROC文件中的fowarding值时,清除默认路由器。PROC文件:/proc/sys/net/ipv6/conf/all/forwarding,forwarding的值决定系统遵循IPv6主机还是路由器行为。

static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
{
    ...
    if (newf)
        rt6_purge_dflt_routers(net);
    return 1;
}

遍历系统中的所有路由表(由256个数组以及每个数组元素为头的哈希链表组成),检查每个表中是否包含默认路由器(RT6_TABLE_HAS_DFLT_ROUTER),由函数__rt6_purge_dflt_routers处理。

void rt6_purge_dflt_routers(struct net *net)
{
    struct fib6_table *table;
    struct hlist_head *head;
    unsigned int h;

    rcu_read_lock();

    for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
        head = &net->ipv6.fib_table_hash[h];
        hlist_for_each_entry_rcu(table, head, tb6_hlist) {
            if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
                __rt6_purge_dflt_routers(net, table);
        }
    }

遍历路由表中的所有叶子节点,删除其中的默认路由器。默认路由器路由信息包括标志位(RTF_DEFAULT和RTF_ADDRCONF)。对于accept_ra为零时,不接收RA报文,删除默认路由器。accept_ra为1时,如果forwarding禁用,系统为IPv6主机,接收RA报文,此时路由表中可能包含默认路由器。

对于accept_ra等于2的情况,即forwarding启用,系统为IPv6路由器,但是接收其它路由器的RA报文,此时不删除默认路由器。

函数最后,清除路由表中的标志RT6_TABLE_HAS_DFLT_ROUTER,此路由表中不包含默认路由器。

static void __rt6_purge_dflt_routers(struct net *net, struct fib6_table *table)
{
    struct fib6_info *rt;

restart:
    rcu_read_lock();
    for_each_fib6_node_rt_rcu(&table->tb6_root) {
        struct net_device *dev = fib6_info_nh_dev(rt);
        struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;

        if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
            (!idev || idev->cnf.accept_ra != 2) && fib6_info_hold_safe(rt)) {
            rcu_read_unlock();
            ip6_del_rt(net, rt, false);
            goto restart;
        }
    }
    rcu_read_unlock();

    table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;

内核版本 5.10

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章