admin管理员组文章数量:1642339
IPV4转发和本地传递
- 转发
- ip_forward
- ip_forward_finish
- 本地传输
在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);
}
版权声明:本文标题:深入理解Linux网络技术内幕 第20章 IPV4转发和本地传递 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1729332394a1196559.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论