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\n"); 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\n"); 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\n"); 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 "\0SNULx", where 211 * x is 0 or 1. The first byte is '\0' to avoid being a multicast 212 * address (the first byte of multicast addrs is odd). 213 */ 214 /* [cgw]: 分配一個假的硬件地址,真正的網卡的時候,這個地址是從網卡讀出來的 */ 215 memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN); 216 /* [cgw]: 因為注冊了兩個虛擬網卡,第二個虛擬網卡的地址跟第一個的地址必須不一樣 217 * 即這兩個網卡地址分別為\0SNUL0和\0SNUL1 218 */ 219 if (dev == snull_devs[1]) 220 dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */ 221 /* [cgw]: 啟動發送隊列 */ 222 netif_start_queue(dev); 223 224 printk("snull_open\n"); 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\n"); 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\n"); 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\n"); 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\n"); 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]\n"); 297 } else { 298 printk("skb->dev is snull_devs[1]\n"); 299 } 300 /* [cgw]: 確定包的協議ID */ 301 skb->protocol = eth_type_trans(skb, dev); 302 303 printk("skb->protocol = %d\n", 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\n"); 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\n"); 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\n"); 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\n", 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\n"); 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\n"); 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)\n", 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\n" KERN_DEBUG "data:",len); 513 /* [cgw]: 14字節以太網首部 */ 514 for (i=0 ; i<14; i++) 515 printk(" %02x",buf[i]&0xff); 516 printk("\n"); 517 518 /* [cgw]: 20字節IP地址首部 */ 519 for (i=14 ; i<34; i++) 520 printk(" %02x",buf[i]&0xff); 521 printk("\n"); 522 523 /* [cgw]: 20字節TCP地址首部 */ 524 for (i=34 ; i<54; i++) 525 printk(" %02x",buf[i]&0xff); 526 printk("\n"); 527 528 /* [cgw]: n字節數據 */ 529 for (i=54 ; i<len; i++) 530 printk(" %02x",buf[i]&0xff); 531 printk("\n"); 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]\n", ih->protocol); 543 printk("saddr = %d.%d.%d.%d\n", *((u8 *)saddr + 0), *((u8 *)saddr + 1), *((u8 *)saddr + 2), *((u8 *)saddr + 3)); 544 printk("daddr = %d.%d.%d.%d\n", *((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\n", 559 printk("%08x:%05i --> %08x:%05i\n", 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\n", 564 printk("%08x:%05i <-- %08x:%05i\n", 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]\n"); 578 } else { 579 printk("snull_devs[1]\n"); 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\n", 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\n", priv->status); 602 /* [cgw]: 執行接收中斷 */ 603 snull_interrupt(0, dest, NULL); 604 printk("snull_interrupt(0, dest, NULL);\n"); 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\n", 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);\n"); 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\n", 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\n"); 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\n"); 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\n", 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\n"); 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\n"); 693 printk("ioctl\n"); 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\n"); 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\n"); 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\n", len); 739 740 printk("type = %02x\n", type); //ETH_P_IP 0x0800 /* Internet Protocol packet */ 741 742 /* htons是將整型變量從主機字節順序轉變成網絡字節順序, 743 * 就是整數在地址空間存儲方式變為:高位字節存放在內存的低地址處 744 */ 745 eth->h_proto = htons(type); 746 printk("h_proto = %d\n", eth->h_proto); 747 748 printk("addr_len = %d\n", dev->addr_len); 749 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]); 750 751 if (saddr) { 752 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)); 753 } 754 755 if (daddr) { 756 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)); 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\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]); 764 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]); 765 766 /* [cgw]: 設置目標網卡硬件地址,即本地網卡和目標網卡硬件地址的最後一個字節的最低有效位 767 * 是相反關系,即本地是\0SNUL0的話,目標就是\0SNUL1,或者本地是\0SNUL1,目標就是\0SNUL0 768 */ 769 eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */ 770 printk("h_dest[ETH_ALEN-1] ^ 0x01 = %02x\n", eth->h_dest[ETH_ALEN-1]); 771 772 printk("hard_header_len = %d\n", 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\n"); 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\"\n", 934 result, snull_devs[i]->name); 935 else 936 ret = 0; 937 938 printk("snull_init_module\n"); 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\n", len); printk("type = %02x\n", type); //ETH_P_IP 0x0800 /* Internet Protocol packet */ /* htons是將整型變量從主機字節順序轉變成網絡字節順序, * 就是整數在地址空間存儲方式變為:高位字節存放在內存的低地址處 */ 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]: 上層應用要發送數據時,通過下層添加硬件地址,才能決定發送到那個目標網卡 */ 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]: 設置目標網卡硬件地址,即本地網卡和目標網卡硬件地址的最後一個字節的最低有效位 * 是相反關系,即本地是\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); }
因為應用層要發送數據包了,所以要為這個數據包添加硬件地址,即以太網地址首部,才能通過網卡發送出去。
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("\n"); 5 6 /* [cgw]: 20字節IP地址首部 */ 7 for (i=14 ; i<34; i++) 8 printk(" %02x",buf[i]&0xff); 9 printk("\n"); 10 11 /* [cgw]: 20字節TCP地址首部 */ 12 for (i=34 ; i<54; i++) 13 printk(" %02x",buf[i]&0xff); 14 printk("\n"); 15 16 /* [cgw]: n字節數據 */ 17 for (i=54 ; i<len; i++) 18 printk(" %02x",buf[i]&0xff); 19 printk("\n");
打印結果:
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 就硬件地址\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 的結構體是:
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網卡驅動。
http://xxxxxx/Linuxjc/1180963.html TechArticle