命名空间接口序号dev_base_seq

网络命名空间初始化时,将序号dev_base_seq赋值为1。

static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
{
    /* Must be called with pernet_ops_rwsem held */
    const struct pernet_operations *ops, *saved_ops;
    int error = 0;
    LIST_HEAD(net_exit_list);

    refcount_set(&net->count, 1);
    refcount_set(&net->passive, 1);
    get_random_bytes(&net->hash_mix, sizeof(u32));
    net->dev_base_seq = 1;

网络命名空间中每增加一个设备,dev_base_seq的值递增1,如下函数list_netdevice所示,函数dev_base_seq_inc完成dev_base_seq的递增。

/* Device list insertion */
static void list_netdevice(struct net_device *dev)
{
    struct net *net = dev_net(dev);

    ASSERT_RTNL();

    write_lock_bh(&dev_base_lock);
    list_add_tail_rcu(&dev->dev_list, &net->dev_base_head);
    netdev_name_node_add(net, dev->name_node);
    hlist_add_head_rcu(&dev->index_hlist, dev_index_hash(net, dev->ifindex));
    write_unlock_bh(&dev_base_lock);

    dev_base_seq_inc(net);

由网络命名空间中注销设备时,也是调用函数dev_base_seq_inc递增dev_base_seq的值。

/* Device list removal
 * caller must respect a RCU grace period before freeing/reusing dev
 */
static void unlist_netdevice(struct net_device *dev)
{
    ASSERT_RTNL();

    /* Unlink dev from the device chain */
    write_lock_bh(&dev_base_lock);
    list_del_rcu(&dev->dev_list);
    netdev_name_node_del(dev->name_node);
    hlist_del_rcu(&dev->index_hlist);
    write_unlock_bh(&dev_base_lock);

    dev_base_seq_inc(dev_net(dev));

在改变设备的命名空间时,旧的命名空间和新的命名空间中的dev_base_seq的值都进行递增。

int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat)
{

    /* And unlink it from device chain */
    unlist_netdevice(dev);

    /* Add the device back in the hashes */
    list_netdevice(dev);

如下为dev_base_seq的递增函数,网络命名空间中的dev_base_seq的值如果递增之后等于0,将其值设置为1。

static inline void dev_base_seq_inc(struct net *net)
{
    while (++net->dev_base_seq == 0)
        ;
}

在获取内核网络设备时,如ip link show命令,内核函数rtnl_dump_ifinfo在最后,将记录下命名空间net中的dev_base_seq,此处的net是套接口所属的命名空间,而不是目标命名空间tgt_net。如果用户没有指定命名空间(IFLA_TARGET_NETNSID),二者是相同的;但是,如果用户指定了另外的命名空间,二者的值不相同。

如果命名空间中接口比较多,rtnl_fill_ifinfo返回值小于零,skb->len中为当前的数据长度,跳出循环,应用层应当在当前位置再次进行接收,在应用层的多次调用之间,网络命名空间中的接口可能发生变化,将可能导致应用层取不到全部正确的接口。函数nl_dump_check_consistent用来检查这一情况。

函数最后使用套接口所属命名空间中的dev_base_seq序号,如果tgt_net中的接口发生改变,是不是不能检测到?

static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{
    struct net *net = sock_net(skb->sk);
    struct net *tgt_net = net;
    ...
    for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
        idx = 0;
        head = &tgt_net->dev_index_head[h];
        hlist_for_each_entry(dev, head, index_hlist) {
            ...
		 err = rtnl_fill_ifinfo(...);
            if (err < 0) {
                if (likely(skb->len))
                    goto out;

                goto out_err;
            }
        ...
    }

out:
    err = skb->len;
out_err:
    cb->args[1] = idx;
    cb->args[0] = h;
    cb->seq = net->dev_base_seq;
    nl_dump_check_consistent(cb, nlmsg_hdr(skb));

如下nl_dump_check_consistent函数,初始情况下,prev_seq为零,将seq赋值给prev_seq。在以上函数dev_base_seq_inc中看到dev_base_seq不为零,如果其为零,将其赋值给prev_seq,当seq后续改变,不等于零时,还会重新给prev_seq赋值,不能检测到接口变化,无法通过设置NLM_F_DUMP_INTR标志通知上层应用。

如果seq发生改变,通过NLM_F_DUMP_INTR标志,应用层可感知到接口变化,应重新开始获取接口。

static inline void
nl_dump_check_consistent(struct netlink_callback *cb,
             struct nlmsghdr *nlh)
{               
    if (cb->prev_seq && cb->seq != cb->prev_seq)
        nlh->nlmsg_flags |= NLM_F_DUMP_INTR;
    cb->prev_seq = cb->seq;

内核版本 5.10

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页