河西网站建设制作,彩票网站里的统计怎么做,个人网站多少钱一年,学电脑培训班struct ifreq ifr; ifr.ifr_flags | IFF_TAP | IFF_NO_PI; ioctl(fd, TUNSETIFF, (void *)ifr); 上面的代码的意思是设置网卡信息#xff0c;并将tun驱动设置为TAP模式。在TAP模式下#xff0c;在用户空间下调用open打开/dev/net/tun驱动文件#xff0c;发送(调用send函… struct ifreq ifr; ifr.ifr_flags | IFF_TAP | IFF_NO_PI; ioctl(fd, TUNSETIFF, (void *)ifr); 上面的代码的意思是设置网卡信息并将tun驱动设置为TAP模式。在TAP模式下在用户空间下调用open打开/dev/net/tun驱动文件发送(调用send函数)的和接收(调用read函数)到的都是以太包。
以上面代码为切入点来看下内核的处理逻辑。
tun驱动的ioctl对应的是tun_chr_ioctl。
一 tun_chr_ioctl
在tun_chr_ioctl中调用了__tun_chr_ioctl。
static long __tun_chr_ioctl(struct file *file, unsigned int cmd,unsigned long arg, int ifreq_len)
{struct tun_file *tfile file-private_data;struct tun_struct *tun;void __user* argp (void __user*)arg;struct ifreq ifr;// 将用户空间的数据拷贝到ifr中if (cmd TUNSETIFF || cmd TUNSETQUEUE || _IOC_TYPE(cmd) SOCK_IOC_TYPE) {if (copy_from_user(ifr, argp, ifreq_len))return -EFAULT;} else {memset(ifr, 0, sizeof(ifr));}ret 0;rtnl_lock();// 获取tun_struct结构首次调用TUNSETIFF时为NULLtun tun_get(tfile);if (cmd TUNSETIFF) {ret -EEXIST;if (tun)goto unlock;ifr.ifr_name[IFNAMSIZ-1] \0;ret tun_set_iff(sock_net(tfile-sk), file, ifr);if (ret)goto unlock;// 将ifr中的数据拷贝到用户空间if (copy_to_user(argp, ifr, ifreq_len))ret -EFAULT;goto unlock;}... ...
}
在__tun_chr_ioctl中调用tun_get获取tfile对应的tun。 tun_get相当于执行下面的操作 struct tun_struct *tun rcu_dereference(tfile-tun); 第一次执行时tun为空调用tun_set_iff。
二 tun_set_iff tun_chr_ioctl |- __tun_chr_ioctl |- tun_set_iff 调用ifconfig命令时左侧显示的名称即网卡名称保存到了ifreq结构的ifr_name字段中。 2.1 获取网卡设备 根据网卡名称(如myeth1)获取网络设备是通过__dev_get_by_name函数来实现的。
net是网络设备空间用来实现网络空间隔离其dev_name_head指向一个hlist_head数组。根据传入的网卡名称计算出该网卡设备在数组中哪个hlist_head上。最后遍历挂到此hlist_head上的所有网络设备(net_device)如果名称一致则为所查找的网络设备。由于还未为tun驱动增加网络设备因此__dev_get_by_name返回空。
2.2 申请网络设备
申请网络设备是通过来实现的。
dev alloc_netdev_mqs(sizeof(struct tun_struct), name,NET_NAME_UNKNOWN, tun_setup, queues,queues);struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,unsigned char name_assign_type,void (*setup)(struct net_device *),unsigned int txqs, unsigned int rxqs)
{struct net_device *dev;unsigned int alloc_size;struct net_device *p;alloc_size sizeof(struct net_device);if (sizeof_priv) {alloc_size sizeof_priv;}p kvzalloc(alloc_size, GFP_KERNEL | __GFP_RETRY_MAYFAIL);setup(dev);return dev;
}
在alloc_netdev_mqs申请的内存的大小为sizeof(struct net_device) sizeof(struct tun_struct)即同时分配了net_device和tun_struct两个结构。申请完内存后调用setup即tun_setup。
2.2.1 设置网络设备 tun_chr_ioctl |- __tun_chr_ioctl |- tun_set_iff |- alloc_netdev_mqs |- tun_setup 内核申请空间时同时申请了net_device和tun_structnet_device的后面紧挨者的是tun_struct。netdev_priv就是通过此方法得到tun_struct的地址。
static void tun_setup(struct net_device *dev)
{struct tun_struct *tun netdev_priv(dev);tun-owner INVALID_UID;tun-group INVALID_GID;// 设置ethtool_opsdev-ethtool_ops tun_ethtool_ops;dev-needs_free_netdev true;// 设置destructordev-priv_destructor tun_free_netdev;/* We prefer our own queue length */dev-tx_queue_len TUN_READQ_SIZE;
}
让我们再回到tun_set_iff中。
static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
{dev alloc_netdev_mqs(sizeof(struct tun_struct), name,NET_NAME_UNKNOWN, tun_setup, queues,queues);err dev_get_valid_name(net, dev, name/*tun1*/); // 将用户空间传入的name拷贝到dev-namedev_net_set(dev, net); // 设置网络空间, dev-nd_net netdev-rtnl_link_ops tun_link_ops;tun netdev_priv(dev); // 取到对应的tun_structtun-dev dev; // 关联tun_struct和net_devicetun_net_init(dev); // 初始化mac地址等err tun_attach(tun, file, false, ifr-ifr_flags IFF_NAPI); // tfile-tun tunerr register_netdevice(tun-dev); // 注册网络设备
}
为了理清各个结构之间的关系直接看下图吧 2.3 关联tun_file和tun_struct
前面说过第一次调用tun_get时返回的结果为空因为tun_file和tun_struct还没有进行关联两个结构是在下面进行关联的。 static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filter, bool napi) { rcu_assign_pointer(tfile-tun, tun); } 后面就可以调用tun_get通过tun_file获取到tun_struct了。
2.4 注册网络设备
注册网络设备除了调用netdev_register_kobject在sysfs中注册跟网络设备关联的项外还调用call_netdevice_notifiers将NETDEV_POST_INIT和NETDEV_REGISTER事件通知到已添加到netdev_chain链表中的notifier_block将网络设备添加到第一张图所示的链表中。
int register_netdevice(struct net_device *dev)
{ret call_netdevice_notifiers(NETDEV_POST_INIT, dev);ret netdev_register_kobject(dev);list_netdevice(dev); // 将dev添加到指定的hlist_head列表中ret call_netdevice_notifiers(NETDEV_REGISTER, dev);
}static void list_netdevice(struct net_device *dev)
{struct net *net dev_net(dev);hlist_add_head_rcu(dev-name_hlist, dev_name_hash(net, dev-name));
}