Linux网卡驱动(2)-回环网卡的设计

1.回环网卡介绍

  • 在Linux系统中有一个叫做lo的网卡,它的IP地址是127.0.0.1,当我们ping  127.0.0.1的时候他是可以ping通的。回环网卡和其他网卡最大的区别就是他的数据包不会真正的方式到网络上,而是通过网卡发送端发送到了自己的接收端。就是自己给自己发送数据。它可以用来检查网络协议栈和网卡工作是否正常。
  • 今天我们的目标就是自己实现回环网卡的驱动。

2.回环网卡的设计

  • 在内核中的driver->net下面有一个文件叫做loopback.c,这个文件就是实现回环网卡的代码,我们接下来就是要实现这个代码。
  • 所需头文件
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include     /* For the statistics structure. */
#include     /* For ARPHRD_ETHER */
#include 
#include 
#include 
#include 
  • 全局变量
unsigned long bites = 0;    //记录数据量
unsigned long packets = 0;   //记录数据包的数量
  • 从上篇网卡驱动架构分析中可知,首先要进行网卡的初始化:
/* Setup and register the loopback device. */
static __net_init int loopback_net_init(struct net *net)
{
	struct net_device *dev;	
	int err = -ENOMEM;
	
	// 1.1分配net_device结构
	dev = alloc_netdev(0, "lo", loopback_setup);
	
	// 1.4注册网卡驱动                                                           
	err = register_netdev(dev);

	// 设置网卡结构
	net->loopback_dev = dev;
	
	return 0;
}
  • 这里没有用alloc_etherdev函数,因为alloc_etherdev只能为以太网卡分配空间。我们这里使用alloc_netdev函数,linux中函数定义为:
  • alloc_netdev(sizeof_priv, name, setup),其中第一个参数表示net_device结构中一个成员priv占用空间的大小,我们这里不使用,所以填0。第二个参数表示回环网卡的名称,第三个参数是一个函数指针,分配完空间后会自动调用setup函数来进行网卡的初始化,所以我们将初始化的操作移至setup函数中。分配好空间之后将网卡驱动注册到linux内核中。最后告诉linux内核回环网卡驱动为dev。
  • 接下来是loopback_setup函数的实现:
static void loopback_setup(struct net_device *dev)
{
    dev->mtu = (16 * 1024) + 20 + 20 + 12;   // 最大能接收数据包的大小
    dev->hard_header_len = ETH_HLEN;         //14
    dev->addr_len = ETH_ALEN;                 //6
    dev->tx_queue_len = 0;
    dev->type = ARPHRD_LOOPBACK;             //0x0001
    dev->flags = IFF_LOOPBACK;               //设置回环标志
    dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
    dev->features = NETIF_F_SG 
                  | NETIF_F_FRAGLIST
                  | NETIF_F_TSO
                  | NETIF_F_NO_CSUM
                  | NETIF_F_HIGHDMA
                  | NETIF_F_LLTX
                  | NETIF_F_NETNS_LOCAL;
    dev->header_ops        = ð_header_ops;    // 构造头的函数集
    dev->netdev_ops        = &loopback_ops;      // 网卡操作函数集	
}
  • loopback_setup函数进行网卡的初始化。上篇文章提到初始化中断号、I/O基地址、MAC地址都是针对物理的网卡,我们今天的回环网卡是一个虚拟网卡,所以不需要对他们进行初始化。这里初始化的有这几项:
    • 1、网卡操作函数集
    • 2、最大能接收数据包的大小
    • 3、网卡驱动的标志,指明是一个回环网卡
    • 4、构造头的函数集
  • 网卡操作函数集实现两个函数:数据的接收和查询网卡状态:
struct net_device_ops loopback_ops = 
{
	.ndo_start_xmit = loopback_xmit,  //发送数据
	.ndo_get_stats = loopback_get_stats,  //发送状态 			
};
  • 首先看发送数据函数的实现:
int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
{
	 // 设置网络协议
	skb->protocol = eth_type_trans(skb, dev);
	
	// 设置发送信息
	bytes += skb->len;
	packets++;	
	
	// 发送到协议栈
	netif_rx(skb);
	
	return 0;
}
  • 由于回环网卡在发送数据之后立刻就将数据接收回来,所以不必让上层协议暂停发送数据。再者回环网卡是虚拟网卡,也不需要将数据写入寄存器中发送。在这个函数中我们只简单的统计数据长度和包数量的信息,然后将数据包发回给上层协议栈即可。
  • 然后是查看状态函数:
static struct net_device_stats *loopback_get_stats(struct net_device *dev)
{
	// 设置发送状态
	struct net_device_stats *stats = &dev->stats;
	
	stats->tx_bytes = bytes;
	stats->rx_bytes = bytes;
	stats->rx_packets = packets;
	stats->tx_packets = packets;
	
	return stats;
}
  • 这样回环网卡的驱动程序大致就设计完了。由于回环网卡是虚拟网卡,不牵扯中断、寄存器等硬件信息,所以驱动程序十分简单。
  • 完整代码:
/*
 * INET		An implementation of the TCP/IP protocol suite for the LINUX
 *		operating system.  INET is implemented using the  BSD Socket
 *		interface as the means of communication with the user level.
 *
 *		Pseudo-driver for the loopback interface.
 *
 * Version:	@(#)loopback.c	1.0.4b	08/16/93
 *
 * Authors:	Ross Biro
 *		Fred N. van Kempen, 
 *		Donald Becker, 
 *
 *		Alan Cox	:	Fixed oddments for NET3.014
 *		Alan Cox	:	Rejig for NET3.029 snap #3
 *		Alan Cox	: 	Fixed NET3.029 bugs and sped up
 *		Larry McVoy	:	Tiny tweak to double performance
 *		Alan Cox	:	Backed out LMV's tweak - the linux mm
 *					can't take it...
 *              Michael Griffith:       Don't bother computing the checksums
 *                                      on packets received on the loopback
 *                                      interface.
 *		Alexey Kuznetsov:	Potential hang under some extreme
 *					cases removed.
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 	/* For the statistics structure. */
#include 	/* For ARPHRD_ETHER */
#include 
#include 
#include 
#include 

unsigned long bytes = 0;  //发送数据长度
unsigned long packets = 0;  //发送报文个数

// 网卡发送数据
int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
{
	 // 设置网络协议
	skb->protocol = eth_type_trans(skb, dev);
	
	// 设置发送信息
	bytes += skb->len;
	packets++;	
	
	// 发送到协议栈
	netif_rx(skb);
	
	return 0;
}

// 网卡发送状态
static struct net_device_stats *loopback_get_stats(struct net_device *dev)
{
	// 设置发送状态
	struct net_device_stats *stats = &dev->stats;
	
	stats->tx_bytes = bytes;
	stats->rx_bytes = bytes;
	stats->rx_packets = packets;
	stats->tx_packets = packets;
	
	return stats;
}

//网卡操作方法
struct net_device_ops loopback_ops = 
{
	.ndo_start_xmit = loopback_xmit,  //发送数据
	.ndo_get_stats = loopback_get_stats,  //发送状态 			
};

static void loopback_setup(struct net_device *dev)
{
	dev->mtu = (16 * 1024) + 20 + 20 + 12;   // 最大能接收数据包的大小
	dev->hard_header_len = ETH_HLEN;         //14
	dev->addr_len = ETH_ALEN;                 //6
	dev->tx_queue_len = 0;
	dev->type = ARPHRD_LOOPBACK;             //0x0001
	dev->flags = IFF_LOOPBACK;               //设置回环标志
	dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
	dev->features = NETIF_F_SG
		| NETIF_F_FRAGLIST
		| NETIF_F_TSO
		| NETIF_F_NO_CSUM
		| NETIF_F_HIGHDMA
		| NETIF_F_LLTX
		| NETIF_F_NETNS_LOCAL;
	dev->header_ops = ð_header_ops;    // 构造头的函数集
	dev->netdev_ops = &loopback_ops;      // 网卡操作函数集	
}

/* Setup and register the loopback device. */
static __net_init int loopback_net_init(struct net *net)
{
	struct net_device *dev;	
	int err = -ENOMEM;
	
	// 1.1分配net_device结构
	dev = alloc_netdev(0, "lo", loopback_setup);
	
	// 1.4注册网卡驱动                                                           
	err = register_netdev(dev);

	// 设置网卡结构
	net->loopback_dev = dev;
	
	return 0;
}

static __net_exit void loopback_net_exit(struct net *net)
{
	// 注销网卡结构
    struct net_device *dev;
    
    dev = net->loopback_dev;
    unregister_netdev(dev);
}

/* Registered in net/core/dev.c */
struct pernet_operations __net_initdata loopback_net_ops = {
	.init = loopback_net_init,
	.exit = loopback_net_exit,
};

 

你可能感兴趣的:(Linux底层驱动,回环网卡设计)