• Linux网络驱动--snull


    snull是《Linux Device Drivers》中的一个网络驱动的例子。这里引用这个例子学习Linux网络驱动。

    因为snull的源码,网上已经更新到适合最新内核,而我自己用的还是2.6.22.6比较旧的内核。而网上好像找不到旧版的snull。因此结合《Linux Device Drivers》把最新的snull例子移植到2.6.22.6内核中。移植也相对简单,这里也提供移植好的代码。

    估计不少网友看到《Linux Device Drivers》的网络驱动部分,一脸懵逼,包括我自己,不理解作者设计这个例子的真正目的,尽管有配图,仍然懵懂,甚至不知道为什么会用到6个IP地址。如图:

    其实作者的本意是想通过虚拟网卡来模拟实际的网卡和外部的网络设备的通信来讨论网络驱动。通过其中任何一个网络接口(sn0或sn1)发送数据,都在另一个网络接口(sn0或sn1)接收到。

    因为sn0和sn1都不在同一个网段,所以sn0和sn1之间直接互ping是不行的,这中间必须必须做点转换。

    例子:

    理论上local0和remote0只能互ping,因为他们都在同一个网段:192.168.0.0,但事实上,local0在发出数据之后,local0的第3个字节最低有效位改取反,就变成了remote1,remote1的数据才能到达local1,因为他们在同一段IP。相反,local1在发出数据之后,local1的第3个字节最低有效位改取反,就变成了remote0,remote0的数据才能到达local0.

    因此,在实验之前,需要添加一些配置:

    在/etc/networks文件中添加如下网段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. 网段IP和IP地址的第三个字节的最低有效位是相反的

             2. local0和remote1第四个字节必须一样,remote0和local1第四个字节必须一样

             3. 如果开发板上的真正网卡用了的网段IP,就不能再用于本实验。如:我的开发板的DM9000网卡使用网段是192.168.1.0, 因此本实验不能再使用192.168.1.0作为网段,否则有冲突。

    代码: snull.c, 其中snull.h没改动,因此不贴出来

      1 /*
      2  * snull.c --  the Simple Network Utility
      3  *
      4  * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
      5  * Copyright (C) 2001 O'Reilly & Associates
      6  *
      7  * The source code in this file can be freely used, adapted,
      8  * and redistributed in source or binary form, so long as an
      9  * acknowledgment appears in derived source files.  The citation
     10  * should list that the code comes from the book "Linux Device
     11  * Drivers" by Alessandro Rubini and Jonathan Corbet, published
     12  * by O'Reilly & Associates.   No warranty is attached;
     13  * we cannot take responsibility for errors or fitness for use.
     14  *
     15  * $Id: snull.c,v 1.21 2004/11/05 02:36:03 rubini Exp $
     16  */
     17 
     18 #include <linux/module.h>
     19 #include <linux/init.h>
     20 #include <linux/moduleparam.h>
     21 
     22 #include <linux/sched.h>
     23 #include <linux/kernel.h> /* printk() */
     24 #include <linux/slab.h> /* kmalloc() */
     25 #include <linux/errno.h>  /* error codes */
     26 #include <linux/types.h>  /* size_t */
     27 #include <linux/interrupt.h> /* mark_bh */
     28 
     29 #include <linux/in.h>
     30 #include <linux/netdevice.h>   /* struct device, and other headers */
     31 #include <linux/etherdevice.h> /* eth_type_trans */
     32 #include <linux/ip.h>          /* struct iphdr */
     33 #include <linux/tcp.h>         /* struct tcphdr */
     34 #include <linux/skbuff.h>
     35 
     36 #include "snull.h"
     37 
     38 #include <linux/in6.h>
     39 #include <asm/checksum.h>
     40 
     41 MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
     42 MODULE_LICENSE("Dual BSD/GPL");
     43 
     44 
     45 /*
     46  * Transmitter lockup simulation, normally disabled.
     47  */
     48 static int lockup = 0;
     49 module_param(lockup, int, 0);
     50 
     51 static int timeout = SNULL_TIMEOUT;
     52 module_param(timeout, int, 0);
     53 
     54 /*
     55  * Do we run in NAPI mode?
     56  */
     57 static int use_napi = 0;
     58 module_param(use_napi, int, 0);
     59 
     60 
     61 /*
     62  * A structure representing an in-flight packet.
     63  */
     64 struct snull_packet {
     65     struct snull_packet *next;
     66     struct net_device *dev;
     67     int    datalen;
     68     u8 data[ETH_DATA_LEN];
     69 };
     70 
     71 int pool_size = 8;
     72 module_param(pool_size, int, 0);
     73 
     74 /*
     75  * This structure is private to each device. It is used to pass
     76  * packets in and out, so there is place for a packet
     77  */
     78 
     79 struct snull_priv {
     80     struct net_device_stats stats;
     81     int status;
     82     struct snull_packet *ppool;
     83     struct snull_packet *rx_queue;  /* List of incoming packets */
     84     int rx_int_enabled;
     85     int tx_packetlen;
     86     u8 *tx_packetdata;
     87     struct sk_buff *skb;
     88     spinlock_t lock;
     89     struct net_device *dev;
     90     //struct napi_struct napi;
     91 };
     92 
     93 static void snull_tx_timeout(struct net_device *dev);
     94 static void (*snull_interrupt)(int, void *, struct pt_regs *);
     95 
     96 /*
     97  * Set up a device's packet pool.
     98  */
     99 void snull_setup_pool(struct net_device *dev)
    100 {
    101     struct snull_priv *priv = netdev_priv(dev);
    102     int i;
    103     struct snull_packet *pkt;
    104 
    105     priv->ppool = NULL;
    106     for (i = 0; i < pool_size; i++) {
    107         pkt = kmalloc (sizeof (struct snull_packet), GFP_KERNEL);
    108         if (pkt == NULL) {
    109             printk (KERN_NOTICE "Ran out of memory allocating packet pool
    ");
    110             return;
    111         }
    112         pkt->dev = dev;
    113         pkt->next = priv->ppool;
    114         priv->ppool = pkt;
    115     }
    116 }
    117 
    118 void snull_teardown_pool(struct net_device *dev)
    119 {
    120     struct snull_priv *priv = netdev_priv(dev);
    121     struct snull_packet *pkt;
    122     
    123     while ((pkt = priv->ppool)) {
    124         priv->ppool = pkt->next;
    125         kfree (pkt);
    126         /* FIXME - in-flight packets ? */
    127     }
    128 }    
    129 
    130 /*
    131  * Buffer/pool management.
    132  */
    133 struct snull_packet *snull_get_tx_buffer(struct net_device *dev)
    134 {
    135     struct snull_priv *priv = netdev_priv(dev);
    136     unsigned long flags;
    137     struct snull_packet *pkt;
    138     
    139     spin_lock_irqsave(&priv->lock, flags);
    140     pkt = priv->ppool;
    141     priv->ppool = pkt->next;
    142     if (priv->ppool == NULL) {
    143         printk (KERN_INFO "Pool empty
    ");
    144         netif_stop_queue(dev);
    145     }
    146     spin_unlock_irqrestore(&priv->lock, flags);
    147     return pkt;
    148 }
    149 
    150 
    151 void snull_release_buffer(struct snull_packet *pkt)
    152 {
    153     unsigned long flags;
    154     struct snull_priv *priv = netdev_priv(pkt->dev);
    155     
    156     spin_lock_irqsave(&priv->lock, flags);
    157     pkt->next = priv->ppool;
    158     priv->ppool = pkt;
    159     spin_unlock_irqrestore(&priv->lock, flags);
    160     if (netif_queue_stopped(pkt->dev) && pkt->next == NULL)
    161         netif_wake_queue(pkt->dev);
    162 
    163     printk("snull_release_buffer
    ");
    164 }
    165 
    166 void snull_enqueue_buf(struct net_device *dev, struct snull_packet *pkt)
    167 {
    168     unsigned long flags;
    169     struct snull_priv *priv = netdev_priv(dev);
    170 
    171     spin_lock_irqsave(&priv->lock, flags);
    172     pkt->next = priv->rx_queue;  /* FIXME - misorders packets */
    173     priv->rx_queue = pkt;
    174     spin_unlock_irqrestore(&priv->lock, flags);
    175 }
    176 
    177 struct snull_packet *snull_dequeue_buf(struct net_device *dev)
    178 {
    179     struct snull_priv *priv = netdev_priv(dev);
    180     struct snull_packet *pkt;
    181     unsigned long flags;
    182 
    183     spin_lock_irqsave(&priv->lock, flags);
    184     pkt = priv->rx_queue;
    185     if (pkt != NULL)
    186         priv->rx_queue = pkt->next;
    187     spin_unlock_irqrestore(&priv->lock, flags);
    188     return pkt;
    189 }
    190 
    191 /*
    192  * Enable and disable receive interrupts.
    193  */
    194 static void snull_rx_ints(struct net_device *dev, int enable)
    195 {
    196     struct snull_priv *priv = netdev_priv(dev);
    197     priv->rx_int_enabled = enable;
    198 }
    199 
    200     
    201 /*
    202  * Open and close
    203  */
    204 
    205 int snull_open(struct net_device *dev)
    206 {
    207     /* request_region(), request_irq(), ....  (like fops->open) */
    208 
    209     /* 
    210      * Assign the hardware address of the board: use "SNULx", where
    211      * x is 0 or 1. The first byte is '' to avoid being a multicast
    212      * address (the first byte of multicast addrs is odd).
    213      */
    214     /* [cgw]: 分配一个假的硬件地址,真正的网卡的时候,这个地址是从网卡读出来的 */
    215     memcpy(dev->dev_addr, "SNUL0", ETH_ALEN);
    216     /* [cgw]: 因为注册了两个虚拟网卡,第二个虚拟网卡的地址跟第一个的地址必须不一样
    217      * 即这两个网卡地址分别为SNUL0和SNUL1
    218      */
    219     if (dev == snull_devs[1])
    220         dev->dev_addr[ETH_ALEN-1]++; /* SNUL1 */
    221     /* [cgw]: 启动发送队列 */
    222     netif_start_queue(dev);
    223 
    224     printk("snull_open
    ");
    225     
    226     return 0;
    227 }
    228 
    229 int snull_release(struct net_device *dev)
    230 {
    231     /* release ports, irq and such -- like fops->close */
    232 
    233     netif_stop_queue(dev); /* can't transmit any more */
    234     
    235     printk("snull_release
    ");
    236     
    237     return 0;
    238 }
    239 
    240 /*
    241  * Configuration changes (passed on by ifconfig)
    242  */
    243 int snull_config(struct net_device *dev, struct ifmap *map)
    244 {
    245     if (dev->flags & IFF_UP) /* can't act on a running interface */
    246         return -EBUSY;
    247 
    248     /* Don't allow changing the I/O address */
    249     if (map->base_addr != dev->base_addr) {
    250         printk(KERN_WARNING "snull: Can't change I/O address
    ");
    251         return -EOPNOTSUPP;
    252     }
    253 
    254     /* Allow changing the IRQ */
    255     if (map->irq != dev->irq) {
    256         dev->irq = map->irq;
    257             /* request_irq() is delayed to open-time */
    258     }
    259 
    260     printk("snull_config
    ");
    261 
    262     /* ignore other fields */
    263     return 0;
    264 }
    265 
    266 /*
    267  * Receive a packet: retrieve, encapsulate and pass over to upper levels
    268  */
    269 void snull_rx(struct net_device *dev, struct snull_packet *pkt)
    270 {
    271     struct sk_buff *skb;
    272     struct snull_priv *priv = netdev_priv(dev);
    273 
    274     /*
    275      * The packet has been retrieved from the transmission
    276      * medium. Build an skb around it, so upper layers can handle it
    277      */
    278     /* [cgw]: 为接收包分配一个skb */
    279     skb = dev_alloc_skb(pkt->datalen + 2);
    280     if (!skb) {
    281         if (printk_ratelimit())
    282             printk(KERN_NOTICE "snull rx: low on mem - packet dropped
    ");
    283         priv->stats.rx_dropped++;
    284         goto out;
    285     }
    286     /* [cgw]: 16字节对齐,即IP首部前是网卡硬件地址首部,其占14字节,需要为其增加2
    287      * 个字节 
    288      */
    289     skb_reserve(skb, 2); /* align IP on 16B boundary */
    290     /* [cgw]: 开辟一个数据缓冲区用于存放接收数据 */
    291     memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
    292 
    293     /* Write metadata, and then pass to the receive level */
    294     skb->dev = dev;
    295     if (skb->dev == snull_devs[0]) {
    296         printk("skb->dev is snull_devs[0]
    ");
    297     } else {
    298         printk("skb->dev is snull_devs[1]
    ");
    299     }
    300     /* [cgw]: 确定包的协议ID */
    301     skb->protocol = eth_type_trans(skb, dev);
    302 
    303     printk("skb->protocol = %d
    ", skb->protocol);
    304     
    305     skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
    306     /* [cgw]: 统计接收包数和字节数 */
    307     priv->stats.rx_packets++;
    308     priv->stats.rx_bytes += pkt->datalen;
    309     /* [cgw]: 上报应用层 */
    310     netif_rx(skb);
    311 
    312     printk("snull_rx
    ");
    313     
    314   out:
    315     return;
    316 }
    317     
    318 
    319 /*
    320  * The poll implementation.
    321  */
    322 //static int snull_poll(struct napi_struct *napi, int budget)
    323 static int snull_poll(struct net_device *dev, int *budget)
    324 {
    325     //int npackets = 0;
    326     //struct sk_buff *skb;
    327     //struct snull_priv *priv = container_of(napi, struct snull_priv, napi);
    328     //struct net_device *dev = priv->dev;
    329     //struct snull_packet *pkt;
    330 
    331     int npackets = 0, quota = min(dev->quota, *budget);
    332     struct sk_buff *skb;
    333     struct snull_priv *priv = netdev_priv(dev);
    334     struct snull_packet *pkt;
    335 
    336     printk("snull_poll
    ");
    337     
    338     //while (npackets < budget && priv->rx_queue) {
    339     while (npackets < quota && priv->rx_queue) {
    340         pkt = snull_dequeue_buf(dev);
    341         skb = dev_alloc_skb(pkt->datalen + 2);
    342         if (! skb) {
    343             if (printk_ratelimit())
    344                 printk(KERN_NOTICE "snull: packet dropped
    ");
    345             priv->stats.rx_dropped++;
    346             snull_release_buffer(pkt);
    347             continue;
    348         }
    349         skb_reserve(skb, 2); /* align IP on 16B boundary */  
    350         memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
    351         skb->dev = dev;
    352         skb->protocol = eth_type_trans(skb, dev);
    353         skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
    354         netif_receive_skb(skb);
    355         
    356             /* Maintain stats */
    357         npackets++;
    358         priv->stats.rx_packets++;
    359         priv->stats.rx_bytes += pkt->datalen;
    360         snull_release_buffer(pkt);
    361     }
    362     /* If we processed all packets, we're done; tell the kernel and reenable ints */
    363     *budget -= npackets;
    364     dev->quota -= npackets;
    365     if (! priv->rx_queue) {
    366         //napi_complete(napi);
    367         netif_rx_complete(dev);
    368         snull_rx_ints(dev, 1);
    369         return 0;
    370     }
    371     /* We couldn't process everything. */
    372     //return npackets;
    373     return 1;
    374 }        
    375         
    376 /*
    377  * The typical interrupt entry point
    378  */
    379 static void snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    380 {
    381     int statusword;
    382     struct snull_priv *priv;
    383     struct snull_packet *pkt = NULL;
    384     /*
    385      * As usual, check the "device" pointer to be sure it is
    386      * really interrupting.
    387      * Then assign "struct device *dev"
    388      */
    389     struct net_device *dev = (struct net_device *)dev_id;
    390     /* ... and check with hw if it's really ours */
    391 
    392     /* paranoid */
    393     if (!dev)
    394         return;
    395 
    396     /* Lock the device */
    397     priv = netdev_priv(dev);
    398     spin_lock(&priv->lock);
    399 
    400     /* [cgw]: 判断产生的是什么类型的中断,接收还是中断 */
    401     /* retrieve statusword: real netdevices use I/O instructions */
    402     statusword = priv->status;
    403     
    404     printk("priv->status = %d
    ", priv->status);
    405     
    406     priv->status = 0;
    407     /* [cgw]: 接收完成中断 */
    408     if (statusword & SNULL_RX_INTR) {
    409         /* send it to snull_rx for handling */
    410         pkt = priv->rx_queue;
    411         if (pkt) {
    412             priv->rx_queue = pkt->next;
    413             /* [cgw]: 网卡接收到数据,上报给应用层 */
    414             snull_rx(dev, pkt);
    415         }
    416     }
    417     /* [cgw]: 发送完成中断 */
    418     if (statusword & SNULL_TX_INTR) {
    419         /* [cgw]: 统计已发送的包数和总字节数,并释放这个包的内存 */
    420         /* a transmission is over: free the skb */
    421         priv->stats.tx_packets++;
    422         priv->stats.tx_bytes += priv->tx_packetlen;
    423         dev_kfree_skb(priv->skb);
    424     }
    425 
    426     /* Unlock the device and we are done */
    427     spin_unlock(&priv->lock);
    428     if (pkt) snull_release_buffer(pkt); /* Do this outside the lock! */
    429 
    430     printk("snull_regular_interrupt
    ");
    431 
    432     return;
    433 }
    434 
    435 /*
    436  * A NAPI interrupt handler.
    437  */
    438 static void snull_napi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    439 {
    440     int statusword;
    441     struct snull_priv *priv;
    442 
    443     /*
    444      * As usual, check the "device" pointer for shared handlers.
    445      * Then assign "struct device *dev"
    446      */
    447     struct net_device *dev = (struct net_device *)dev_id;
    448     /* ... and check with hw if it's really ours */
    449 
    450     printk("snull_napi_interrupt
    ");
    451 
    452     /* paranoid */
    453     if (!dev)
    454         return;
    455 
    456     /* Lock the device */
    457     priv = netdev_priv(dev);
    458     spin_lock(&priv->lock);
    459 
    460     /* retrieve statusword: real netdevices use I/O instructions */
    461     statusword = priv->status;
    462     priv->status = 0;
    463     if (statusword & SNULL_RX_INTR) {
    464         snull_rx_ints(dev, 0);  /* Disable further interrupts */
    465         //napi_schedule(&priv->napi);
    466         netif_rx_schedule(dev);
    467     }
    468     if (statusword & SNULL_TX_INTR) {
    469             /* a transmission is over: free the skb */
    470         priv->stats.tx_packets++;
    471         priv->stats.tx_bytes += priv->tx_packetlen;
    472         dev_kfree_skb(priv->skb);
    473     }
    474 
    475     /* Unlock the device and we are done */
    476     spin_unlock(&priv->lock);
    477     return;
    478 }
    479 
    480 
    481 /*
    482  * Transmit a packet (low level interface)
    483  */
    484 static void snull_hw_tx(char *buf, int len, struct net_device *dev)
    485 {
    486     /*
    487      * This function deals with hw details. This interface loops
    488      * back the packet to the other snull interface (if any).
    489      * In other words, this function implements the snull behaviour,
    490      * while all other procedures are rather device-independent
    491      */
    492     struct iphdr *ih;
    493     struct net_device *dest;
    494     struct snull_priv *priv;
    495     u32 *saddr, *daddr;
    496     struct snull_packet *tx_buffer;
    497     
    498     /* I am paranoid. Ain't I? */
    499     if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
    500         printk("snull: Hmm... packet too short (%i octets)
    ",
    501                 len);
    502         return;
    503     }
    504 
    505     /* [cgw]: 打印上层应用(即ping)要发的这个包的内容
    506      * 这个包的格式为:
    507      * 14字节以太网首部+20字节IP地址首部+20字节TCP地址首部+n字节数据
    508      */
    509     
    510     if (1) { /* enable this conditional to look at the data */
    511         int i;
    512         PDEBUG("len is %i
    " KERN_DEBUG "data:",len);
    513         /* [cgw]: 14字节以太网首部 */
    514         for (i=0 ; i<14; i++)
    515             printk(" %02x",buf[i]&0xff);
    516         printk("
    ");
    517 
    518         /* [cgw]: 20字节IP地址首部 */
    519         for (i=14 ; i<34; i++)
    520             printk(" %02x",buf[i]&0xff);
    521         printk("
    ");
    522 
    523         /* [cgw]: 20字节TCP地址首部 */
    524         for (i=34 ; i<54; i++)
    525             printk(" %02x",buf[i]&0xff);
    526         printk("
    ");
    527 
    528         /* [cgw]: n字节数据 */
    529         for (i=54 ; i<len; i++)
    530             printk(" %02x",buf[i]&0xff);
    531         printk("
    ");
    532     }
    533     /*
    534      * Ethhdr is 14 bytes, but the kernel arranges for iphdr
    535      * to be aligned (i.e., ethhdr is unaligned)
    536      */
    537     /* [cgw]: 提取本地和目标IP地址 */
    538     ih = (struct iphdr *)(buf+sizeof(struct ethhdr));
    539     saddr = &ih->saddr;
    540     daddr = &ih->daddr;
    541     
    542     printk("ih->protocol = %d is buf[23]
    ", ih->protocol);
    543     printk("saddr = %d.%d.%d.%d
    ", *((u8 *)saddr + 0), *((u8 *)saddr + 1), *((u8 *)saddr + 2), *((u8 *)saddr + 3));
    544     printk("daddr = %d.%d.%d.%d
    ", *((u8 *)daddr + 0), *((u8 *)daddr + 1), *((u8 *)daddr + 2), *((u8 *)daddr + 3));
    545 
    546     /* [cgw]: 改变本地和目标IP地址的第三个字节的最低位,即原来是0,则改为1,原来是1,则改为0
    547      */
    548     ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
    549     ((u8 *)daddr)[2] ^= 1;
    550 
    551     /* [cgw]: 从新计算校验,因为IP已改变 */
    552     ih->check = 0;         /* and rebuild the checksum (ip needs it) */
    553     ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
    554 
    555     /* [cgw]: 打印更改后的IP地址,和TCP地址,
    556      */
    557     if (dev == snull_devs[0])
    558         //PDEBUGG("%08x:%05i --> %08x:%05i
    ",
    559         printk("%08x:%05i --> %08x:%05i
    ",
    560                 ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source),
    561                 ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest));
    562     else
    563         //PDEBUGG("%08x:%05i <-- %08x:%05i
    ",
    564         printk("%08x:%05i <-- %08x:%05i
    ",
    565                 ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),
    566                 ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source));
    567 
    568     /*
    569      * Ok, now the packet is ready for transmission: first simulate a
    570      * receive interrupt on the twin device, then  a
    571      * transmission-done on the transmitting device
    572      */
    573     /* [cgw]: 获得目的网卡设备 */
    574     dest = snull_devs[dev == snull_devs[0] ? 1 : 0];
    575     
    576     if (dev == snull_devs[0]) {
    577         printk("snull_devs[0]
    ");
    578     } else {
    579         printk("snull_devs[1]
    ");
    580     }
    581     
    582     priv = netdev_priv(dest);
    583     /* [cgw]: 取出一块内存分配给本地网卡 */
    584     tx_buffer = snull_get_tx_buffer(dev);
    585     /* [cgw]: 设置数据包大小 */
    586     tx_buffer->datalen = len;
    587     
    588     printk("tx_buffer->datalen = %d
    ", tx_buffer->datalen);
    589 
    590     /* [cgw]: 填充发送网卡的数据 */
    591     memcpy(tx_buffer->data, buf, len);
    592     /* [cgw]: 把发送的数据直接加入到接收队列,这里相当于本地网卡要发送的数据
    593      * 已经给目标网卡直接接收到了
    594      */
    595     snull_enqueue_buf(dest, tx_buffer);
    596     /* [cgw]: 如果接收中断使能,这个也是模拟的接收中断,因为上面已经模拟接收
    597      * 到数据,所以立刻产生一个中断
    598      */
    599     if (priv->rx_int_enabled) {
    600         priv->status |= SNULL_RX_INTR;
    601         printk("priv->status = %d
    ", priv->status);
    602         /* [cgw]: 执行接收中断 */
    603         snull_interrupt(0, dest, NULL);
    604         printk("snull_interrupt(0, dest, NULL);
    ");
    605     }
    606 
    607     /* [cgw]: 获得本地网卡的私有数据指针 */
    608     priv = netdev_priv(dev);
    609     /* [cgw]: 把本地网卡要发送的数据存到私有数据缓冲区,接着产生一个发送中断
    610      */
    611     priv->tx_packetlen = len;
    612     priv->tx_packetdata = buf;
    613     priv->status |= SNULL_TX_INTR;
    614     if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) {
    615             /* Simulate a dropped transmit interrupt */
    616         netif_stop_queue(dev);
    617         PDEBUG("Simulate lockup at %ld, txp %ld
    ", jiffies,
    618                 (unsigned long) priv->stats.tx_packets);
    619     }
    620     else {
    621         /* [cgw]: 产生一个发送中断 */
    622         snull_interrupt(0, dev, NULL);
    623         printk("snull_interrupt(0, dev, NULL);
    ");
    624     }
    625 }
    626 
    627 /*
    628  * Transmit a packet (called by the kernel)
    629  */
    630 int snull_tx(struct sk_buff *skb, struct net_device *dev)
    631 {
    632     int len;
    633     char *data, shortpkt[ETH_ZLEN];
    634     struct snull_priv *priv = netdev_priv(dev);
    635 
    636     /* [cgw]: 获取上层需要发送的数据和长度 */
    637     data = skb->data;
    638     len = skb->len;
    639 
    640     printk("skb->len = %d
    ", skb->len);
    641     
    642     if (len < ETH_ZLEN) {
    643         memset(shortpkt, 0, ETH_ZLEN);
    644         memcpy(shortpkt, skb->data, skb->len);
    645         len = ETH_ZLEN;
    646         data = shortpkt;
    647     }
    648     /* [cgw]: 开始计算时间截,用于处理发送超时 */
    649     dev->trans_start = jiffies; /* save the timestamp */
    650 
    651     /* Remember the skb, so we can free it at interrupt time */
    652     priv->skb = skb;
    653     
    654     printk("snull_tx
    ");
    655 
    656     /* actual deliver of data is device-specific, and not shown here */
    657     /* [cgw]: 模拟把数据包写入硬件,通过硬件发送出去,但实际上不是 */
    658     snull_hw_tx(data, len, dev);
    659 
    660     //printk("snull_tx
    ");
    661 
    662     return 0; /* Our simple device can not fail */
    663 }
    664 
    665 /*
    666  * Deal with a transmit timeout.
    667  */
    668 void snull_tx_timeout (struct net_device *dev)
    669 {
    670     struct snull_priv *priv = netdev_priv(dev);
    671 
    672     PDEBUG("Transmit timeout at %ld, latency %ld
    ", jiffies,
    673             jiffies - dev->trans_start);
    674         /* Simulate a transmission interrupt to get things moving */
    675     priv->status = SNULL_TX_INTR;
    676     snull_interrupt(0, dev, NULL);
    677     priv->stats.tx_errors++;
    678     netif_wake_queue(dev);
    679 
    680     printk("snull_tx_timeout
    ");
    681     
    682     return;
    683 }
    684 
    685 
    686 
    687 /*
    688  * Ioctl commands 
    689  */
    690 int snull_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
    691 {
    692     PDEBUG("ioctl
    ");
    693     printk("ioctl
    ");
    694     return 0;
    695 }
    696 
    697 /*
    698  * Return statistics to the caller
    699  */
    700 struct net_device_stats *snull_stats(struct net_device *dev)
    701 {
    702     struct snull_priv *priv = netdev_priv(dev);
    703 
    704     printk("snull_stats
    ");
    705     
    706     return &priv->stats;
    707 }
    708 
    709 /*
    710  * This function is called to fill up an eth header, since arp is not
    711  * available on the interface
    712  */
    713 int snull_rebuild_header(struct sk_buff *skb)
    714 {
    715     struct ethhdr *eth = (struct ethhdr *) skb->data;
    716     struct net_device *dev = skb->dev;
    717     
    718     memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
    719     memcpy(eth->h_dest, dev->dev_addr, dev->addr_len);
    720     eth->h_dest[ETH_ALEN-1]   ^= 0x01;   /* dest is us xor 1 */
    721 
    722     printk("snull_rebuild_header
    ");
    723     
    724     return 0;
    725 }
    726 
    727 
    728 //int snull_header(struct sk_buff *skb, struct net_device *dev,
    729 //                unsigned short type, const void *daddr, const void *saddr,
    730 //                unsigned len)
    731 
    732 int snull_header(struct sk_buff *skb, struct net_device *dev,
    733                 unsigned short type, void *daddr, void *saddr,
    734                 unsigned len)              
    735 {
    736     struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
    737 
    738     printk("len = %d
    ", len);
    739 
    740     printk("type = %02x
    ", type); //ETH_P_IP    0x0800        /* Internet Protocol packet    */
    741 
    742     /* htons是将整型变量从主机字节顺序转变成网络字节顺序, 
    743      * 就是整数在地址空间存储方式变为:高位字节存放在内存的低地址处
    744      */
    745     eth->h_proto = htons(type);
    746     printk("h_proto = %d
    ", eth->h_proto);
    747     
    748     printk("addr_len = %d
    ", dev->addr_len);
    749     printk("dev_addr = %02x.%02x.%02x.%02x.%02x.%02x
    ", dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
    750 
    751     if (saddr) {
    752         printk("saddr = %02x.%02x.%02x.%02x.%02x.%02x
    ", *((unsigned char *)saddr + 0), *((unsigned char *)saddr + 1), *((unsigned char *)saddr + 2), *((unsigned char *)saddr + 3), *((unsigned char *)saddr + 4), *((unsigned char *)saddr + 5));
    753     }
    754 
    755     if (daddr) {
    756         printk("daddr = %02x.%02x.%02x.%02x.%02x.%02x
    ", *((unsigned char *)daddr + 0), *((unsigned char *)daddr + 1), *((unsigned char *)daddr + 2), *((unsigned char *)daddr + 3), *((unsigned char *)daddr + 4), *((unsigned char *)daddr + 5));
    757     }
    758 
    759     /* [cgw]: 上层应用要发送数据时,通过下层添加硬件地址,才能决定发送到那个目标网卡
    760      */
    761     memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
    762     memcpy(eth->h_dest,   daddr ? daddr : dev->dev_addr, dev->addr_len);
    763     printk("h_source = %02x.%02x.%02x.%02x.%02x.%02x
    ", eth->h_source[0], eth->h_source[1], eth->h_source[2],eth->h_source[3], eth->h_source[4], eth->h_source[5]);
    764     printk("h_dest = %02x.%02x.%02x.%02x.%02x.%02x
    ", eth->h_dest[0], eth->h_dest[1], eth->h_dest[2], eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);
    765 
    766     /* [cgw]: 设置目标网卡硬件地址,即本地网卡和目标网卡硬件地址的最后一个字节的最低有效位
    767      * 是相反关系,即本地是SNUL0的话,目标就是SNUL1,或者本地是SNUL1,目标就是SNUL0
    768      */
    769     eth->h_dest[ETH_ALEN-1]   ^= 0x01;   /* dest is us xor 1 */
    770     printk("h_dest[ETH_ALEN-1] ^ 0x01 = %02x
    ", eth->h_dest[ETH_ALEN-1]);
    771     
    772     printk("hard_header_len = %d
    ", dev->hard_header_len);
    773     
    774     return (dev->hard_header_len);
    775 }
    776 
    777 
    778 
    779 
    780 
    781 /*
    782  * The "change_mtu" method is usually not needed.
    783  * If you need it, it must be like this.
    784  */
    785 int snull_change_mtu(struct net_device *dev, int new_mtu)
    786 {
    787     unsigned long flags;
    788     struct snull_priv *priv = netdev_priv(dev);
    789     spinlock_t *lock = &priv->lock;
    790     
    791     /* check ranges */
    792     if ((new_mtu < 68) || (new_mtu > 1500))
    793         return -EINVAL;
    794     /*
    795      * Do anything you need, and the accept the value
    796      */
    797     spin_lock_irqsave(lock, flags);
    798     dev->mtu = new_mtu;
    799     spin_unlock_irqrestore(lock, flags);
    800     return 0; /* success */
    801 }
    802 
    803 #if 0
    804 static const struct header_ops snull_header_ops = {
    805         .create  = snull_header,
    806     .rebuild = snull_rebuild_header
    807 };
    808 
    809 static const struct net_device_ops snull_netdev_ops = {
    810     .ndo_open            = snull_open,
    811     .ndo_stop            = snull_release,
    812     .ndo_start_xmit      = snull_tx,
    813     .ndo_do_ioctl        = snull_ioctl,
    814     .ndo_set_config      = snull_config,
    815     .ndo_get_stats       = snull_stats,
    816     .ndo_change_mtu      = snull_change_mtu,
    817     .ndo_tx_timeout      = snull_tx_timeout
    818 };
    819 #endif
    820 
    821 /*
    822  * The init function (sometimes called probe).
    823  * It is invoked by register_netdev()
    824  */
    825 void snull_init(struct net_device *dev)
    826 {
    827     struct snull_priv *priv;
    828 #if 0
    829         /*
    830      * Make the usual checks: check_region(), probe irq, ...  -ENODEV
    831      * should be returned if no device found.  No resource should be
    832      * grabbed: this is done on open(). 
    833      */
    834 #endif
    835 
    836         /* 
    837      * Then, assign other fields in dev, using ether_setup() and some
    838      * hand assignments
    839      */
    840     ether_setup(dev); /* assign some of the fields */
    841     dev->watchdog_timeo = timeout;
    842     
    843     //dev->netdev_ops = &snull_netdev_ops;
    844     //dev->header_ops = &snull_header_ops;
    845     
    846     dev->hard_header = snull_header;
    847     dev->rebuild_header = snull_rebuild_header;
    848     
    849     dev->open = snull_open;
    850     dev->stop = snull_release;
    851     dev->hard_start_xmit = snull_tx;
    852     dev->do_ioctl = snull_ioctl;
    853     dev->set_config = snull_config;
    854     dev->get_stats = snull_stats;
    855     dev->change_mtu = snull_change_mtu;
    856     dev->tx_timeout = snull_tx_timeout;
    857     
    858     /* keep the default flags, just add NOARP */
    859     dev->flags           |= IFF_NOARP;
    860     dev->features        |= NETIF_F_HW_CSUM;
    861 
    862     dev->hard_header_cache = NULL;
    863 
    864     /*
    865      * Then, initialize the priv field. This encloses the statistics
    866      * and a few private fields.
    867      */
    868     priv = netdev_priv(dev);
    869     #if 0
    870     if (use_napi) {
    871         netif_napi_add(dev, &priv->napi, snull_poll,2);
    872     } 
    873     #else
    874     if (use_napi) {
    875         dev->poll = snull_poll;
    876         dev->weight = 2;
    877     }
    878     #endif
    879     memset(priv, 0, sizeof(struct snull_priv));
    880     spin_lock_init(&priv->lock);
    881     snull_rx_ints(dev, 1);        /* enable receive interrupts */
    882     snull_setup_pool(dev);
    883 
    884     printk("snull_init
    ");
    885 }
    886 
    887 /*
    888  * The devices
    889  */
    890 
    891 struct net_device *snull_devs[2];
    892 
    893 
    894 
    895 /*
    896  * Finally, the module stuff
    897  */
    898 
    899 void snull_cleanup(void)
    900 {
    901     int i;
    902     
    903     for (i = 0; i < 2;  i++) {
    904         if (snull_devs[i]) {
    905             unregister_netdev(snull_devs[i]);
    906             snull_teardown_pool(snull_devs[i]);
    907             free_netdev(snull_devs[i]);
    908         }
    909     }
    910     return;
    911 }
    912 
    913 
    914 
    915 
    916 int snull_init_module(void)
    917 {
    918     int result, i, ret = -ENOMEM;
    919 
    920     snull_interrupt = use_napi ? snull_napi_interrupt : snull_regular_interrupt;
    921     
    922     /* Allocate the devices */
    923     snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
    924             snull_init);
    925     snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
    926             snull_init);
    927     if (snull_devs[0] == NULL || snull_devs[1] == NULL)
    928         goto out;
    929 
    930     ret = -ENODEV;
    931     for (i = 0; i < 2;  i++)
    932         if ((result = register_netdev(snull_devs[i])))
    933             printk("snull: error %i registering device "%s"
    ",
    934                     result, snull_devs[i]->name);
    935         else
    936             ret = 0;
    937 
    938     printk("snull_init_module
    ");
    939             
    940    out:
    941     if (ret) 
    942         snull_cleanup();
    943     return ret;
    944 }
    945 
    946 
    947 module_init(snull_init_module);
    948 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


    分析现象:

    1.当执行ping命令后,驱动首先会调用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
    ", len);
    
        printk("type = %02x
    ", type); //ETH_P_IP    0x0800        /* Internet Protocol packet    */
    
        /* htons是将整型变量从主机字节顺序转变成网络字节顺序, 
         * 就是整数在地址空间存储方式变为:高位字节存放在内存的低地址处
         */
        eth->h_proto = htons(type);
        printk("h_proto = %d
    ", eth->h_proto);
        
        printk("addr_len = %d
    ", dev->addr_len);
        printk("dev_addr = %02x.%02x.%02x.%02x.%02x.%02x
    ", 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
    ", *((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
    ", *((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]: 上层应用要发送数据时,通过下层添加硬件地址,才能决定发送到那个目标网卡
         */
        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
    ", 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
    ", 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]: 设置目标网卡硬件地址,即本地网卡和目标网卡硬件地址的最后一个字节的最低有效位
         * 是相反关系,即本地是SNUL0的话,目标就是SNUL1,或者本地是SNUL1,目标就是SNUL0
         */
        eth->h_dest[ETH_ALEN-1]   ^= 0x01;   /* dest is us xor 1 */
        printk("h_dest[ETH_ALEN-1] ^ 0x01 = %02x
    ", eth->h_dest[ETH_ALEN-1]);
        
        printk("hard_header_len = %d
    ", dev->hard_header_len);
        
        return (dev->hard_header_len);
    }


    因为应用层要发送数据包了,所以要为这个数据包添加硬件地址,即以太网地址首部,才能通过网卡发送出去。

    2.  然后内核会通过调用snull_tx发送数据包,snull_tx调用了snull_hw_tx,在这里更改本地IP为目标IP,并把本地要发的数据直接拷贝给目标网卡,代表目标网卡以接收到数据,并触发接收完成中断,向应用层上报数据,接着触发发送完成中断,表示数据已经发送到目标网卡。

    3. 数据包分析:

    static void snull_hw_tx(char *buf, int len, struct net_device *dev)

    这里的buf为应用层要发送的数据包,数据包格式为:14字节以太网首部+20字节IP地址首部+20字节TCP地址首部+n字节数据

     1 /* [cgw]: 14字节以太网首部 */
     2         for (i=0 ; i<14; i++)
     3             printk(" %02x",buf[i]&0xff);
     4         printk("
    ");
     5 
     6         /* [cgw]: 20字节IP地址首部 */
     7         for (i=14 ; i<34; i++)
     8             printk(" %02x",buf[i]&0xff);
     9         printk("
    ");
    10 
    11         /* [cgw]: 20字节TCP地址首部 */
    12         for (i=34 ; i<54; i++)
    13             printk(" %02x",buf[i]&0xff);
    14         printk("
    ");
    15 
    16         /* [cgw]: n字节数据 */
    17         for (i=54 ; i<len; i++)
    18             printk(" %02x",buf[i]&0xff);
    19         printk("
    ");

    打印结果:

    00 53 4e 55 4c 30 00 53 4e 55 4c 31 08 00                      //14字节以太网首部
     45 00 00 54 a0 17 00 00 40 01 53 30 c0 a8 03 09 c0 a8 03 08   //20字节IP地址首部
     00 00 d8 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00   //20字节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字节数据

    其中:00 53 4e 55 4c 30 就硬件地址SNUL0的ASCII码,00 53 4e 55 4c 31 就硬件地址SNUL1的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 的结构体是:

    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,经过htons转换为0x08)

    代表45 00 00 54 00 00 40 00 40 01 b5 47 c0 a8 02 08 c0 a8 02 09的结构体是:

     1 struct iphdr {
     2 #if defined(__LITTLE_ENDIAN_BITFIELD)
     3     __u8    ihl:4,
     4         version:4;
     5 #elif defined (__BIG_ENDIAN_BITFIELD)
     6     __u8    version:4,
     7           ihl:4;
     8 #else
     9 #error    "Please fix <asm/byteorder.h>"
    10 #endif
    11     __u8    tos;
    12     __be16    tot_len;
    13     __be16    id;
    14     __be16    frag_off;
    15     __u8    ttl;
    16     __u8    protocol;
    17     __sum16    check;
    18     __be32    saddr;
    19     __be32    daddr;
    20     /*The options start here. */
    21 };


    代表 08 00 d0 0e 09 03 00 00 bc e8 62 05 00 00 00 00 00 00 00 00 的结构体是:

     1 struct tcphdr {
     2     __be16    source;
     3     __be16    dest;
     4     __be32    seq;
     5     __be32    ack_seq;
     6 #if defined(__LITTLE_ENDIAN_BITFIELD)
     7     __u16    res1:4,
     8         doff:4,
     9         fin:1,
    10         syn:1,
    11         rst:1,
    12         psh:1,
    13         ack:1,
    14         urg:1,
    15         ece:1,
    16         cwr:1;
    17 #elif defined(__BIG_ENDIAN_BITFIELD)
    18     __u16    doff:4,
    19         res1:4,
    20         cwr:1,
    21         ece:1,
    22         urg:1,
    23         ack:1,
    24         psh:1,
    25         rst:1,
    26         syn:1,
    27         fin:1;
    28 #else
    29 #error    "Adjust your <asm/byteorder.h> defines"
    30 #endif    
    31     __be16    window;
    32     __sum16    check;
    33     __be16    urg_ptr;
    34 };


    NAPI

    NAPI的全称是“NEW API”。

    要使用NAPI功能,只要在加载snull.ko的添加一句use_napi=1就行了

    如:#insmod snull.ko use_napi=1

    NAPI有什么作用?

    NAPI是一种使用轮询(poll)的方式去接收数据。如当系统需要接收一大坨数据时,数据量比较大时,这个时候数据的接收就不应该在中断中进行。即产生接收完成中断后,立即禁止中断,通知内核调用poll,轮询接收数据,接收完成后,再使能接收中断。这样大大提高系统的性能。

    在驱动初始化时:分配好poll函数

    1 if (use_napi) {
    2         dev->poll = snull_poll;
    3         dev->weight = 2;
    4     }


    在接收中断中

    1 if (statusword & SNULL_RX_INTR) {
    2         /* send it to snull_rx for handling */
    3         pkt = priv->rx_queue;
    4         if (pkt) {
    5             priv->rx_queue = pkt->next;
    6             /* [cgw]: 网卡接收到数据,上报给应用层 */
    7             snull_rx(dev, pkt);
    8         }
    9     }

    改为

    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     }

    在中断中,直接通知内核调用snull_poll即可,snull_poll轮询接收数据,并上报给应用层。

    下一篇博客介绍DM9000网卡驱动。

  • 相关阅读:
    web服务器12 中间件函数
    web11 路由
    web服务器15 jsonp格式接口
    web服务器14 cors跨域资源共享 报错及解决方案
    小程序弹窗(模态框)遮罩层 弹窗右上角按钮关闭
    数据库1 mysq数据库的安装全套
    MySQL数据库3 的 where语句 and 和 or运算符
    MySQL数据库2 增删改查
    MySQL分组排序取每组第一条
    IDEA频繁闪退解决
  • 原文地址:https://www.cnblogs.com/hackfun/p/6195139.html
Copyright © 2020-2023  润新知