學習筆記 — LINUX網絡卡驅動框架分析

學習筆記 — LINUX網絡卡驅動框架分析

轉自http://blog.csdn.net/wangshuchangfrank/article/details/22376489

網絡卡的驅動很簡單,就是填充net_device結構體,其應用層到網路協議層核心已經完成了,我們的工作就是填寫這個net_device,然後註冊就可以了。

修正一下:上面第三步應該是:register_netdev

下面程式碼實現一個虛擬網絡卡,這裡沒有實際的網絡卡,只是把發出去的資料模擬遠端回覆過來,把資料轉發到接收快取裡面,也就是迴環操作。

[cpp] view
plain
 copy

  1. /* 
  2. * 參考 drivers\net\cs89x0.c 
  3. */  
  4. #include <linux/module.h>  
  5. #include <linux/errno.h>  
  6. #include <linux/netdevice.h>  
  7. #include <linux/etherdevice.h>  
  8. #include <linux/kernel.h>  
  9. #include <linux/types.h>  
  10. #include <linux/fcntl.h>  
  11. #include <linux/interrupt.h>  
  12. #include <linux/ioport.h>  
  13. #include <linux/in.h>  
  14. #include <linux/skbuff.h>  
  15. #include <linux/slab.h>  
  16. #include <linux/spinlock.h>  
  17. #include <linux/string.h>  
  18. #include <linux/init.h>  
  19. #include <linux/bitops.h>  
  20. #include <linux/delay.h>  
  21. #include <linux/ip.h>  
  22.   
  23. #include <asm/system.h>  
  24. #include <asm/io.h>  
  25. #include <asm/irq.h>  
  26.   
  27. static struct net_device *vnet_dev;  
  28.   
  29. static void emulator_rx_packet(struct sk_buff *skb, struct net_device *dev)   //調換源與目的,完成迴環  
  30. {  
  31. /* 參考LDD3 */  
  32. unsigned char *type;  
  33. struct iphdr *ih;  
  34. __be32 *saddr, *daddr, tmp;  
  35. unsigned char tmp_dev_addr[ETH_ALEN];  
  36. struct ethhdr *ethhdr;  
  37. struct sk_buff *rx_skb;  
  38. // 從硬體讀出/儲存資料  
  39. /* 對調”源/目的”的mac地址 */  
  40. ethhdr = (struct ethhdr *)skb->data;//提取出MAC結構體指標  
  41. memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);//目的送給臨時  
  42. memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);//源送給目的  
  43. memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);//臨時送給源  
  44.   
  45. /* 對調”源/目的”的ip地址 */  
  46. ih = (struct iphdr *)(skb->data   sizeof(struct ethhdr));//提取出ip結構體指標,ip頭在MAC之後  
  47. saddr = &ih->saddr;//源  
  48. daddr = &ih->daddr;//目的  
  49.   
  50. tmp = *saddr;  
  51. *saddr = *daddr;  
  52. *daddr = tmp;  
  53. type = skb->data   sizeof(struct ethhdr)   sizeof(struct iphdr);//提取type  
  54.    
  55. // 修改型別, 原來0x8表示ping  
  56. *type = 0; /* 0表示reply */  
  57. ih->check = 0; /* and rebuild the checksum (ip needs it) */  
  58. ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);  
  59. // 構造一個sk_buff  
  60. rx_skb = dev_alloc_skb(skb->len   2);  
  61. skb_reserve(rx_skb, 2); /* 預留兩個位元組 */   
  62. memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);//關於本行程式碼詳解見註釋2  
  63.   
  64. /* Write metadata, and then pass to the receive level */  
  65. rx_skb->dev = dev;  
  66. rx_skb->protocol = eth_type_trans(rx_skb, dev);  
  67. rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don’t check it */  
  68. dev->stats.rx_packets ;  
  69. dev->stats.rx_bytes  = skb->len;  
  70.   
  71. // 提交sk_buff  
  72. netif_rx(rx_skb);  
  73. }  
  74.   
  75. static int virt_net_send_packet(struct sk_buff *skb, struct net_device *dev)  //傳送函式  
  76. {  
  77. static int cnt = 0;  
  78. printk(“virt_net_send_packet cnt = %d\n”,  cnt);  
  79.   
  80. /* 對於真實的網絡卡, 把skb裡的資料通過網絡卡傳送出去 */  
  81. netif_stop_queue(dev); /* 停止該網絡卡的佇列 */  
  82. /* …… */ /* 把skb的資料寫入網絡卡 */  
  83.   
  84. /* 構造一個假的sk_buff,上報 */  
  85. emulator_rx_packet(skb, dev);  //實現迴環,源與目的對換,把資料發給自己  
  86.   
  87. dev_kfree_skb (skb); /* 釋放skb */  
  88. netif_wake_queue(dev); /* 資料全部傳送出去後,喚醒網絡卡的佇列 */  
  89.   
  90. /* 更新統計資訊 */  
  91. dev->stats.tx_packets ;  
  92. dev->stats.tx_bytes  = skb->len;  
  93. return 0;  
  94. }  
  95.   
  96.   
  97. static int virt_net_init(void)  
  98. {  
  99. /* 1. 分配一個net_device結構體 */  
  100. vnet_dev = alloc_netdev(0, “vnet%d”, ether_setup);; /* alloc_etherdev */  
  101.   
  102. /* 2.設定 */  
  103. vnet_dev->hard_start_xmit = virt_net_send_packet;  
  104.   
  105. /* 設定MAC地址 */  
  106. vnet_dev->dev_addr[0] = 0x08;  
  107. vnet_dev->dev_addr[1] = 0x89;  
  108. vnet_dev->dev_addr[2] = 0x89;  
  109. vnet_dev->dev_addr[3] = 0x89;  
  110. vnet_dev->dev_addr[4] = 0x89;  
  111. vnet_dev->dev_addr[5] = 0x11;  
  112.   
  113. /* 設定下面兩項才能ping通 */  
  114. vnet_dev->flags |= IFF_NOARP;  
  115. vnet_dev->features |= NETIF_F_NO_CSUM;   
  116.   
  117. /* 3. 註冊 */  
  118. register_netdev(vnet_dev);  
  119. return 0;  
  120. }  
  121.   
  122. static void virt_net_exit(void)  
  123. {  
  124. unregister_netdev(vnet_dev);  
  125. free_netdev(vnet_dev);  
  126. }  
  127.   
  128. module_init(virt_net_init);  
  129. module_exit(virt_net_exit);  
  130. MODULE_LICENSE(“GPL”);  

上面程式碼裡面主要工作在於實現迴環,把傳送的資料轉給接收,再上報。資料放在net_device裡面的data,那麼這段資料的格式是什麼?

MAC頭:

[cpp] view
plain
 copy

  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));  

IP頭:

[cpp] view
plain
 copy

  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;//源ip地址  
  19. __be32 daddr;//目的ip地址  
  20. /*The options start here. */  
  21. };  

上面程式碼編譯,insmod做實驗:

ifconfig vnet0 3.3.3.3 up

ping 3.3.3.3 :發現可以ping通

ping 3.3.3.4 :發現也可以ping通,而且收包狀態和發包狀態都有變化

綜合上面可以知道,網路驅動核心就是兩個函式:hard_start_xmit和netifrx

這兩個函式裡面需要操作實際網絡卡傳送和接收資料,一般網路晶片都會提供驅動程式,我們的核心工作就是移植這個驅動。