Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1242022
  • 博文数量: 122
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4004
  • 用 户 组: 普通用户
  • 注册时间: 2014-02-20 08:27
文章分类
文章存档

2016年(1)

2015年(21)

2014年(100)

分类: LINUX

2014-09-12 17:02:34

1、基本原理
关于UDP相关协议和原理,这里就不啰嗦了~。
简单说,UDP是无连接的协议,相关流程比TCP相对简单,但效率更高。

2、基本流程
Linux内核中协议栈中,UDP报文发送的主要流程如下(仅关注L4):
send(或sendto)系统调用-->
    sock_sendmsg()-->
        __sock_sendmsg()-->
            __sock_sendmsg_nosec()-->
                sock->ops->sendmsg()-->
                    udp_sendmsg()
udp_sendmsg()主要流程如下:
1)前期处理。包括,对数据长度合法性判断、pending数据的判断、目的地址的处理和获取、控制信息的处理、组播处理、connected信息处理、MSG_CONFIRM标志的处理等。
2)调用ip_append_data()接口将其添加到传输控制块(sock)的发送队列中(利用发送队列中的现有skb,或者新创建skb,详细原理和流程请参见ip_append_data()接口的分析)
3)判断是否有cork标记(MSG_MORE),如果没有,则说明需要立即发送,则调用udp_push_pending_frames()接口发送报文,实际是将包提交至IP层;如果设置了cork,则说明需要阻塞等待直到数据达到MTU大小,则完成本次的udp_sendmsg()处理。

3、代码分
UDP报文发送在L4中的主要接口为udp_sendmsg()函数,本文重点分析此函数:
udp_sendmsg():

点击(此处)折叠或打开

  1. /*
  2.   * 用户态sendto/send(先调用connect建立连接)调用接口对应的UDP包发送的内核入口
  3.   * @iocb: 异步控制块(尚未使用?)
  4.   * @sk: sock描述符(传输控制块)
  5.   * @msg: 描述发送数据的msghdr的指针
  6.   * @len: 待发送数据的长度
  7.   */
  8. int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
  9.         size_t len)
  10. {
  11.     // 将sock结构转换为inet_sock结构
  12.     struct inet_sock *inet = inet_sk(sk);
  13.     // 将sock接口转换为udp_sock结构(UDP的传输控制块)
  14.     struct udp_sock *up = udp_sk(sk);
  15.     struct flowi4 fl4_stack;
  16.     struct flowi4 *fl4;
  17.     int ulen = len;
  18.     struct ipcm_cookie ipc;
  19.     struct rtable *rt = NULL;
  20.     int free = 0;
  21.     int connected = 0;
  22.     __be32 daddr, faddr, saddr;
  23.     __be16 dport;
  24.     u8 tos;
  25.     int err, is_udplite = IS_UDPLITE(sk);
  26.     /*设置是否需要cork(阻塞),通常由MSG_MORE标记控制*/
  27.     int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
  28.     /*用户从用户态拷贝实际数据的接口*/
  29.     int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
  30.     struct sk_buff *skb;
  31.     struct ip_options_data opt_copy;
  32.     /*数据长度不能超过64k*/
  33.     if (len > 0xFFFF)
  34.         return -EMSGSIZE;

  35.     /*
  36.      *    Check the flags.
  37.      */

  38.     if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */
  39.         return -EOPNOTSUPP;

  40.     ipc.opt = NULL;
  41.     ipc.tx_flags = 0;
  42.     /*
  43.      * 根据is_udplite标志来指定"从用户态复制数据到UDP分片"的函数,UDP和轻量级UDP的实现共用了一套函数,
  44.      * 只是在计算校验和上有点区别。轻量级UDP可以在发送前(而不是在复制数据到分片中时)对数据前部
  45.      * 指定数目的字节或全部数据执行校验和。而UDP如果是由软件执行校验和(当网卡硬件支持udp checksum offload
  46.      * 并开启相关功能后,校验和由硬件执行),则在复制数据到分片中时对数据包中的全部数据执行校验和。
  47.      * 所以,轻量级UDP和UDP使用不同的"getfrag"函数。
  48.      */
  49.     getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;

  50.     fl4 = &inet->cork.fl.u.ip4;
  51.     /*
  52.      * 当前的sock还有pending的帧(skb)没有发送到IP层,在设置了CORK标记的场景下才会设置这个标记
  53.      *
  54.      */
  55.     if (up->pending) {
  56.         /*
  57.          * There are pending frames.
  58.          * The socket lock must be held while it's corked.
  59.          */
  60.         lock_sock(sk);
  61.         /*
  62.          * 为什么再判断一次?为了提升效率。
  63.          * 因为大部分情况下pending标记是没有的,这样的话就不会进入到这里,就可以省掉一个lock_sock(比较复杂、耗时),仅当设置了pending后,
  64.          * 才加锁并再检查一次,这样就能在大部分情况下不用锁,少数情况下加锁,这种方法是内核中常用的提升效率的策略。
  65.          */
  66.         if (likely(up->pending)) {
  67.             if (unlikely(up->pending != AF_INET)) {/*pending既不是0,又不是AF_INET,那就是有问题了*/
  68.                 release_sock(sk);
  69.                 return -EINVAL;
  70.             }
  71.             /*利用当前sock发送队列中的原有的skb发送数据,将新数据附加到相应skb中的数据区即可*/
  72.             goto do_append_data;
  73.         }
  74.         release_sock(sk);
  75.     }
  76.     /*UDP数据报长度累积,加上UDP头长度*/
  77.     ulen += sizeof(struct udphdr);

  78.     /*
  79.      *    Get and verify the address.
  80.      */
  81.     /*判断msg中是否带了目的地址信息,如果有的话,则通常是由sendto调用进入*/
  82.     if (msg->msg_name) {
  83.         /*从msg中提取目的地址,并判断合法性*/
  84.         struct sockaddr_in *usin = (struct sockaddr_in *)msg->msg_name;
  85.         /*判断长度*/
  86.         if (msg->msg_namelen < sizeof(*usin))
  87.             return -EINVAL;
  88.         /*判断地址族,必须为AF_INET*/
  89.         if (usin->sin_family != AF_INET) {
  90.             if (usin->sin_family != AF_UNSPEC)
  91.                 return -EAFNOSUPPORT;
  92.         }
  93.         /*提取目的地址和端口信息*/
  94.         daddr = usin->sin_addr.s_addr;
  95.         dport = usin->sin_port;
  96.         if (dport == 0)
  97.             return -EINVAL;
  98.     } else {
  99.      /*
  100.      * 如果msg中不带目的地址信息,则通常是用send调用进入,在send之前调用了
  101.          * connect建立连接,建立连接后相应状态为TCP_ESTABLISHED
  102.      */
  103.         if (sk->sk_state != TCP_ESTABLISHED)
  104.             /*如果既不是sendto,又不是connect+send的方式进入的话,那就有问题了*/
  105.             return -EDESTADDRREQ;
  106.         daddr = inet->inet_daddr;
  107.         dport = inet->inet_dport;
  108.         /* Open fast path for connected socket.
  109.          Route will not be used, if at least one option is set.
  110.          */
  111.         /*对于已连接的UDP套接口,设置connected标志,在后续查找路由时用,可以根据此做快速处理*/
  112.         connected = 1;
  113.     }
  114.     ipc.addr = inet->inet_saddr;

  115.     ipc.oif = sk->sk_bound_dev_if;

  116.     sock_tx_timestamp(sk, &ipc.tx_flags);
  117.     /*处理待发送的控制信息,如果msg->msg_controllen不为0,则说明有控制信息需要处理*/
  118.     if (msg->msg_controllen) {
  119.         /*处理控制信息,比如对IP选项的处理*/
  120.         err = ip_cmsg_send(sock_net(sk), msg, &ipc);
  121.         if (err)
  122.             return err;
  123.         /*如果ipc中存在IP选项,则设置free标记,表示需要在处理完成后释放。因为此时的ipc->opt肯定是在ip_cmsg_send中分配的*/
  124.         if (ipc.opt)
  125.             free = 1;
  126.         connected = 0;
  127.     }
  128.     /*如果发送数据中的控制信息中没有IP选项信息,则从inet_sock结构中获取*/
  129.     if (!ipc.opt) {
  130.         struct ip_options_rcu *inet_opt;
  131.         /*这里的rcu锁很关键,老版本中不用就触发了bug*/
  132.         rcu_read_lock();
  133.         inet_opt = rcu_dereference(inet->inet_opt);
  134.         if (inet_opt) {
  135.             memcpy(&opt_copy, inet_opt,
  136.              sizeof(*inet_opt) + inet_opt->opt.optlen);
  137.             ipc.opt = &opt_copy.opt;
  138.         }
  139.         rcu_read_unlock();
  140.     }
  141.     /*由于控制信息需要保存目的地址,因此将源地址保存*/
  142.     saddr = ipc.addr;
  143.     ipc.addr = faddr = daddr;
  144.     /*
  145.      * 如果存在宽松或严格源站选路的IP选项,则不能根据目的地址选路,而需要将IP选项中
  146.      * 下一站地址作为目的地址来选路,因此从IP选项中提取下一站地址,供后续选路时作为
  147.      * 目的地址使用。另外,因为需要重新选路,所以清除connected标记
  148.      */
  149.     if (ipc.opt && ipc.opt->opt.srr) {
  150.         if (!daddr)
  151.             return -EINVAL;
  152.         faddr = ipc.opt->opt.faddr;
  153.         connected = 0;
  154.     }
  155.     tos = RT_TOS(inet->tos);
  156.     /*
  157.      * 如果设置了SOCK_LOCALROUTE或者发送时设置了MSG_DONTROUTE标记,再或者IP选项中存在严格源站选路
  158.      * 选项,则说明目的地址或下一跳必然位于本地子网中。此时需要设置tos中的RTO_ONLINK标记,表示
  159.      * 后续查找路由时与目的地直连。
  160.      */
  161.     if (sock_flag(sk, SOCK_LOCALROUTE) ||
  162.      (msg->msg_flags & MSG_DONTROUTE) ||
  163.      (ipc.opt && ipc.opt->opt.is_strictroute)) {
  164.         tos |= RTO_ONLINK;
  165.         connected = 0;
  166.     }
  167.     /*广播相关处理*/
  168.     if (ipv4_is_multicast(daddr)) {
  169.         if (!ipc.oif)
  170.             ipc.oif = inet->mc_index;
  171.         if (!saddr)
  172.             saddr = inet->mc_addr;
  173.         connected = 0;
  174.     } else if (!ipc.oif)
  175.         ipc.oif = inet->uc_index;
  176.     /*选路相关处理,获取相应的路由缓存项*/
  177.     if (connected)
  178.         rt = (struct rtable *)sk_dst_check(sk, 0);

  179.     if (rt == NULL) {
  180.         struct net *net = sock_net(sk);

  181.         fl4 = &fl4_stack;
  182.         flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos,
  183.                  RT_SCOPE_UNIVERSE, sk->sk_protocol,
  184.                  inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP,
  185.                  faddr, saddr, dport, inet->inet_sport);

  186.         security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
  187.         rt = ip_route_output_flow(net, fl4, sk);
  188.         if (IS_ERR(rt)) {
  189.             err = PTR_ERR(rt);
  190.             rt = NULL;
  191.             if (err == -ENETUNREACH)
  192.                 IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
  193.             goto out;
  194.         }

  195.         err = -EACCES;
  196.         if ((rt->rt_flags & RTCF_BROADCAST) &&
  197.          !sock_flag(sk, SOCK_BROADCAST))
  198.             goto out;
  199.         if (connected)
  200.             sk_dst_set(sk, dst_clone(&rt->dst));
  201.     }
  202.     /*如果设置了MSG_CONFIRM标记,表明应用层确认网关有效并可达,则直接跳转到do_confirm对目的路由缓存项进行确认*/
  203.     if (msg->msg_flags&MSG_CONFIRM)
  204.         goto do_confirm;
  205. back_from_confirm:
  206.     /*
  207.      * 从获取到的路由中获取源地址和目的地址。在发送UDP数据时其实可以不指定目的地址,而在通过在发送控制信
  208.      * 息中加入严格或宽松源站选路选项,所以如此此时还没有获取目的地址,则需从路由缓存项中获取
  209.      */
  210.     saddr = fl4->saddr;
  211.     if (!ipc.addr)
  212.         daddr = ipc.addr = fl4->daddr;

  213.     /* Lockless fast path for the non-corking case. */
  214.     /*快速路径(大部分情况下都没有使用CORK,此时不需要lock_sock,能避免锁带来的开销,提升效率)。不需要cork时,直接新建skb,并直接发送*/
  215.     if (!corkreq) {
  216.         /*
  217.            * 将sock相应的skb队列中的所有skb合并成一个数据报文(skb),实际使用skb_shinfo->frag_list将所有skb连接起来。
  218.          * 为什么要这样?这里将所有skb都合并后,可能导致这个包的size大于mtu,那到IP层的时候还会进行进一步分片?
  219.          * 原因是:udp是面向数据报文的,报文必须完整,属于同一个报文的数据必须要放到同一个skb中,否则对端无法知道这是同一个报文。
  220.          * 那IP层怎么处理呢?就不考虑同一个报文的问题?IP层分片会携带相关头信息,对端会根据这些信息进行重组,重组后对传输层来说就是一个报文。
  221.          * 分片其实就是IP层应该负责的。此时IP分片实际就是将原来skb->frag_list中的skb摘出来,不会做其它的操作,效率很高。
  222.            */
  223.         skb = ip_make_skb(sk, fl4, getfrag, msg->msg_iov, ulen,
  224.                  sizeof(struct udphdr), &ipc, &rt,
  225.                  msg->msg_flags);
  226.         err = PTR_ERR(skb);
  227.         if (!IS_ERR_OR_NULL(skb))
  228.             /*直接发送数据*/
  229.             err = udp_send_skb(skb, fl4);
  230.         goto out;
  231.     }
  232.     /*要往skb中添加数据了并访问sock中的相关数据了,此时需要加锁了,因为同一个socket可能被多个进程在多个CPU上同时访问*/
  233.     lock_sock(sk);
  234.     if (unlikely(up->pending)) {
  235.         /* The socket is already corked while preparing it. */
  236.         /* ... which is an evident application bug. --ANK */
  237.         /*
  238.          * 运行到这里说明,设置了cork,而在进入此函数的开始时已经检测过pending了,pending为空时才会走到这里,
  239.           * 而这里再次检查时,pending又有了,此时说明有多个进程在同时使用socket,其它核上的流程进行了相关修改,此时认定为
  240.           * 应用程序的bug。而实际上如果对访问pending时进行加锁保护,也不会有问题,但是这个pending不太好加锁,因为外面也在
  241.           * 访问比如udp_sendpage中,此时很容易死锁。所以,无奈之下,这里进行了这样的判断,认定为是app bug。
  242.          */
  243.         release_sock(sk);

  244.         LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("cork app bug 2\n"));
  245.         err = -EINVAL;
  246.         goto out;
  247.     }
  248.     /*
  249.      *    Now cork the socket to pend data.
  250.      */
  251.     /*缓存目的地址、目的端口、源地址和源端口信息,便于在发送处理时方便获取信息。*/
  252.     fl4 = &inet->cork.fl.u.ip4;
  253.     fl4->daddr = daddr;
  254.     fl4->saddr = saddr;
  255.     fl4->fl4_dport = dport;
  256.     fl4->fl4_sport = inet->inet_sport;
  257.     /*设置AF_INET标记,表明正在处理UDP数据包*/
  258.     up->pending = AF_INET;
  259. /*
  260.   * 运行到这里,有两种情况:1.最初进入函数时pending=AF_INET,即有udp数据包正在处理中;2.设置了cork,
  261.   * 则表明需要阻塞,使用原有的skb一起发送数据.
  262.   */
  263. do_append_data:
  264.     /*增加包长*/
  265.     up->len += ulen;
  266.     /*
  267.      * 调用IP层接口函数ip_append_data,进入IP层处理,主要工作为:
  268.      * 将数据拷贝到适合的skb(利用发送队列中现有的或新创建)中,可能有两种情况: 1. 放入skb的线性
  269.      *(skb->data)中,或者放入skb_shared_info的分片(frag)中,同时还需要考虑MTU对skb数据进行分割。
  270.      */
  271.     err = ip_append_data(sk, fl4, getfrag, msg->msg_iov, ulen,
  272.              sizeof(struct udphdr), &ipc, &rt,
  273.              corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
  274.     if (err)
  275.         /*出错则清空所有pending的数据帧,并清空pending标记*/
  276.         udp_flush_pending_frames(sk);
  277.     /*未设置cork(如果设置了cork,则需要等待组成64k大小的UDP数据报后再发送),则直接发送数据到IP层*/
  278.     else if (!corkreq)
  279.         err = udp_push_pending_frames(sk);
  280.     else if (unlikely(skb_queue_empty(&sk->sk_write_queue)))
  281.         /*如果发送队列为空,则说明没有数据正在处理了,则复位pending标记*/
  282.         up->pending = 0;
  283.     /*Fixme:为什么此时把sock释放了,如果数据还没发完咋办?因为发送队列传递到下一层了?*/
  284.     release_sock(sk);

  285. out:
  286.     /*发送完成,不再需要路由信息,因此递减对路由的引用计数*/
  287.     ip_rt_put(rt);
  288.     /*之前设置的free标记,用于是否IP选项*/
  289.     if (free)
  290.         kfree(ipc.opt);
  291.     /*如果发送成功,则返回发送数据的字节数*/
  292.     if (!err)
  293.         return len;
  294.     /*
  295.      * ENOBUFS = no kernel mem, SOCK_NOSPACE = no sndbuf space. Reporting
  296.      * ENOBUFS might not be good (it's not tunable per se), but otherwise
  297.      * we don't have a good statistic (IpOutDiscards but it can be too many
  298.      * things). We could add another new stat but at least for now that
  299.      * seems like overkill.
  300.      */
  301.     /*如果发送失败,则返回相应的错误码。*/
  302.     if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
  303.         UDP_INC_STATS_USER(sock_net(sk),
  304.                 UDP_MIB_SNDBUFERRORS, is_udplite);
  305.     }
  306.     return err;
  307. /*发送时设置了MSG_CONFIRM标记时,跳转到这里*/
  308. do_confirm:
  309.     /*应用层确认网关可达,因此直接对目的路由缓存项进行确认。*/
  310.     dst_confirm(&rt->dst);
  311.     /*
  312.         * MSG_PROBE标志用于发现路径,并非真正发送数据。因此此处检查此标记,如果设置,
  313.      * 这直接跳转到out,退出;如果未设置,且有需要发送的数据,则跳回到back_from_confirm
  314.      * 处理相关数据。
  315.      */
  316.     if (!(msg->msg_flags&MSG_PROBE) || len)
  317.         goto back_from_confirm;
  318.     err = 0;
  319.     goto out;
  320. }

udp_sendmsg()-->udp_push_pending_frames():

点击(此处)折叠或打开

  1. /*
  2.  * Push out all pending data as one UDP datagram. Socket is locked.
  3.  */
  4. /*
  5.   * 将待发送的数据打包成一个UDP数据报发送。
  6.   */
  7. int udp_push_pending_frames(struct sock *sk)
  8. {
  9.     struct udp_sock *up = udp_sk(sk);
  10.     struct inet_sock *inet = inet_sk(sk);
  11.     struct flowi4 *fl4 = &inet->cork.fl.u.ip4;
  12.     struct sk_buff *skb;
  13.     int err = 0;
  14.     /*
  15.      * 将sock相应的skb队列中的所有skb合并成一个数据报文(skb),实际使用skb_shinfo->frag_list将所有skb连接起来。
  16.      */
  17.     skb = ip_finish_skb(sk, fl4);
  18.     if (!skb)
  19.         goto out;
  20.     /*将组合后的skb发送出去,交由IP层处理*/
  21.     err = udp_send_skb(skb, fl4);

  22. out:
  23.     /*发送完成后清除pending标记,并向上层返回发送的字节数或者错误*/
  24.     up->len = 0;
  25.     up->pending = 0;
  26.     return err;
  27. }

udp_push_pending_frames()-->ip_finish_skb()-->__ip_make_skb():

点击(此处)折叠或打开

  1. /*
  2.  *    Combined all pending IP fragments on the socket as one IP datagram
  3.  *    and push them out.
  4.  */
  5. /*
  6.   * 将sock相应的skb队列中的所有skb合并成一个数据报文(skb),实际使用skb_shinfo->frag_list将所有skb连接起来。
  7.   */

  8. struct sk_buff *__ip_make_skb(struct sock *sk,
  9.              struct flowi4 *fl4,
  10.              struct sk_buff_head *queue,
  11.              struct inet_cork *cork)
  12. {
  13.     struct sk_buff *skb, *tmp_skb;
  14.     struct sk_buff **tail_skb;
  15.     struct inet_sock *inet = inet_sk(sk);
  16.     struct net *net = sock_net(sk);
  17.     struct ip_options *opt = NULL;
  18.     struct rtable *rt = (struct rtable *)cork->dst;
  19.     struct iphdr *iph;
  20.     __be16 df = 0;
  21.     __u8 ttl;

  22.     if ((skb = __skb_dequeue(queue)) == NULL)
  23.         goto out;
  24.     /*使用skb_shinfo->frag_list,将所有的skb合并成一个*/
  25.     tail_skb = &(skb_shinfo(skb)->frag_list);

  26.     /* move skb->data to ip header from ext header */
  27.     /*将skb->data指向IP头部,__skb_pull将skb->data指向向后启动offset距离,同时skb->len减小offset*/
  28.     if (skb->data < skb_network_header(skb))
  29.         __skb_pull(skb, skb_network_offset(skb));
  30.     /*从skb队列中逐个取出skb,并进行合并*/
  31.     while ((tmp_skb = __skb_dequeue(queue)) != NULL) {
  32.         __skb_pull(tmp_skb, skb_network_header_len(skb));
  33.         /*用当前skb的skb_shinfo(skb)->frag_list指向新取出的skb,使其链入同一个skb中*/
  34.         *tail_skb = tmp_skb;
  35.         /*移向下一个,实现逐个串联*/
  36.         tail_skb = &(tmp_skb->next);
  37.         /*skb->len(包括线性和非线性区数据的总长度)增加*/
  38.         skb->len += tmp_skb->len;
  39.         /*skb->data_len(仅包括非线性区的数据长度,包括frags和frag_list中的数据)增加len,因为后面的skb都链入frag_list列表了*/
  40.         skb->data_len += tmp_skb->len;
  41.         /*skb->truesize(包括数据和skb结构自身的总长度)增加*/
  42.         skb->truesize += tmp_skb->truesize;
  43.         /*删除已被合并的skb*/
  44.         tmp_skb->destructor = NULL;
  45.         tmp_skb->sk = NULL;
  46.     }

  47.     /* Unless user demanded real pmtu discovery (IP_PMTUDISC_DO), we allow
  48.      * to fragment the frame generated here. No matter, what transforms
  49.      * how transforms change size of the packet, it will come out.
  50.      */
  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.     if (inet->pmtudisc >= IP_PMTUDISC_DO ||
  57.      (skb->len <= dst_mtu(&rt->dst) &&
  58.      ip_dont_fragment(sk, &rt->dst)))
  59.         df = htons(IP_DF);

  60.     if (cork->flags & IPCORK_OPT)
  61.         opt = cork->opt;

  62.     if (rt->rt_type == RTN_MULTICAST)
  63.         ttl = inet->mc_ttl;
  64.     else
  65.         ttl = ip_select_ttl(inet, &rt->dst);

  66.     iph = ip_hdr(skb);
  67.     iph->version = 4;
  68.     iph->ihl = 5;
  69.     iph->tos = inet->tos;
  70.     iph->frag_off = df;
  71.     iph->ttl = ttl;
  72.     iph->protocol = sk->sk_protocol;
  73.     ip_copy_addrs(iph, fl4);
  74.     ip_select_ident(iph, &rt->dst, sk);

  75.     if (opt) {
  76.         iph->ihl += opt->optlen>>2;
  77.         ip_options_build(skb, opt, cork->addr, rt, 0);
  78.     }

  79.     skb->priority = sk->sk_priority;
  80.     skb->mark = sk->sk_mark;
  81.     /*
  82.      * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec
  83.      * on dst refcount
  84.      */
  85.     cork->dst = NULL;
  86.     skb_dst_set(skb, &rt->dst);

  87.     if (iph->protocol == IPPROTO_ICMP)
  88.         icmp_out_count(net, ((struct icmphdr *)
  89.             skb_transport_header(skb))->type);

  90.     ip_cork_release(cork);
  91. out:
  92.     return skb;
  93. }

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