深入理解Linux网络技术内幕 第20章 IPV4转发和本地传递

在ip_rcv_finish最后会调用先前设置在skb中的struct dst_entry结构,从而根据目的IP设置不同的处理函数
其中转发设置的函数为ip_forward
本地传递设置的函数为ip_local_deliver

转发

转发需要做的事情有:

  • 处理IP选项
  • 确定报文报文转发相关信息
  • 递减TTL
  • 根据PMTU必要时做分段处理工作
  • 传递报文到网卡

ip_forward

ip_forward用来处理所有不是传给本地的IP报文,函数需要的信息都在前边的流程里初始化到_skb_refdst结构中。

经过一些检查后,做的第一件事情是在ip_call_ra_chain中处理Router Alert选项。

	/* that should never happen */
	if (skb->pkt_type != PACKET_HOST)
		goto drop;

	if (unlikely(skb->sk))
		goto drop;

	if (skb_warn_if_lro(skb))
		goto drop;

	if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
		goto drop;

	if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
		return NET_RX_SUCCESS;

报文转发在L3层,不需要担心L4校验工作,在skb_forward_csum中将ip_summed设置为CHECKSUM_NONE。
接下来检查TTL是否合法,如果为0了就将报文丢弃并返回ICMP信息给报文来源地。

	skb_forward_csum(skb);
	net = dev_net(skb->dev);

	/*
	 *	According to the RFC, we must first decrease the TTL field. If
	 *	that reaches zero, we must reply an ICMP control message telling
	 *	that the packet's lifetime expired.
	 */
	if (ip_hdr(skb)->ttl <= 1)
		goto too_many_hops;

下面检查Strict Source Route选项,如果确定的下一跳找到的IP和IP选项中的不一致,就需要丢弃报文。传递ICMP报错信息。

	rt = skb_rtable(skb);

	if (opt->is_strictroute && rt->rt_uses_gateway)
		goto sr_failed;

接下来的任务需要改变报文,所以检查报文是否有共享或者该skb头空间不足以容纳一个L2的头,如果有以上情况需要clone一份新的skb结构。
接下来递减TTL。
如果有比选项中指定的下一跳更好的,需要给源主机发送一个ICMP HOST REDIRECT。
根据IP头中的TOS字段设定skb->priority,这个优先级会在后续 Qos层使用。
最后将控制权交给防火墙Netfilter执行ip_forward_finish函数。

	/* We are about to mangle packet. Copy it! */
	if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+rt->dst.header_len))
		goto drop;
	iph = ip_hdr(skb);

	/* Decrease ttl after skb cow done */
	ip_decrease_ttl(iph);

	/*
	 *	We now generate an ICMP HOST REDIRECT giving the route
	 *	we calculated.
	 */
	if (IPCB(skb)->flags & IPSKB_DOREDIRECT && !opt->srr &&
	    !skb_sec_path(skb))
		ip_rt_send_redirect(skb);

	if (net->ipv4.sysctl_ip_fwd_update_priority)
		skb->priority = rt_tos2priority(iph->tos)

ip_forward_finish

该函数整体比较简短,主要做的是调用ip_forward_options处理选项。
ip_forward_options函数主要处理Record Route选项,将出口设备IP复制到报文体中。该函数也会Source Route选项中的本机IP信息数据删除。如果报文头数据有改变就需要重新计算IP头中的csum值。

static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	struct ip_options *opt	= &(IPCB(skb)->opt);

	__IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
	__IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len);

	if (unlikely(opt->optlen))
		ip_forward_options(skb);

	skb->tstamp = 0;
	return dst_output(net, sk, skb);
}

dst_output函数将调用struct dst_entry结构中的output函数指针。这个函数发送需要发送的报文。该指针的初始化和input初始化位置大概相同,单播被设置为ip_output,多播被设置为ip_mc_output函数。

本地传输

ip_local_deliver用来处理传给本地的IP报文。和转发报文不同,传递给本地的报文需要做IP层的重组工作,交给上层协议的报文一定是一个分片重组后的才可以。
重组工作由ip_defrag完成。
之后交给防火墙Netfilter决定是否调用ip_local_deliver_finish将报文传递给L4处理。

/*
 * 	Deliver IP Packets to the higher protocol layers.
 */
int ip_local_deliver(struct sk_buff *skb)
{
	/*
	 *	Reassemble IP fragments.
	 */
	struct net *net = dev_net(skb->dev);

	if (ip_is_fragment(ip_hdr(skb))) {
		if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))
			return 0;
	}

	return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
		       net, NULL, skb, skb->dev, NULL,
		       ip_local_deliver_finish);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值