学习笔记 --- LINUX网卡驱动框架分析

来源:互联网 发布:淘宝店铺可以转吗 编辑:程序博客网 时间:2024/05/22 04:25

转自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

这两个函数里面需要操作实际网卡发送和接收数据,一般网络芯片都会提供驱动程序,我们的核心工作就是移植这个驱动。



原创粉丝点击