Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2175542
  • 博文数量: 436
  • 博客积分: 9833
  • 博客等级: 中将
  • 技术积分: 5558
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-29 10:27
文章存档

2013年(47)

2012年(79)

2011年(192)

2010年(118)

分类: LINUX

2010-11-05 22:43:13

 view plaincopy to clipboardprint?

int ip_queue_xmit(struct sk_buff *skb, int ipfragok)  

{  

    struct sock *sk = skb->sk;  

    struct inet_sock *inet = inet_sk(sk);  

    struct ip_options *opt = inet->opt;  

    struct rtable *rt;  

    struct iphdr *iph;  

    /* Skip all of this if the packet is already routed, 

     * f.e. by something like SCTP. 

     */ 

    //首先检测skb->rtable是否为空,不为空说明已经指定了路由,跳到packet_routed继续执行  

    //根据上面注释,似乎sctp可能提前指定路由  

    rt = skb->rtable;  

    if (rt != NULL)  

        goto packet_routed;  

    /* Make sure we can route this packet. */ 

    //检测socket路由合法性,如果不合法也需要重新查找路由  

    rt = (struct rtable *)__sk_dst_check(sk, 0);  

    if (rt == NULL) {  

        __be32 daddr;  

        /* Use correct destination address if we have options. */ 

        daddr = inet->daddr;  

        if(opt && opt->srr)  

            daddr = opt->faddr;  

        {  

            struct flowi fl = { .oif = sk->sk_bound_dev_if,  

                        .nl_u = { .ip4_u =  

                              { .daddr = daddr,  

                            .saddr = inet->saddr,  

                            .tos = RT_CONN_FLAGS(sk) } },  

                        .proto = sk->sk_protocol,  

                        .flags = inet_sk_flowi_flags(sk),  

                        .uli_u = { .ports =  

                               { .sport = inet->sport,  

                             .dport = inet->dport } } };  

            /* If this fails, retransmit mechanism of transport layer will 

             * keep trying until route appears or the connection times 

             * itself out. 

             */ 

            security_sk_classify_flow(sk, &fl);  

            //下面是主要的出口路由查找函数 

            if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0))  

                goto no_route;  

        }  

        //下面函数做的其中一件事是sk->sk_dst_cache = dst;并释放旧的dst缓存  

        sk_setup_caps(sk, &rt->u.dst);  

    }  

    //增加路由缓存引用计数  

    skb->dst = dst_clone(&rt->u.dst);  

packet_routed:  

    //如果sk_buff指向的sockopt中包含严格源站路由选项,  

    //而刚刚查找到的路由项目标地址又不等于网关地址的话前往no_route  

    //说明严格源站路由无法满足  

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

        goto no_route;  

    /* OK, we know where to send it, allocate and build IP header. */ 

    //skb的数据中预留出ip首部包括选项的空间给ip报头,并将  

    //skb->network_header指向它  

    skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));  

    skb_reset_network_header(skb);  

    iph = ip_hdr(skb);  

    //ip首部填入版本号4ip首部长度5(20字节,这个值在后面要根据选项  

    //的长度增加),以及服务类型  

    *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));  

    //如果socket要求ip不分片(这是通过检测sock->pmtudisc做到的,  

    //如果使用路径mtu发现则说明要求不分片,否则允许分片)并且参数ipfragok等于0  

    //那么将DF标志置1,否则清0  

    if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok)  

        iph->frag_off = htons(IP_DF);  

    else 

        iph->frag_off = 0;  

    //设置ip首部的ttl(sockuc_ttl获得,如果小于0则从路由项的metrics获得),  

    //protocol(sock->sk_protocol),源地址,目标地址(两者都从路由项获得)  

    iph->ttl      = ip_select_ttl(inet, &rt->u.dst);  

    iph->protocol = sk->sk_protocol;  

    iph->saddr    = rt->rt_src;  

    iph->daddr    = rt->rt_dst;  

    /* Transport layer set skb->h.foo itself. */ 

    //opt不为NULL,则在ip首部长度中加上选项长度,  

    //并且调用ip_options_buildIP首部中写入ip选项  

    if (opt && opt->optlen) {  

        iph->ihl += opt->optlen >> 2;  

        //这个函数值得一看,opt是从inet_sock中获得的  

        ip_options_build(skb, opt, inet->daddr, rt, 0);  

    }  

    //调用ip_select_ident_more填入IP首部的id字段  

    //关于ipidULNI上讲得很清楚,Linux为了防止id回绕采取的策略是对于每一个ip  

    //分配一个inet_peer结构,在这个inet_peer中记录针对这个ipid号,  

    //这样可以很大程度上减缓id回绕的速度,但是仍不能完全避免  

    ip_select_ident_more(iph, &rt->u.dst, sk,  

                 (skb_shinfo(skb)->gso_segs ?: 1) - 1);  

    skb->priority = sk->sk_priority;  

    skb->mark = sk->sk_mark;  

    return ip_local_out(skb);  

no_route:  

    IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);  

    kfree_skb(skb);  

    return -EHOSTUNREACH;  

} 

int ip_queue_xmit(struct sk_buff *skb, int ipfragok)

{

       struct sock *sk = skb->sk;

       struct inet_sock *inet = inet_sk(sk);

       struct ip_options *opt = inet->opt;

       struct rtable *rt;

       struct iphdr *iph;

       /* Skip all of this if the packet is already routed,

        * f.e. by something like SCTP.

        */

       //首先检测skb->rtable是否为空,不为空说明已经指定了路由,跳到packet_routed继续执行

       //根据上面注释,似乎sctp可能提前指定路由

       rt = skb->rtable;

       if (rt != NULL)

              goto packet_routed;

       /* Make sure we can route this packet. */

       //检测socket路由合法性,如果不合法也需要重新查找路由

       rt = (struct rtable *)__sk_dst_check(sk, 0);

       if (rt == NULL) {

              __be32 daddr;

              /* Use correct destination address if we have options. */

              daddr = inet->daddr;

              if(opt && opt->srr)

                     daddr = opt->faddr;

              {

                     struct flowi fl = { .oif = sk->sk_bound_dev_if,

                                       .nl_u = { .ip4_u =

                                                { .daddr = daddr,

                                                 .saddr = inet->saddr,

                                                 .tos = RT_CONN_FLAGS(sk) } },

                                       .proto = sk->sk_protocol,

                                       .flags = inet_sk_flowi_flags(sk),

                                       .uli_u = { .ports =

                                                 { .sport = inet->sport,

                                                  .dport = inet->dport } } };

                     /* If this fails, retransmit mechanism of transport layer will

                      * keep trying until route appears or the connection times

                      * itself out.

                      */

                     security_sk_classify_flow(sk, &fl);

                     //下面是主要的出口路由查找函数

                     if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0))

                            goto no_route;

              }

              //下面函数做的其中一件事是sk->sk_dst_cache = dst;并释放旧的dst缓存

              sk_setup_caps(sk, &rt->u.dst);

       }

       //增加路由缓存引用计数

       skb->dst = dst_clone(&rt->u.dst);

packet_routed:

       //如果sk_buff指向的sockopt中包含严格源站路由选项,

       //而刚刚查找到的路由项目标地址又不等于网关地址的话前往no_route

       //说明严格源站路由无法满足

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

              goto no_route;

       /* OK, we know where to send it, allocate and build IP header. */

       //skb的数据中预留出ip首部包括选项的空间给ip报头,并将

       //skb->network_header指向它

       skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));

       skb_reset_network_header(skb);

       iph = ip_hdr(skb);

       //ip首部填入版本号4ip首部长度5(20字节,这个值在后面要根据选项

       //的长度增加),以及服务类型

       *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));

       //如果socket要求ip不分片(这是通过检测sock->pmtudisc做到的,

       //如果使用路径mtu发现则说明要求不分片,否则允许分片)并且参数ipfragok等于0

       //那么将DF标志置1,否则清0

       if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok)

              iph->frag_off = htons(IP_DF);

       else

              iph->frag_off = 0;

       //设置ip首部的ttl(sockuc_ttl获得,如果小于0则从路由项的metrics获得),

       //protocol(sock->sk_protocol),源地址,目标地址(两者都从路由项获得)

       iph->ttl      = ip_select_ttl(inet, &rt->u.dst);

       iph->protocol = sk->sk_protocol;

       iph->saddr    = rt->rt_src;

       iph->daddr    = rt->rt_dst;

       /* Transport layer set skb->h.foo itself. */

       //opt不为NULL,则在ip首部长度中加上选项长度,

       //并且调用ip_options_buildIP首部中写入ip选项

       if (opt && opt->optlen) {

              iph->ihl += opt->optlen >> 2;

              //这个函数值得一看,opt是从inet_sock中获得的

              ip_options_build(skb, opt, inet->daddr, rt, 0);

       }

       //调用ip_select_ident_more填入IP首部的id字段

       //分配一个inet_peer结构,在这个inet_peer中记录针对这个ipid号,

       //这样可以很大程度上减缓id回绕的速度,但是仍不能完全避免

       ip_select_ident_more(iph, &rt->u.dst, sk,

                          (skb_shinfo(skb)->gso_segs ?: 1) - 1);

       skb->priority = sk->sk_priority;

       skb->mark = sk->sk_mark;

       return ip_local_out(skb);

no_route:

       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);

       kfree_skb(skb);

       return -EHOSTUNREACH;

}

阅读(3340) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~