数据包接收系列 — 上半部实现(网卡驱动)

来源:互联网 发布:电子数据调查分析师 编辑:程序博客网 时间:2024/05/25 21:32

本文主要内容:网络数据包接收的上半部实现,主要分析网卡驱动相关部分。

内核版本:2.6.37

Author:zhangskd @ csdn blog

 

网卡概述

 

(1) 网卡收包

网线上的物理帧首先被网卡芯片获取,网卡芯片会检查物理帧的CRC,保证完整性。

然后网卡芯片将物理帧头去掉,得到MAC包。

网卡芯片会检查MAC包内的目的MAC地址,如果和本网卡的MAC地址不一样则丢弃(混杂模式除外)。

之后网卡芯片将MAC帧拷贝到网卡内部的缓冲区,触发硬中断。

网卡的驱动程序通过硬中断处理函数,构建sk_buff,把它拷贝到内存中,接下来交给内核处理。

在这个过程中,网卡芯片对物理帧进行了MAC匹配过滤,以减小系统负荷。

 

(2) 网卡发包

网卡驱动程序将IP包添加14字节的MAC头,构成MAC包。

MAC包中含有发送端和接收端的MAC地址,由于是驱动程序创建MAC头,所以可以随便输入地址

进行主机伪装。

驱动程序将MAC包拷贝到网卡芯片内部的缓冲区,接下来由网卡芯片处理。

网卡芯片将MAC包再次封装为物理帧,添加头部同步信息和CRC校验,然后丢到网线上,就完成

一个IP报的发送了,所有接到网线上的网卡都可以看到该物理帧。

 

(3) 网卡数据结构

网卡用net_device来表示,用register_netdevice()注册到系统中,注册过的网卡可以通过

unregister_netdevice()注销掉。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. struct net_device {  
  2.     char name[IFNAME]; /* 网卡名称,如eth0 */  
  3.     ...  
  4.     unsigned long mem_end; /* shared mem end,共享内存结束地址 */  
  5.     unsigned long mem_start; /* shared mem start ,共享内存起始地址 */  
  6.     unsigned long base_addr; /* device I/O address,设备内存映射到I/O内存的起始地址 */  
  7.     unsigned int irq; /* device IRQ number,硬中断编号 */  
  8.     ...  
  9.     unsigned long state; /* 网卡的状态 */  
  10.     ...  
  11.     struct list_head dev_list;  
  12.     struct list_head napi_list; /* NAPI使用 */  
  13.     ...  
  14.     unsigned long features; /* 网卡功能标识 */  
  15.     ...  
  16.     int ifindex; /* Interface index. Unique device identifier. 设备ID */  
  17.     ...  
  18.     struct net_device_stats stats; /* 统计变量 */  
  19.     /* dropped packets by core network. 
  20.      * Do not use this in drivers. 
  21.      * 被内核丢弃的数据包个数。 
  22.      */  
  23.     atomic_long_t rx_dropped;  
  24.     ...  
  25.     /* Management operations */  
  26.     const struct net_device_ops *netdev_ops; /* 网卡的操作函数集 */  
  27.     const struct ethtool_ops *ethtool_ops; /* 配置网卡 */  
  28.     ...  
  29.     unsigned int promiscuity; /* 混杂模式计数器 */  
  30.     ...  
  31.     struct netdev_queue *ingress_queue;  
  32.     struct netdev_queue *_tx;  
  33.     ...  
  34. };  
  35.   
  36. /* 网卡的操作函数集 */  
  37. struct net_device_ops {  
  38.     int (*ndo_init) (struct net_device *dev);  
  39.     void (*ndo_uninit) (struct net_device *dev);  
  40.     int (*ndo_open) (struct net_device *dev);  
  41.     int (*ndo_stop) (struct net_device *dev);  
  42.     /* Called when a packet needs to be transmitted */  
  43.     netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev);  
  44.     ...  
  45. };  

 

(4) 网卡中断处理函数

产生中断的每个设备都有一个相应的中断处理程序,是设备驱动程序的一部分。

每个网卡都有一个中断处理程序,用于通知网卡该中断已经被接收了,以及把网卡缓冲区的

数据包拷贝到内存中。

当网卡接收来自网络的数据包时,需要通知内核数据包到了。网卡立即发出中断:嗨,内核,

我这里有最新的数据包了。内核通过执行网卡已注册的中断处理函数来做出应答。

中断处理程序开始执行,通知硬件,拷贝最新的网络数据包到内存,然后读取网卡更多的数据包。

这些都是重要、紧迫而又与硬件相关的工作。内核通常需要快速的拷贝网络数据包到系统内存,

因为网卡上接收网络数据包的缓存大小固定,而且相比系统内存也要小得多。所以上述拷贝动作

一旦被延迟,必然造成网卡缓存溢出 - 进入的数据包占满了网卡的缓存,后续的包只能被丢弃。

当网络数据包被拷贝到系统内存后,中断的任务算是完成了,这时它把控制权交还给被系统中断

前运行的程序。处理和操作数据包的其他工作在随后的下半部中进行。

 

上半部的实现

 

接收数据包的上半部处理流程为:

el_interrupt() // 网卡驱动

    |--> el_receive() // 网卡驱动

                |--> netif_rx() // 内核接口

                           |--> enqueue_to_backlog() // 内核接口

 

这里以3c501网卡驱动为例来进行分析(这是个古董级网卡,实现简单:)

 

el_interrupt

 

3c501的网卡中断处理函数为el_interrupt(),调用inb()来获取当前中断处理结果,如果是RX_GOOD,

表明网卡成功接收了数据包,则调用el_receive()来进行接收处理。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * el_interrupt: 
  3.  * @irq: Interrupt number 
  4.  * @dev_id: The 3c501 that burped 
  5.  */  
  6.   
  7. static irqreturn_t el_interrupt(int irq, void *dev_id)  
  8. {  
  9.     struct net_device *dev = dev_id;  
  10.     struct net_local *lp;  
  11.     int ioaddr;  
  12.     int axsr; /* Aux. status reg. */  
  13.   
  14.     ioaddr = dev->base_addr; /* I/O映射地址 */  
  15.     lp = netdev_priv(dev); /* 网卡的私有数据 */  
  16.     spin_lock(&lp->lock); /* 上锁 */  
  17.   
  18.     /* What happened? */  
  19.     axsr = inb(AX_STATUS);  
  20.   
  21.     /* log it */  
  22.     if (el_debug > 3)  
  23.         pr_debug("%s: el_interrupt() aux = %#02x\n", dev->name, axsr);  
  24.     if (lp->loading == 1 && ! lp->txing)  
  25.         pr_warning("%s: Inconsistent state loading while not in tx\n", dev->name);  
  26.   
  27.     if (lp->txing) { /* 处于发送模式,这里不研究 */  
  28.         /* Board in transmit mode. */  
  29.         ...  
  30.   
  31.     } else {  
  32.         /* In receive mode. 处于接收模式 */  
  33.         int rxsr = inb(RX_STATUS); /* 获取中断处理的结果 */  
  34.         if (el_debug > 5)  
  35.             pr_debug("%s: rxsr=%02x txsr=%02x rp=%04x\n", dev->name, rxsr,  
  36.                 inb(TX_STATUS), inw(RX_LOW));  
  37.   
  38.         /* Just reading rx_status fixes most errors. */  
  39.         if (rxsr & RX_MISSED) /* 没有接收到数据包 */  
  40.             dev->stats.rx_missed_errors++;  
  41.         else if (rxsr & RX_RUNT) { /* 数据包长度错误 */  
  42.             /* Handled to avoid board lock-up. */  
  43.             dev->stats.rx_length_errors++;  
  44.             if (el_debug > 5)  
  45.                 pr_debug("%s: runt.\n", dev->name);  
  46.   
  47.         } else if (rxsr & RX_GOOD) {  
  48.             /* Receive worked, 成功接收数据包 */  
  49.             el_receive(dev); /* 接收函数 */  
  50.   
  51.         } else {  
  52.             /* Nothing? Something is broken! */  
  53.             if (el_debug > 2)  
  54.                 pr_debug("%s: No packet seen, rxsr=%02x **resetting 3c501***\n", dev->name, rxsr);  
  55.   
  56.             el_reset(dev); /* 网卡出错,重置 */  
  57.         }  
  58.     }  
  59.   
  60.     /* Move into receive mode */  
  61.     outb(AX_RX, AX_CMD);  
  62.     outw(0x00, RX_BUF_CLR);  
  63.     inb(RX_STATUS);  
  64.     inb(TX_STATUS);  
  65.     spin_unlock(&lp->lock);  
  66. out:  
  67.     return IRQ_HANDLED;  
  68. }  

 

网卡的私有数据

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* Board-specific info in netdev_priv(dev). */  
  2. struct net_local {  
  3.     int tx_pkt_start; /* The length of the current Tx packet. */  
  4.     int collisions; /* Tx collisions this packet */  
  5.     int loading; /* Spot buffer load collisions */  
  6.     int txing; /* True if card is in TX mode */  
  7.     spinlock_t lock; /* Serializing lock */  
  8. };  
  9.   
  10. /** 
  11.  * netdev_priv - access network device private data 
  12.  * @dev: network device 
  13.  * Get network device private data 
  14.  */  
  15. static inline void *netdev_priv(const struct net_device *dev)  
  16. {  
  17.     return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);  
  18. }  

 

el_receive

 

el_receive()首先申请一个sk_buff,然后把网卡缓冲区中的数据包拷贝到skb->data。

调用eth_type_trans()来判断数据包使用的三层协议(skb->protocol)、数据包的类型(skb->pkt_type)。

最后调用内核入口函数 — netif_rx()继续处理。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * el_receive: 
  3.  * @dev: Device to pull the packets from 
  4.  * 
  5.  * We have a good packet. Well, not really "good", just mostly not broken. 
  6.  * We must check everything to see if it is good. In particular we occasionally 
  7.  * get wild packet sizes from the card. If the packet seems sane we PIO it off 
  8.  * the card and queue it for the protocol layers. 
  9.  */   
  10.   
  11. static void el_receive(struct net_device *dev)  
  12. {  
  13.     int ioaddr = dev->base_addr;  
  14.     int pkt_len;  
  15.     struct sk_buff *skb;  
  16.   
  17.     pkt_len = inw(RX_LOW);  
  18.     if (el_debug > 4)  
  19.         pr_debug("el_receive %d.\n", pkt_len);  
  20.   
  21.     if (pkt_len < 60 || pkt_len > 1536) { /* 数据包长度错误 */  
  22.         if (el_debug)  
  23.             pr_debug("%s: bogus packet, length=%d\n", dev->name, pkt_len);  
  24.         dev->stats.rx_over_errors++;  
  25.         return;  
  26.     }  
  27.   
  28.     /* Command mode so we can empty the buffer */  
  29.     outb(AX_SYS, AX_CMD);  
  30.   
  31.     skb = dev_alloc_skb(pkt_len + 2); /* 申请一个skb,2字节用于对齐IP */  
  32.   
  33.     /* Start of frame */  
  34.     outw(0x00, GP_LOW);  
  35.   
  36.     if (skb == NULL) {  
  37.         pr_info("%s: Memory squeeze, dropping packet.\n", dev->name);  
  38.         dev->stats.rx_dropped++;  
  39.         return;  
  40.   
  41.     } else {  
  42.         /* 因为mac头是14字节,预留2字节可使IP以16字节对齐 */  
  43.         skb_reserve(skb, 2);   
  44.   
  45.         /* 扩展skb的data room,并把网卡缓存的数据包拷贝到skb->data中 */  
  46.         insb(DATAPORT, skb_put(skb, pkt_len), pkt_len);  
  47.   
  48.         /* 使用哪种三层协议,一般是IP协议 */  
  49.         skb->protocol = eth_type_trans(skb, dev);  
  50.   
  51.         netif_rx(skb); /* 旧的接收处理函数 */  
  52.   
  53.         dev->stats.rx_packets++;  
  54.         dev->stats.rx_bytes += pkt_len;  
  55.     }  
  56.   
  57. }  

 

以太网协议头

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #define ETH_ALEN 6 /* Octets in one ethernet addr */  
  2. #define ETH_HLEN 14 /* Total octects in header */  
  3.   
  4. struct ethhdr {  
  5.     unsigned char h_dest[ETH_ALEN]; /* destination eth addr */  
  6.     unsigned char h_source[ETH_ALEN]; /* source ether addr */  
  7.     unsigned short h_proto; /* packet type ID field */  
  8. };  

eth_type_trans()进行二层协议的简单处理,主要是判断数据包的类型(skb->pkt_type)

是单播/广播/多播,返回三层协议类型(skb->protocol),一般是IP协议。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * eth_type_trans - determine the packet's protocol ID. 
  3.  * @skb: received socket data 
  4.  * @dev: receiving network device 
  5.  * The rule here is that we assume 802.3 if the type field is short enough to be a length. 
  6.  * This is normal practice and works for any now in use protocol. 
  7.  */  
  8.   
  9. __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)  
  10. {  
  11.     struct ethhdr *eth;  
  12.     skb->dev = dev; /* 记录接收网卡设备 */  
  13.     skb_reset_mac_header(skb); /* 赋值skb->mac_header */  
  14.   
  15.     /* skb->data指向三层协议头,skb->len -= 14 */  
  16.     skb_pull_inline(skb, ETH_HLEN);  
  17.     eth = eth_hdr(skb);  
  18.   
  19.     /* 记录数据包类型skb->pkt_type */    
  20.     if (unlikely(is_multicast_ether_addr(eth->h_dest))) { /* 是否为多播 */  
  21.         if (! compare_ether_addr_64bits(eth->h_dest, dev->broadcast)) /* 是否为广播 */  
  22.             skb->pkt_type = PACKET_BROADCAST;  
  23.         else  
  24.             skb->pkt_type = PACKET_MULTICAST;  
  25.   
  26.     } else if (unlikely(compare_ether_addr_64bits(eth->h_dest, dev->dev_addr)))  
  27.         skb->pkt_type = PACKET_OTHERHOST; /* 其它网卡的 */  
  28.   
  29.     /* 判断使用的三层协议 */  
  30.     if (netdev_uses_dsa_tags(dev))  
  31.         return htons(ETH_P_DSA);  
  32.     if (netdev_uses_trailer_tags(dev))  
  33.         return htons(ETH_P_TRAILER);  
  34.   
  35.     if (ntohs(eth->h_proto) >= 1536/* 大于1536即为协议ID */  
  36.         return eth->h_proto;  
  37.   
  38.     if (skb->len >= 2 && *(unsigned short *)(skb->data) == 0xFFFF)  
  39.         return htons(ETH_P_802_3);  
  40.     return htons(ETH_P_802_2);  
0 0
原创粉丝点击