zoukankan      html  css  js  c++  java
  • IP 层收发报文简要剖析6ip报文输出3 ip_push_pending_frames

    L4层的协议会把数据通过ip_append_data或ip_append_page把数据线放在缓冲区,然后再显示调用ip_push_pending_frames传送数据。
    把数据放在缓冲区有两个优点,一方面,缓冲区的数据可以被后续的一些函数使用,构成一些片段;另一方面,把数据放缓冲区,等缓冲区满了(达到PMTU)再传送数据,可以更有效率。
    如果在一些情况下,L4层希望去放在缓冲区的数据立即被传输,那么在调用ip_append_data把数据放缓冲区后,立即调用ip_push_pending_frames进行传输。

    ip_push_pending_frames:其作用为将输出队列上的多个片段合成一个完整的ip数据报文,并通过ip_output输出;此函数相当于一个notify函数,当4层决定传输帧到ip层的时候,他就需要调用这个函数.通过前面我们知道此时所有的数据(如果不支持Scatter/Gather I/O),都在sk_write_queue中;

    根据是否使用分散聚集I/O 封包数据的组织和在sk_buf结构中的组织会有所不同:

    int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4)
    {
        struct sk_buff *skb;
    
        skb = ip_finish_skb(sk, fl4);
        if (!skb)
            return 0;
    
        /* Netfilter gets whole the not fragmented skb. */
        return ip_send_skb(sock_net(sk), skb);
    }
    static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4)
    {
    	return __ip_make_skb(sk, fl4, &sk->sk_write_queue, &inet_sk(sk)->cork.base);
    }
    

     函数__ip_make_skb除了填充L3头外,还对skb的组织进行了修改:

    所有的大小都会在第一个skb中体现,即本例中skb->len=load+L3+L4
    后续调用ip_finish_output进行发送

     __ip_make_skb 简析

    如果路径rt开启了PMTU 且没有被锁定ip_dont_fragment
     IP_PMTUDISC_DO  路径mtu发现开启   则禁止分片

    /*
     *    Combined all pending IP fragments on the socket as one IP datagram
     *    and push them out.
     */
    struct sk_buff *__ip_make_skb(struct sock *sk,
                      struct flowi4 *fl4,
                      struct sk_buff_head *queue,
                      struct inet_cork *cork)
    {
        struct sk_buff *skb, *tmp_skb;
        struct sk_buff **tail_skb;
        struct inet_sock *inet = inet_sk(sk);
        struct net *net = sock_net(sk);
        struct ip_options *opt = NULL;
        struct rtable *rt = (struct rtable *)cork->dst;
        struct iphdr *iph;
        __be16 df = 0;
        __u8 ttl;
    
        skb = __skb_dequeue(queue);/* 发送队列可能为空 */
        if (!skb)
            goto out;
        tail_skb = &(skb_shinfo(skb)->frag_list); /* 获得分片链表 */
    
        /* move skb->data to ip header from ext header */
        if (skb->data < skb_network_header(skb)) /* 调整data指针位置 */
            __skb_pull(skb, skb_network_offset(skb));
        while ((tmp_skb = __skb_dequeue(queue)) != NULL) { /* 调整所有发送缓冲中的sk_buff的data指针位置,并更新第一个sk_buff的数据长度 */
            __skb_pull(tmp_skb, skb_network_header_len(skb));
            *tail_skb = tmp_skb;
            tail_skb = &(tmp_skb->next);
            skb->len += tmp_skb->len;
            skb->data_len += tmp_skb->len;
            skb->truesize += tmp_skb->truesize;
            tmp_skb->destructor = NULL;
            tmp_skb->sk = NULL;
        }
    
        /* Unless user demanded real pmtu discovery (IP_PMTUDISC_DO), we allow
         * to fragment the frame generated here. No matter, what transforms
         * how transforms change size of the packet, it will come out.
         */
        skb->ignore_df = ip_sk_ignore_df(sk);
    
        /* DF bit is set when we want to see DF on outgoing frames.
         * If ignore_df is set too, we still allow to fragment this frame
         * locally. */
        if (inet->pmtudisc == IP_PMTUDISC_DO ||
            inet->pmtudisc == IP_PMTUDISC_PROBE ||
            (skb->len <= dst_mtu(&rt->dst) &&
             ip_dont_fragment(sk, &rt->dst))) /* 不允许分片,或者不需要分片 */
            df = htons(IP_DF);
    
        if (cork->flags & IPCORK_OPT)/* ip option 保存在cork中, 则使用cork中的option */
            opt = cork->opt;
    
        if (cork->ttl != 0)/* 选择合适的TTL值 */
            ttl = cork->ttl;
        else if (rt->rt_type == RTN_MULTICAST)
            ttl = inet->mc_ttl;
        else
            ttl = ip_select_ttl(inet, &rt->dst);
    
        iph = ip_hdr(skb);/* 得到IP报文头的地址 */ 
        iph->version = 4;
        iph->ihl = 5;
        iph->tos = (cork->tos != -1) ? cork->tos : inet->tos;
        iph->frag_off = df;
        iph->ttl = ttl;
        iph->protocol = sk->sk_protocol;
        ip_copy_addrs(iph, fl4);
        ip_select_ident(net, skb, sk);
    
        if (opt) { /* 
            填充IP option
            看到这里,可以发现opt只可能从cork中获得
             */
            iph->ihl += opt->optlen>>2;
            ip_options_build(skb, opt, cork->addr, rt, 0);
        }
    
        skb->priority = (cork->tos != -1) ? cork->priority: sk->sk_priority;
        skb->mark = sk->sk_mark;
        /*
         * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec
         * on dst refcount
         */
        cork->dst = NULL;
        skb_dst_set(skb, &rt->dst);
    
        if (iph->protocol == IPPROTO_ICMP)
            icmp_out_count(net, ((struct icmphdr *)
                skb_transport_header(skb))->type);
    
        ip_cork_release(cork);
    out:
        return skb;
    }
    View Code
    /* Output packet to network from transport.  */
    static inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb)
    {
       /*
         * 如果是单播数据包,设置的是ip_output(),
         * 如果是组播数据包,设置的是ip_mc_output().dev_queue_xmit
         */
        return skb_dst(skb)->output(net, sk, skb);
    }



    int
    __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) { struct iphdr *iph = ip_hdr(skb); iph->tot_len = htons(skb->len); ip_send_check(iph); return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, net, sk, skb, NULL, skb_dst(skb)->dev, dst_output); } int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) { int err; err = __ip_local_out(net, sk, skb); if (likely(err == 1)) err = dst_output(net, sk, skb); return err; }
    http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子
  • 相关阅读:
    .NET内存管理、垃圾回收
    C#容器类,性能介绍
    与LINQ有关的语言特性
    IMEI
    IMSI
    无源码调试smali
    IDA远程调试 在内存中dump Dex文件
    error C4996: 'scanf': This function or variable may be unsafe.
    vue 用axios实现调用接口下载excel
    读《JavaScript权威指南》笔记(三)--对象
  • 原文地址:https://www.cnblogs.com/codestack/p/9265886.html
Copyright © 2011-2022 走看看