[Linux驱动开发九] 简单虚拟网卡制作 |
您所在的位置:网站首页 › tcpdump查看mac地址 › [Linux驱动开发九] 简单虚拟网卡制作 |
目录 一、网卡设备简介 二、虚拟网卡驱动程序编写 2.1 init初始函数编写( vir_dev_register() ) 2.2 发包函数编写( vir_dev_xmit() ) 2.3 收包函数编写( vir_dev_rcpacket() ) 三、运行测试 3.1 编译加载 3.2 网卡启动及ip地址配置 3.3 ping测试 四、总结 一、网卡设备简介网卡设备主要负责对网络数据包进行收发操作。网卡主要有两个功能:接收数据和发送数据。它将上层协议传递下来的数据包传递给Linux内核,并且将Linux内核的数据发送出去。 与字符设备不同,网卡设备在/dev目录下没有对应文件,但在/sys/class/net目录下可以查看网卡。接下来,我们简单介绍一下网卡的收发包流程。 (1)网卡收包 step1:网卡芯片获取网线上的物理帧,并检查物理帧的CRC,保证完整性; step2:网卡芯片去除物理帧头,得到MAC数据包; step3:网卡芯片检查MAC包中的目的MAC地址,如果和本网卡MAC不一致则丢弃(混杂模式除外); step4:网卡芯片将MAC帧拷贝到内部缓冲区,触发硬件中断; step5:网卡驱动程序通过中断处理函数,构造sk_buff,将其拷贝到内存中,交付内核处理。 (2)网卡发包 step1:网卡驱动程序将上层协议传递下来的数据包(IP数据包)构造成MAC包; step2:网卡驱动程序将MAC包拷贝至网卡芯片内部缓冲区; step3:网卡芯片将MAC包封装成物理帧,添加同步信息和CRC校验,通过网线发送出去(网线上所有网卡都能收到该帧)。 二、虚拟网卡驱动程序编写本章节编写一个虚拟网卡驱动程序,利用ping命令发包,然后构造一个发包函数伪造一个收的ping包函数,实现该虚拟网卡能ping通任何ip地址。 2.1 init初始函数编写( vir_dev_register() )(1)使用alloc_netdev()函数分配一个net_device结构体; (2)设置net_device结构体成员; (3)使用register_netdev()来注册net_device结构体; static const struct net_device_ops vnet_ops = { .ndo_open = vnet_open, .ndo_stop = vnet_stop, .ndo_start_xmit = vnet_tx, }; static int vir_dev_register(void) { int ret = 0; vir_dev = alloc_netdev(sizeof(struct net_device), "eth_xzx", NET_NAME_UNKNOWN, ether_setup); if (IS_ERR(vir_dev)) { return -ENOMEM; } /* 初始化MAC地址 */ vir_dev->dev_addr[0] = 0x00; vir_dev->dev_addr[1] = 0x01; vir_dev->dev_addr[2] = 0x02; vir_dev->dev_addr[3] = 0x03; vir_dev->dev_addr[4] = 0x04; vir_dev->dev_addr[5] = 0x05; /* 设置操作函数 */ vir_dev->netdev_ops = &vir_dev_ops; vir_dev->flags |= IFF_NOARP; vir_dev->features |= NETIF_F_HW_CSUM; /* 注册net_device结构体 */ ret = register_netdev(vir_dev); if (ret) { free_netdev(vir_dev); return ret; } return ret; } 2.2 发包函数编写( vir_dev_xmit() )(1)使用netif_stop_queue()来阻止上层向网络设备驱动层发送数据包; (2)使用收包函数( vir_dev_rcpacket() )伪造收的ping函数; (3)使用dev_kfree_skb()函数释放发送的sk_buff缓冲区; (4)更新发送统计信息; (5)使用netif_wake_queue()唤醒被阻塞的上层; static int vir_dev_xmit(struct sk_buff *skb, struct net_device *dev) { printk("Running vir_dev_xmit\n"); /* 调用netif_stop_queue()阻止上层向网络设备驱动层发送数据包 */ netif_stop_queue(dev); /* 调用收包函数,伪造接收ping包 */ vir_dev_rcpacket(skb, dev); /* 调用dev_kfree_skb()函数释放发送的sk_buff */ dev_kfree_skb(skb); /* 更新发送的统计信息 */ dev->stats.tx_packets ++; dev->stats.tx_bytes += skb->len; dev->trans_start = jiffies; /* 调用netif_wake_queue()唤醒被阻塞的上层 */ netif_wake_queue(dev); return 0; } 2.3 收包函数编写( vir_dev_rcpacket() )(1)交换ethhdr结构体的“源/目”MAC地址; (2)交换iphdr结构体的“源/目” IP地址; (3)使用ip_fast_csum()来重新获取iphdr结构体的校验码; (4)修改数据类型(发送ping包为0x08,接收ping包类型为0x00); (5)使用dev_alloc_skb()构造一个新的sk_buff; (6)调用skb_reserve()预留2字节的头部空间,16字节对齐,(以太网的协议头长度是14个字节); (7)将skb_buff->data复制到新的sk_buff中 利用skb_put动态扩大数据区,避免溢出; (8)设置新的sk_buff的其他成员; (9)调用eth_type_trans()获取上层协议; (10)更新接收的统计信息,调用netif_rx()来传递sk_buff数据包。 static int vir_dev_rcpacket(struct sk_buff *skb, struct net_device *dev) { struct ethhdr *ethhdr; struct iphdr *ih; unsigned char *type; unsigned char tmp_dev_addr[ETH_ALEN]; __be32 *saddr, *daddr, tmp; struct sk_buff *rx_skb; /* 1、对调 源/目 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); /* 2、对调 源/目 IP地址 */ ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr)); saddr = &ih->saddr; daddr = &ih->daddr; tmp = *saddr; *saddr = *daddr; *daddr = tmp; /* 3、调用ip_fast_csum()获取iphr结构体的校验码 */ ih->check = 0; ih->check = ip_fast_csum((unsigned char *)ih, ih->ihl); /* 4、设置数据类型为0,表示接收ping包 */ type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr); *type = 0; /* 5、调用dev_alloc_skb()构造新的接收sk_buff */ rx_skb = dev_alloc_skb(skb->len + 2); /* * skb_reserve()增加头部空间 * skb_put()增加数据区长度 */ /* 6、调用skb_reserve()预留2字节的头部空间,16字节对齐,(以太网的协议头长度是14个字节) */ skb_reserve(rx_skb, 2); /* 7、将skb_buff->data复制到新的sk_buff中 利用skb_put动态扩大数据区,避免溢出*/ memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len); /* 8、设置新的sk_buff的其他成员 */ rx_skb->dev = dev; rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* 9、调用eth_type_trans()获取上层协议 */ rx_skb->protocol = eth_type_trans(rx_skb, dev); /* 10、更新接收的统计信息,调用netif_rx()来传递sk_buff数据包 */ dev->stats.rx_packets ++; dev->stats.rx_bytes += skb->len; dev->last_rx = jiffies; netif_rx(rx_skb); return 0; }【完整源代码及Makefile下载链接】 三、运行测试 3.1 编译加载 3.2 网卡启动及ip地址配置 3.3 ping测试 四、总结本章编写了一个简单的网卡驱动程序,该驱动程序只实现了ndo_start_xmit操作方法。驱动程序中vir_dev_rcpacket()函数将上层传递来的待发送数据进行了源地址和目的地址的交换,又返回给了系统上层,实现了类似于回环接口的功能。
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |