ip_queue_xmit函数

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指向的sock的opt中包含严格源站路由选项,  

    //而刚刚查找到的路由项目标地址又不等于网关地址的话前往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首部填入版本号4,ip首部长度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(从sock的uc_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_build向IP首部中写入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字段  

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

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

    //这样可以很大程度上减缓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;  

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值