第二十章:因特网协议第四版(IPv4):转发和本地传递

在ip_rcv_finish函数的尾端,如果目的地址和本地接口不同,内核必须把封包转发至适当的主机,若目的地址是本地地址,内核必须把封包准备好,以便高层使用。本章主要介绍转发和本地传递是怎么完成的。

转发:

转发的工作由ip_forward函数和ip_forward_finish完成。此时,ip_rcv_finish里已经调用了ip_route_input,所以skb_buff中已经包含了转发封包所需要的所有信息。转发由下列步骤组成:

    处理IP选项。

    确定封包可以被转发。

    递减IP报头的TTL字段。

    根据路径相关MTU,必要时处理分片工作。

    把封包传送至外出设备。

如果封包无法转发会发送ICMP,或者被转发了,但使用的是次佳路由,因而触发了重导项事件,发送ICMP来通知重导项事件。

和IPsec交互也是转发工作的一部分,由xfrm4_xxx函数实现,这些函数是进入IPsec基础架构的钩子。本书不会讨论IPsec,就是说后续讨论假设IPsec没有配置。

 

ip_forward函数:

原型:int ip_forward(struct sk_buff *skb);

如果报头中发现了Router Alert选项,则封包现在就会被处理。处理函数ip_call_ra_chain会先重组整个ip封包,然后把封包传给那些Raw套接字(这些套接字设置了IP_ROUTER_ALERT)。然后ip_forward直接返回。

如果报头中没有Router Alert选项,或者存在但没有感兴趣的程序在运行,ip_forward会继续下去:

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

        return NET_RX_SUCCESS;

    if(skb->pkt_type !=  PACKET_HOST)

        goto drop;

    

    skb->ip_summed = CHECKSUM_NONE;//转发封包,不涉及L4,所以用CHECKSUM_NONE指出当前校验和无误,若有些处理工作修改了IP报头,TCP报头或有效载荷,那么在传输前,内核就会重算校验和。

真实的转发流程是由递减TTL字段开始的:

    if(iph->ttl <= 1)

        goto too_many_hops;

如果IP报头包含一个Strict Source Route选项,且下个跳点和路由子系统找到的不一样,则Source Routing选项失败了,丢弃该封包,发送一个ICMP报文给传送者。

    rt = (struct rtable *)skb->dst;

    if(opt->is_strictroute && rt->rt_dst != rt->rt_gateway)

        goto sr_failed;

多数健康检查做完后,此函数会更新报头,所以需要拷贝缓冲区。

    if(skb_cow(skb,LL_RESERVED_SAPCE(rt->u.dst.dev)+rt->udst.header_len))

        goto drop;

现在,TTL会由ip_decrease_ttl递减,并且该函数还会更新ip校验和:

    ip_decrease_ttl(iph);

如果有比请求的还要更好的下一跳点存在,原来的主机就会收到ICMP REDIRECT 消息(前提是原来的主机没有请求源路由之时??这里怎么理解?)。

    if(rt->rt_flags & RTCF_DOREDIRECT && !opt->ssr)

        ip_rt_send_redirect(skb);

这里,priority字段是使用ip头的TOS字段设定的,由Traffic Control(Qos层)使用。

    skb->priority = rt_tos2priority(iph->tos);

最后,ip_forward函数会要求Netfilter执行ip_forward_finish(如果没有禁止转发的规则)

    return NF_HOOK(PF_INET,NF_IP_FORWARD,skb,skb->dev,rt->u.dst.dev,ip_forward_finish);

ip_forward_finish函数:

 到了这一步,说明封包已经通过所有会使其停止的检查,而且真的已经就绪,可以传输到另一个系统了。

先前在ip_forward函数中已经处理过Route Alert和Strict Source Routing选项了(还有其他选项,只是列举了这两个例子而已)。ip_forward_finish会调用ip_forward_options来处理那些选项所需的最后工作。最后,ip_forward_finish函数会调用dst_output函数将封包传递出去。

dst_output函数:

无论是本地产生还是转发,所有传输都会通过dst_output上路。此时,IP报头已经完成,内含传输所需信息以及本地系统负责添加的其他任何信息。dst_output函数会调用虚拟函数output,分段也是在该函数内部处理的。最后,会调用ip_finish_output来衔接邻居子系统,只有当Netfilter放行时,ip_finish_output才会被调用,否则丢弃封包。

    static inline int dst_out(struct sk_buff *skb)

    {

        int err;

        for(; ;){

            err = skb->dst->output(&skb);

            if(likely(err = 0))

                return err;

            if(unlikely(err != NET_XMIT_BYPASS))

                return err;

        }

    }

本地传递:

第三十五章会说明转发(路由)引擎如何得知本地主机是封包的地址。当封包抵达目的主机时,ip_rcv_finish开端调用ip_route_input,就会把skb->dst->input初始化为ip_local_deliver。此外,Netfilter有最后的权力决定do_something函数是否可以调用相应的do_something_finish函数。

int ip_local_deliver(struct sk_buff *skb)

{

    if(skb->nh.iph->frag_off & htons(IP_MF | IP_OFFSET)){

        skb = ip_defrag(skb,IP_DEFRAG_LOCAL_DELIVER);//转发几乎不需要重组,而本地传递必须重组整个ip封包

        if(!skb)

            return 0;

    }

    return NF_HOOK(PF_INET,NF_IP_LOCAL_IN,skb,skb->dev,rt->u.dst.dev,ip_local_deliver_finish); //第二十四章会谈论ip_local_deliver_finish函数的细节

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值