当前位置:   article > 正文

链路层到网络层的数据传递

数据如何从数据链路层传向网络层
我们知道在tcp/ip模型中,基本每一层都可以处理多重协议类型,那么当一个输入帧到达后,内核的每一层是如何来取得相应的处理函数呢?也就是说当我要把包传递给上层的时候,如何取得相应协议的处理函数。


我们这里先来看从二层如何把把数据传递给三层。
struct sk_buff {....................................	__be16			protocol;.....................................};


在sk_buff中的protocol字段能够表示输入帧的3层的协议,或者输入帧的mac头。在内核里面,是在函数netif_receive_skb中,通过protocol域来决定在三层的协议,以及处理函数。

如果内核没有找到protocol所对应协议的处理函数,那么这个帧将会被丢掉。而且一个packet也有可能被分发到多个handler,举个例子,当packet sniffer运行的时候,这里的protocol域有时就会为ETH_P_ALL.此时就会放所有的包进入下一层。不过使用ETH_P_ALL一般只是为了监测网络设备或者debug。比如tcpdump。

来看下netif_receive_skb的代码片段:


type = skb->protocol;	list_for_each_entry_rcu(ptype,			&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {		if (ptype->type == type &&		    (ptype->dev == null_or_orig || ptype->dev == skb->dev ||		     ptype->dev == orig_dev)) {			if (pt_prev)				ret = deliver_skb(skb, pt_prev, orig_dev);			pt_prev = ptype;		}	}static inline int deliver_skb(struct sk_buff *skb,			      struct packet_type *pt_prev,			      struct net_device *orig_dev){	atomic_inc(&skb->users);	return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);}



我们可以看到最终会通过判断protocol来决定调用哪一个ptype,并调用相应的虚函数func,接下来我们就来看packet_type这个结构体,也就是所有的协议handler结构。

首先来看它的整体结构:


[img]/upload/attachment/106799/e7e70376-8b32-30d6-8750-34a5aa956e31.jpg[/img]


static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;

这里有一个全局的ptype_base,他是一个hash链表。它表示所有的协议handler结构。当通过dev_add_pack注册协议的时候就插入到相应的hashcode的相应链表。

static struct list_head ptype_all __read_mostly;	/* Taps */


ptype_all也是一个全局链表,可以看到他完全是和ptype_base独立的。他就表示了ETH_P_ALL协议。

接下来就来看packet_type的结构
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 *);	struct sk_buff		*(*gso_segment)(struct sk_buff *skb,						int features);	int			(*gso_send_check)(struct sk_buff *skb);	void			*af_packet_priv;	struct list_head	list;};


type表示了协议的类型,dev表示了那个设备的协议类型,如果是NULL,则表示是所有设备,比如tcpdum就能通过指定设备名来监测某个指定设备。func对应协议的处理函数。af_pack_priv,被PF_PACKET类型的socket所使用(具体看unix网络编程). list链表(也就是hash链表中,相同的桶所处的链表). 中间的两个gso开头的函数,可以自己去看下gso的相关资料。

来看下ip协议的注册,以及初始化:

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){。。。。。。。。。。。。。。。。。。。。	dev_add_pack(&ip_packet_type);.........................................}


可以看到在初始化函数中,调用dev_add_pack来上面定义的ip_packet_type加入到全局的hash链表中。

void dev_add_pack(struct packet_type *pt){	int hash;	spin_lock_bh(&ptype_lock);///判断类型是否为ETH_P_ALL	if (pt->type == htons(ETH_P_ALL))		list_add_rcu(&pt->list, &ptype_all);	else {///计算hash值		hash = ntohs(pt->type) & PTYPE_HASH_MASK;///插入到相应的hash表位置		list_add_rcu(&pt->list, &ptype_base[hash]);	}	spin_unlock_bh(&ptype_lock);}



而链路层得到相应的输入包的协议类型是通过eth_type_trans来实现的,可以随便看一下驱动的代码,当驱动调用netif_rx之前,都会先调用这个函数来得到protocol值。

先来看下不同的帧类型的区别:

[img]/upload/attachment/106821/d88a4e2a-86c6-3702-aa27-09384c8ebc2e.jpg[/img]


eth_type_trans有两个作用,一个是设置packet type,一个是设置协议类型。
其中packet type也就是skb->pkt_type字段,表示了链路层的数据类型,他可以为下面几种类型:

#define PACKET_HOST		0		/* To us		*/#define PACKET_BROADCAST	1		/* To all		*/#define PACKET_MULTICAST	2		/* To group		*/#define PACKET_OTHERHOST	3		/* To someone else 	*/#define PACKET_OUTGOING		4		/* Outgoing of any type */


这里要注意的是PACKET_OTHERHOST类型,它表示这个帧不属于这个接收接口,可是他并不会立即被扔掉,当传递给高层的时候。它主要用来protocol sniffer.

/* *	This is an Ethernet frame header. *////帧头的表示。struct ethhdr {	unsigned char	h_dest[ETH_ALEN];	/* destination eth addr	*/	unsigned char	h_source[ETH_ALEN];	/* source ether addr	*/	__be16		h_proto;		/* packet type ID field	*/} __attribute__((packed));__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev){	struct ethhdr *eth;	unsigned char *rawp;///一些初始化	skb->dev = dev;	skb_reset_mac_header(skb);	skb_pull(skb, ETH_HLEN);	eth = eth_hdr(skb);	if (is_multicast_ether_addr(eth->h_dest)) {///判断是否为广播。		if (!compare_ether_addr(eth->h_dest, dev->broadcast))///数据位广播			skb->pkt_type = PACKET_BROADCAST;		else///多播			skb->pkt_type = PACKET_MULTICAST;	}	else if (1 /*dev->flags&IFF_PROMISC */ ) {///主要用来网络的监测。		if (unlikely(compare_ether_addr(eth->h_dest, dev->dev_addr)))			skb->pkt_type = PACKET_OTHERHOST;	}///通过上面的图我们知道大于1536为ethernet 类型的帧,因此直接返回h_proto。	if (ntohs(eth->h_proto) >= 1536)		return eth->h_proto;	rawp = skb->data;///取数据位来判断是否是802.3的帧。	if (*(unsigned short *)rawp == 0xFFFF)		return htons(ETH_P_802_3);	/*	 *      Real 802.2 LLC	 */	return htons(ETH_P_802_2);}
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/562084
推荐阅读
相关标签
  

闽ICP备14008679号