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, "