linux内核协议栈 三 / 四层协议接收数据处理函数以及相关的全局 hash表 / 数组

本文详细介绍了Linux内核中,从二层到四层协议接收数据的处理流程。从netif_receive_skb()开始,经过三层协议处理,包括packet_type结构、ptype_base全局哈希表和dev_add_pack(),再到四层协议处理,如net_protocol结构、inet_protos全局数组以及inet_add_protocol()。最后,通过ip_local_deliver_finish()函数完成四层协议的分流,如UDP、TCP、ICMP等。
摘要由CSDN通过智能技术生成

目录

前言 

1 二层数据包处理 netif_receive_skb()

2 三层数据接收处理

2.1 三层数据包协议处理数据结构 struct packet_type

2.2 三层协议栈 全局 hash 表  struct list_head ptype_base

2.3 三层协议处理添加 dev_add_pack()

3 四层数据接收处理

3.1 四层数据包协议处理数据结构 struct net_protocol

3.2 四层协议栈 全局数组  struct net_protocol *inet_protos[MAX_INET_PROTOS]

3.3 四层协议处理添加 inet_add_protocol()

3.4 ipv4中的四层协议分流 ip_local_deliver_finish()


前言 

在底层接收到数据后,通过底层网卡驱动处理完成后,会调用函数netif_receive_skb()进行二层mac子层进行处理,对于需要本机处理的三层数据包,是如何调用各三层处理函数的呢?

对于ip子层处理完以后,需要发送到本地上层继续处理的数据包,又是如何调用相应四层处理函数的呢?

1 二层数据包处理 netif_receive_skb()

将三层协议 packet_type 添加到hash链表 ptype_base[PTYPE_HASH_SIZE],就是调用链表的处理函数,还是比较简单的。遍历hash链表ptype_base,可以使用list_for_each_entry_rcu实现,具体参见 __netif_receive_skb_core()函数实现。

int netif_receive_skb(struct sk_buff *skb)
{
	...
	return __netif_receive_skb(skb);
}
EXPORT_SYMBOL(netif_receive_skb);

static int __netif_receive_skb(struct sk_buff *skb)
{
	int ret;

	if (sk_memalloc_socks() && skb_pfmemalloc(skb)) {
		unsigned long pflags = current->flags;
		...
		current->flags |= PF_MEMALLOC;
		ret = __netif_receive_skb_core(skb, true);
		tsk_restore_flags(current, pflags, PF_MEMALLOC);
	} else
		ret = __netif_receive_skb_core(skb, false);

	return ret;
}

static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
	struct packet_type *ptype, *pt_prev;
	rx_handler_func_t *rx_handler;
	struct net_device *orig_dev;
	struct net_device *null_or_dev;
	bool deliver_exact = false;
	int ret = NET_RX_DROP;
	__be16 type;

	...
	if (vlan_tx_nonzero_tag_present(skb))
		skb->pkt_type = PACKET_OTHERHOST;

	/* deliver only exact match when indicated */
	null_or_dev = deliver_exact ? skb->dev : NULL;

	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_dev || ptype->dev == skb->dev ||
		     ptype->dev == orig_dev)) {
			if (pt_prev)
				ret = deliver_skb(skb, pt_prev, orig_dev);
			pt_prev = ptype;
		}
	}

	if (pt_prev) {
		if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
			goto drop;
		else
			ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);//三层协议在此处分流
	} else {
drop:
		atomic_long_inc(&skb->dev->rx_dropped);
		kfree_skb(skb);
		/* Jamal, now you will not able to escape explaining
		 * me how you were going to use this. :-)
		 */
		ret = NET_RX_DROP;
	}

unlock:
	rcu_read_unlock();
out:
	return ret;
}

2 三层数据接收处理

 三层的协议有arp、ipv4、ipv6、ipx等,我比较熟悉且用到的是arp、ip这两个协议的数据包接收处理函数。

2.1 三层数据包协议处理数据结构 struct packet_type

struct packet_type {
	/*三层协议类型*/
	__be16			type;	/* This is really htons(ether_type). */
	
	 /* 与该协议相关联的设备,可以对不同的设备绑定不同的三层协议,
	    我们一般将该值设置为NULL,即匹配所有设备*/
	struct net_device	*dev;	/* NULL is wildcarded here	     */
	
	/*协议接收处理函数,处理接收的三层数据包的协议处理函数*/
	int			(*func) (struct sk_buff *,
					 struct net_device *,
					 struct packet_type *,
					 struct net_device *);
	bool			(*id_match)(struct packet_type *ptype,
					    struct sock *sk);
						
	/*用于PF_SOCKET类型的socket。它指向相关的sock数据结构*/
	void			*af_packet_priv;
	
	/*将该三层协议处理结构添加到三层协议处理链表ptype_base 中*/
	struct list_head	list;
};

2.2 三层协议栈 全局 hash 表  struct list_head ptype_base

#define PTYPE_HASH_SIZE	(16)
#define PTYPE_HASH_MASK	(PTYPE_HASH_SIZE - 1)

static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;

/*
 *	These are the defined Ethernet Protocol ID's.
 */

#define ETH_P_LOOP	0x0060		/* Ethernet Loopback packet	*/
#define ETH_P_PUP	0x0200		/* Xerox PUP packet		*/
#define ETH_P_PUPAT	0x0201		/* Xerox PUP Addr Trans packet	*/
#define ETH_P_IP	0x0800		/* Internet Protocol packet	*/
#define ETH_P_X25	0x0805		/* CCITT X.25			*/
#define ETH_P_ARP	0x0806		/* Address Resolution packet	*/
#define	ETH_P_BPQ	0x08FF		/* G8BPQ AX.25 Ethernet Packet	[ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_IEEEPUP	0x0a00		/* Xerox IEEE802.3 PUP packet */
#define ETH_P_IEEEPUPAT	0x0a01		/* Xerox IEEE802.3 PUP Addr Trans packet */
#define ETH_P_BATMAN	0x4305		/* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_DEC       0x6000          /* DEC Assigned proto           */
#define ETH_P_DNA_DL    0x6001          /* DEC DNA Dump/Load            */
#define ETH_P_DNA_RC    0x6002          /* DEC DNA Remote Console       */
#define ETH_P_DNA_RT    0x6003          /* DEC DNA Routing              */
#define ETH_P_LAT       0x6004          /* DEC LAT                      */
#define ETH_P_DIAG      0x6005          /* DEC Diagnostics              */
#define ETH_P_CUST      0x6006          /* DEC Customer use             */
#define ETH_P_SCA       0x6007          /* DEC Systems Comms Arch       */
#define ETH_P_TEB	0x6558		/* Trans Ether Bridging		*/
#define ETH_P_RARP      0x8035		/* Reverse Addr Res packet	*/
#define ETH_P_ATALK	0x809B		/* Appletalk DDP		*/
#define ETH_P_AARP	0x80F3		/* Appletalk AARP		*/
#define ETH_P_8021Q	0x8100          /* 802.1Q VLAN Extended Header  */
#define ETH_P_IPX	0x8137		/* IPX over DIX			*/
#define ETH_P_IPV6	0x86DD		/* IPv6 over bluebook		*/
#define ETH_P_PAUSE	0x8808		/* IEEE Pause frames. See 802.3 31B */
#define ETH_P_SLOW	0x8809		/* Slow Protocol. See 802.3ad 43B */
#define ETH_P_WCCP	0x883E		/* Web-cache coordination protocol
...

2.3 三层协议处理添加 dev_add_pack()

相对来说,使用一个链表,对于需要发送到本机的三层数据包,遍历链表ptype_base,找到与数据包的三层协议对应的三层协议packet_type,然后就调用packet_type->(*func),由相应的三层处理函数进行后续处理。对于ipv4,即为ip_rcv;对于arp即为arp_rcv;对于ipv6,即为ipv6_rcv。


static inline struct list_head *ptype_head(const struct packet_type *pt)
{
	if (pt->type == htons(ETH_P_ALL))
		return &ptype_all;
	else
		return &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK];
}

void dev_add_pack(struct packet_type *pt)
{
	struct list_head *head = ptype_head(pt);

	spin_lock(&ptype_lock);
	list_add_rcu(&pt->list, head);
	spin_unlock(&ptype_lock);
}
EXPORT_SYMBOL(dev_add_pack);

==========================================================================
static struct packet_type ip_packet_type __read_mostly = {
	.type = cpu_to_be16(ETH_P_IP),
	.func = ip_rcv,
};

static struct packet_type ipv6_packet_type __read_mostly = {
	.type = cpu_to_be16(ETH_P_IPV6),
	.func = ipv6_rcv,
};

static struct packet_type arp_packet_type __read_mostly = {
	.type =	cpu_to_be16(ETH_P_ARP),
	.func =	arp_rcv,
};

3 四层数据接收处理

 对于ipv4、ipv6的三层处理函数ip_rcv、ipv6_rcv处理完以后,需要由四层协议处理的函数,其处理流程与三层协议链表ptype类似。

3.1 四层数据包协议处理数据结构 struct net_protocol

/* This is used to register protocols. */
struct net_protocol {

	void			(*early_demux)(struct sk_buff *skb);

	/*四层协议相关的函数*/
	int			(*handler)(struct sk_buff *skb);
	/*错误处理*/
	void			(*err_handler)(struct sk_buff *skb, u32 info);

	unsigned int		no_policy:1,
				netns_ok:1;
};

3.2 四层协议栈 全局数组  struct net_protocol *inet_protos[MAX_INET_PROTOS]

与三层协议相关的数据结构packet_type相比,该数据结构中并没有标记协议的类型,那四层协议数据结构是如何查找的呢?

因为四层协议相关的数据结构,主要是链接到hash链表struct net_protocol *inet_protos[MAX_INET_PROTOS]。虽说是hash数组,但是该数组的最大值为256,而三、四层协议相关的定义如下,而hash数组的最大值即为256,即退化为了数组指针。所以每一个协议均对应一个net_protocol指针,此时只要从ip头部周获取到相应的四层的类型,即可从数组中取出相应的四层协议结构。而在三层结构,有可能出现同一个链表存在多个三层协议,此时就需要在三层协议中增加一个协议类型成员了。

#define MAX_INET_PROTOS		256
const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;

/* Standard well-defined IP protocols.  */
enum {
  IPPROTO_IP = 0,		/* Dummy protocol for TCP		*/
  IPPROTO_ICMP = 1,		/* Internet Control Message Protocol	*/
  IPPROTO_IGMP = 2,		/* Internet Group Management Protocol	*/
  IPPROTO_IPIP = 4,		/* IPIP tunnels (older KA9Q tunnels use 94) */
  IPPROTO_TCP = 6,		/* Transmission Control Protocol	*/
  IPPROTO_EGP = 8,		/* Exterior Gateway Protocol		*/
  IPPROTO_PUP = 12,		/* PUP protocol				*/
  IPPROTO_UDP = 17,		/* User Datagram Protocol		*/
  IPPROTO_IDP = 22,		/* XNS IDP protocol			*/
  IPPROTO_DCCP = 33,		/* Datagram Congestion Control Protocol */
  IPPROTO_RSVP = 46,		/* RSVP protocol			*/
  IPPROTO_GRE = 47,		/* Cisco GRE tunnels (rfc 1701,1702)	*/

  IPPROTO_IPV6	 = 41,		/* IPv6-in-IPv4 tunnelling		*/

  IPPROTO_ESP = 50,            /* Encapsulation Security Payload protocol */
  IPPROTO_AH = 51,             /* Authentication Header protocol       */
  IPPROTO_BEETPH = 94,	       /* IP option pseudo header for BEET */
  IPPROTO_PIM    = 103,		/* Protocol Independent Multicast	*/

  IPPROTO_COMP   = 108,                /* Compression Header protocol */
  IPPROTO_SCTP   = 132,		/* Stream Control Transport Protocol	*/
  IPPROTO_UDPLITE = 136,	/* UDP-Lite (RFC 3828)			*/

  IPPROTO_RAW	 = 255,		/* Raw IP packets			*/
  IPPROTO_MAX
};

3.3 四层协议处理添加 inet_add_protocol()

/*
 *	Add a protocol handler to the hash tables
 */

int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
{
	if (!prot->netns_ok) {
		pr_err("Protocol %u is not namespace aware, cannot register.\n",
			protocol);
		return -EINVAL;
	}

	return !cmpxchg((const struct net_protocol **)&inet_protos[protocol],
			NULL, prot) ? 0 : -1;
}
EXPORT_SYMBOL(inet_add_protocol);

static const struct net_protocol tcp_protocol = {
	.early_demux	=	tcp_v4_early_demux,
	.handler	=	tcp_v4_rcv,
	.err_handler	=	tcp_v4_err,
	.no_policy	=	1,
	.netns_ok	=	1,
};

static const struct net_protocol udp_protocol = {
	.handler =	udp_rcv,
	.err_handler =	udp_err,
	.no_policy =	1,
	.netns_ok =	1,
};

static const struct net_protocol icmp_protocol = {
	.handler =	icmp_rcv,
	.err_handler =	icmp_err,
	.no_policy =	1,
	.netns_ok =	1,
};

3.4 ipv4中的四层协议分流 ip_local_deliver_finish()

在ip_local_deliver_finish中根据从三层协议头中获取的四层协议类型,从数组inet_protos中取出相应的四层协议net_protocol,执行net_protocol->handler,即将数据包交由四层协议处理。对于udp为udp_rcv;对于tcp为tcp_v4_rcv;对于icmp为icmp_rcv;对于igmp为igmp_rcv。这样即实现了将三层数据包交由上层继续处理。关于函数 ip_local_deliver_finish() 的解析参见 《linux内核协议栈 IPv4之接收数据包流程》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值