回环网卡是一个虚拟网卡,当上层协议栈发出包后,包又会被发回给上层协议栈。下面来编程实现回环网卡驱动。
所需头文件
#include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/string.h> #include <linux/socket.h> #include <linux/errno.h> #include <linux/fcntl.h> #include <linux/in.h> #include <linux/init.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/ethtool.h> #include <net/sock.h> #include <net/checksum.h> #include <linux/if_ether.h> /* For the statistics structure. */ #include <linux/if_arp.h> /* For ARPHRD_ETHER */ #include <linux/ip.h> #include <linux/tcp.h> #include <linux/percpu.h> #include <net/net_namespace.h>
全局变量
unsigned long bites = 0; //记录数据量 unsigned long packets = 0; //记录数据包的数量 struct net_device *dev; //定义一个网卡驱动
从上篇网卡驱动架构分析中可知,首先要进行网卡的初始化
static __net_init int loopback_net_init(struct net *net) { dev = alloc_netdev(0,"lo%d",lo_setup); //给网卡驱动分配空间 register_netdev(dev); //注册网卡驱动 net->loopback_dev = dev; return 0; }
这里没有用alloc_etherdev函数,因为alloc_etherdev只能为以太网卡分配空间。我们这里使用alloc_netdev函数,linux中函数定义为
define alloc_netdev(sizeof_priv, name, setup),其中第一个参数表示net_device结构中一个成员priv占用空间的大小,我们这里不使用,所以填0。第二个参数表示回环网卡的名称,第三个参数是一个函数指针,分配完空间后会自动调用setup函数来进行网卡的初始化,所以我们将初始化的操作移至setup函数中。分配好空间之后将网卡驱动注册到linux内核中。最后告诉linux内核回环网卡驱动为dev。
接下来是lo_setup函数的实现
static void __init lo_setup(struct net_device *dev) { dev->netdev_ops = &lo_ops; //网卡操作函数集 dev->mtu = (16*1024) + 20 + 20 + 12; //最大能接收数据包的大小 dev->flags = IFF_LOOPBACK; //网卡驱动的标志 dev->header_ops = ð_header_ops; //构造头的函数集 }
lo_setup函数进行网卡的初始化。上篇文章提到初始化中断号、I/O基地址、MAC地址都是针对物理的网卡,我们今天的回环网卡是一个虚拟网卡,所以不需要对他们进行初始化。这里初始化的有这几项:
1、网卡操作函数集
2、最大能接收数据包的大小
3、网卡驱动的标志,指明是一个回环网卡
4、构造头的函数集
网卡操作函数集实现两个函数:数据的接收和查询网卡状态
struct net_device_ops lo_ops = { .ndo_start_xmit = lo_xmit, .ndo_get_stats = lo_get_stats, };
首先看发送数据函数的实现:
static int lo_xmit(struct sk_buff *skb, struct net_device *dev) { /*标明以太网协议*/ skb->protocol = eth_type_trans(skb,dev); /*统计信息*/ bites += skb->len; packets ++; netif_rx(skb); return 0; }
由于回环网卡在发送数据之后立刻就将数据接收回来,所以不必让上层协议暂停发送数据。再者回环网卡是虚拟网卡,也不需要将数据写入寄存器中发送。在这个函数中我们只简单的统计数据长度和包数量的信息,然后将数据包发回给上层协议栈即可。
然后是查看状态函数
static struct net_device_stats *lo_get_stats(struct net_device *dev) { struct net_device_stats *stats = &dev->stats; stats->rx_packets = packets; stats->tx_packets = packets; stats->rx_bytes = bites; stats->tx_bytes = bites; return stats; }
这样回环网卡的驱动程序大致就设计完了。由于回环网卡是虚拟网卡,不牵扯中断、寄存器等硬件信息,所以驱动程序十分简单。
如果有疑问或错误,欢迎指出。