Chinaunix首页 | 论坛 | 博客

分类: LINUX

2014-03-30 12:15:21

int ip_forward(struct sk_buff *skb)
{
        struct iphdr *iph;        /* Our header */
        struct rtable *rt;        /* Route we use */
        struct ip_options * opt        = &(IPCB(skb)->opt);

        if (skb_warn_if_lro(skb))
                goto drop;

        if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))         /*xfrm(ipsec)的相关检测*/
                goto drop;

        /* 这也是在处理IP的选项,在IPv6中IP报头固定死了,IP选项的意义不大. */
        if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
                return NET_RX_SUCCESS;

        /*当帧的目的地址就是本机2层地址的时候,skb->pkt_type设置为PCAKET_HOST. 所以本地广播(MAC=FF:FF...)的数据包不会被转发.*/
        if (skb->pkt_type != PACKET_HOST)         
                goto drop;

        /* 既然是转发,那么就涉及到网卡对L4层校验和的计算. 设置ip_summed为CHECKSUM_NONE,表示我们不需要让网卡对校验和做任何事情.*/
        skb_forward_csum(skb);                 

        /*
         *        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)                 /*ttl小于1,此时丢掉这个包*/
                goto too_many_hops;

        if (!xfrm4_route_forward(skb))                 /*ipSec相关检查.*/
                goto drop;

        rt = skb_rtable(skb);
        /* 介绍了rtable结构体的每个成员变量. rt->rt_dst是路由的目的地址,在严格源路由
           选项中,路由的目的地址应该是直达的,不需要经过下一跳的网关.所以有了下面的判定.
           问题在于:rt->rt_dst是怎么给赋值的.在第一次解析路由选项时,如果是严格路由选项,那么会发送ICMP_PARAMETERPROB报文.
                     不费脑子了.
        */
        if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway) 
                goto sr_failed;

        /*
           第一个&:如果IP报文长度大于MTU,且不允许分片, 且frag_list=NULL
           第二个&:不允许本机对该skb数据分片.
           综上可知,不担心IP报文发送不出去,而是担心能不能分段.
        */
        if (unlikely(skb->len > dst_mtu(&rt->u.dst) && !skb_is_gso(skb) &&
                     (ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) {
                IP_INC_STATS(dev_net(rt->u.dst.dev), IPSTATS_MIB_FRAGFAILS);
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
                          htonl(dst_mtu(&rt->u.dst)));
                goto drop;
        }

        /* We are about to mangle packet. Copy it! skb_cow的第二个参数可理解为MAC+IP报头(有可能和接收到的报文的报头不一样.) */
        if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))
                goto drop;
        iph = ip_hdr(skb);         //正是skb_cow才能调用iph_hdr来完整的对IP报头进行操作.

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

        /*
         *        We now generate an ICMP HOST REDIRECT giving the route we calculated.
         *        如果我们所找到的下一跳地址比请求的更好的话,源host现在将会收到一个ICMP REDIRESCT消息(只有当源host没有请求 source routing option时)
         */
        if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb_sec_path(skb))
                ip_rt_send_redirect(skb);
        skb->priority = rt_tos2priority(iph->tos);

        return NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev,
                       ip_forward_finish);
sr_failed:
        /*
         *        Strict routing permits no gatewaying
         */
         icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
         goto drop;

too_many_hops:
        /* Tell the sender its packet died... */
        IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_INHDRERRORS);
        icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:
        kfree_skb(skb);
        return NET_RX_DROP;
}
总体来说:好像也没做什么事情,路由信息不用管了。需要:1、检查MTU大小与报文大小是否匹配,2、TTL值处理. 3、调整线性缓冲区大小. 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static int ip_forward_finish(struct sk_buff *skb)
{
        struct ip_options * opt        = &(IPCB(skb)->opt);

        IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);

        if (unlikely(opt->optlen))
                ip_forward_options(skb);         /*对IP Options中有关foreard的选项进行解析*/

        /*调用输出函数.ip_output用于单播输出,ip_mc_output用于多播输出.*/
        return dst_output(skb);                        /*调用输出函数.NO::::ip_local_out用于单播输出,ip_mc_output用于多播输出.*/
}
阅读(3049) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~