UDP socket流程(13)——ip_push_pending_frames

简介: 作者:gfree.wind@gmail.com博客:linuxfocus.blog.chinaunix.net今天开始学习新的函数ip_push_pending_frames,这个函数会被icmp_push_reply,ip_send_reply,raw_sendmsg,和udp_push_pending_frames调用。
作者: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的大致流程和用途。当整个框架建立好以后,再慢慢补充细节。




目录
相关文章
|
6月前
|
安全 网络协议 Java
Thread类的用法 && 线程安全 && 多线程代码案例 && 文件操作和 IO && 网络原理初识 &&UDP socket
Thread类的用法 && 线程安全 && 多线程代码案例 && 文件操作和 IO && 网络原理初识 &&UDP socket
39 0
|
2天前
|
存储 网络协议 关系型数据库
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
|
18天前
|
Python
Python网络编程基础(Socket编程)UDP服务器编程
【4月更文挑战第8天】Python UDP服务器编程使用socket库创建UDP套接字,绑定到特定地址(如localhost:8000),通过`recvfrom`接收客户端数据报,显示数据长度、地址和内容。无连接的UDP协议使得服务器无法主动发送数据,通常需应用层实现请求-响应机制。当完成时,用`close`关闭套接字。
|
1月前
|
网络协议 Linux
TCP 和 UDP 的 Socket 调用
【2月更文挑战第19天】
TCP 和 UDP 的 Socket 调用
|
4月前
|
存储 网络协议 安全
网络编程『socket套接字 ‖ 简易UDP网络程序』
网络编程『socket套接字 ‖ 简易UDP网络程序』
77 0
|
4月前
|
网络协议
百度搜索:蓝易云【基于TCP/UDP的Socket编程】
通过使用上述示例,您可以基于TCP或UDP协议进行Socket编程,实现网络通信功能。根据您的需求,可以进一步扩展和定制这些示例代码。
37 1
|
3月前
|
网络协议
百度搜索:蓝易云【基于TCP/UDP的Socket编程。】
以上是基于TCP/UDP的Socket编程的基本步骤和函数调用。通过理解和掌握这些概念和操作,可以实现网络应用程序的数据传输和通信功能。
49 1
|
6月前
|
域名解析 存储 移动开发
TCP socket && UDP && TCP协议 && IP协议 && 以太网等
TCP socket && UDP && TCP协议 && IP协议 && 以太网等
37 0
|
6月前
|
存储 网络协议 Java
网络编程:UDP socket
网络编程:UDP socket
57 0
|
7月前
|
网络协议 安全
基于TCP和UDP的Socket通信
TCP是面向连接的,安全的协议,它是一对一的关系 udp是面向无连接的,不安全,不可靠的,但是效率很高,支持一对一,一对多,多对多发送,udp传输的格式为数据报,要将其封装为数据报才能发送,
50 1