? ? ? snull是《Linux Device Drivers》中的一個網(wǎng)絡(luò)驅(qū)動的例子。這里引用這個例子學(xué)習(xí)Linux網(wǎng)絡(luò)驅(qū)動。
因為snull的源碼,網(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)核中。移植也相對簡單,這里也提供移植好的代碼。
估計不少網(wǎng)友看到《Linux Device Drivers》的網(wǎng)絡(luò)驅(qū)動部分,一臉懵逼,包括我自己,不理解作者設(shè)計這個例子的真正目的,盡管有配圖,仍然懵懂,甚至不知道為什么會用到6個IP地址。如圖:
其實作者的本意是想通過虛擬網(wǎng)卡來模擬實際的網(wǎng)卡和外部的網(wǎng)絡(luò)設(shè)備的通信來討論網(wǎng)絡(luò)驅(qū)動。通過其中任何一個網(wǎng)絡(luò)接口(sn0或sn1)發(fā)送數(shù)據(jù),都在另一個網(wǎng)絡(luò)接口(sn0或sn1)接收到。
因為sn0和sn1都不在同一個網(wǎng)段,所以sn0和sn1之間直接互ping是不行的,這中間必須必須做點轉(zhuǎn)換。
例子:
理論上local0和remote0只能互ping,因為他們都在同一個網(wǎng)段:192.168.0.0,但事實上,local0在發(fā)出數(shù)據(jù)之后,local0的第3個字節(jié)最低有效位改取反,就變成了remote1,remote1的數(shù)據(jù)才能到達local1,因為他們在同一段IP。相反,local1在發(fā)出數(shù)據(jù)之后,local1的第3個字節(jié)最低有效位改取反,就變成了remote0,remote0的數(shù)據(jù)才能到達local0.
因此,在實驗之前,需要添加一些配置:
在/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地址的第三個字節(jié)的最低有效位是相反的
2. local0和remote1第四個字節(jié)必須一樣,remote0和local1第四個字節(jié)必須一樣
3. 如果開發(fā)板上的真正網(wǎng)卡用了的網(wǎng)段IP,就不能再用于本實驗。如:我的開發(fā)板的DM9000網(wǎng)卡使用網(wǎng)段是192.168.1.0, 因此本實驗不能再使用192.168.1.0作為網(wǎng)段,否則有沖突。
代碼: snull.c, 其中snull.h沒改動,因此不貼出來
/*
?* 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]: 分配一個假的硬件地址,真正的網(wǎng)卡的時候,這個地址是從網(wǎng)卡讀出來的 */
? ? memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN);
? ? /* [cgw]: 因為注冊了兩個虛擬網(wǎng)卡,第二個虛擬網(wǎng)卡的地址跟第一個的地址必須不一樣
? ? * 即這兩個網(wǎng)卡地址分別為\0SNUL0和\0SNUL1
? ? */
? ? if (dev == snull_devs[1])
? ? ? ? dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */
? ? /* [cgw]: 啟動發(fā)送隊列 */
? ? 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]: 為接收包分配一個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é)對齊,即IP首部前是網(wǎng)卡硬件地址首部,其占14字節(jié),需要為其增加2
? ? * 個字節(jié)?
? ? */
? ? skb_reserve(skb, 2); /* align IP on 16B boundary */
? ? /* [cgw]: 開辟一個數(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)計接收包數(shù)和字節(jié)數(shù) */
? ? priv->stats.rx_packets++;
? ? priv->stats.rx_bytes += pkt->datalen;
? ? /* [cgw]: 上報應(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)生的是什么類型的中斷,接收還是中斷 */
? ? /* 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ù),上報給應(yīng)用層 */
? ? ? ? ? ? snull_rx(dev, pkt);
? ? ? ? }
? ? }
? ? /* [cgw]: 發(fā)送完成中斷 */
? ? if (statusword & SNULL_TX_INTR) {
? ? ? ? /* [cgw]: 統(tǒng)計已發(fā)送的包數(shù)和總字節(jié)數(shù),并釋放這個包的內(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ā)的這個包的內(nèi)容
? ? * 這個包的格式為:
? ? * 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]: 提取本地和目標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]: 改變本地和目標IP地址的第三個字節(jié)的最低位,即原來是0,則改為1,原來是1,則改為0
? ? */
? ? ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
? ? ((u8 *)daddr)[2] ^= 1;
/* [cgw]: 從新計算校驗,因為IP已改變 */
? ? 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ù)直接加入到接收隊列,這里相當于本地網(wǎng)卡要發(fā)送的數(shù)據(jù)
? ? * 已經(jīng)給目標網(wǎng)卡直接接收到了
? ? */
? ? snull_enqueue_buf(dest, tx_buffer);
? ? /* [cgw]: 如果接收中斷使能,這個也是模擬的接收中斷,因為上面已經(jīng)模擬接收
? ? * 到數(shù)據(jù),所以立刻產(chǎn)生一個中斷
? ? */
? ? 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)生一個發(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)生一個發(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ù)和長度 */
? ? 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]: 開始計算時間截,用于處理發(fā)送超時 */
? ? 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ù)包寫入硬件,通過硬件發(fā)送出去,但實際上不是 */
? ? 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是將整型變量從主機字節(jié)順序轉(zhuǎn)變成網(wǎng)絡(luò)字節(jié)順序,?
? ? * 就是整數(shù)在地址空間存儲方式變?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ù)時,通過下層添加硬件地址,才能決定發(fā)送到那個目標網(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è)置目標網(wǎng)卡硬件地址,即本地網(wǎng)卡和目標網(wǎng)卡硬件地址的最后一個字節(jié)的最低有效位
? ? * 是相反關(guān)系,即本地是\0SNUL0的話,目標就是\0SNUL1,或者本地是\0SNUL1,目標就是\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
運行:
# 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.當執(zhí)行ping命令后,驅(qū)動首先會調(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是將整型變量從主機字節(jié)順序轉(zhuǎn)變成網(wǎng)絡(luò)字節(jié)順序,?
? ? * 就是整數(shù)在地址空間存儲方式變?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ù)時,通過下層添加硬件地址,才能決定發(fā)送到那個目標網(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è)置目標網(wǎng)卡硬件地址,即本地網(wǎng)卡和目標網(wǎng)卡硬件地址的最后一個字節(jié)的最低有效位
? ? * 是相反關(guān)系,即本地是\0SNUL0的話,目標就是\0SNUL1,或者本地是\0SNUL1,目標就是\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īng)用層要發(fā)送數(shù)據(jù)包了,所以要為這個數(shù)據(jù)包添加硬件地址,即以太網(wǎng)地址首部,才能通過網(wǎng)卡發(fā)送出去。
2.? 然后內(nèi)核會通過調(diào)用snull_tx發(fā)送數(shù)據(jù)包,snull_tx調(diào)用了snull_hw_tx,在這里更改本地IP為目標IP,并把本地要發(fā)的數(shù)據(jù)直接拷貝給目標網(wǎng)卡,代表目標網(wǎng)卡以接收到數(shù)據(jù),并觸發(fā)接收完成中斷,向應(yīng)用層上報數(shù)據(jù),接著觸發(fā)發(fā)送完成中斷,表示數(shù)據(jù)已經(jīng)發(fā)送到目標網(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)過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的全稱是“NEW API”。
要使用NAPI功能,只要在加載snull.ko的添加一句use_napi=1就行了
如:#insmod snull.ko use_napi=1
NAPI有什么作用?
NAPI是一種使用輪詢(poll)的方式去接收數(shù)據(jù)。如當系統(tǒng)需要接收一大坨數(shù)據(jù)時,數(shù)據(jù)量比較大時,這個時候數(shù)據(jù)的接收就不應(yīng)該在中斷中進行。即產(chǎn)生接收完成中斷后,立即禁止中斷,通知內(nèi)核調(diào)用poll,輪詢接收數(shù)據(jù),接收完成后,再使能接收中斷。這樣大大提高系統(tǒng)的性能。
在驅(qū)動初始化時:分配好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ù),上報給應(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輪詢接收數(shù)據(jù),并上報給應(yīng)用層。
?
評論
查看更多