【Linux5.4】【TUN】代码学习记录(11)--tun_set_iff

【Linux5.4】【TUN】代码学习记录(11)–tun_set_iff

tun_set_iff

tun_set_iff仅在__tun_chr_ioctl中当标识符为TUNSETIFF时调用过。
tun_set_iff主要功能就是按需创建一个网络设备struct net_device *dev。
函数代码过长,分前后两部分看

上半部分

dev = __dev_get_by_name(net, ifr->ifr_name)是查找是否已经存在设备名为ifr->ifr_name的虚拟网卡。
如果已存在,就要区分情况,如果虚拟网卡设备支持IFF_MULTI_QUEUE,而且当前调用tun_set_iff的应用程序也设置了IFF_MULTI_QUEUE,那么应用程序可以直接使用以创建的虚拟网卡(也就是tfile与tun互联),调用tun_attach关联当前应用程序的struct tun_file *tfile与虚拟网卡struct tun_struct *tun两个指针,tun_attach最后参数是true,表明*tun指针已经初始化完成,可以和*tfile进行互连。关于tun_attach后续再写。

static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
{
	...
	/* 按照设备名称找到设备指针dev */
	dev = __dev_get_by_name(net, ifr->ifr_name);
    /* 找到说明设备已存在 */
	if (dev) {
		if (ifr->ifr_flags & IFF_TUN_EXCL)//IFF_TUN_EXCL表示打开 TUN/TAP 接口时以排他模式进行,即只有一个进程可以使用该接口。
			return -EBUSY;
		if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops)
			tun = netdev_priv(dev);
		else if ((ifr->ifr_flags & IFF_TAP) && dev->netdev_ops == &tap_netdev_ops)
			tun = netdev_priv(dev);
		else
			return -EINVAL;

		if (!!(ifr->ifr_flags & IFF_MULTI_QUEUE) !=
		    !!(tun->flags & IFF_MULTI_QUEUE))
			return -EINVAL;

		if (tun_not_capable(tun))
			return -EPERM;
		err = security_tun_dev_open(tun->security);
		if (err < 0)
			return err;

		err = tun_attach(tun, file, ifr->ifr_flags & IFF_NOFILTER,
				 ifr->ifr_flags & IFF_NAPI,
				 ifr->ifr_flags & IFF_NAPI_FRAGS, true);
		if (err < 0)
			return err;

		if (tun->flags & IFF_MULTI_QUEUE &&
		    (tun->numqueues + tun->numdisabled > 1)) {
			/* One or more queue has already been attached, no need
			 * to initialize the device again.
			 */
			netdev_state_change(dev);
			return 0;
		}
		/* 这里代码可能是应对一种特殊情况,即设备开启了持续模式但是不支持IFF_MULTI_QUEUE,此时设备已创建
		 * 但是创建的应用结束了,设备依然存在,而且新的应用程序调用tun_set_iff并设置了相同的ifr.ifr_name,
		 * 那么相应的tun设备flags就需要与新的应用设置的flags一致。
		 */
		tun->flags = (tun->flags & ~TUN_FEATURES) |
			      (ifr->ifr_flags & TUN_FEATURES);

		netdev_state_change(dev);
	} 
	...
}

下半部分

如果__dev_get_by_name(net, ifr->ifr_name)未能找到设备名为ifr->ifr_name的虚拟网卡,就需要创建一个新的虚拟网卡。
首先alloc_netdev_mqs申请新的内存用来创建dev,之后初始化dev。
接着netdev_priv(dev)获取*tun的位置,后续代码对tun和dev进行初始化。
调用tun_attach,此时tun还未完全准备好,因此最后参数是false,tun与tfile只能进行半连接,将tun->tfiles[tun->numqueues]指向当前的tfile。
当完成虚拟网卡设备注册register_netdevice(tun->dev),tun准备完毕,rcu_assign_pointer(tfile->tun, tun)将tfile->tun指向当前的tun,完成互连。

static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
{
	...
 	else { /* 设备不存在,需要创建设备 */
		char *name;
		unsigned long flags = 0;
		int queues = ifr->ifr_flags & IFF_MULTI_QUEUE ?
			     MAX_TAP_QUEUES : 1;

		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
			return -EPERM;
		err = security_tun_dev_create();
		if (err < 0)
			return err;

		/* Set dev type */
		if (ifr->ifr_flags & IFF_TUN) {
			/* TUN device */
			flags |= IFF_TUN;
			name = "tun%d";
		} else if (ifr->ifr_flags & IFF_TAP) {
			/* TAP device */
			flags |= IFF_TAP;
			name = "tap%d";
		} else
			return -EINVAL;

		if (*ifr->ifr_name)
			name = ifr->ifr_name;
		/* 申请内存创建网络设备 */
		dev = alloc_netdev_mqs(sizeof(struct tun_struct), name,
				       NET_NAME_UNKNOWN, tun_setup, queues,
				       queues);

		if (!dev)
			return -ENOMEM;
		err = dev_get_valid_name(net, dev, name);
		if (err < 0)
			goto err_free_dev;

		dev_net_set(dev, net);
		dev->rtnl_link_ops = &tun_link_ops;
		dev->ifindex = tfile->ifindex;
		dev->sysfs_groups[0] = &tun_attr_group;

		tun = netdev_priv(dev);
		tun->dev = dev;
		tun->flags = flags;
		tun->txflt.count = 0;
		tun->vnet_hdr_sz = sizeof(struct virtio_net_hdr);

		tun->align = NET_SKB_PAD;
		tun->filter_attached = false;
		tun->sndbuf = tfile->socket.sk->sk_sndbuf;
		tun->rx_batched = 0;
		RCU_INIT_POINTER(tun->steering_prog, NULL);

		tun->pcpu_stats = netdev_alloc_pcpu_stats(struct tun_pcpu_stats);
		if (!tun->pcpu_stats) {
			err = -ENOMEM;
			goto err_free_dev;
		}

		spin_lock_init(&tun->lock);

		err = security_tun_dev_alloc_security(&tun->security);
		if (err < 0)
			goto err_free_stat;

		tun_net_init(dev);
		tun_flow_init(tun);

		dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
				   TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
				   NETIF_F_HW_VLAN_STAG_TX;
		dev->features = dev->hw_features | NETIF_F_LLTX;
		dev->vlan_features = dev->features &
				     ~(NETIF_F_HW_VLAN_CTAG_TX |
				       NETIF_F_HW_VLAN_STAG_TX);
		/* 将tun->flags设置为用户输入的ifr->ifr_flags */
		tun->flags = (tun->flags & ~TUN_FEATURES) |
			      (ifr->ifr_flags & TUN_FEATURES);

		INIT_LIST_HEAD(&tun->disabled);
		err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI,
				 ifr->ifr_flags & IFF_NAPI_FRAGS, false);
		if (err < 0)
			goto err_free_flow;

		err = register_netdevice(tun->dev);
		if (err < 0)
			goto err_detach;
		/* free_netdev() won't check refcnt, to aovid race
		 * with dev_put() we need publish tun after registration.
		 */
		rcu_assign_pointer(tfile->tun, tun);
	}

	netif_carrier_on(tun->dev);

	tun_debug(KERN_INFO, tun, "tun_set_iff\n");

	/* Make sure persistent devices do not get stuck in
	 * xoff state.
	 * 确保持久化设备不会卡在xoff状态。
	 */
	if (netif_running(tun->dev))
		netif_tx_wake_all_queues(tun->dev);

	strcpy(ifr->ifr_name, tun->dev->name);
	return 0;

err_detach:
	tun_detach_all(dev);
	/* register_netdevice() already called tun_free_netdev() */
	goto err_free_dev;

err_free_flow:
	tun_flow_uninit(tun);
	security_tun_dev_free_security(tun->security);
err_free_stat:
	free_percpu(tun->pcpu_stats);
err_free_dev:
	free_netdev(dev);
	return err;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值