linux内核网络收包过程—IP协议处理

Posted 为了维护世界和平_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux内核网络收包过程—IP协议处理相关的知识,希望对你有一定的参考价值。

目录

网络协议栈

IP协议层处理


网络协议栈

netif_receive_skb_list_internal->__netif_receive_skb_list->__netif_receive_skb_list_core

函数会根据包的协议,假如是 udp 包,会将包依次送到 ip_rcv(), udp_rcv() 协议处理函数中进⾏处

static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
				    struct packet_type **ppt_prev)


	//将数据送入抓包点,tcpdump
	list_for_each_entry_rcu(ptype, &ptype_all, list) 
		if (pt_prev)
			ret = deliver_skb(skb, pt_prev, orig_dev);
		pt_prev = ptype;
	

    //取出协议
	type = skb->protocol;

	/* deliver only exact match when indicated */
	if (likely(!deliver_exact)) 
		deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,
				       &ptype_base[ntohs(type) &
						   PTYPE_HASH_MASK]);
	
...
	return ret;


//遍历list
static inline void deliver_ptype_list_skb(struct sk_buff *skb,
					  struct packet_type **pt,
					  struct net_device *orig_dev,
					  __be16 type,
					  struct list_head *ptype_list)

	struct packet_type *ptype, *pt_prev = *pt;

	list_for_each_entry_rcu(ptype, ptype_list, list) 
		if (ptype->type != type)
			continue;
		if (pt_prev)
			deliver_skb(skb, pt_prev, orig_dev);
		pt_prev = ptype;
	
	*pt = pt_prev;


//fun
static inline int deliver_skb(struct sk_buff *skb,
			      struct packet_type *pt_prev,
			      struct net_device *orig_dev)


	return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
函数处理的任务
  1. type = skb->protocol取出协议信息,
  2. 遍历注册在这个协议上的回调函数列表, ptype_base 是 hash table初始化时注册的
  3. pt_prev->func 协议层注册的处理函数ip_rcv

IP协议层处理

int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
	   struct net_device *orig_dev)

	struct net *net = dev_net(dev);
	skb = ip_rcv_core(skb, net);

	return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
		       net, NULL, skb, dev, NULL,
		       ip_rcv_finish);


static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)

	struct net_device *dev = skb->dev;
	int ret;

	skb = l3mdev_ip_rcv(skb);
	ret = ip_rcv_finish_core(net, sk, skb, dev, NULL);

	if (ret != NET_RX_DROP)
		ret = dst_input(skb);
	return ret;

NF_HOOK 是⼀个钩⼦函数,执行ip_rcv_finish
函数调用关系 ip_rcv_finish_core         ip_route_input_noref                 ip_route_input_rcu                         ip_route_input_mc                                 rt_dst_alloc
struct rtable *rt_dst_alloc(struct net_device *dev,
			    unsigned int flags, u16 type,
			    bool nopolicy, bool noxfrm, bool will_cache)

		rt->dst.output = ip_output;
		if (flags & RTCF_LOCAL)
			rt->dst.input = ip_local_deliver;
        ...
skb_dst(skb)->input 调⽤的 input ⽅法就是路由⼦系统赋的 ip_local_deliver
int ip_local_deliver(struct sk_buff *skb)


	struct net *net = dev_net(skb->dev);
    ...
	return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
		       net, NULL, skb, skb->dev, NULL,
		       ip_local_deliver_finish);


static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb)

	__skb_pull(skb, skb_network_header_len(skb));

	ip_protocol_deliver_rcu(net, skb, ip_hdr(skb)->protocol);

	return 0;


void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int protocol)

	const struct net_protocol *ipprot;
	int raw, ret;

resubmit:
	raw = raw_local_deliver(skb, protocol);
	ipprot = rcu_dereference(inet_protos[protocol]);
	if (ipprot) 
		ret = INDIRECT_CALL_2(ipprot->handler, tcp_v4_rcv, udp_rcv,skb);
        ...
		__IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
	
...
inet_protos 中保存着 tcp_v4_rcv() 和 udp_rcv() 的函数地址。

 参考

https://course.0voice.com/v1/course/intro?courseId=2&agentId=0


以上是关于linux内核网络收包过程—IP协议处理的主要内容,如果未能解决你的问题,请参考以下文章

linux内核网络收包过程—网络子系统与协议栈初始化

趣谈协议基础篇:图解Linux网络包接收过程

Linux Kernel TCP/IP Stack — 协议栈收包处理流程

Linux Kernel TCP/IP Stack — 协议栈收包处理流程

linux内核网络收包过程—硬中断与软中断

C 语言网络编程 — 内核协议栈收包/发包流程