《Linux驱动:网络设备驱动》
文章目录
- 一,前言
- 二,网络设备驱动的结构
- 2.1 网络协议接口层
- 2.2 网络设备接口层
- 2.3 设备驱动功能层
- 2.4 网络设备与媒介层
- 三,构建虚拟网卡驱动
- 四,总结
- 4.1 数据传输过程
- 4.2 构建网络设备驱动程序的一般流程(设备驱动功能层)
一,前言
前面学习分析过字符设备驱动、块设备驱动的基本框架和构建流程。本文接着学习网络设备驱动,网络设备面向数据的接收和发送设计,它不对应文件系统的设备节点。内核和网络设备的通信与内核和字符设备、块设备的通信方式完全不同,网络设备主要还是使用套接字接口。在这里引入网络设备驱动的基本结构,以及构建一个网络设备驱动的基本流程。
二,网络设备驱动的结构
在构建具体的网络设备驱动程序时,我们需要完成的主要工作是编写设备驱动功能层中的相关函数以填充net_device数据结构注册到内核。
2.1 网络协议接口层
向网络层协议提供统一的数据包收发接口,不论上层协议是ARP还是IP都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接收数据。这一层的存在使得上层协议独立于具体的设备。
2.2 网络设备接口层
向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体net_device,该结构体是设备驱动功能层中各函数的容器。
2.3 设备驱动功能层
该层设计的各函数接口是网络设备接口层net_device数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,它通过hard_start_xmit() 函数启动发送操作,并通过网络设备上的中断触发接收操作。
2.4 网络设备与媒介层
完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网络适配器被设备驱动功能层中的各函数在物理上驱动。
三,构建虚拟网卡驱动
向内核注册一个虚拟网卡设备,手动给该虚拟设备设置IP后,可以随意ping通一个同网段的ip。
/** 参考 drivers\net\cs89x0.c*/#include <linux/module.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/ip.h>#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>static struct net_device *vnet_dev;static void emulator_rx_packet(struct sk_buff *skb, struct net_device *dev)
{/* 参考LDD3 */unsigned char *type;struct iphdr *ih;__be32 *saddr, *daddr, tmp;unsigned char tmp_dev_addr[ETH_ALEN];struct ethhdr *ethhdr;struct sk_buff *rx_skb;/*这里是使用了发送出去的sk_buff构建假的接收到的数据,即原来的是源mac发送给目的mac,现在调换一下就是原先的源mac变成目的mac,目的mac变成源mac,下面的IP也要调换*/// 从硬件读出/保存数据/* 对调"源/目的"的mac地址 */ethhdr = (struct ethhdr *)skb->data;memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);/* 对调"源/目的"的ip地址 */ ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));saddr = &ih->saddr;daddr = &ih->daddr;tmp = *saddr;*saddr = *daddr;*daddr = tmp;//((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) *///((u8 *)daddr)[2] ^= 1;type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);//printk("tx package type = %02x\n", *type);// 修改类型, 原来0x8表示ping*type = 0; /* 0表示reply */ih->check = 0; /* and rebuild the checksum (ip needs it) */ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);// 构造一个sk_buffrx_skb = dev_alloc_skb(skb->len + 2);skb_reserve(rx_skb, 2); /* align IP on 16B boundary */ memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);/* Write metadata, and then pass to the receive level */rx_skb->dev = dev;rx_skb->protocol = eth_type_trans(rx_skb, dev);rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */dev->stats.rx_packets++;dev->stats.rx_bytes += skb->len;// 提交sk_buffnetif_rx(rx_skb);
}static int virt_net_send_packet(struct sk_buff *skb, struct net_device *dev)
{static int cnt = 0;printk("virt_net_send_packet cnt = %d\n", ++cnt);/* 对于真实的网卡, 把skb里的数据通过网卡发送出去 */netif_stop_queue(dev); /* 停止该网卡的队列 *//* ...... */ /* 把skb的数据写入网卡 *//* 由于这里是一个假的网络设备,没有收包能力,直接在这里构造一个假的sk_buff,上报。真实的网络设备驱动应该在一个网卡中断中,接收并处理数据后,再发送给上层处理。*/emulator_rx_packet(skb, dev);dev_kfree_skb (skb); /* 释放skb */netif_wake_queue(dev); /* 数据全部发送出去后,唤醒网卡的队列 *//* 更新统计信息 */dev->stats.tx_packets++;dev->stats.tx_bytes += skb->len;return 0;
}static int virt_net_init(void)
{/* 1. 分配一个net_device结构体 */vnet_dev = alloc_netdev(0, "vnet%d", ether_setup);; /* alloc_etherdev *//* 2. 设置 */vnet_dev->hard_start_xmit = virt_net_send_packet;/* 设置MAC地址 */vnet_dev->dev_addr[0] = 0x08;vnet_dev->dev_addr[1] = 0x89;vnet_dev->dev_addr[2] = 0x89;vnet_dev->dev_addr[3] = 0x89;vnet_dev->dev_addr[4] = 0x89;vnet_dev->dev_addr[5] = 0x11;/* 设置下面两项才能ping通 */// 因为在真实的环境中,ping通其他的网络设备前需要先通过arp协议获取其MAC地址。// 而现在ping的设备是不存在的,所以通过设置该参数,默认对方存在,直接发送出数据// 即直接调用hard_start_xmit接口。vnet_dev->flags |= IFF_NOARP; vnet_dev->features |= NETIF_F_NO_CSUM; /* 3. 注册 *///register_netdevice(vnet_dev);register_netdev(vnet_dev);return 0;
}static void virt_net_exit(void)
{unregister_netdev(vnet_dev);free_netdev(vnet_dev);
}module_init(virt_net_init);
module_exit(virt_net_exit);MODULE_LICENSE("GPL");
四,总结
4.1 数据传输过程
发包:网络协议层调用网络协议接口层dev_queue_xmit( )接口发包;设备驱动功能层给网络设备接口层的net_device结构体的hard_start_xmit成员赋值,即提供发包函数;dev_queue_xmit( )函数调用该发包函数将数据通过网卡传输给其他网络设备。
收包:一般是在设备驱动功能层中,创建一个中断,在中断中接收网卡传输过来的数据,然后通过网络协议接口层接口netif_rx发送给网络协议层处理->传输层处理->应用层处理。
4.2 构建网络设备驱动程序的一般流程(设备驱动功能层)
- 调用alloc_netdev接口申请分配一个net_device结构体。
- 设置net_device结构体,至少提供一个发包函数。
- 根据硬件电路申请一个网卡相关中断,在中断中接收网卡接收到的其他设备发送过来的数据,通过netif_rx函数发送给上层处理。
- 调用register_netdev接口注册该驱动。
《Linux驱动:网络设备驱动》相关推荐
- ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)
视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...
- ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析
视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...
- 信息学奥赛真题解析(玩具谜题)
玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...
- 信息学奥赛之初赛 第1轮 讲解(01-08课)
信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...
- 信息学奥赛一本通习题答案(五)
最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...
- 信息学奥赛一本通习题答案(三)
最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...
- 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题
第1章 快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章 素数 第 3 章 约数 第 4 章 同余问题 第 5 章 矩阵乘法 第 6 章 ...
- 信息学奥赛一本通题目代码(非题库)
为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...
- 信息学奥赛一本通(C++版) 刷题 记录
总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...
- 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离
首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...
最新文章
- 守护进程中创建的对象php,在PHP中生成守护进程(Daemon Process)
- jtoken判断是否包含键_c#-确定JToken是否为叶子
- php伪类型,解密PHP伪类型和伪变量的含义
- UVa1153 Keep The Customer Satisfied(贪心)
- 【算法随记一】Canny边缘检测算法实现和优化分析。
- SQL笛卡尔积结合前后行数据的统计案例
- 权限基本操作:实体类和dao
- django foreign key 自动加_id问题
- mysql的权限管理
- 如何利用大数据做好数据分析
- CDOJ 485 UESTC 485 	Game (八数码变形,映射,逆cantor展开)
- 关于Activity跳转动画大汇总
- Web开发之旅--使用Flask 实现REST架构的API
- Rails中select2 实现多选框的效果
- Hadoop-Spark企业应用实战
- 《APUE.3E》用gdb调试ftw函数(图4-22)
- Android Service之设备存储空间监控
- 期货开户线上线下开户流程
- 2022年经历太多,大家都太艰难了,听一首悲伤的歌曲,代表现在的心情,送别老朋友
- 表单input标签type属性详解