分享

ip_push_pending_frames函数简单注释

 enchen008 2013-09-11
下面这个函数主要由udp调用,
调用几次ip_append_data之后udp缓存中有一些数据
这时想要将数据发送出去就调用ip_push_pending_frames
这个函数将skb从第一个skb的next指针取下,链入frag_list指针
然后将后面skb的len全部加到第一个skb的len,datalen,truesize上
最后填充ip首部并将包发送出去。
//输入参数
struck sock *sk: 包含待发送数据的sock
//局部变量说明
struct sk_buff *skb:指向skb列表的第一个skb指针
struct sk_buff *tmp_skb:循环时当前准备加入链表的skb
struct sk_buff **tail_skb:指向当前最后一个链入frag_list的skb的next指针
struct rtable *rt:路由表缓存项
  1. /* 
  2.  *  Combined all pending IP fragments on the socket as one IP datagram 
  3.  *  and push them out. 
  4.  */  
  5. int ip_push_pending_frames(struct sock *sk)  
  6. {  
  7.     struct sk_buff *skb, *tmp_skb;  
  8.     struct sk_buff **tail_skb;  
  9.     struct inet_sock *inet = inet_sk(sk);  
  10.     struct net *net = sock_net(sk);  
  11.     struct ip_options *opt = NULL;  
  12.     struct rtable *rt = (struct rtable *)inet->cork.dst;  
  13.     struct iphdr *iph;  
  14.     __be16 df = 0;  
  15.     __u8 ttl;  
  16.     int err = 0;  
  17.     //从sock的sk_write_queue上取下第一个skbuff,赋给skb   
  18.     if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)  
  19.         goto out;  
  20.     //将tail_skb指向第一个skb的frag_list指针,此步和上一步算是为循环作准备   
  21.     tail_skb = &(skb_shinfo(skb)->frag_list);  
  22.     /* move skb->data to ip header from ext header */  
  23.     //将skb的data指针指向ip首部,越过exthdr   
  24.     if (skb->data < skb_network_header(skb))  
  25.         __skb_pull(skb, skb_network_offset(skb));  
  26.     //主循环开始,每次循环从sock的sk_write_queue中取下一个skbuf,   
  27.     //链入上一个skb的frag_list域,直到sk_write_queue为空。两点要注意   
  28.     //(1).每次循环会改变第一个skb的三个变量skb->len和skb->data_len会加上tmp_skb->len,   
  29.     //skb->truesize加上tmp_skb->truesize,也就是说,所有skb的len和data_len以前是分开算的,   
  30.     //现在把所有的长度都算到第一个skb上了。   
  31.     //(2).这里会调用__sock_put(tmp_skb->sk)来递减sock的引用计数,   
  32.     //这个计数曾在ip_append_data的sock_alloc_send_skb --> sock_alloc_send_pskb --> skb_set_owner_w   
  33.     //和sock_wmalloc --> skb_set_owner_w中递增过。同时将tmp_skb->sk设置成NULL,   
  34.     //这样一来这个分片就已经完全脱离socket而交给了内核协议栈。   
  35.     while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {  
  36.         __skb_pull(tmp_skb, skb_network_header_len(skb));  
  37.         *tail_skb = tmp_skb;  
  38.         tail_skb = &(tmp_skb->next);  
  39.         skb->len += tmp_skb->len;  
  40.         skb->data_len += tmp_skb->len;  
  41.         skb->truesize += tmp_skb->truesize;  
  42.         __sock_put(tmp_skb->sk);  
  43.         tmp_skb->destructor = NULL;  
  44.         tmp_skb->sk = NULL;  
  45.     }  
  46.     /* Unless user demanded real pmtu discovery (IP_PMTUDISC_DO), we allow 
  47.      * to fragment the frame generated here. No matter, what transforms 
  48.      * how transforms change size of the packet, it will come out. 
  49.      */  
  50.     //以下条件表示pmtu发现使用WANT或者DONT,即没有强制要求路径MTU发现,则将skb->local_df置1   
  51.     if (inet->pmtudisc < IP_PMTUDISC_DO)  
  52.         skb->local_df = 1;  
  53.     /* DF bit is set when we want to see DF on outgoing frames. 
  54.      * If local_df is set too, we still allow to fragment this frame 
  55.      * locally. */  
  56.     //如果满足以下几个条件之一则将df设置为IP_DF(即把ip首部DF置1),   
  57.     //(1)要求了PMTU发现   
  58.     //(2)数据包总长度小于目标路由的mtu并且ip_dont_fragment返回真。ip_dont_fragment测试条件如下:   
  59.     //(inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO || (inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT /   
  60.     //&& !(dst_metric_locked(dst, RTAX_MTU))));最后一个dst_metric_locked还不明白什么意思。   
  61.     if (inet->pmtudisc >= IP_PMTUDISC_DO ||  
  62.         (skb->len <= dst_mtu(&rt->u.dst) &&  
  63.          ip_dont_fragment(sk, &rt->u.dst)))  
  64.         df = htons(IP_DF);  
  65.     //如果socket的cork的flags包含IPCORK_OPT则表明在cork中缓存有ip首部选项,这时将选项读入opt变量。   
  66.     if (inet->cork.flags & IPCORK_OPT)  
  67.         opt = inet->cork.opt;  
  68.     //检查目标路由的类型,如果是多播则将ttl设置为inet->mc_ttl(书上说这个值默认是1,因为多播不能夸局域网),   
  69.     //单播则用ip_select_ttl来设置ttl   
  70.     if (rt->rt_type == RTN_MULTICAST)  
  71.         ttl = inet->mc_ttl;  
  72.     else  
  73.         ttl = ip_select_ttl(inet, &rt->u.dst);  
  74.     //设置ip首部,版本号4,首部长度=5+opt->optlen>>2,调用ip_options_build建立ip首部选项。   
  75.     //tos=sock->tos,用df设置DF位,调用ip_select_ident设置ip的id字段,用sock->protocol设置protocol,   
  76.     //用rt的目的地址和源地址设置ip首部的dstip和srcip。   
  77.     iph = (struct iphdr *)skb->data;  
  78.     iph->version = 4;  
  79.     iph->ihl = 5;  
  80.     if (opt) {  
  81.         iph->ihl += opt->optlen>>2;  
  82.         ip_options_build(skb, opt, inet->cork.addr, rt, 0);  
  83.     }  
  84.     iph->tos = inet->tos;  
  85.     iph->frag_off = df;  
  86.     ip_select_ident(iph, &rt->u.dst, sk);  
  87.     iph->ttl = ttl;  
  88.     iph->protocol = sk->sk_protocol;  
  89.     iph->saddr = rt->rt_src;  
  90.     iph->daddr = rt->rt_dst;  
  91.     skb->priority = sk->sk_priority;  
  92.     skb->mark = sk->sk_mark;  
  93.     skb->dst = dst_clone(&rt->u.dst);  
  94.     //如果ip->protocol是icmp的话,调用icmp_out_count计算icmp的一些统计信息,   
  95.     if (iph->protocol == IPPROTO_ICMP)  
  96.         icmp_out_count(net, ((struct icmphdr *)  
  97.             skb_transport_header(skb))->type);  
  98.     /* Netfilter gets whole the not fragmented skb. */  
  99.     err = ip_local_out(skb);  
  100.     if (err) {  
  101.         if (err > 0)  
  102.             err = inet->recverr ? net_xmit_errno(err) : 0;  
  103.         if (err)  
  104.             goto error;  
  105.     }  
  106. out:  
  107.     //发送完毕,调用ip_cork_release释放sock的cork成员   
  108.     //从ip_append_data可以看到cork是在第一个片段被缓存时分配,与这里遥相呼应   
  109.     ip_cork_release(inet);  
  110.     return err;  
  111. error:  
  112.     IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);  
  113.     goto out;  
  114. }  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多