深入理解Linux网络技术内幕——协议处理函数

网络帧在进入网络层时,需要区分不同的网络协议进行处理,这就需要涉及协议处理函数。

首先我们从驱动接收到一个数据帧,分析数据帧在协议栈中自下而上的传输流程。

设备驱动程序在接收到一个数据帧时,会将其保存在一个sk_buff缓冲区数据结构,并对其进行初始化。

struct sk_buff {
......
	__be16          protocol:16;  
......
}
在这个缓冲区结构体中,有一个protocol字段,用于标识网络层的协议。
我们知道网络帧在设备驱动程序中处理后,设备驱动程序会调用netif_receive_skb用以将数据帧向协议栈上层传输(网络层),netif_receive_skb这是就会查询sk_buff结构体中的protocol,识别网络帧的网络层协议,然后决定网络帧接下来的处理函数。(如protocol为ETH_P_IP,则调用ip_rcv函数。)

protocol的定义在include/linux/if_ether.h中,如下所示:

#define ETH_P_802_3 0x0001      /* Dummy type for 802.3 frames  */
#define ETH_P_AX25  0x0002      /* Dummy protocol id for AX.25  */
#define ETH_P_ALL   0x0003      /* Every packet (be careful!!!) */
                        /*ETH_P_ALL不是真正的协议,而是作为通配符,主要用于sniff嗅探器等*/                                      
#define ETH_P_802_2 0x0004      /* 802.2 frames         */
#define ETH_P_SNAP  0x0005      /* Internal only        */
#define ETH_P_DDCMP     0x0006          /* DEC DDCMP: Internal only     */
#define ETH_P_WAN_PPP   0x0007          /* Dummy type for WAN PPP frames*/
#define ETH_P_PPP_MP    0x0008          /* Dummy type for PPP MP frames */
......


协议处理函数的组织

在内核中,每种协议都有结构体packet_tpye描述:
struct packet_type {
    __be16  type;   /* This is really htons(ether_type). */ //二层协议类型,ETH_P_IP、ETH_P_ARP等等
    struct net_device   *dev;   /* NULL is wildcarded here       */
    //钩子函数了,如 ip_rcv()、arp_rcv()等等
    int         (*func) (struct sk_buff *,
                     struct net_device *,
                     struct packet_type *,
                     struct net_device *);
    struct sk_buff      *(*gso_segment)(struct sk_buff *skb,
                        int features);
    int         (*gso_send_check)(struct sk_buff *skb);
    struct sk_buff      **(*gro_receive)(struct sk_buff **head,
                           struct sk_buff *skb);
    int         (*gro_complete)(struct sk_buff *skb);
    void            *af_packet_priv;
    struct list_head    list;
};
其中成员func即为各个协议的钩子函数(协议处理函数).
为了加快方便速度,大部分协议通过哈希表来组织,十六个列表组织成一个数组ptype_base[16]. 
而ETH_P_ALL类的协议则被组织到全局变量ptype_all中.

协议处理函数的注册

各种协议通过dev_add_pack注册.

//注册协议:把packet_type结构挂在与type对应的list_head上面                                                                               
void dev_add_pack(struct packet_type *pt)
{
    int hash;                 

    spin_lock_bh(&ptype_lock);
    if (pt->type == htons(ETH_P_ALL))  //type为ETH_P_ALL时,挂在ptype_all上面
        list_add_rcu(&pt->list, &ptype_all);
    else {  //否则,挂在ptype_base[type&15]上面
        hash = ntohs(pt->type) & PTYPE_HASH_MASK;
        list_add_rcu(&pt->list, &ptype_base[hash]);
    }  
    spin_unlock_bh(&ptype_lock);   
}
EXPORT_SYMBOL(dev_add_pack);  

接下来我们以ipv4为例,可靠具体的协议栈函数是如何组织的.(我们需要进入ipv4模块的初始化分析)

static struct packet_type ip_packet_type __read_mostly = {
    .type = cpu_to_be16(ETH_P_IP),
    .func = ip_rcv,   //ipv4 的协议处理函数 ,在netif_receive_skb会使用  
    .gso_send_check = inet_gso_send_check,
    .gso_segment = inet_gso_segment,
    .gro_receive = inet_gro_receive,
    .gro_complete = inet_gro_complete,
};     


static int __init inet_init(void)
{
	......
    dev_add_pack(&ip_packet_type);
	......                                                                                                                        
}

由此,网络帧,经过网络驱动程序处理,通过netif_receive_skb,最终转入具体的协议处理函数进行处理.







郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。