Chinaunix首页 | 论坛 | 博客
  • 博客访问: 647285
  • 博文数量: 363
  • 博客积分: 110
  • 博客等级: 民兵
  • 技术积分: 1347
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-22 16:07
文章分类

全部博文(363)

文章存档

2018年(83)

2016年(1)

2014年(2)

2013年(34)

2012年(236)

2011年(7)

分类: LINUX

2018-02-01 12:47:47

作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net

今天开始学习新的函数ip_push_pending_frames,这个函数会被icmp_push_reply,ip_send_reply,raw_sendmsg,和udp_push_pending_frames调用。该函数用于将该socket上的所有pending的IP分片,组成一个IP报文发送出去。

下面是函数的具体的代码。
  1. int ip_push_pending_frames(struct sock *sk)
  2. {
  3.     struct sk_buff *skb, *tmp_skb;
  4.     struct sk_buff **tail_skb;
  5.     struct inet_sock *inet = inet_sk(sk);
  6.     struct net *net = sock_net(sk);
  7.     struct ip_options *opt = NULL;
  8.     struct rtable *rt = (struct rtable *)inet->cork.dst;
  9.     struct iphdr *iph;
  10.     __be16 df = 0;
  11.     __u8 ttl;
  12.     int err = 0;
     /* 发送队列可能为空 */
  1.     if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)
  2.         goto out;
  3.     /* 获得分片链表 */
  4.     tail_skb = &(skb_shinfo(skb)->frag_list);

  5.     /* move skb->data to ip header from ext header */
  6.     /* 调整data指针位置 */
  7.     if (skb->data < skb_network_header(skb))
  8.         __skb_pull(skb, skb_network_offset(skb));
     /* 调整所有发送缓冲中的sk_buff的data指针位置,并更新第一个sk_buff的数据长度 */
  1.     while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
  2.         __skb_pull(tmp_skb, skb_network_header_len(skb));
  3.         *tail_skb = tmp_skb;
  4.         tail_skb = &(tmp_skb->next);
  5.         skb->len = tmp_skb->len;
  6.         skb->data_len = tmp_skb->len;
  7.         skb->truesize = tmp_skb->truesize;
  8.         tmp_skb->destructor = NULL;
  9.         tmp_skb->sk = NULL;
  10.     }

  11.     /* Unless user demanded real pmtu discovery (IP_PMTUDISC_DO), we allow
  12.      * to fragment the frame generated here. No matter, what transforms
  13.      * how transforms change size of the packet, it will come out.
  14.      */
  15.     /* 允许本地分片 */
  16.     if (inet->pmtudisc < IP_PMTUDISC_DO)
  17.         skb->local_df = 1;

  18.     /* DF bit is set when we want to see DF on outgoing frames.
  19.      * If local_df is set too, we still allow to fragment this frame
  20.      * locally. */
  21.     /* 不允许分片,或者不需要分片 */
  22.     if (inet->pmtudisc >= IP_PMTUDISC_DO ||
  23.      (skb->len <= dst_mtu(&rt->dst) &&
  24.      ip_dont_fragment(sk, &rt->dst)))
  25.         df = htons(IP_DF);
      /* ip option 保存在cork中, 则使用cork中的option */
  1.     if (inet->cork.flags & IPCORK_OPT)
  2.         opt = inet->cork.opt;
      /* 选择合适的TTL值 */
  1.     if (rt->rt_type == RTN_MULTICAST)
  2.         ttl = inet->mc_ttl;
  3.     else
  4.         ttl = ip_select_ttl(inet, &rt->dst);
     /* 得到IP报文头的地址 */ 
  1.     iph = (struct iphdr *)skb->data;
  2.     /* 初始化IP报文头的内容 */
  3.     iph->version = 4;
  4.     iph->ihl = 5;
  5.     if (opt) {
  6.         /*
            填充IP option
  7.         看到这里,可以发现opt只可能从cork中获得
  8.          */
  9.         iph->ihl = opt->optlen>>2;
  10.         ip_options_build(skb, opt, inet->cork.addr, rt, 0);
  11.     }
  12.     iph->tos = inet->tos;
  13.     iph->frag_off = df;
  14.     ip_select_ident(iph, &rt->dst, sk);
  15.     iph->ttl = ttl;
  16.     iph->protocol = sk->sk_protocol;
  17.     iph->saddr = rt->rt_src;
  18.     iph->daddr = rt->rt_dst;

  19.     skb->priority = sk->sk_priority;
  20.     skb->mark = sk->sk_mark;
  21.     /*
  22.      * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec
  23.      * on dst refcount
  24.      */
  25.     inet->cork.dst = NULL;
  26.     skb_dst_set(skb, &rt->dst);

  27.     if (iph->protocol == IPPROTO_ICMP)
  28.         icmp_out_count(net, ((struct icmphdr *)
  29.             skb_transport_header(skb))->type);

  30.     /* Netfilter gets whole the not fragmented skb. */
  31.     /* 发送数据 */
  32.     err = ip_local_out(skb);
  33.     if (err) {
  34.         if (err > 0)
  35.             err = net_xmit_errno(err);
  36.         if (err)
  37.             goto error;
  38.     }

  39. out:
  40.     ip_cork_release(inet);
  41.     return err;

  42. error:
  43.     IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
  44.     goto out;
  45. }

这个函数比较容易看懂——当然不是所有细节都非常清楚了。
第一遍浏览这些API,只要搞懂该API的大致流程和用途。当整个框架建立好以后,再慢慢补充细节。




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