当前位置: 首页 > news >正文

tun驱动之ioctl

 

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_struct,net_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));
}

相关文章:

tun驱动之ioctl

struct ifreq ifr; ifr.ifr_flags | IFF_TAP | IFF_NO_PI; ioctl(fd, TUNSETIFF, (void *)&ifr); 上面的代码的意思是设置网卡信息,并将tun驱动设置为TAP模式。在TAP模式下,在用户空间下调用open打开/dev/net/tun驱动文件,发送(调用send函…...

[acwing周赛复盘] 第 93 场周赛20230304

[acwing周赛复盘] 第 93 场周赛20230304 一、本周周赛总结二、 4867. 整除数1. 题目描述2. 思路分析3. 代码实现三、 4868. 数字替换1. 题目描述2. 思路分析3. 代码实现四、4869. 异或值1. 题目描述2. 思路分析3. 代码实现六、参考链接一、本周周赛总结 彩笔了,只A…...

NOIP2022 T4 比赛

P8868 [NOIP2022] 比赛 题目大意 有两个长度为nnn的序列aaa和bbb,有qqq次询问,每次询问给出l,rl,rl,r,求 ∑ilr∑ji1r(max⁡kijak)(max⁡lijbl)\sum\limits_{il}^r\sum\limits_{ji1}^r(\max\limits_{ki}^ja_k)\times(\max\limits_{li}^jb_l…...

计算机组成原理

目录 ❤ 控制器 ❤ 运算器 ❤ 控制器运算器(计算机的中央处理器CPU) ❤ 存储器 内存(主存) 外存 内存和外村的区别 ❤ 输入设备 ❤ 输出设备 ❤ 适配器 ❤ 总线 ❤ 启动计算机的流程 ❤ 机械硬盘 ❤ 固态硬盘 python从小白到总裁完整教程目录:https://b…...

1. 命名规范

1. 命名规范 成绩10开启时间2021年09月17日 星期五 18:00折扣0.8折扣时间2021年11月6日 星期六 00:00允许迟交否关闭时间2021年11月21日 星期日 00:00 家有家法,行有行规。在家有家的规矩,入行有行的规矩。我们计算机一行就有一个命名的规矩,…...

论文投稿指南——中文核心期刊推荐(新闻事业)

【前言】 🚀 想发论文怎么办?手把手教你论文如何投稿!那么,首先要搞懂投稿目标——论文期刊 🎄 在期刊论文的分布中,存在一种普遍现象:即对于某一特定的学科或专业来说,少数期刊所含…...

【Linux】工具(4)——make/Makefile

本期博客我们的任务就是搞懂自动化构建工具——make/Makefile一、什么是make/Makefile📌make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make&…...

【企业服务器LNMP环境搭建】nginx安装

1、介绍(官方网址:nginx news ) 1.1 常见用法 1) web服务器软件 httpd http协议 同类的web服务器软件:apache nginx(俄罗斯) IIS(微软 fastcgi) lighttpd(德国) 2)代理服务器 反向代理 3)邮箱代理服务器 IMAP POP3 SMTP 4)负载均…...

Linux 配置规范 操作系统 _S3A3G3

目录 1.检查使用IP协议远程维护的设备是否配置SSH协议,禁用telnet协议 2.检查账户认证失败次数限制...

基于信息间隙决策理论的碳捕集电厂调度(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

【C语言进阶:指针的进阶】回调函数

本章重点内容: 字符指针指针数组数组指针数组传参和指针传参函数指针函数指针数组指向函数指针数组的指针回调函数指针和数组面试题的解析什么是回调函数: 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作…...

C++模板的使用

在平时的工作和学习过程中,经常会用到泛型,这里对泛型和模板进行一下梳理,以便理解和使用。 模板关键字 template。为什么要使用模板? 假如设计一个两个参数的函数,用来求两个对象的乘积,在实践中我们可能需要定义n多个函数 int multipli…...

三天Golang快速入门—面向对象

面向对象Golang接口的定义go中类空接口空接口作为函数的参数切片实现空接口map的值实现空接口类型断言值接收者和指针接收者值接收者指针接收者接口嵌套Golang接口的定义 接口interface是一种抽象的类型。接口定义了一个对象的行为规范,只定义规范不实现&#xff0…...

开发手册——一、编程规约_6.并发处理

这篇文章主要梳理了在java的实际开发过程中的编程规范问题。本篇文章主要借鉴于《阿里巴巴java开发手册终极版》 下面我们一起来看一下吧。 1. 【强制】获取单例对象需要保证线程安全,其中的方法也要保证线程安全。 说明:资源驱动类、工具类、单例工厂…...

ACM---大一第三周周赛(Floyd算法+并查集算法学习周)

🚀write in front🚀 📝个人主页:认真写博客的夏目浅石.CSDN 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝​ 📣系列专栏:ACM周训练题目合集.CSDN 💬总结&#xff1a…...

spring整合mybatis和Junit

该项目使用spring纯注解方式开发&#xff0c;用配置类取代spring的配置文件 一、导入依赖 整合Junit需要导入spring-test 整合mybatis需要导入spring-jdbc、mybatis-spring <dependencies><!-- https://mvnrepository.com/artifact/org.springframework/spring-cont…...

Spring Boot 3.0系列【7】核心特性篇之JSON

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本3.0.3 源码地址:https://gitee.com/pearl-organization/study-spring-boot3 文章目录 前言JSON什么是JSON常用JSON 库GsonFastJsonJacksonJackson 还是 FastjsonSpring Boot 中的 JSON1. 自动配置 Jackson2. @…...

【数据结构初阶】二叉树顺序结构:堆的实现

前言 前边077带着大家学习了树与二叉树的相关概念&#xff0c;这篇文章我们来实现一个二叉树的顺序结构。 二叉树的顺序结构 普通的二叉树是不适合用数组来存储的&#xff0c;因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉…...

C/C++:动态内存管理

目录 一. C/C内存分布 二. C/C动态内存管理 2.1 C语言动态内存管理 2.2 C动态内存管理 2.2.1 new/delete操作符 2.2.2 operator new与operator delete函数 2.3 new/delete的实现原理 2.4 定位new&#xff08;placement - new&#xff09; 2.5 new/delete和malloc/free的…...

黑猫带你学eMMC协议第28篇:eMMC的开漏和推挽模式(push-pull open drain)

本文依据eMMC JEDEC5.1及个人工作经验整理而成,如有错误请留言。 文章为个人辛苦整理,付费内容,已加入原创侵权保护,禁止私自转载。 文章所在专栏:《黑猫带你学:eMMC协议详解》 1 什么是开漏和推挽 1.1 推挽电路是什么 关于推挽和开漏电路,更多介绍详见我的另一篇文章…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...