回環網卡驅動
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,
};