ip_rcv_finish

49 篇文章 11 订阅
43 篇文章 1 订阅

上一章我们分析了ip_rcv,这个函数主要是对数据包做各种正确性验证,然后调用掉网络过滤子系统的在PRE_ROUTEING链上的回调函数,经过网络子系统的处理在调用ip_rec_finish,ip_rcv_finish主要的工作:

1)、确定数据包是前送还是在本机协议栈上传,如果是前送要确保输出网络设备和下一个接受栈的地址。

2)、解析和处理部分IP选项。

 

1、获取路由信息

skb->dst该数据域包含了如何到达目的地址的路由信息,如果该数据域是NULL,就通过路由子系统函数ip_route_input_noref路由,p_route_input_noref的输入参数有源IP地址、目的IP地址、服务类型、接受数据包的网络设备,根据这5个参数决策路由。

static int ip_rcv_finish(struct sk_buff *skb)
{
	//获取IP协议头
	const struct iphdr *iph = ip_hdr(skb);
	struct rtable *rt;

	 //获取路由信息,如果skb->dst是空,调用ip_route_input_noref获取路由
	if (skb_dst(skb) == NULL) {
		int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
					       iph->tos, skb->dev);
		if (unlikely(err)) {
			if (err == -EHOSTUNREACH)
				IP_INC_STATS_BH(dev_net(skb->dev),
						IPSTATS_MIB_INADDRERRORS);
			else if (err == -ENETUNREACH)
				IP_INC_STATS_BH(dev_net(skb->dev),
						IPSTATS_MIB_INNOROUTES);
			goto drop;
		}
	}
...

2、更新路由流量控制系统QoS的统计信息

主要更新接受数据包的数量和接受数据包总的长度。

#ifdef CONFIG_NET_CLS_ROUTE
	if (unlikely(skb_dst(skb)->tclassid)) {
		struct ip_rt_acct *st = per_cpu_ptr(ip_rt_acct, smp_processor_id());
		u32 idx = skb_dst(skb)->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

3、IP选项处理

如果IP协议头的长度大于20字节(5*4),就表示有IP选项需要处理,IP选项处理的函数是ip_rcv_options,如果Socket Buffer有别的进程共享,首先要调用skb_cow拷贝一个,因为处理IP协议选项可能会修改IP协议头信息。

//ip长度大于5(20字节)需要处理IP选项
	if (iph->ihl > 5 && ip_rcv_options(skb))
		goto drop;
	
	rt = skb_rtable(skb);
	//统计各类数据包数量,组传送包、广播包
	if (rt->rt_type == RTN_MULTICAST) {
		IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INMCAST,
				skb->len);
	} else if (rt->rt_type == RTN_BROADCAST)
		IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INBCAST,
				skb->len);
	//调用skb->dst->input指向的处理函数
	return dst_input(skb);

4、函数接受处理

ip_rcv_finish的结束是调用了dst_input,实际是调用存放在skb->dst->input的数据域。该函数确定了下一步对数据包的处理,根据数据包的目的地地址,可能是往本地协议栈上传就调用 ip_local_deliver,如果是前送转发就调用ip_forward

ip_rcv_finish完成代码:

static int ip_rcv_finish(struct sk_buff *skb)
{
	//获取IP协议头
	const struct iphdr *iph = ip_hdr(skb);
	struct rtable *rt;

	/*
	 *	Initialise the virtual path cache for the packet. It describes
	 *	how the packet travels inside Linux networking.
	 */
	 //获取路由信息,如果skb->dst是空,调用ip_route_input_noref获取路由
	if (skb_dst(skb) == NULL) {
		int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
					       iph->tos, skb->dev);
		if (unlikely(err)) {
			if (err == -EHOSTUNREACH)
				IP_INC_STATS_BH(dev_net(skb->dev),
						IPSTATS_MIB_INADDRERRORS);
			else if (err == -ENETUNREACH)
				IP_INC_STATS_BH(dev_net(skb->dev),
						IPSTATS_MIB_INNOROUTES);
			goto drop;
		}
	}

#ifdef CONFIG_NET_CLS_ROUTE
	if (unlikely(skb_dst(skb)->tclassid)) {
		struct ip_rt_acct *st = per_cpu_ptr(ip_rt_acct, smp_processor_id());
		u32 idx = skb_dst(skb)->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

	//ip长度大于5(20字节)需要处理IP选项
	if (iph->ihl > 5 && ip_rcv_options(skb))
		goto drop;
	
	rt = skb_rtable(skb);
	//统计各类数据包数量,组传送包、广播包
	if (rt->rt_type == RTN_MULTICAST) {
		IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INMCAST,
				skb->len);
	} else if (rt->rt_type == RTN_BROADCAST)
		IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INBCAST,
				skb->len);
	//调用skb->dst->input指向的处理函数
	return dst_input(skb);

drop:
	kfree_skb(skb);
	return NET_RX_DROP;
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值