libowenhit 2016-01-29
回环网卡驱动
1.回环网卡和普通网卡的区别是他是虚拟的不是实际的物理网卡,它相当于把普通网卡的发送端和接收端短接在一起。
2.在内核源代码里的回环网卡程序(drivers/net/loopback.c)不是以一个模块的形式给出,但是他的初始化(loopback_net_init)和退出函数(loopback_dev_free)会被内核的其他部分调用到。
3.参照网卡初始化的流程图进行设计驱动程序,其中分配net_device结构不能用alloc_etherdev函数,因为该函数是分配以太网卡的结构体的,要用alloc_netdev函数来给回环网卡分配结构体。参考内核源代码别人如何用使用这个函数alloc_netdev(0, "lo", loopback_setup);第一个0表示net_device这个结构体的私有成员的大小,一般选择0,第二个表示网卡名字,ifconfig显示的名称,第三个就是具体的网卡结构体的初始化函数指针。
struct net_device *dev;
dev = alloc_netdev(0, "lo", loopback_setup);
最终会调用到loopback_setup函数。(至于在将函数指针作为参数的时候如何传递形参,就要复习C语言了)
4.回环网卡不需要初始化硬件。所以直接注册回环网卡的结构体到内核(第三步)。最后指定回环网卡注册到网络子系统。
net->loopback_dev = dev;这一步很关键。
5.具体的初始化(loopback_setup)(第二步)
(1)基地址,MAC地址以及中断号都用不着,主要是netdev_ops这个结构体,他包含了这个网卡支持的操作。
(2)表示回环网卡支持的最大的接收数据的包的大小,除了正式数据,还有相关网络协议的头部分。
dev->mtu = (16 * 1024) + 20 + 20 + 12;有效数据一般定义为16KB。
(3)加上表示回环网卡专有的标志。
dev->flags = IFF_LOOPBACK;
、
(4)加上构造报头的结构体指针,这个结构体指针指向的结构体成员是众多构造以太网报头的函数指针。
dev->header_ops = ð_header_ops;
理想查找可看到
extern const struct header_ops eth_header_ops;
struct header_ops {
int (*create) (struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *daddr,
const void *saddr, unsigned len);
int (*parse)(const struct sk_buff *skb, unsigned char *haddr);
int (*rebuild)(struct sk_buff *skb);
#define HAVE_HEADER_CACHE
int (*cache)(const struct neighbour *neigh, struct hh_cache *hh);
void (*cache_update)(struct hh_cache *hh,
const struct net_device *dev,
const unsigned char *haddr);
};
6.数据发送
static int loopback_net_xmit(struct sk_buff *skb,struct net_device *dev)
{
skb->protocol = eth_type_trans(skb,dev);
packets++;
bytes += skb->len;
netif_rx(skb);
return 0;
}
}
(1)第一个参数是协议栈传送给回环网卡的包数据,第二个参数是回环网卡的结构体。
(2)停止发送队列
通知上层暂停送数据,好让txd发送已送达的数据,但是不涉及硬件,所以在回环网卡可忽略。相应的,将数据写入寄存器和唤醒再次发送以及释放队列就可忽略。
(3)信息统计,表明上层送下来的包的协议
skb->protocol = eth_type_trans(skb, dev);
(4)统计发送过来的数据大小以及包的个数。
bytes += skb->len;
netif_rx(skb);
7.由于从协议栈来的数据包(skb)存放到txd,而且txd不需要往外发送,txd和rxd“连”在一起,所以直接在发送部分调用普通网卡的接收部分的netif_rx(skb)函数,所以发送的同时就完成了接收。同时我们更清楚地看到在发送的时候不能释放skb,否则没有可接收的数据。
8.实现获取网卡状态的函数
static struct net_device_stats *loopback_get_stats(struct net_device *dev)
{
struct net_device_stats *stats = &dev->stats;
stats->rx_packets = packets;
stats->tx_packets = packets;
stats->rx_bytes = bytes;
stats->tx_bytes = bytes;
return stats;
}
从这个结构体可以获取网卡状态信息
/* The main device statistics structure */
struct rtnl_link_stats64 {
__u64 rx_packets; /* total packets received */
__u64 tx_packets; /* total packets transmitted */
__u64 rx_bytes; /* total bytes received */
__u64 tx_bytes; /* total bytes transmitted */
__u64 rx_errors; /* bad packets received */
__u64 tx_errors; /* packet transmit problems */
__u64 rx_dropped; /* no space in linux buffers */
__u64 tx_dropped; /* no space available in linux */
__u64 multicast; /* multicast packets received */
__u64 collisions;
/* detailed rx_errors: */
__u64 rx_length_errors;
__u64 rx_over_errors; /* receiver ring buff overflow */
__u64 rx_crc_errors; /* recved pkt with crc error */
__u64 rx_frame_errors; /* recv'd frame alignment error */
__u64 rx_fifo_errors; /* recv'r fifo overrun */
__u64 rx_missed_errors; /* receiver missed packet */
/* detailed tx_errors */
__u64 tx_aborted_errors;
__u64 tx_carrier_errors;
__u64 tx_fifo_errors;
__u64 tx_heartbeat_errors;
__u64 tx_window_errors;
/* for cslip etc */
__u64 rx_compressed;
__u64 tx_compressed;
};
主要是这四个成员
stats->rx_packets = packets;
stats->tx_packets = packets;
stats->rx_bytes = bytes;
stats->tx_bytes = bytes;
。实际上自己可以重写这个获取状态的函数,因为struct net_device *dev有一个成员就是struct net_device_stats stats;所以可以在重写的时候定义一个struct net_device_stats stats指向形参传递进来的回环网卡结构体的stats成员,同样只需要注意stats成员的上述四个成员即可。
9.在退出该驱动的时候就是取消注册结构体。达到注销网卡的目的。
static __net_exit void loopback_net_exit(struct net *net)
{
struct net_device *dev = net->loopback_dev;
unregister_netdev(dev);
}
下面为范例代码
#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>
#include <linux/u64_stats_sync.h>
unsigned long packets = 0;
unsigned long bytes = 0;
//struct net_device *dev;
static __net_exit void loopback_net_exit(struct net *net)
{
struct net_device *dev = net->loopback_dev;
unregister_netdev(dev);
}
static int loopback_net_xmit(struct sk_buff *skb,struct net_device *dev)
{
skb->protocol = eth_type_trans(skb,dev);
packets++;
bytes += skb->len;
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->rx_packets = packets;
stats->tx_packets = packets;
stats->rx_bytes = bytes;
stats->tx_bytes = bytes;
return stats;
}
static const struct net_device_ops loopback_ops =
{
.ndo_start_xmit = loopback_net_xmit,
.ndo_get_stats = loopback_get_stats,
};
/*
* The loopback device is special. There is only one instance
* per network namespace.
*/
static void loopback_setup(struct net_device *dev)
{
dev->mtu = (16 * 1024) + 20 + 20 + 12;
dev->flags = IFF_LOOPBACK;
dev->header_ops = ð_header_ops;
dev->netdev_ops = &loopback_ops;
}
/* Setup and register the loopback device. */
static int loopback_net_init(struct net *net)
{
struct net_device *dev;
//1.
dev = alloc_netdev(0, "lo", loopback_setup);
//4.
register_netdev(dev);
net->loopback_dev = dev;
return 0;
}
/* Registered in net/core/dev.c */
struct pernet_operations __net_initdata loopback_net_ops = {
.init = loopback_net_init,
.exit = loopback_net_exit,
};