Linux内核之网络层处理流程及各函数作用

1. 网络层处理框架(三层)

在这里插入图片描述

声明:未经本人同意,严禁一切形式转载!!!
函数 作用
ip_rcv 报文检查(格式、是否发往本地)
ip_rcv_finish 查询路由表确定转发还是本地处理
dst_input 真正转发/本地处理接口
ip_local_deliver 如果是分片报文,重组后交由上层处理
ip_defrag 分片报文重组
ip_local_deliver_finish 去IP头、交由传输层协议继续处理
ip_forward 是否转发、查询安全策略路由(如IPSec策略), 修改TTL等
ip_forward_finish IP选项处理
ip_queue_xmit 检查路由是否有效(无效则重新查询),添加IP头部
dst_output 根据路由结果,进行转发操作
ip_output 如果IP超过MTU,则进行分片
ip_fragment IP报文分片处理接口
ip_finish_output 指定二层头中的报文类型为IP类型(0x0800),IP层最后一个函数
ip_finish_output2 填充二层头(涉及到ARP查询目的MAC)
neigh_resolve_output ARP相关处理

1.1 PRE_ROUTING部分函数介绍

1.1.1 ip_rcv()

此函数是接收报文时IP层的入口函数,very very 重要。它的主要功能如下:

  • 是否发给本设备的报文(目的MAC是本设备)
  • IP报文头部格式检查(其中包括校验和)
  • 进入NF_IP_PRE_ROUTING点

代码如下:

/*
 * 	Main IP Receive routine.
 */ 
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
   
	struct iphdr *iph;

	/* When the interface is in promisc. mode, drop all the crap
	 * that it receives, do not try to analyse it.
	 */
	/*混杂模式时丢弃所有报文????
	*/
	if (skb->pkt_type == PACKET_OTHERHOST)/*根据目的MAC来设置的,只有发给自己的报文,才会继续后续IP层处理*/
		goto drop;

	IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);

	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
   
		IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
		goto out;
	}
	/*线性区域是指:存放报文的那部分区域,head~data~tail~end区域*/
	if (!pskb_may_pull(skb, sizeof(struct iphdr)))/*如果skb线性区域的长度比标准iph还小,则说明报文格式有误*/
		goto inhdr_error;

	iph = skb->nh.iph;

	/*
	 *	RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
	 *
	 *	Is the datagram acceptable?
	 *
	 *	1.	Length at least the size of an ip header
	 *	2.	Version of 4
	 *	3.	Checksums correctly. [Speed optimisation for later, skip loopback checksums]
	 *	4.	Doesn't have a bogus length
	 */

	if (iph->ihl < 5 || iph->version != 4)/*只处理ipv4*/
		goto inhdr_error; 

	/*
	*ph->ihl*4真实头部长度,因为可能包含option字段部分
	*意思是说:既然包含了ip选项字段,那么skb线性区长度必然会大于ip头部长度
	*/
	if (!pskb_may_pull(skb, iph->ihl*4))
		goto inhdr_error;

	iph = skb->nh.iph;

	/*
	*校验报文校验和是否正确的方式是:重新计算整个报文,为0说明校验和正确
	*/
	if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)/*校验和为0,说明校验和正确*/
		goto inhdr_error; 

	{
   
		__u32 len = ntohs(iph->tot_len); 
		if (skb->len < len || len < (iph->ihl<<2))/*报文长度校验*/
			goto inhdr_error;

		/* Our transport medium may have padded the buffer out. Now we know it
		 * is IP we can trim to the true length of the frame.
		 * Note this now means skb->len holds ntohs(iph->tot_len).
		 */
		if (pskb_trim_rcsum(skb, len)) {
   
			IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
			goto drop;
		}
	}

	return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
		       ip_rcv_finish);

inhdr_error:
	IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
drop:
        kfree_skb(skb);
out:
        return NET_RX_DROP;
}

1.1.2 ip_rcv_finish()

此函数的功能包括:

  • 查询路由表
  • IP头部中选项字段解析
  • 进入dst_input()函数,此函数中会根据查询的路由结果,决定报文是“转发”、“交由上层处理”、“丢弃”。

代码如下:

/*
*	ip_rcv_finish:最主要的工作是查询路由表;解析选项字段
*	然后根据路由决定是
*		1. 向上层传递
*		2. 转发
*		3. 丢弃
**/
static inline int ip_rcv_finish(struct sk_buff *skb)
{
   
	struct net_device *dev = skb->dev;
	struct iphdr *iph = skb->nh.iph;

	/*
	 *	Initialise the virtual path cache for the packet. It describes
	 *	how the packet travels inside Linux networking.
	 */ 
	 /*所有收到的数据包由于都不包含路由信息,so应该都需要在此进行路由查询*/
	if (skb->dst == NULL) {
   /*目的路由表项为空,且查询路由失败,则丢弃报文*/
		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))/*!!!!!!!!core opterations!!!!!!!!!*/
			goto drop; 
	}

#ifdef CONFIG_NET_CLS_ROUTE
	if (skb->dst->tclassid) {
   
		struct ip_rt_acct *st = ip_rt_acct + 256*smp_processor_id();
		u32 idx = skb->dst->tclassid;
		st[idx&0xFF].o_packets++;
		st[idx&0xFF].o_bytes+=skb->len;
		st[(idx>>16)&0xFF].i_packets++;
		st[(idx>>16)&0xFF].i_bytes+=skb->len;
	}
#endif

	if (iph->ihl > 5) {
   /*IP头部中包含选项字段*/
		struct ip_options *opt;

		/* It looks as overkill, because not all
		   IP options require packet mangling.
		   But it is the easiest for now, especially taking
		   into account that combination of IP options
		   and running sniffer is extremely rare condition.
		                                      --ANK (980813)
		*/

		if (skb_cow(skb, skb_headroom(skb))) {
   /*headroom空间不足的话,扩展头部*/
			IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
			goto drop;
		}
		iph = skb->nh.iph;

		if (ip_options_compile(NULL, skb))/*解析opt字段*/
			goto inhdr_error;

		opt = &(IPCB(skb
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叨陪鲤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值