tun驱动之tun_init

该文详细介绍了Linux内核中tun驱动的初始化步骤,包括rtnl_link_register用于注册link操作,misc_register用于注册杂设备,以及register_netdevice_notifier用于注册网络设备通知器。这些步骤涉及设备驱动的注册、设备号分配和网络设备事件监听。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tun驱动的初始化方法是tun_init。

static int __init tun_init(void)
{
	int ret = 0;

	pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);

	ret = rtnl_link_register(&tun_link_ops);
	if (ret) {
		pr_err("Can't register link_ops\n");
		goto err_linkops;
	}

	ret = misc_register(&tun_miscdev);
	if (ret) {
		pr_err("Can't register misc device %d\n", TUN_MINOR);
		goto err_misc;
	}

	ret = register_netdevice_notifier(&tun_notifier_block);
	if (ret) {
		pr_err("Can't register netdevice notifier\n");
		goto err_notifier;
	}

	return  0;

err_notifier:
	misc_deregister(&tun_miscdev);
err_misc:
	rtnl_link_unregister(&tun_link_ops);
err_linkops:
	return ret;
}

一 rtnl_link_register

rtnl_link_register的原型如下:

参数如下:

int rtnl_link_register(struct rtnl_link_ops *ops);

其中ops对应的是:

static struct rtnl_link_ops tun_link_ops __read_mostly = {

  .kind = DRV_NAME, // 区分不同的驱动

  .priv_size = sizeof(struct tun_struct),

  .setup = tun_setup,

  .validate = tun_validate,

};

rtnl_link_register的主要逻辑是将ops添加到全局列表link_ops中:

list_add_tail(&ops->list, &link_ops);

二 misc_register 

misc_register的参数是:

static struct miscdevice tun_miscdev = {

  .minor = TUN_MINOR,

  .name = "tun",

  .nodename = "net/tun",

  .fops = &tun_fops,

};

字符设备和 块设备利用主设备号和次设备号来区分设备驱动。如一台机器接了一个打印机,其驱动的主设备号是X,次设备号是Y。这台机器再接入同一型号的一台打印机,则驱动主设备号是X,次设备号是Y1。X标识打印机的型号,Y标识此型号的具体一台打印机。

主设备号最大2^12,即4096,而次设备号最大2^20。为了解决主设备号短缺的问题,引入了miscdevice,杂设备。

杂设备驱动,主设备号固定是10;次设备号可以指定,也可以自动获取。

// 自动获取

if (is_dynamic) {
    int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);

    if (i >= DYNAMIC_MINORS) {
        err = -EBUSY;
        goto out;
    }
    misc->minor = DYNAMIC_MINORS - i - 1;
    set_bit(i, misc_minors);
} else {
    struct miscdevice *c;

    // 指定次设备号,进行判重

    list_for_each_entry(c, &misc_list, list) {
        if (c->minor == misc->minor) {
            err = -EBUSY;
            goto out;
        }
    }
}

最后将杂设备添加到全局列表misc_list中:

list_add(&misc->list, &misc_list); 

在用户空间,调用open打开一个杂设备时,就能看到杂设备设计的巧妙之处,后面会再详细介绍。

三 register_netdevice_notifier

register_netdevice_notifier的参数为:

static struct notifier_block tun_notifier_block __read_mostly = {

.notifier_call = tun_device_event,

};

int register_netdevice_notifier(struct notifier_block *nb)
{
	struct net_device *dev;
	struct net_device *last;
	struct net *net;
	int err;

	rtnl_lock();
	err = raw_notifier_chain_register(&netdev_chain, nb);
	if (err)
		goto unlock;
	if (dev_boot_phase)
		goto unlock;
	for_each_net(net) { // 遍历net_namespace_list
		for_each_netdev(net, dev) { // 遍历net->dev_base_head
			err = call_netdevice_notifier(nb, NETDEV_REGISTER, dev);
			err = notifier_to_errno(err);
			if (err)
				goto rollback;

			if (!(dev->flags & IFF_UP))
				continue;

			call_netdevice_notifier(nb, NETDEV_UP, dev);
		}
	}

unlock:
	rtnl_unlock();
	return err;

raw_notifier_chain_register将nb(即tun_notifier_block)添加到netdev_chain中的链表head上。

register_netdevice_notifier中的两层for循环,遍历网络空间中的每个设备,通知其NETDEV_REGISTER和NETDEV_UP。

call_netdevice_notifier(nb, NETDEV_REGISTER, dev);

call_netdevice_notifier(nb, NETDEV_UP, dev);

static int call_netdevice_notifier(struct notifier_block *nb, unsigned long val,
				   struct net_device *dev)
{
	struct netdev_notifier_info info = {
		.dev = dev,
	};

	// 对于tun驱动,nb为 tun_notifier_block
	return nb->notifier_call(nb, val, &info);
}

最终调用到:

static int tun_device_event(struct notifier_block *unused,
			    unsigned long event, void *ptr)
{
	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
	struct tun_struct *tun = netdev_priv(dev);

	if (dev->rtnl_link_ops != &tun_link_ops)
		return NOTIFY_DONE;

	switch (event) {
	case NETDEV_CHANGE_TX_QUEUE_LEN:
		if (tun_queue_resize(tun))
			return NOTIFY_BAD;
		break;
	default:
		break;
	}

	return NOTIFY_DONE;
}

两次通知,tun_device_event仅仅返回NOTIFY_DONE。

<think>嗯,用户问的是在iStoreOS上安装ZeroTier时无法创建以“zt”开头的网卡的问题,可能原因是否因为单网口。我需要先回忆一下ZeroTier的工作原理和相关限制。 首先,ZeroTier在安装时会自动创建一个虚拟网络接口,通常命名为zt0、zt1等。这需要内核支持TUN/TAP设备,以及有正确的权限。用户提到无法创建这样的网卡,可能涉及几个方面: 1. **内核模块缺失或未加载**:iStoreOS基于OpenWrt,可能默认没有包含TUN/TAP内核模块。需要检查是否安装了kmod-tun或者其他相关模块。 2. **权限问题**:创建网络接口可能需要root权限,或者ZeroTier服务没有足够的权限。需要检查运行ZeroTier的用户权限,以及设备节点的访问权限,比如/dev/net/tun是否存在并且可读写。 3. **单网口的影响**:单网口物理机通常不影响虚拟接口的创建,因为zt接口是虚拟的,不依赖物理网口数量。但可能存在其他配置冲突,比如网络接口命名规则冲突,或者现有的网络配置阻止了新接口的创建。 4. **ZeroTier版本兼容性**:iStoreOS可能使用的是较旧的ZeroTier版本,存在已知的bug。需要确认版本,并考虑升级到最新版本。 5. **冲突的服务或防火墙规则**:某些防火墙配置可能会阻止虚拟接口的创建,或者有其他服务占用了相关资源。 接下来,我需要验证这些可能性,并提供逐步排查步骤。用户可能需要检查内核模块、安装必要软件包、检查权限、查看日志等。此外,单网口本身通常不是直接原因,但需要排除其他因素。</think>### 逐步分析及解决方案: #### 1. **检查内核模块支持** ZeroTier依赖**TUN/TAP**虚拟网络设备驱动。若iStoreOS未加载相关模块,会导致无法创建`zt*`网卡。 - **操作步骤**: ```bash # 检查TUN模块是否加载 lsmod | grep tun # 若无输出,尝试手动加载 modprobe tun # 安装内核模块(若未集成) opkg update opkg install kmod-tun ``` #### 2. **验证ZeroTier安装完整性** iStoreOS可能缺少ZeroTier依赖组件。 - **操作步骤**: ```bash # 卸载旧版本(如有) opkg remove zerotier # 安装完整依赖 opkg install zerotier luci-app-zerotier ``` #### 3. **检查权限问题** 确保`/dev/net/tun`设备存在且权限正确。 - **操作步骤**: ```bash # 检查设备节点 ls -l /dev/net/tun # 正确权限应为: # crw-rw-rw- 1 root root 10, 200 Mar 1 12:00 /dev/net/tun # 手动创建(若缺失) mkdir -p /dev/net mknod /dev/net/tun c 10 200 chmod 666 /dev/net/tun ``` #### 4. **单网口的影响分析** 单物理网口**不会直接影响**虚拟网卡创建。但需排查以下问题: - **网络接口命名冲突**:检查现有接口是否占用了`zt0`名称(如手动配置过)。 - **防火墙拦截**:临时关闭防火墙测试: ```bash /etc/init.d/firewall stop ``` #### 5. **查看日志定位问题** 通过系统日志获取具体错误信息。 - **操作步骤**: ```bash # 启动ZeroTier并查看日志 /etc/init.d/zerotier start logread | grep zt ``` --- ### 常见错误及修复 | 现象 | 可能原因 | 解决方案 | |------|----------|----------| | `ioctl setup error: No such device` | TUN模块未加载 | 执行`opkg install kmod-tun` | | `permission denied` | `/dev/net/tun`权限错误 | 运行`chmod 666 /dev/net/tun` | | `zt0 already exists` | 残留配置冲突 | 重启系统或执行`ifconfig zt0 down && ip link delete zt0` | --- ### 最终验证 ```bash # 加入网络并检查接口 zerotier-cli join xxxxxxx ifconfig | grep zt ``` 若看到`zt`开头的接口(如`zt2ulxxxxx`),则表明成功。 --- ### 补充说明 单网口设备**可以正常使用**ZeroTier虚拟网络,实际流量通过物理网口的NAT或路由转发。若问题持续,建议提供`logread`完整输出以便进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值