70 linux网络设备驱动之虚拟网卡及arp应答的实现
来源:互联网 发布:梦想世界跨服传输数据 编辑:程序博客网 时间:2024/06/05 02:14
网络设备驱动的调用不像字符设备驱动,不是通过打开设备文件或属性文件的方式来操作的.
通常情况下用socket编程来实现网络通信, 当使用tcp/udp协议收发网络数据时,无需处理网络协议的包头。
因为网络设备驱动应用程序是无法直接调用, socket编程时是经过网络协议栈处理包头信息后, 再由网络协议栈调用网络设备来收发数据的.
在linux内核里以结构体net_device的对象来描述一个网络设备.
include/linux/netdevice.hstruct net_device { ... char name[IFNAMSIZ]; //设备名, 如"eth0", "enp3s0" unsigned long last_rx; //上次接收数据包的时间,用jiffies unsigned char dev_addr[MAX_ADDR_LEN]; //mac地址 unsigned long last_rx; //记录上一次接收网络数据包的时间,以jiffies时间为准 unsigned long trans_start; //记录上一次发送网络数据包的时间,以jiffies时间为准 const struct net_device_ops *netdev_ops; //网络设备的操作对象,如发送数据包的功能函数 ...};struct net_device_ops { int (*ndo_init)(struct net_device *dev); //网络设备初始化时自动调用的, 只调用一次 void (*ndo_uninit)(struct net_device *dev); //网络设备退出工作时调用 int (*ndo_open)(struct net_device *dev); //在ifconfig 设备名 up时调用 int (*ndo_stop)(struct net_device *dev); //在ifconfig 设备名 down时调用 int (*ndo_set_mac_address)(struct net_device *dev, void *addr); //设置mac地址触发 如ifconfig enp3s0 hw ether "00:0e:4c:1d:55:66" netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev); //此网络设备发送数据包的接口, 也就是让硬件发出数据包的代码实现. 这个接口是由网络协议栈需要发出网络数据时调用. //网络设备接收到数据时,需调用全局函数netif_rx(..)把网络数据提交到网络协议栈去. ...};
///////////////////////////////////////
一个系统里可有多个网络设备,socket通信时,数据从哪个网络设备发出?
首先每个网络设备有不同的IP地址, 而且不能在同一网段. 如网络掩码是255.255.255.0, 有网络设备A(192.168.0.11), 网络设备B(192.168.250.250). 当用户进程需要连接或发数据到192.168.0.250时,则从同网段的IP地址的网络设备发出.
当用户进程需与都不属于这两个网段的IP地址通信时, 则从默认网关指定的IP网段的网络设备发出.
以上的工作都是由网络协议栈来完成的。
网络数据的发送:
socket —> 网络协议栈 —-> net_device —> net_device的函数成员hard_start_xmit —> 硬件发出数据
网络数据的接收:
socket <— 网络协议栈 <— 全局函数netif_rx(struct sk_buff *skb) <— 硬件收到数据后
网络协议栈与网络设备之间的数据交互是以struct sk_buff来描述的.
struct sk_buff 表示一个套接字的缓冲区{ unsigned int len, //数据总长 data_len; __u16 mac_len, hdr_len; unsigned char *data; //指向用户的数据包缓冲区地址};
/////////////////////////////////////////
网络设备驱动里常用的函数:
alloc_etherdev(sizeof_priv);alloc_netdev(sizeof_priv, name, setup)//用来动态创建net_device的对象还创建对象里的私有数据, sizeof_priv指创建priv成员指向的空间大小, name为设备名, setup指定网络设备的初始化函数 struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, void (*setup)(struct net_device *), unsigned int queue_count)void ether_setup(struct net_device *dev); //给网络设备对象填入通用的值extern int register_netdev(struct net_device *dev);//注册一个网络设备extern void unregister_netdev(struct net_device *dev);//反注册网络设备void netif_start_queue(struct net_device *dev);//开启网络数据接收发送队列void netif_stop_queue(struct net_device *dev); //停止接收发送网络数据队列int netif_rx(struct sk_buff *skb); //把收到的数据递给网络协议层//////关于sk_buff的操作函数:struct sk_buff *dev_alloc_skb(unsigned int length); //动态申请套接字缓冲区dev_kfree_skb(struct sk_buff *skb); //把skb指向的套接字缓冲区释放掉unsigned char *skb_put(struct sk_buff *skb, unsigned int len);//动态申请套接字缓冲区里的data, 返回首地址__be16 eth_type_trans(struct sk_buff *skb, struct net_device*dev);//检查skb数据包的协议, 返回协议id
代码:
test.c
#include <linux/init.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/netdevice.h>struct net_device *vnet;u8 mymac[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; //本网络设备用的mac地址int my_hard_start_xmit(struct sk_buff *skb, struct net_device *dev){ printk("in my hard_start_xmit\n"); //enc28j60: 网络协议栈 --> 此函数 --> spi控制器 --> spi设备 ---> 网络数据 return 0;}int myopen(struct net_device *dev){ printk("in myopen\n"); return 0;}int mystop(struct net_device *dev){ printk("in mystop\n"); return 0;}int my_set_mac_address(struct net_device *dev, void *addr){ printk("in my set mac addr\n"); return 0;}void vnet_init(struct net_device *vnet) //网络设备初始时调用{ ether_setup(vnet); //因net_device里成员多, 这里就给其它成员设通用的值 printk("vnet init ...\n");}struct net_device_ops nops = { .ndo_open = myopen, .ndo_stop = mystop, .ndo_set_mac_address = my_set_mac_address, .ndo_start_xmit = my_hard_start_xmit,};int __init test_init(void){ int ret; vnet = alloc_netdev(0, "mynet", vnet_init); //动态创建net_device对象, 并指定vnet_init函数作为网络设备的初始函数, 则vnet->netdev_ops->ndo_init = vnet_init. 网络设备名为mynet if (!vnet) goto err0; memcpy(vnet->dev_addr, mymac, 6); //设置网络设备的mac地址 vnet->netdev_ops = &nops; //指定网络设备的操作对象 ret = register_netdev(vnet); //注册网络设备 if (ret < 0) goto err1; return 0;err1: free_netdev(vnet); return ret;err0: return -EINVAL;}void __exit test_exit(void){ unregister_netdev(vnet); free_netdev(vnet);}module_init(test_init);module_exit(test_exit);MODULE_LICENSE("GPL");
加载驱动模块后, ifconfig -a查看到mynet网络设备
ifconfig mynet up 触发函数myopen
ifconfig mynet down 触发函数mystop
ifconfig mynet hw ehter “11:22:33:44:55:66” 触发函数my_set_mac_address
ifconfig mynet 192.168.88.88 设置ip地址 ping 192.168.88.11 会触发函数my_hard_start_xmit
/////////////////////////////////////////////////////////////////////////////////
实现一个虚拟网络设备,只要通过它发出arp请求指定ip地址的pc的mac地址时,都会回arp应答包,表示要请求的mac地址是”11:22:33:44:55:66”
test.c
#include <linux/init.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#pragma pack(1)typedef struct { u8 dst_mac[6]; u8 src_mac[6]; u16 type;}eth_t;typedef struct { u16 hw_type; u16 pro_type; u8 hw_addr_len; u8 pro_addr_len; u16 op; // 1请求 2应答 u8 src_mac[6]; u32 src_ip; u8 dst_mac[6]; u32 dst_ip;}arp_t;#pragma pack()struct net_device *vnet;u8 mymac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; //本网络设备用的mac地址u8 mac_rx[6] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; //用于arp应答包的mac地址int my_hard_start_xmit(struct sk_buff *skb, struct net_device *dev){ eth_t *eth = (eth_t *)(skb->data), *eth_rx; arp_t *arp = (arp_t *)(skb->data + sizeof(eth_t)), *arp_rx; struct sk_buff *skb_rx; if (eth->type != htons(0x0806)) //arp协议号: 0x0806. 协议栈发出的不是arp相关的数据包,则丢弃. goto exit; if (arp->op != htons(1)) //如果不是arp请求包的话,则进行丢弃处理 goto exit; //这里回伪数据包,不管请求什么IP地址的mac地址,都回mac地址是"11:22:33:44:55:66". arp应答包 skb_rx = dev_alloc_skb(sizeof(eth_t) + sizeof(arp_t)); skb_put(skb_rx, sizeof(eth_t) + sizeof(arp_t)); //分配skb_rx->data的空间 eth_rx = (eth_t *)(skb_rx->data); arp_rx = (arp_t *)(skb_rx->data + sizeof(eth_t));//以太网包头 memcpy(eth_rx->dst_mac, mymac, 6); memcpy(eth_rx->src_mac, mac_rx, 6); eth_rx->type = htons(0x0806);//arp包头 arp_rx->hw_type = htons(1); arp_rx->pro_type = htons(0x0800); arp_rx->hw_addr_len = 6; arp_rx->pro_addr_len = 4; arp_rx->op = htons(2); //应答 memcpy(arp_rx->src_mac, mac_rx, 6); memcpy(arp_rx->dst_mac, mymac, 6); arp_rx->src_ip = arp->dst_ip; arp_rx->dst_ip = arp->src_ip; skb_rx->protocol = eth_type_trans(skb_rx, vnet); // netif_rx(skb_rx); //提交伪数据包到网络协议栈exit: dev_kfree_skb(skb); //需要回收协议栈传过来的缓冲区 return 0;}int myopen(struct net_device *dev){ printk("in myopen\n"); return 0;}int mystop(struct net_device *dev){ printk("in mystop\n"); return 0;}int my_set_mac_address(struct net_device *dev, void *addr){ printk("in my set mac addr\n"); return 0;}void vnet_init(struct net_device *vnet) //网络设备初始时调用{ ether_setup(vnet); //因net_device里成员多, 这里就给其它成员设通用的值 printk("vnet init ...\n");}struct net_device_ops nops = { .ndo_open = myopen, .ndo_stop = mystop, .ndo_set_mac_address = my_set_mac_address, .ndo_start_xmit = my_hard_start_xmit,};int __init test_init(void){ int ret; vnet = alloc_netdev(0, "mynet", vnet_init); //动态创建net_device对象, 并指定vnet_init函数作为网络设备的初始函数, 则vnet->netdev_ops->ndo_init = vnet_init. 网络设备名为mynet if (!vnet) goto err0; strcpy(vnet->name, "mynet"); memcpy(vnet->dev_addr, mymac, 6); //设置网络设备的mac地址 vnet->netdev_ops = &nops; //指定网络设备的操作对象 ret = register_netdev(vnet); //注册网络设备 if (ret < 0) goto err1; return 0;err1: free_netdev(vnet); return ret;err0: return -EINVAL;}void __exit test_exit(void){ unregister_netdev(vnet); free_netdev(vnet);}module_init(test_init);module_exit(test_exit);MODULE_LICENSE("GPL");
加载驱动模块后:
1) ifconfig mynet 192.168.88.88 //随便设置一个ip地址
2) arping -I mynet 192.168.88.1 //指定从mynet网络设置发出一个arp请求,用于获取ip地址为192.168.88.1的网络设备的mac地址。
[root@jk 02]# ifconfig mynet 192.168.88.88[root@jk 02]# arping -I mynet 192.168.88.1ARPING 192.168.88.1 from 192.168.88.88 mynetUnicast reply from 192.168.88.1 [11:22:33:44:55:66] 0.540msUnicast reply from 192.168.88.1 [11:22:33:44:55:66] 0.534msUnicast reply from 192.168.88.1 [11:22:33:44:55:66] 0.535msUnicast reply from 192.168.88.1 [11:22:33:44:55:66] 0.533msUnicast reply from 192.168.88.1 [11:22:33:44:55:66] 0.533ms
- 70 linux网络设备驱动之虚拟网卡及arp应答的实现
- [Linux驱动开发] 网络设备之虚拟网卡
- Linux驱动之虚拟网卡
- linux驱动之网卡驱动-虚拟网卡驱动编写
- 11-S3C2440驱动学习(五)嵌入式linux-网络设备驱动(一)虚拟网卡驱动程序
- linux虚拟网卡驱动
- linux虚拟网卡驱动
- 网卡驱动之虚拟网卡驱动编写
- 网卡驱动之虚拟网卡驱动编写
- linux 下千兆网卡驱动开发 网络设备驱动的层次结构 (三)
- linux虚拟网卡驱动代码
- CC3200的ARP应答
- Linux虚拟网卡实现
- linux网络设备驱动的结构
- Linux网络设备驱动的结构
- Linux设备驱动之网络设备驱动
- 网络设备驱动及程序实现步骤
- Linux-虚拟网络设备-LinuxBridge
- MySQL之完整性约束
- c++ debugger package is missing or incompatible
- Mybatis Generator在Intellij中的使用
- 从Git仓库中恢复已删除的分支或丢失的commit
- spring boot添加mongo-java驱动包之后报错的原因
- 70 linux网络设备驱动之虚拟网卡及arp应答的实现
- mongoose基本配置
- JS中字符串的长度计算、字符串截取
- 编辑距离 -LintCode
- SuperEasyRefreshLayout的使用介绍
- SpringBoot+SpringSecurityOAuth2.0 实现SSO单点登录(一)--客户端
- android应用签名
- java.lang.NoSuchMethodException:
- 【C#语言基础】重写和覆盖