? ? ? snull是《Linux Device Drivers》中的一個(gè)網(wǎng)絡(luò)驅(qū)動(dòng)的例子。這里引用這個(gè)例子學(xué)習(xí)Linux網(wǎng)絡(luò)驅(qū)動(dòng)。
因?yàn)閟null的源碼,網(wǎng)上已經(jīng)更新到適合最新內(nèi)核,而我自己用的還是2.6.22.6比較舊的內(nèi)核。而網(wǎng)上好像找不到舊版的snull。因此結(jié)合《Linux Device Drivers》把最新的snull例子移植到2.6.22.6內(nèi)核中。移植也相對(duì)簡(jiǎn)單,這里也提供移植好的代碼。
估計(jì)不少網(wǎng)友看到《Linux Device Drivers》的網(wǎng)絡(luò)驅(qū)動(dòng)部分,一臉懵逼,包括我自己,不理解作者設(shè)計(jì)這個(gè)例子的真正目的,盡管有配圖,仍然懵懂,甚至不知道為什么會(huì)用到6個(gè)IP地址。如圖:
其實(shí)作者的本意是想通過(guò)虛擬網(wǎng)卡來(lái)模擬實(shí)際的網(wǎng)卡和外部的網(wǎng)絡(luò)設(shè)備的通信來(lái)討論網(wǎng)絡(luò)驅(qū)動(dòng)。通過(guò)其中任何一個(gè)網(wǎng)絡(luò)接口(sn0或sn1)發(fā)送數(shù)據(jù),都在另一個(gè)網(wǎng)絡(luò)接口(sn0或sn1)接收到。
因?yàn)閟n0和sn1都不在同一個(gè)網(wǎng)段,所以sn0和sn1之間直接互ping是不行的,這中間必須必須做點(diǎn)轉(zhuǎn)換。
例子:
理論上local0和remote0只能互ping,因?yàn)樗麄兌荚谕粋€(gè)網(wǎng)段:192.168.0.0,但事實(shí)上,local0在發(fā)出數(shù)據(jù)之后,local0的第3個(gè)字節(jié)最低有效位改取反,就變成了remote1,remote1的數(shù)據(jù)才能到達(dá)local1,因?yàn)樗麄冊(cè)谕欢蜪P。相反,local1在發(fā)出數(shù)據(jù)之后,local1的第3個(gè)字節(jié)最低有效位改取反,就變成了remote0,remote0的數(shù)據(jù)才能到達(dá)local0.
因此,在實(shí)驗(yàn)之前,需要添加一些配置:
在/etc/networks文件中添加如下網(wǎng)段IP:
snullnet0 192.168.2.0
snullnet1 192.168.3.0
在/etc/hosts文件中添加如下IP地址
192.168.2.8 local0
192.168.2.9 remote0
192.168.3.9 local1
192.168.3.8 remote1
注意: 1. 網(wǎng)段IP和IP地址的第三個(gè)字節(jié)的最低有效位是相反的
2. local0和remote1第四個(gè)字節(jié)必須一樣,remote0和local1第四個(gè)字節(jié)必須一樣
3. 如果開(kāi)發(fā)板上的真正網(wǎng)卡用了的網(wǎng)段IP,就不能再用于本實(shí)驗(yàn)。如:我的開(kāi)發(fā)板的DM9000網(wǎng)卡使用網(wǎng)段是192.168.1.0, 因此本實(shí)驗(yàn)不能再使用192.168.1.0作為網(wǎng)段,否則有沖突。
代碼: snull.c, 其中snull.h沒(méi)改動(dòng),因此不貼出來(lái)
/*
?* snull.c --? the Simple Network Utility
?*
?* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
?* Copyright (C) 2001 O'Reilly & Associates
?*
?* The source code in this file can be freely used, adapted,
?* and redistributed in source or binary form, so long as an
?* acknowledgment appears in derived source files.? The citation
?* should list that the code comes from the book "Linux Device
?* Drivers" by Alessandro Rubini and Jonathan Corbet, published
?* by O'Reilly & Associates.? No warranty is attached;
?* we cannot take responsibility for errors or fitness for use.
?*
?* $Id: snull.c,v 1.21 2004/11/05 02:36:03 rubini Exp $
?*/
#include
#include
#include
#include
#include /* printk() */
#include /* kmalloc() */
#include ? /* error codes */
#include ? /* size_t */
#include /* mark_bh */
#include
#include ? /* struct device, and other headers */
#include /* eth_type_trans */
#include ? ? ? ? ? /* struct iphdr */
#include ? ? ? ? /* struct tcphdr */
#include
#include "snull.h"
#include
#include
MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
MODULE_LICENSE("Dual BSD/GPL");
/*
?* Transmitter lockup simulation, normally disabled.
?*/
static int lockup = 0;
module_param(lockup, int, 0);
static int timeout = SNULL_TIMEOUT;
module_param(timeout, int, 0);
/*
?* Do we run in NAPI mode?
?*/
static int use_napi = 0;
module_param(use_napi, int, 0);
/*
?* A structure representing an in-flight packet.
?*/
struct snull_packet {
? ? struct snull_packet *next;
? ? struct net_device *dev;
? ? int? ? datalen;
? ? u8 data[ETH_DATA_LEN];
};
int pool_size = 8;
module_param(pool_size, int, 0);
/*
?* This structure is private to each device. It is used to pass
?* packets in and out, so there is place for a packet
?*/
struct snull_priv {
? ? struct net_device_stats stats;
? ? int status;
? ? struct snull_packet *ppool;
? ? struct snull_packet *rx_queue;? /* List of incoming packets */
? ? int rx_int_enabled;
? ? int tx_packetlen;
? ? u8 *tx_packetdata;
? ? struct sk_buff *skb;
? ? spinlock_t lock;
? ? struct net_device *dev;
? ? //struct napi_struct napi;
};
static void snull_tx_timeout(struct net_device *dev);
static void (*snull_interrupt)(int, void *, struct pt_regs *);
/*
?* Set up a device's packet pool.
?*/
void snull_setup_pool(struct net_device *dev)
{
? ? struct snull_priv *priv = netdev_priv(dev);
? ? int i;
? ? struct snull_packet *pkt;
priv->ppool = NULL;
? ? for (i = 0; i < pool_size; i++) {
? ? ? ? pkt = kmalloc (sizeof (struct snull_packet), GFP_KERNEL);
? ? ? ? if (pkt == NULL) {
? ? ? ? ? ? printk (KERN_NOTICE "Ran out of memory allocating packet pool\n");
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? pkt->dev = dev;
? ? ? ? pkt->next = priv->ppool;
? ? ? ? priv->ppool = pkt;
? ? }
}
void snull_teardown_pool(struct net_device *dev)
{
? ? struct snull_priv *priv = netdev_priv(dev);
? ? struct snull_packet *pkt;
? ??
? ? while ((pkt = priv->ppool)) {
? ? ? ? priv->ppool = pkt->next;
? ? ? ? kfree (pkt);
? ? ? ? /* FIXME - in-flight packets ? */
? ? }
}? ?
/*
?* Buffer/pool management.
?*/
struct snull_packet *snull_get_tx_buffer(struct net_device *dev)
{
? ? struct snull_priv *priv = netdev_priv(dev);
? ? unsigned long flags;
? ? struct snull_packet *pkt;
? ??
? ? spin_lock_irqsave(&priv->lock, flags);
? ? pkt = priv->ppool;
? ? priv->ppool = pkt->next;
? ? if (priv->ppool == NULL) {
? ? ? ? printk (KERN_INFO "Pool empty\n");
? ? ? ? netif_stop_queue(dev);
? ? }
? ? spin_unlock_irqrestore(&priv->lock, flags);
? ? return pkt;
}
void snull_release_buffer(struct snull_packet *pkt)
{
? ? unsigned long flags;
? ? struct snull_priv *priv = netdev_priv(pkt->dev);
? ??
? ? spin_lock_irqsave(&priv->lock, flags);
? ? pkt->next = priv->ppool;
? ? priv->ppool = pkt;
? ? spin_unlock_irqrestore(&priv->lock, flags);
? ? if (netif_queue_stopped(pkt->dev) && pkt->next == NULL)
? ? ? ? netif_wake_queue(pkt->dev);
printk("snull_release_buffer\n");
}
void snull_enqueue_buf(struct net_device *dev, struct snull_packet *pkt)
{
? ? unsigned long flags;
? ? struct snull_priv *priv = netdev_priv(dev);
spin_lock_irqsave(&priv->lock, flags);
? ? pkt->next = priv->rx_queue;? /* FIXME - misorders packets */
? ? priv->rx_queue = pkt;
? ? spin_unlock_irqrestore(&priv->lock, flags);
}
struct snull_packet *snull_dequeue_buf(struct net_device *dev)
{
? ? struct snull_priv *priv = netdev_priv(dev);
? ? struct snull_packet *pkt;
? ? unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
? ? pkt = priv->rx_queue;
? ? if (pkt != NULL)
? ? ? ? priv->rx_queue = pkt->next;
? ? spin_unlock_irqrestore(&priv->lock, flags);
? ? return pkt;
}
/*
?* Enable and disable receive interrupts.
?*/
static void snull_rx_ints(struct net_device *dev, int enable)
{
? ? struct snull_priv *priv = netdev_priv(dev);
? ? priv->rx_int_enabled = enable;
}
/*
?* Open and close
?*/
int snull_open(struct net_device *dev)
{
? ? /* request_region(), request_irq(), ....? (like fops->open) */
/*?
? ? * Assign the hardware address of the board: use "\0SNULx", where
? ? * x is 0 or 1. The first byte is '\0' to avoid being a multicast
? ? * address (the first byte of multicast addrs is odd).
? ? */
? ? /* [cgw]: 分配一個(gè)假的硬件地址,真正的網(wǎng)卡的時(shí)候,這個(gè)地址是從網(wǎng)卡讀出來(lái)的 */
? ? memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN);
? ? /* [cgw]: 因?yàn)樽?cè)了兩個(gè)虛擬網(wǎng)卡,第二個(gè)虛擬網(wǎng)卡的地址跟第一個(gè)的地址必須不一樣
? ? * 即這兩個(gè)網(wǎng)卡地址分別為\0SNUL0和\0SNUL1
? ? */
? ? if (dev == snull_devs[1])
? ? ? ? dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */
? ? /* [cgw]: 啟動(dòng)發(fā)送隊(duì)列 */
? ? netif_start_queue(dev);
printk("snull_open\n");
? ??
? ? return 0;
}
int snull_release(struct net_device *dev)
{
? ? /* release ports, irq and such -- like fops->close */
netif_stop_queue(dev); /* can't transmit any more */
? ??
? ? printk("snull_release\n");
? ??
? ? return 0;
}
/*
?* Configuration changes (passed on by ifconfig)
?*/
int snull_config(struct net_device *dev, struct ifmap *map)
{
? ? if (dev->flags & IFF_UP) /* can't act on a running interface */
? ? ? ? return -EBUSY;
/* Don't allow changing the I/O address */
? ? if (map->base_addr != dev->base_addr) {
? ? ? ? printk(KERN_WARNING "snull: Can't change I/O address\n");
? ? ? ? return -EOPNOTSUPP;
? ? }
/* Allow changing the IRQ */
? ? if (map->irq != dev->irq) {
? ? ? ? dev->irq = map->irq;
? ? ? ? ? ? /* request_irq() is delayed to open-time */
? ? }
printk("snull_config\n");
/* ignore other fields */
? ? return 0;
}
/*
?* Receive a packet: retrieve, encapsulate and pass over to upper levels
?*/
void snull_rx(struct net_device *dev, struct snull_packet *pkt)
{
? ? struct sk_buff *skb;
? ? struct snull_priv *priv = netdev_priv(dev);
/*
? ? * The packet has been retrieved from the transmission
? ? * medium. Build an skb around it, so upper layers can handle it
? ? */
? ? /* [cgw]: 為接收包分配一個(gè)skb */
? ? skb = dev_alloc_skb(pkt->datalen + 2);
? ? if (!skb) {
? ? ? ? if (printk_ratelimit())
? ? ? ? ? ? printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n");
? ? ? ? priv->stats.rx_dropped++;
? ? ? ? goto out;
? ? }
? ? /* [cgw]: 16字節(jié)對(duì)齊,即IP首部前是網(wǎng)卡硬件地址首部,其占14字節(jié),需要為其增加2
? ? * 個(gè)字節(jié)?
? ? */
? ? skb_reserve(skb, 2); /* align IP on 16B boundary */
? ? /* [cgw]: 開(kāi)辟一個(gè)數(shù)據(jù)緩沖區(qū)用于存放接收數(shù)據(jù) */
? ? memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
/* Write metadata, and then pass to the receive level */
? ? skb->dev = dev;
? ? if (skb->dev == snull_devs[0]) {
? ? ? ? printk("skb->dev is snull_devs[0]\n");
? ? } else {
? ? ? ? printk("skb->dev is snull_devs[1]\n");
? ? }
? ? /* [cgw]: 確定包的協(xié)議ID */
? ? skb->protocol = eth_type_trans(skb, dev);
printk("skb->protocol = %d\n", skb->protocol);
? ??
? ? skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
? ? /* [cgw]: 統(tǒng)計(jì)接收包數(shù)和字節(jié)數(shù) */
? ? priv->stats.rx_packets++;
? ? priv->stats.rx_bytes += pkt->datalen;
? ? /* [cgw]: 上報(bào)應(yīng)用層 */
? ? netif_rx(skb);
printk("snull_rx\n");
? ??
? out:
? ? return;
}
? ?
/*
?* The poll implementation.
?*/
//static int snull_poll(struct napi_struct *napi, int budget)
static int snull_poll(struct net_device *dev, int *budget)
{
? ? //int npackets = 0;
? ? //struct sk_buff *skb;
? ? //struct snull_priv *priv = container_of(napi, struct snull_priv, napi);
? ? //struct net_device *dev = priv->dev;
? ? //struct snull_packet *pkt;
int npackets = 0, quota = min(dev->quota, *budget);
? ? struct sk_buff *skb;
? ? struct snull_priv *priv = netdev_priv(dev);
? ? struct snull_packet *pkt;
printk("snull_poll\n");
? ??
? ? //while (npackets < budget && priv->rx_queue) {
? ? while (npackets < quota && priv->rx_queue) {
? ? ? ? pkt = snull_dequeue_buf(dev);
? ? ? ? skb = dev_alloc_skb(pkt->datalen + 2);
? ? ? ? if (! skb) {
? ? ? ? ? ? if (printk_ratelimit())
? ? ? ? ? ? ? ? printk(KERN_NOTICE "snull: packet dropped\n");
? ? ? ? ? ? priv->stats.rx_dropped++;
? ? ? ? ? ? snull_release_buffer(pkt);
? ? ? ? ? ? continue;
? ? ? ? }
? ? ? ? skb_reserve(skb, 2); /* align IP on 16B boundary */??
? ? ? ? memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
? ? ? ? skb->dev = dev;
? ? ? ? skb->protocol = eth_type_trans(skb, dev);
? ? ? ? skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
? ? ? ? netif_receive_skb(skb);
? ? ? ??
? ? ? ? ? ? /* Maintain stats */
? ? ? ? npackets++;
? ? ? ? priv->stats.rx_packets++;
? ? ? ? priv->stats.rx_bytes += pkt->datalen;
? ? ? ? snull_release_buffer(pkt);
? ? }
? ? /* If we processed all packets, we're done; tell the kernel and reenable ints */
? ? *budget -= npackets;
? ? dev->quota -= npackets;
? ? if (! priv->rx_queue) {
? ? ? ? //napi_complete(napi);
? ? ? ? netif_rx_complete(dev);
? ? ? ? snull_rx_ints(dev, 1);
? ? ? ? return 0;
? ? }
? ? /* We couldn't process everything. */
? ? //return npackets;
? ? return 1;
}? ? ? ??
? ? ? ??
/*
?* The typical interrupt entry point
?*/
static void snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
? ? int statusword;
? ? struct snull_priv *priv;
? ? struct snull_packet *pkt = NULL;
? ? /*
? ? * As usual, check the "device" pointer to be sure it is
? ? * really interrupting.
? ? * Then assign "struct device *dev"
? ? */
? ? struct net_device *dev = (struct net_device *)dev_id;
? ? /* ... and check with hw if it's really ours */
/* paranoid */
? ? if (!dev)
? ? ? ? return;
/* Lock the device */
? ? priv = netdev_priv(dev);
? ? spin_lock(&priv->lock);
/* [cgw]: 判斷產(chǎn)生的是什么類(lèi)型的中斷,接收還是中斷 */
? ? /* retrieve statusword: real netdevices use I/O instructions */
? ? statusword = priv->status;
? ??
? ? printk("priv->status = %d\n", priv->status);
? ??
? ? priv->status = 0;
? ? /* [cgw]: 接收完成中斷 */
? ? if (statusword & SNULL_RX_INTR) {
? ? ? ? /* send it to snull_rx for handling */
? ? ? ? pkt = priv->rx_queue;
? ? ? ? if (pkt) {
? ? ? ? ? ? priv->rx_queue = pkt->next;
? ? ? ? ? ? /* [cgw]: 網(wǎng)卡接收到數(shù)據(jù),上報(bào)給應(yīng)用層 */
? ? ? ? ? ? snull_rx(dev, pkt);
? ? ? ? }
? ? }
? ? /* [cgw]: 發(fā)送完成中斷 */
? ? if (statusword & SNULL_TX_INTR) {
? ? ? ? /* [cgw]: 統(tǒng)計(jì)已發(fā)送的包數(shù)和總字節(jié)數(shù),并釋放這個(gè)包的內(nèi)存 */
? ? ? ? /* a transmission is over: free the skb */
? ? ? ? priv->stats.tx_packets++;
? ? ? ? priv->stats.tx_bytes += priv->tx_packetlen;
? ? ? ? dev_kfree_skb(priv->skb);
? ? }
/* Unlock the device and we are done */
? ? spin_unlock(&priv->lock);
? ? if (pkt) snull_release_buffer(pkt); /* Do this outside the lock! */
printk("snull_regular_interrupt\n");
return;
}
/*
?* A NAPI interrupt handler.
?*/
static void snull_napi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
? ? int statusword;
? ? struct snull_priv *priv;
/*
? ? * As usual, check the "device" pointer for shared handlers.
? ? * Then assign "struct device *dev"
? ? */
? ? struct net_device *dev = (struct net_device *)dev_id;
? ? /* ... and check with hw if it's really ours */
printk("snull_napi_interrupt\n");
/* paranoid */
? ? if (!dev)
? ? ? ? return;
/* Lock the device */
? ? priv = netdev_priv(dev);
? ? spin_lock(&priv->lock);
/* retrieve statusword: real netdevices use I/O instructions */
? ? statusword = priv->status;
? ? priv->status = 0;
? ? if (statusword & SNULL_RX_INTR) {
? ? ? ? snull_rx_ints(dev, 0);? /* Disable further interrupts */
? ? ? ? //napi_schedule(&priv->napi);
? ? ? ? netif_rx_schedule(dev);
? ? }
? ? if (statusword & SNULL_TX_INTR) {
? ? ? ? ? ? /* a transmission is over: free the skb */
? ? ? ? priv->stats.tx_packets++;
? ? ? ? priv->stats.tx_bytes += priv->tx_packetlen;
? ? ? ? dev_kfree_skb(priv->skb);
? ? }
/* Unlock the device and we are done */
? ? spin_unlock(&priv->lock);
? ? return;
}
/*
?* Transmit a packet (low level interface)
?*/
static void snull_hw_tx(char *buf, int len, struct net_device *dev)
{
? ? /*
? ? * This function deals with hw details. This interface loops
? ? * back the packet to the other snull interface (if any).
? ? * In other words, this function implements the snull behaviour,
? ? * while all other procedures are rather device-independent
? ? */
? ? struct iphdr *ih;
? ? struct net_device *dest;
? ? struct snull_priv *priv;
? ? u32 *saddr, *daddr;
? ? struct snull_packet *tx_buffer;
? ??
? ? /* I am paranoid. Ain't I? */
? ? if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
? ? ? ? printk("snull: Hmm... packet too short (%i octets)\n",
? ? ? ? ? ? ? ? len);
? ? ? ? return;
? ? }
/* [cgw]: 打印上層應(yīng)用(即ping)要發(fā)的這個(gè)包的內(nèi)容
? ? * 這個(gè)包的格式為:
? ? * 14字節(jié)以太網(wǎng)首部+20字節(jié)IP地址首部+20字節(jié)TCP地址首部+n字節(jié)數(shù)據(jù)
? ? */
? ??
? ? if (1) { /* enable this conditional to look at the data */
? ? ? ? int i;
? ? ? ? PDEBUG("len is %i\n" KERN_DEBUG "data:",len);
? ? ? ? /* [cgw]: 14字節(jié)以太網(wǎng)首部 */
? ? ? ? for (i=0 ; i<14; i++)
? ? ? ? ? ? printk(" %02x",buf[i]&0xff);
? ? ? ? printk("\n");
/* [cgw]: 20字節(jié)IP地址首部 */
? ? ? ? for (i=14 ; i<34; i++)
? ? ? ? ? ? printk(" %02x",buf[i]&0xff);
? ? ? ? printk("\n");
/* [cgw]: 20字節(jié)TCP地址首部 */
? ? ? ? for (i=34 ; i<54; i++)
? ? ? ? ? ? printk(" %02x",buf[i]&0xff);
? ? ? ? printk("\n");
/* [cgw]: n字節(jié)數(shù)據(jù) */
? ? ? ? for (i=54 ; i? ? ? ? ? ? printk(" %02x",buf[i]&0xff);
? ? ? ? printk("\n");
? ? }
? ? /*
? ? * Ethhdr is 14 bytes, but the kernel arranges for iphdr
? ? * to be aligned (i.e., ethhdr is unaligned)
? ? */
? ? /* [cgw]: 提取本地和目標(biāo)IP地址 */
? ? ih = (struct iphdr *)(buf+sizeof(struct ethhdr));
? ? saddr = &ih->saddr;
? ? daddr = &ih->daddr;
? ??
? ? printk("ih->protocol = %d is buf[23]\n", ih->protocol);
? ? printk("saddr = %d.%d.%d.%d\n", *((u8 *)saddr + 0), *((u8 *)saddr + 1), *((u8 *)saddr + 2), *((u8 *)saddr + 3));
? ? printk("daddr = %d.%d.%d.%d\n", *((u8 *)daddr + 0), *((u8 *)daddr + 1), *((u8 *)daddr + 2), *((u8 *)daddr + 3));
/* [cgw]: 改變本地和目標(biāo)IP地址的第三個(gè)字節(jié)的最低位,即原來(lái)是0,則改為1,原來(lái)是1,則改為0
? ? */
? ? ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
? ? ((u8 *)daddr)[2] ^= 1;
/* [cgw]: 從新計(jì)算校驗(yàn),因?yàn)镮P已改變 */
? ? ih->check = 0;? ? ? ? /* and rebuild the checksum (ip needs it) */
? ? ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
/* [cgw]: 打印更改后的IP地址,和TCP地址,
? ? */
? ? if (dev == snull_devs[0])
? ? ? ? //PDEBUGG("%08x:%05i --> %08x:%05i\n",
? ? ? ? printk("%08x:%05i --> %08x:%05i\n",
? ? ? ? ? ? ? ? ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source),
? ? ? ? ? ? ? ? ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest));
? ? else
? ? ? ? //PDEBUGG("%08x:%05i <-- %08x:%05i\n",
? ? ? ? printk("%08x:%05i <-- %08x:%05i\n",
? ? ? ? ? ? ? ? ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),
? ? ? ? ? ? ? ? ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source));
/*
? ? * Ok, now the packet is ready for transmission: first simulate a
? ? * receive interrupt on the twin device, then? a
? ? * transmission-done on the transmitting device
? ? */
? ? /* [cgw]: 獲得目的網(wǎng)卡設(shè)備 */
? ? dest = snull_devs[dev == snull_devs[0] ? 1 : 0];
? ??
? ? if (dev == snull_devs[0]) {
? ? ? ? printk("snull_devs[0]\n");
? ? } else {
? ? ? ? printk("snull_devs[1]\n");
? ? }
? ??
? ? priv = netdev_priv(dest);
? ? /* [cgw]: 取出一塊內(nèi)存分配給本地網(wǎng)卡 */
? ? tx_buffer = snull_get_tx_buffer(dev);
? ? /* [cgw]: 設(shè)置數(shù)據(jù)包大小 */
? ? tx_buffer->datalen = len;
? ??
? ? printk("tx_buffer->datalen = %d\n", tx_buffer->datalen);
/* [cgw]: 填充發(fā)送網(wǎng)卡的數(shù)據(jù) */
? ? memcpy(tx_buffer->data, buf, len);
? ? /* [cgw]: 把發(fā)送的數(shù)據(jù)直接加入到接收隊(duì)列,這里相當(dāng)于本地網(wǎng)卡要發(fā)送的數(shù)據(jù)
? ? * 已經(jīng)給目標(biāo)網(wǎng)卡直接接收到了
? ? */
? ? snull_enqueue_buf(dest, tx_buffer);
? ? /* [cgw]: 如果接收中斷使能,這個(gè)也是模擬的接收中斷,因?yàn)樯厦嬉呀?jīng)模擬接收
? ? * 到數(shù)據(jù),所以立刻產(chǎn)生一個(gè)中斷
? ? */
? ? if (priv->rx_int_enabled) {
? ? ? ? priv->status |= SNULL_RX_INTR;
? ? ? ? printk("priv->status = %d\n", priv->status);
? ? ? ? /* [cgw]: 執(zhí)行接收中斷 */
? ? ? ? snull_interrupt(0, dest, NULL);
? ? ? ? printk("snull_interrupt(0, dest, NULL);\n");
? ? }
/* [cgw]: 獲得本地網(wǎng)卡的私有數(shù)據(jù)指針 */
? ? priv = netdev_priv(dev);
? ? /* [cgw]: 把本地網(wǎng)卡要發(fā)送的數(shù)據(jù)存到私有數(shù)據(jù)緩沖區(qū),接著產(chǎn)生一個(gè)發(fā)送中斷
? ? */
? ? priv->tx_packetlen = len;
? ? priv->tx_packetdata = buf;
? ? priv->status |= SNULL_TX_INTR;
? ? if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) {
? ? ? ? ? ? /* Simulate a dropped transmit interrupt */
? ? ? ? netif_stop_queue(dev);
? ? ? ? PDEBUG("Simulate lockup at %ld, txp %ld\n", jiffies,
? ? ? ? ? ? ? ? (unsigned long) priv->stats.tx_packets);
? ? }
? ? else {
? ? ? ? /* [cgw]: 產(chǎn)生一個(gè)發(fā)送中斷 */
? ? ? ? snull_interrupt(0, dev, NULL);
? ? ? ? printk("snull_interrupt(0, dev, NULL);\n");
? ? }
}
/*
?* Transmit a packet (called by the kernel)
?*/
int snull_tx(struct sk_buff *skb, struct net_device *dev)
{
? ? int len;
? ? char *data, shortpkt[ETH_ZLEN];
? ? struct snull_priv *priv = netdev_priv(dev);
/* [cgw]: 獲取上層需要發(fā)送的數(shù)據(jù)和長(zhǎng)度 */
? ? data = skb->data;
? ? len = skb->len;
printk("skb->len = %d\n", skb->len);
? ??
? ? if (len < ETH_ZLEN) {
? ? ? ? memset(shortpkt, 0, ETH_ZLEN);
? ? ? ? memcpy(shortpkt, skb->data, skb->len);
? ? ? ? len = ETH_ZLEN;
? ? ? ? data = shortpkt;
? ? }
? ? /* [cgw]: 開(kāi)始計(jì)算時(shí)間截,用于處理發(fā)送超時(shí) */
? ? dev->trans_start = jiffies; /* save the timestamp */
/* Remember the skb, so we can free it at interrupt time */
? ? priv->skb = skb;
? ??
? ? printk("snull_tx\n");
/* actual deliver of data is device-specific, and not shown here */
? ? /* [cgw]: 模擬把數(shù)據(jù)包寫(xiě)入硬件,通過(guò)硬件發(fā)送出去,但實(shí)際上不是 */
? ? snull_hw_tx(data, len, dev);
//printk("snull_tx\n");
return 0; /* Our simple device can not fail */
}
/*
?* Deal with a transmit timeout.
?*/
void snull_tx_timeout (struct net_device *dev)
{
? ? struct snull_priv *priv = netdev_priv(dev);
PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies,
? ? ? ? ? ? jiffies - dev->trans_start);
? ? ? ? /* Simulate a transmission interrupt to get things moving */
? ? priv->status = SNULL_TX_INTR;
? ? snull_interrupt(0, dev, NULL);
? ? priv->stats.tx_errors++;
? ? netif_wake_queue(dev);
printk("snull_tx_timeout\n");
? ??
? ? return;
}
/*
?* Ioctl commands?
?*/
int snull_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
? ? PDEBUG("ioctl\n");
? ? printk("ioctl\n");
? ? return 0;
}
/*
?* Return statistics to the caller
?*/
struct net_device_stats *snull_stats(struct net_device *dev)
{
? ? struct snull_priv *priv = netdev_priv(dev);
printk("snull_stats\n");
? ??
? ? return &priv->stats;
}
/*
?* This function is called to fill up an eth header, since arp is not
?* available on the interface
?*/
int snull_rebuild_header(struct sk_buff *skb)
{
? ? struct ethhdr *eth = (struct ethhdr *) skb->data;
? ? struct net_device *dev = skb->dev;
? ??
? ? memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
? ? memcpy(eth->h_dest, dev->dev_addr, dev->addr_len);
? ? eth->h_dest[ETH_ALEN-1]? ^= 0x01;? /* dest is us xor 1 */
printk("snull_rebuild_header\n");
? ??
? ? return 0;
}
//int snull_header(struct sk_buff *skb, struct net_device *dev,
//? ? ? ? ? ? ? ? unsigned short type, const void *daddr, const void *saddr,
//? ? ? ? ? ? ? ? unsigned len)
int snull_header(struct sk_buff *skb, struct net_device *dev,
? ? ? ? ? ? ? ? unsigned short type, void *daddr, void *saddr,
? ? ? ? ? ? ? ? unsigned len)? ? ? ? ? ? ??
{
? ? struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
printk("len = %d\n", len);
printk("type = %02x\n", type); //ETH_P_IP? ? 0x0800? ? ? ? /* Internet Protocol packet? ? */
/* htons是將整型變量從主機(jī)字節(jié)順序轉(zhuǎn)變成網(wǎng)絡(luò)字節(jié)順序,?
? ? * 就是整數(shù)在地址空間存儲(chǔ)方式變?yōu)椋焊呶蛔止?jié)存放在內(nèi)存的低地址處
? ? */
? ? eth->h_proto = htons(type);
? ? printk("h_proto = %d\n", eth->h_proto);
? ??
? ? printk("addr_len = %d\n", dev->addr_len);
? ? printk("dev_addr = %02x.%02x.%02x.%02x.%02x.%02x\n", dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
if (saddr) {
? ? ? ? printk("saddr = %02x.%02x.%02x.%02x.%02x.%02x\n", *((unsigned char *)saddr + 0), *((unsigned char *)saddr + 1), *((unsigned char *)saddr + 2), *((unsigned char *)saddr + 3), *((unsigned char *)saddr + 4), *((unsigned char *)saddr + 5));
? ? }
if (daddr) {
? ? ? ? printk("daddr = %02x.%02x.%02x.%02x.%02x.%02x\n", *((unsigned char *)daddr + 0), *((unsigned char *)daddr + 1), *((unsigned char *)daddr + 2), *((unsigned char *)daddr + 3), *((unsigned char *)daddr + 4), *((unsigned char *)daddr + 5));
? ? }
/* [cgw]: 上層應(yīng)用要發(fā)送數(shù)據(jù)時(shí),通過(guò)下層添加硬件地址,才能決定發(fā)送到那個(gè)目標(biāo)網(wǎng)卡
? ? */
? ? memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
? ? memcpy(eth->h_dest,? daddr ? daddr : dev->dev_addr, dev->addr_len);
? ? printk("h_source = %02x.%02x.%02x.%02x.%02x.%02x\n", eth->h_source[0], eth->h_source[1], eth->h_source[2],eth->h_source[3], eth->h_source[4], eth->h_source[5]);
? ? printk("h_dest = %02x.%02x.%02x.%02x.%02x.%02x\n", eth->h_dest[0], eth->h_dest[1], eth->h_dest[2], eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);
/* [cgw]: 設(shè)置目標(biāo)網(wǎng)卡硬件地址,即本地網(wǎng)卡和目標(biāo)網(wǎng)卡硬件地址的最后一個(gè)字節(jié)的最低有效位
? ? * 是相反關(guān)系,即本地是\0SNUL0的話(huà),目標(biāo)就是\0SNUL1,或者本地是\0SNUL1,目標(biāo)就是\0SNUL0
? ? */
? ? eth->h_dest[ETH_ALEN-1]? ^= 0x01;? /* dest is us xor 1 */
? ? printk("h_dest[ETH_ALEN-1] ^ 0x01 = %02x\n", eth->h_dest[ETH_ALEN-1]);
? ??
? ? printk("hard_header_len = %d\n", dev->hard_header_len);
? ??
? ? return (dev->hard_header_len);
}
/*
?* The "change_mtu" method is usually not needed.
?* If you need it, it must be like this.
?*/
int snull_change_mtu(struct net_device *dev, int new_mtu)
{
? ? unsigned long flags;
? ? struct snull_priv *priv = netdev_priv(dev);
? ? spinlock_t *lock = &priv->lock;
? ??
? ? /* check ranges */
? ? if ((new_mtu < 68) || (new_mtu > 1500))
? ? ? ? return -EINVAL;
? ? /*
? ? * Do anything you need, and the accept the value
? ? */
? ? spin_lock_irqsave(lock, flags);
? ? dev->mtu = new_mtu;
? ? spin_unlock_irqrestore(lock, flags);
? ? return 0; /* success */
}
#if 0
static const struct header_ops snull_header_ops = {
? ? ? ? .create? = snull_header,
? ? .rebuild = snull_rebuild_header
};
static const struct net_device_ops snull_netdev_ops = {
? ? .ndo_open? ? ? ? ? ? = snull_open,
? ? .ndo_stop? ? ? ? ? ? = snull_release,
? ? .ndo_start_xmit? ? ? = snull_tx,
? ? .ndo_do_ioctl? ? ? ? = snull_ioctl,
? ? .ndo_set_config? ? ? = snull_config,
? ? .ndo_get_stats? ? ? = snull_stats,
? ? .ndo_change_mtu? ? ? = snull_change_mtu,
? ? .ndo_tx_timeout? ? ? = snull_tx_timeout
};
#endif
/*
?* The init function (sometimes called probe).
?* It is invoked by register_netdev()
?*/
void snull_init(struct net_device *dev)
{
? ? struct snull_priv *priv;
#if 0
? ? ? ? /*
? ? * Make the usual checks: check_region(), probe irq, ...? -ENODEV
? ? * should be returned if no device found.? No resource should be
? ? * grabbed: this is done on open().?
? ? */
#endif
/*?
? ? * Then, assign other fields in dev, using ether_setup() and some
? ? * hand assignments
? ? */
? ? ether_setup(dev); /* assign some of the fields */
? ? dev->watchdog_timeo = timeout;
? ??
? ? //dev->netdev_ops = &snull_netdev_ops;
? ? //dev->header_ops = &snull_header_ops;
? ??
? ? dev->hard_header = snull_header;
? ? dev->rebuild_header = snull_rebuild_header;
? ??
? ? dev->open = snull_open;
? ? dev->stop = snull_release;
? ? dev->hard_start_xmit = snull_tx;
? ? dev->do_ioctl = snull_ioctl;
? ? dev->set_config = snull_config;
? ? dev->get_stats = snull_stats;
? ? dev->change_mtu = snull_change_mtu;
? ? dev->tx_timeout = snull_tx_timeout;
? ??
? ? /* keep the default flags, just add NOARP */
? ? dev->flags? ? ? ? ? |= IFF_NOARP;
? ? dev->features? ? ? ? |= NETIF_F_HW_CSUM;
dev->hard_header_cache = NULL;
/*
? ? * Then, initialize the priv field. This encloses the statistics
? ? * and a few private fields.
? ? */
? ? priv = netdev_priv(dev);
? ? #if 0
? ? if (use_napi) {
? ? ? ? netif_napi_add(dev, &priv->napi, snull_poll,2);
? ? }?
? ? #else
? ? if (use_napi) {
? ? ? ? dev->poll = snull_poll;
? ? ? ? dev->weight = 2;
? ? }
? ? #endif
? ? memset(priv, 0, sizeof(struct snull_priv));
? ? spin_lock_init(&priv->lock);
? ? snull_rx_ints(dev, 1);? ? ? ? /* enable receive interrupts */
? ? snull_setup_pool(dev);
printk("snull_init\n");
}
/*
?* The devices
?*/
struct net_device *snull_devs[2];
/*
?* Finally, the module stuff
?*/
void snull_cleanup(void)
{
? ? int i;
? ??
? ? for (i = 0; i < 2;? i++) {
? ? ? ? if (snull_devs[i]) {
? ? ? ? ? ? unregister_netdev(snull_devs[i]);
? ? ? ? ? ? snull_teardown_pool(snull_devs[i]);
? ? ? ? ? ? free_netdev(snull_devs[i]);
? ? ? ? }
? ? }
? ? return;
}
int snull_init_module(void)
{
? ? int result, i, ret = -ENOMEM;
snull_interrupt = use_napi ? snull_napi_interrupt : snull_regular_interrupt;
? ??
? ? /* Allocate the devices */
? ? snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
? ? ? ? ? ? snull_init);
? ? snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
? ? ? ? ? ? snull_init);
? ? if (snull_devs[0] == NULL || snull_devs[1] == NULL)
? ? ? ? goto out;
ret = -ENODEV;
? ? for (i = 0; i < 2;? i++)
? ? ? ? if ((result = register_netdev(snull_devs[i])))
? ? ? ? ? ? printk("snull: error %i registering device "%s"\n",
? ? ? ? ? ? ? ? ? ? result, snull_devs[i]->name);
? ? ? ? else
? ? ? ? ? ? ret = 0;
printk("snull_init_module\n");
? ? ? ? ? ??
? out:
? ? if (ret)?
? ? ? ? snull_cleanup();
? ? return ret;
}
module_init(snull_init_module);
module_exit(snull_cleanup);
makefile:
# Comment/uncomment the following line to disable/enable debugging
#DEBUG = y
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
? DEBFLAGS = -O -g -DSBULL_DEBUG # "-O" is needed to expand inlines
else
? DEBFLAGS = -O2
endif
EXTRA_CFLAGS += $(DEBFLAGS)
EXTRA_CFLAGS += -I..
ifneq ($(KERNELRELEASE),)
# call from kernel build system
obj-m? ? := snull.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD? ? ? := $(shell pwd)
default:
? ? $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
運(yùn)行:
# insmod snull.ko
snull_init
snull_init
snull_stats
snull_stats
snull_init_module
# ifconfig sn0 local0
snull_open
snull_stats
# ifconfig sn1 local1
snull_open
snull_stats
# ping -c 1 remote0
PING remote0 (192.168.2.9): 56 data bytes
len = 84
type = 800
h_proto = 8
addr_len = 6
dev_addr = 00.53.4e.55.4c.30
daddr = 00.53.4e.55.4c.30
h_source = 00.53.4e.55.4c.30
h_dest = 00.53.4e.55.4c.30
h_dest[ETH_ALEN-1] ^ 0x01 = 31
hard_header_len = 14
skb->len = 98
snull_tx
?00 53 4e 55 4c 31 00 53 4e 55 4c 30 08 00
?45 00 00 54 00 00 40 00 40 01 b5 47 c0 a8 02 08 c0 a8 02 09
?08 00 d0 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00
?00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ih->protocol = 1 is buf[23]
saddr = 192.168.2.8
daddr = 192.168.2.9
c0a80308:02048 --> c0a80309:53262
snull_devs[0]
tx_buffer->datalen = 98
priv->status = 1
priv->status = 1
skb->dev is snull_devs[1]
skb->protocol = 8
snull_rx
snull_release_buffer
snull_regular_interrupt
snull_interrupt(0, dest, NULL);
priv->status = 2
snull_regular_interrupt
snull_interrupt(0, dev, NULL);
len = 84
type = 800
h_proto = 8
addr_len = 6
dev_addr = 00.53.4e.55.4c.31
daddr = 00.53.4e.55.4c.31
h_source = 00.53.4e.55.4c.31
h_dest = 00.53.4e.55.4c.31
h_dest[ETH_ALEN-1] ^ 0x01 = 30
hard_header_len = 14
skb->len = 98
snull_tx
?00 53 4e 55 4c 30 00 53 4e 55 4c 31 08 00
?45 00 00 54 a0 17 00 00 40 01 53 30 c0 a8 03 09 c0 a8 03 08
?00 00 d8 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00
?00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ih->protocol = 1 is buf[23]
saddr = 192.168.3.9
daddr = 192.168.3.8
c0a80208:55310 <-- c0a80209:00000
snull_devs[1]
tx_buffer->datalen = 98
priv->status = 1
priv->status = 1
skb->dev is snull_devs[0]
skb->protocol = 8
snull_rx
snull_release_buffer
snull_regular_interrupt
snull_interrupt(0, dest, NULL);
priv->status = 2
snull_regular_interrupt
snull_interrupt(0, dev, NULL);
64 bytes from 192.168.2.9: seq=0 ttl=64 time=159.673 ms
--- remote0 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 159.673/159.673/159.673 ms
分析現(xiàn)象:
1.當(dāng)執(zhí)行ping命令后,驅(qū)動(dòng)首先會(huì)調(diào)用snull_header
int snull_header(struct sk_buff *skb, struct net_device *dev,
? ? ? ? ? ? ? ? unsigned short type, void *daddr, void *saddr,
? ? ? ? ? ? ? ? unsigned len)? ? ? ? ? ? ??
{
? ? struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
printk("len = %d\n", len);
printk("type = %02x\n", type); //ETH_P_IP? ? 0x0800? ? ? ? /* Internet Protocol packet? ? */
/* htons是將整型變量從主機(jī)字節(jié)順序轉(zhuǎn)變成網(wǎng)絡(luò)字節(jié)順序,?
? ? * 就是整數(shù)在地址空間存儲(chǔ)方式變?yōu)椋焊呶蛔止?jié)存放在內(nèi)存的低地址處
? ? */
? ? eth->h_proto = htons(type);
? ? printk("h_proto = %d\n", eth->h_proto);
? ??
? ? printk("addr_len = %d\n", dev->addr_len);
? ? printk("dev_addr = %02x.%02x.%02x.%02x.%02x.%02x\n", dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
if (saddr) {
? ? ? ? printk("saddr = %02x.%02x.%02x.%02x.%02x.%02x\n", *((unsigned char *)saddr + 0), *((unsigned char *)saddr + 1), *((unsigned char *)saddr + 2), *((unsigned char *)saddr + 3), *((unsigned char *)saddr + 4), *((unsigned char *)saddr + 5));
? ? }
if (daddr) {
? ? ? ? printk("daddr = %02x.%02x.%02x.%02x.%02x.%02x\n", *((unsigned char *)daddr + 0), *((unsigned char *)daddr + 1), *((unsigned char *)daddr + 2), *((unsigned char *)daddr + 3), *((unsigned char *)daddr + 4), *((unsigned char *)daddr + 5));
? ? }
/* [cgw]: 上層應(yīng)用要發(fā)送數(shù)據(jù)時(shí),通過(guò)下層添加硬件地址,才能決定發(fā)送到那個(gè)目標(biāo)網(wǎng)卡
? ? */
? ? memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
? ? memcpy(eth->h_dest,? daddr ? daddr : dev->dev_addr, dev->addr_len);
? ? printk("h_source = %02x.%02x.%02x.%02x.%02x.%02x\n", eth->h_source[0], eth->h_source[1], eth->h_source[2],eth->h_source[3], eth->h_source[4], eth->h_source[5]);
? ? printk("h_dest = %02x.%02x.%02x.%02x.%02x.%02x\n", eth->h_dest[0], eth->h_dest[1], eth->h_dest[2], eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);
/* [cgw]: 設(shè)置目標(biāo)網(wǎng)卡硬件地址,即本地網(wǎng)卡和目標(biāo)網(wǎng)卡硬件地址的最后一個(gè)字節(jié)的最低有效位
? ? * 是相反關(guān)系,即本地是\0SNUL0的話(huà),目標(biāo)就是\0SNUL1,或者本地是\0SNUL1,目標(biāo)就是\0SNUL0
? ? */
? ? eth->h_dest[ETH_ALEN-1]? ^= 0x01;? /* dest is us xor 1 */
? ? printk("h_dest[ETH_ALEN-1] ^ 0x01 = %02x\n", eth->h_dest[ETH_ALEN-1]);
? ??
? ? printk("hard_header_len = %d\n", dev->hard_header_len);
? ??
? ? return (dev->hard_header_len);
}
因?yàn)閼?yīng)用層要發(fā)送數(shù)據(jù)包了,所以要為這個(gè)數(shù)據(jù)包添加硬件地址,即以太網(wǎng)地址首部,才能通過(guò)網(wǎng)卡發(fā)送出去。
2.? 然后內(nèi)核會(huì)通過(guò)調(diào)用snull_tx發(fā)送數(shù)據(jù)包,snull_tx調(diào)用了snull_hw_tx,在這里更改本地IP為目標(biāo)IP,并把本地要發(fā)的數(shù)據(jù)直接拷貝給目標(biāo)網(wǎng)卡,代表目標(biāo)網(wǎng)卡以接收到數(shù)據(jù),并觸發(fā)接收完成中斷,向應(yīng)用層上報(bào)數(shù)據(jù),接著觸發(fā)發(fā)送完成中斷,表示數(shù)據(jù)已經(jīng)發(fā)送到目標(biāo)網(wǎng)卡。
3. 數(shù)據(jù)包分析:
static void snull_hw_tx(char *buf, int len, struct net_device *dev)
這里的buf為應(yīng)用層要發(fā)送的數(shù)據(jù)包,數(shù)據(jù)包格式為:14字節(jié)以太網(wǎng)首部+20字節(jié)IP地址首部+20字節(jié)TCP地址首部+n字節(jié)數(shù)據(jù)
/* [cgw]: 14字節(jié)以太網(wǎng)首部 */
? ? ? ? for (i=0 ; i<14; i++)
? ? ? ? ? ? printk(" %02x",buf[i]&0xff);
? ? ? ? printk("\n");
/* [cgw]: 20字節(jié)IP地址首部 */
? ? ? ? for (i=14 ; i<34; i++)
? ? ? ? ? ? printk(" %02x",buf[i]&0xff);
? ? ? ? printk("\n");
/* [cgw]: 20字節(jié)TCP地址首部 */
? ? ? ? for (i=34 ; i<54; i++)
? ? ? ? ? ? printk(" %02x",buf[i]&0xff);
? ? ? ? printk("\n");
/* [cgw]: n字節(jié)數(shù)據(jù) */
? ? ? ? for (i=54 ; i? ? ? ? ? ? printk(" %02x",buf[i]&0xff);
? ? ? ? printk("\n");
打印結(jié)果:
00 53 4e 55 4c 30 00 53 4e 55 4c 31 08 00 //14字節(jié)以太網(wǎng)首部 45 00 00 54 a0 17 00 00 40 01 53 30 c0 a8 03 09 c0 a8 03 08 //20字節(jié)IP地址首部 00 00 d8 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00 //20字節(jié)TCP地址首部 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 //n字節(jié)數(shù)據(jù)
其中:00 53 4e 55 4c 30 就硬件地址\0SNUL0的ASCII碼,00 53 4e 55 4c 31 就硬件地址\0SNUL1的ASCII碼。
c0 a8 02 08表示本地IP地址local0:192.168.2.8, c0 a8 02 09表示本地IP地址remote0:192.168.2.9。
代表 00 53 4e 55 4c 31 00 53 4e 55 4c 30 08 00 的結(jié)構(gòu)體是:
1 struct ethhdr {2 unsigned char h_dest[ETH_ALEN]; /* destination eth addr */3 unsigned char h_source[ETH_ALEN]; /* source ether addr */4 __be16 h_proto; /* packet type ID field */5 } __attribute__((packed));
即h_ptoto = 0x08 (0x0800,經(jīng)過(guò)htons轉(zhuǎn)換為0x08)
代表45 00 00 54 00 00 40 00 40 01 b5 47 c0 a8 02 08 c0 a8 02 09的結(jié)構(gòu)體是:
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
? ? __u8? ? ihl:4,
? ? ? ? version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
? ? __u8? ? version:4,
? ? ? ? ? ihl:4;
#else
#error? ? "Please fix "
#endif
? ? __u8? ? tos;
? ? __be16? ? tot_len;
? ? __be16? ? id;
? ? __be16? ? frag_off;
? ? __u8? ? ttl;
? ? __u8? ? protocol;
? ? __sum16? ? check;
? ? __be32? ? saddr;
? ? __be32? ? daddr;
? ? /*The options start here. */
};
代表 08 00 d0 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00 的結(jié)構(gòu)體是:
struct tcphdr {
? ? __be16? ? source;
? ? __be16? ? dest;
? ? __be32? ? seq;
? ? __be32? ? ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
? ? __u16? ? res1:4,
? ? ? ? doff:4,
? ? ? ? fin:1,
? ? ? ? syn:1,
? ? ? ? rst:1,
? ? ? ? psh:1,
? ? ? ? ack:1,
? ? ? ? urg:1,
? ? ? ? ece:1,
? ? ? ? cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
? ? __u16? ? doff:4,
? ? ? ? res1:4,
? ? ? ? cwr:1,
? ? ? ? ece:1,
? ? ? ? urg:1,
? ? ? ? ack:1,
? ? ? ? psh:1,
? ? ? ? rst:1,
? ? ? ? syn:1,
? ? ? ? fin:1;
#else
#error? ? "Adjust your defines"
#endif? ??
? ? __be16? ? window;
? ? __sum16? ? check;
? ? __be16? ? urg_ptr;
};
NAPI
NAPI的全稱(chēng)是“NEW API”。
要使用NAPI功能,只要在加載snull.ko的添加一句use_napi=1就行了
如:#insmod snull.ko use_napi=1
NAPI有什么作用?
NAPI是一種使用輪詢(xún)(poll)的方式去接收數(shù)據(jù)。如當(dāng)系統(tǒng)需要接收一大坨數(shù)據(jù)時(shí),數(shù)據(jù)量比較大時(shí),這個(gè)時(shí)候數(shù)據(jù)的接收就不應(yīng)該在中斷中進(jìn)行。即產(chǎn)生接收完成中斷后,立即禁止中斷,通知內(nèi)核調(diào)用poll,輪詢(xún)接收數(shù)據(jù),接收完成后,再使能接收中斷。這樣大大提高系統(tǒng)的性能。
在驅(qū)動(dòng)初始化時(shí):分配好poll函數(shù)
1 if (use_napi) {2 dev->poll = snull_poll;3 dev->weight = 2;4 }
在接收中斷中
if (statusword & SNULL_RX_INTR) {
? ? ? ? /* send it to snull_rx for handling */
? ? ? ? pkt = priv->rx_queue;
? ? ? ? if (pkt) {
? ? ? ? ? ? priv->rx_queue = pkt->next;
? ? ? ? ? ? /* [cgw]: 網(wǎng)卡接收到數(shù)據(jù),上報(bào)給應(yīng)用層 */
? ? ? ? ? ? snull_rx(dev, pkt);
? ? ? ? }
? ? }
改為
1 if (statusword & SNULL_RX_INTR) {2 snull_rx_ints(dev, 0); /* Disable further interrupts */3 //napi_schedule(&priv->napi);4 netif_rx_schedule(dev);5 }
在中斷中,直接通知內(nèi)核調(diào)用snull_poll即可,snull_poll輪詢(xún)接收數(shù)據(jù),并上報(bào)給應(yīng)用層。
?
評(píng)論
查看更多