Linux 数据链路层接收的以太帧传递给网络层的过程分析

这篇小文是个阶段记录,如有错误,请给位大虾指出...

 

(14字节)以太网首部 = (6字节)目的MAC地址 + (6字节)源MAC地址 + (两字节)帧类型值,

帧类型值:如IPv4为0x0800,ARP为0x0806等。

 

链路层(网卡驱动macb_rx_frame())接收到以太网帧后,通过接口函数netif_receive_skb()(netif_rx实际最后也是调用netif_receive_skb)交到网络层,而这个接口函数就完成对帧类型的区分,再交到不同的协议处理程序。

 

数据结构:

 

       每种协议都要定义一个packet_type结构,引导进入相关的协议数据处理函数,所有节点组成一个链表(HASH链表)。

/* include/linux/netdevice.h */

struct packet_type {

        __be16 type; /* This is really htons(ether_type). */

        struct net_device *dev; /* NULL is wildcarded here */

        int (*func) (struct sk_buff *,

        struct net_device *,

        struct packet_type *,

        struct net_device *);

        void *af_packet_priv;

        struct list_head list;

        };

 

参数说明:
type:以太帧类型,16位。
dev:所附着的网卡设备,如果为NULL则匹配全部网卡。
func:协议入口接收处理函数。
af_packet_priv:协议私有数据。
list:链表扣。

 


一般各协议的packet_type结构都是静态存在,初始化时只提供type和func两个参数就可以了,每个协议在初始化时都要将此结构加入到系统类型链表中。

/* net/core/dev.c */

1.添加节点:void dev_add_pack(struct packet_type *pt);

2.删除节点:void __dev_remove_pack(struct packet_type *pt) ;

例如:/* net/ipv4/af_inet.c */

/*
 * IP protocol layer initialiser
 */

static struct packet_type ip_packet_type = {
 .type = __constant_htons(ETH_P_IP),
 .func = ip_rcv,
 .gso_send_check = inet_gso_send_check,
 .gso_segment = inet_gso_segment,
};

 

static int __init inet_init(void)
{

 struct sk_buff *dummy_skb;
 struct inet_protosw *q;
 struct list_head *r;
 int rc = -EINVAL;

...

 dev_add_pack(&ip_packet_type);

...

}

由于IP协议部分不能作为内核模块,所以是没有卸载函数的,没必要调用dev_remove_pack()函数。

 

 

接收流程:

通过链表挂接方式,Linux内核可以很容易的添加各种协议的接收处理函数。

 

 

/* net/core/dev.c */

int netif_receive_skb(struct sk_buff *skb)
{

struct packet_type *ptype, *pt_prev;

......

 

// 先查处理所有以太类型的链表各节点

list_for_each_entry_rcu(ptype, &ptype_all, list)

{

   if (!ptype->dev || ptype->dev == skb->dev)

   {

      if (pt_prev)

      ret = deliver_skb(skb, pt_prev, orig_dev);

      pt_prev = ptype;

   }

}

......

 

type = skb->protocol;

// 再查指定协议的HASH链表

list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list)

{

   if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev))

   {

       if (pt_prev)

       ret = deliver_skb(skb, pt_prev, orig_dev);

       pt_prev = ptype;

   }

}

......

}

 网卡驱动--->netif_rx()--->netif_receive_skb()->deliver_skb()->packet_type.func ()

你可能感兴趣的:(Linux 数据链路层接收的以太帧传递给网络层的过程分析)