admin管理员组

文章数量:1642161

在邻居发现协议处理中,对于NA(Neighbour Advertisement)报文,如果其通过的地址已经存在于邻居表中,但是此次通过没有了NTF_ROUTER标志,表明发送NA报文的设备由路由转换为主机模式,其不在转发报文。因此,需要删除其之前通告的路由。

static void ndisc_recv_na(struct sk_buff *skb)
{
    neigh = neigh_lookup(&nd_tbl, &msg->target, dev);

    if (neigh) {
        u8 old_flags = neigh->flags;

        ...
        if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
            /*
             * Change: router to host
             */
            rt6_clean_tohost(dev_net(dev),  saddr);
        }

如下rt6_clean_tohost,由fib6_clean_all遍历命名空间中的所有路由信息,每个信息项由fib6_clean_tohost处理,参数为发送NA报文的设备地址。

void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
{
    fib6_clean_all(net, fib6_clean_tohost, gateway);
}

由于RA(Router Advertisement)报文通过的路由不会指向nexthop节点,所以,如果当前遍历的路由信息指向nexthop节点,不进行处理。如果路由信息是有RA报文生成,并且路由下一跳网关等于设备地址(此设备由路由转为主机模式),返回-1,删除此路由信息。返回值的处理可参考fib6_clean_node函数。

另外,如果以上不成立,路由信息不符合,需要遍历下一跳的exception,因为重定向生成的exception可能下一跳指向失效的设备地址。

#define RTF_RA_ROUTER       (RTF_ADDRCONF | RTF_DEFAULT)

static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
{
    struct in6_addr *gateway = (struct in6_addr *)arg;
    struct fib6_nh *nh;

    /* RA routes do not use nexthops */
    if (rt->nh)
        return 0;

    nh = rt->fib6_nh;
    if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
        nh->fib_nh_gw_family && ipv6_addr_equal(gateway, &nh->fib_nh_gw6))
        return -1;

    /* Further clean up cached routes in exception table.
     * This is needed because cached route may have a different
     * gateway than its 'parent' in the case of an ip redirect.
     */
    fib6_nh_exceptions_clean_tohost(nh, gateway);
    return 0;

根据下一跳找到其所属的exception数组,遍历所有的数组,以及每个数组成员表示的链表,取出每个链表元素,如果其网关等于失效的设备地址,将此exception移除。

#define RTF_CACHE_GATEWAY   (RTF_GATEWAY | RTF_CACHE)

static void fib6_nh_exceptions_clean_tohost(const struct fib6_nh *nh, const struct in6_addr *gateway)
{
    struct rt6_exception_bucket *bucket;
    struct rt6_exception *rt6_ex;
    struct hlist_node *tmp;
    
    ...
    bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
    if (bucket) {
        for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
            hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) {
                struct rt6_info *entry = rt6_ex->rt6i;

                if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY &&
                    ipv6_addr_equal(gateway, &entry->rt6i_gateway)) {
                    rt6_remove_exception(bucket, rt6_ex);
                }
            }
            bucket++;
        }

内核版本 5.10

本文标签: 路由器转变为主机