概述

ip_queue_xmit是ip层提供给tcp层发送回调,大多数tcp发送都会使用这个回调,tcp层使用tcp_transmit_skb封装了tcp头之后,调用该函数,该函数提供了路由查找校验、封装ip头和ip选项的功能,封装完成之后调用ip_local_out发送数据包;

ip_build_and_send_pkt函数是服务器端在给客户端回复syn+ack时调用的,该函数在构造ip头之后,调用ip_local_out发送数据包;

ip_send_unicast_reply函数目前只用于发送ACK和RST,该函数根据对端发过来的skb构造ip头,然后调用ip_append_data向发送队列中附加/新增数据,最后调用ip_push_pending_frames发送数据包;

源码分析
  1. /* Note: skb->sk can be different from sk, in case of tunnels */
  2. int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
  3. {
  4. struct inet_sock *inet = inet_sk(sk);
  5. struct net *net = sock_net(sk);
  6. struct ip_options_rcu *inet_opt;
  7. struct flowi4 *fl4;
  8. struct rtable *rt;
  9. struct iphdr *iph;
  10. int res;
  11.  
  12. /* Skip all of this if the packet is already routed,
  13. * f.e. by something like SCTP.
  14. */
  15. rcu_read_lock();
  16. inet_opt = rcu_dereference(inet->inet_opt);
  17. fl4 = &fl->u.ip4;
  18.  
  19. /* 获取skb中的路由缓存 */
  20. rt = skb_rtable(skb);
  21.  
  22. /* skb中有缓存则跳转处理 */
  23. if (rt)
  24. goto packet_routed;
  25.  
  26. /* Make sure we can route this packet. */
  27. /* 检查控制块中的路由缓存 */
  28. rt = (struct rtable *)__sk_dst_check(sk, );
  29. /* 缓存过期 */
  30. if (!rt) {
  31. __be32 daddr;
  32.  
  33. /* Use correct destination address if we have options. */
  34. /* 目的地址 */
  35. daddr = inet->inet_daddr;
  36.  
  37. /* 严格路由选项 */
  38. if (inet_opt && inet_opt->opt.srr)
  39. daddr = inet_opt->opt.faddr;
  40.  
  41. /* If this fails, retransmit mechanism of transport layer will
  42. * keep trying until route appears or the connection times
  43. * itself out.
  44. */
  45. /* 查找路由缓存 */
  46. rt = ip_route_output_ports(net, fl4, sk,
  47. daddr, inet->inet_saddr,
  48. inet->inet_dport,
  49. inet->inet_sport,
  50. sk->sk_protocol,
  51. RT_CONN_FLAGS(sk),
  52. sk->sk_bound_dev_if);
  53. /* 失败 */
  54. if (IS_ERR(rt))
  55. goto no_route;
  56.  
  57. /* 设置控制块的路由缓存 */
  58. sk_setup_caps(sk, &rt->dst);
  59. }
  60.  
  61. /* 将路由设置到skb中 */
  62. skb_dst_set_noref(skb, &rt->dst);
  63.  
  64. packet_routed:
  65. /* 严格路由选项 &&使用网关,无路由 */
  66. if (inet_opt && inet_opt->opt.is_strictroute && rt->rt_uses_gateway)
  67. goto no_route;
  68.  
  69. /* OK, we know where to send it, allocate and build IP header. */
  70. /* 加入ip头 */
  71. skb_push(skb, sizeof(struct iphdr) + (inet_opt ? inet_opt->opt.optlen : ));
  72. skb_reset_network_header(skb);
  73.  
  74. /* 构造ip头 */
  75. iph = ip_hdr(skb);
  76. *((__be16 *)iph) = htons(( << ) | ( << ) | (inet->tos & 0xff));
  77. if (ip_dont_fragment(sk, &rt->dst) && !skb->ignore_df)
  78. iph->frag_off = htons(IP_DF);
  79. else
  80. iph->frag_off = ;
  81. iph->ttl = ip_select_ttl(inet, &rt->dst);
  82. iph->protocol = sk->sk_protocol;
  83. ip_copy_addrs(iph, fl4);
  84.  
  85. /* Transport layer set skb->h.foo itself. */
  86. /* 构造ip选项 */
  87. if (inet_opt && inet_opt->opt.optlen) {
  88. iph->ihl += inet_opt->opt.optlen >> ;
  89. ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, );
  90. }
  91.  
  92. /* 设置id */
  93. ip_select_ident_segs(net, skb, sk,
  94. skb_shinfo(skb)->gso_segs ?: );
  95.  
  96. /* TODO : should we use skb->sk here instead of sk ? */
  97. /* QOS等级 */
  98. skb->priority = sk->sk_priority;
  99. skb->mark = sk->sk_mark;
  100.  
  101. /* 输出 */
  102. res = ip_local_out(net, sk, skb);
  103. rcu_read_unlock();
  104. return res;
  105.  
  106. no_route:
  107. /* 无路由处理 */
  108. rcu_read_unlock();
  109. IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
  110. kfree_skb(skb);
  111. return -EHOSTUNREACH;
  112. }
  1. int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk,
  2. __be32 saddr, __be32 daddr, struct ip_options_rcu *opt)
  3. {
  4. struct inet_sock *inet = inet_sk(sk);
  5. struct rtable *rt = skb_rtable(skb);
  6. struct net *net = sock_net(sk);
  7. struct iphdr *iph;
  8.  
  9. /* Build the IP header. */
  10. /* 构造ip头 */
  11. skb_push(skb, sizeof(struct iphdr) + (opt ? opt->opt.optlen : ));
  12. skb_reset_network_header(skb);
  13. iph = ip_hdr(skb);
  14. iph->version = ;
  15. iph->ihl = ;
  16. iph->tos = inet->tos;
  17. iph->ttl = ip_select_ttl(inet, &rt->dst);
  18. iph->daddr = (opt && opt->opt.srr ? opt->opt.faddr : daddr);
  19. iph->saddr = saddr;
  20. iph->protocol = sk->sk_protocol;
  21.  
  22. /* 分片与否 */
  23. if (ip_dont_fragment(sk, &rt->dst)) {
  24. iph->frag_off = htons(IP_DF);
  25. iph->id = ;
  26. } else {
  27. iph->frag_off = ;
  28. __ip_select_ident(net, iph, );
  29. }
  30.  
  31. /* 选项 */
  32. if (opt && opt->opt.optlen) {
  33. iph->ihl += opt->opt.optlen>>;
  34. ip_options_build(skb, &opt->opt, daddr, rt, );
  35. }
  36.  
  37. /* QOS优先级 */
  38. skb->priority = sk->sk_priority;
  39. skb->mark = sk->sk_mark;
  40.  
  41. /* Send it out. */
  42. /* 输出 */
  43. return ip_local_out(net, skb->sk, skb);
  44. }
  1. void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,
  2. const struct ip_options *sopt,
  3. __be32 daddr, __be32 saddr,
  4. const struct ip_reply_arg *arg,
  5. unsigned int len)
  6. {
  7. struct ip_options_data replyopts;
  8. struct ipcm_cookie ipc;
  9. struct flowi4 fl4;
  10. struct rtable *rt = skb_rtable(skb);
  11. struct net *net = sock_net(sk);
  12. struct sk_buff *nskb;
  13. int err;
  14. int oif;
  15.  
  16. /* 获取ip选项 */
  17. if (__ip_options_echo(&replyopts.opt.opt, skb, sopt))
  18. return;
  19.  
  20. ipc.addr = daddr;
  21. ipc.opt = NULL;
  22. ipc.tx_flags = ;
  23. ipc.ttl = ;
  24. ipc.tos = -;
  25.  
  26. /* 选项存在 */
  27. if (replyopts.opt.opt.optlen) {
  28. ipc.opt = &replyopts.opt;
  29.  
  30. /* 源路由存在,设置下一跳ip地址为目的地址 */
  31. if (replyopts.opt.opt.srr)
  32. daddr = replyopts.opt.opt.faddr;
  33. }
  34.  
  35. /* 输出接口设置 */
  36. oif = arg->bound_dev_if;
  37. if (!oif && netif_index_is_l3_master(net, skb->skb_iif))
  38. oif = skb->skb_iif;
  39.  
  40. /* 查路由 */
  41. flowi4_init_output(&fl4, oif,
  42. IP4_REPLY_MARK(net, skb->mark),
  43. RT_TOS(arg->tos),
  44. RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol,
  45. ip_reply_arg_flowi_flags(arg),
  46. daddr, saddr,
  47. tcp_hdr(skb)->source, tcp_hdr(skb)->dest,
  48. arg->uid);
  49. security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
  50. rt = ip_route_output_key(net, &fl4);
  51. if (IS_ERR(rt))
  52. return;
  53.  
  54. /* 根据skb更新sk的属性 */
  55. inet_sk(sk)->tos = arg->tos;
  56.  
  57. sk->sk_priority = skb->priority;
  58. sk->sk_protocol = ip_hdr(skb)->protocol;
  59. sk->sk_bound_dev_if = arg->bound_dev_if;
  60. sk->sk_sndbuf = sysctl_wmem_default;
  61. sk->sk_mark = fl4.flowi4_mark;
  62. /* 数据追加到前一个skb或者新建skb后添加到发送队列 */
  63. err = ip_append_data(sk, &fl4, ip_reply_glue_bits, arg->iov->iov_base,
  64. len, , &ipc, &rt, MSG_DONTWAIT);
  65. if (unlikely(err)) {
  66. ip_flush_pending_frames(sk);
  67. goto out;
  68. }
  69.  
  70. /* 如果发送队列有skb,则计算校验和,发送 */
  71. nskb = skb_peek(&sk->sk_write_queue);
  72. if (nskb) {
  73. if (arg->csumoffset >= )
  74. *((__sum16 *)skb_transport_header(nskb) +
  75. arg->csumoffset) = csum_fold(csum_add(nskb->csum,
  76. arg->csum));
  77. nskb->ip_summed = CHECKSUM_NONE;
  78.  
  79. /* 发送数据包 */
  80. ip_push_pending_frames(sk, &fl4);
  81. }
  82. out:
  83. ip_rt_put(rt);
  84. }

TCP->IP输出 之 ip_queue_xmit、ip_build_and_send_pkt、ip_send_unicast_reply的更多相关文章

  1. TCP/IP协议栈在Linux内核中的运行时序分析

    网络程序设计调研报告 TCP/IP协议栈在Linux内核中的运行时序分析 姓名:柴浩宇 学号:SA20225105 班级:软设1班 2021年1月 调研要求 在深入理解Linux内核任务调度(中断处理 ...

  2. TCP/IP协议学习(五) 基于C# Socket的C/S模型

    TCP/IP协议作为现代网络通讯的基石,内容包罗万象,直接去理解理论是比较困难的:然而通过实践先理解网络通讯的理解,在反过来理解学习TCP/IP协议栈就相对简单很多.C#通过提供的Socket API ...

  3. TCP/IP详解 (转)

    TCP/IP详解学习笔记(1)-基本概念 为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣经中 ...

  4. 『TCP/IP详解——卷一:协议』读书笔记——18

    2013-08-27 15:44:52 第7章 Ping程序 7.1 引言 “ping”这个名字来源于声纳定为操作.Ping程序由Mike Muuss编写,目的是为了测试另一台主机是否可达.该程序发送 ...

  5. 『TCP/IP详解——卷一:协议』读书笔记——14

    2013-08-25 11:32:06 第5章 RARP:逆地址解析协议 5.1 引言 具有本地磁盘的系统引导时,一般是从磁盘上的配置文件中读取IP地址.但是无盘机,如X终端或无盘工作站,则需要采用其 ...

  6. TCP/IP详解 笔记十四

    TCP/IP协议(二)  连接的建立与终止 tcpdump -S输出TCP报文的格式 格式: 源>目的:标志 (标志就是tcp头部).标识首字符意义如下: 例如:telnet 某服务的输出(包括 ...

  7. TCP/IP详解

    第一篇 TCPIP协议详解 第1章 TCPIP协议族 第2章 IP协议详解 第3章 TCP协议详解 第4章 TCP/IP通信案例:访问Internet上的Web服务器 一.TCP/IP协议族 TCP/ ...

  8. OSI七层&TCP&IP协议

    OSI七层: OSI七层与ICP/IP概念层的对应: ICP/IP概念层上的网络设备: IP(Internet Protocol网际协议):计算机之间的通信 IP(网络协议)位于网络层,作用是把各种数 ...

  9. TCP IP详解(转)

    大学学习网络基础的时候老师讲过,网络由下往上分为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. 网络七层协议简称OSI.TCP/IP刨除了物理层,并把上三层(会话层.表示层和应用层)统称 ...

  10. TCP/IP协议学习(四) 协议概述

    生活中有舒适区,借口成为懒惰的护身符,学习也有舒适区,逃避便是阻止进步的最大障碍. 经过半年多嵌入式方面的工作和学习,我提高了很多,但同时我也对自己所面临的问题逐渐清晰: 1. 偏于实践,理论基础不牢 ...

随机推荐

  1. 实现div可以调整高度(div实现resize)

    实现div可以调整高度(div实现resize) 一.div 实现resize(类似textarea) 代码如下: <!DOCTYPE html> <html> <hea ...

  2. Windows命令行命令总结

    转载地址:https://www.cnblogs.com/accumulater/p/7110811.html   1. gpedit.msc-----组策略 2. sndrec32-------录音 ...

  3. php.ini配置文件参数中文说明文档

    转自  https://blog.csdn.net/seoyundu/article/details/101147041 中文翻译php.ini配置文件 [PHP php.ini-dist] ;;;; ...

  4. linux中安装jdk+jmeter-

    --------------linux中安装jdk+jmeter-------------------- 一.安装JDK7.0版本 .先卸载服务器自带的jdk软件包 # java -version # ...

  5. 3、Rsync备份服务实战

    1.Rsync基本概述 rsync是一款开源的备份工具,可以在不同主机之间进行同步,可实现全量备份与增量备份,因此非常适合用于架构集中式备份或异地备份等应用. rsync官方地址:传送门http:// ...

  6. 【获取url 问号后参数】防中文乱码

    function getQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&] ...

  7. unzip解压3G或者4G以上文件失败的解决方法

    Linux下,使用unzip解压时,报错:End-of-central-directory signature not found.  Either this file is nota zipfile ...

  8. 【安徽集训】Emerald

    Description \(n\) 座城市在数轴上,第 \(i\) 座城市有一条连向第 \(i+1\) 座城市的单向边.每座城市有一个类型 A/B 以及一个非负整数人口,A 类城市的人觉得自己的城市比 ...

  9. ClassLoader心得

              我们都知道,jvm执行的代码,都是通过jvm加载系统加入的.加载系统的第一步是通过ClassLoader加载class二进制信息,jvm规范中并没有规定class的来源类型,这就给 ...

  10. oracle plsql登陆用户名密码都正确,拒绝登陆

    先通过sqlplus  或者 sql developer 或者其他用户登陆 然后更改 登陆不上的用户的密码  然后再用plsql登陆就可以了  然后还可以再把用户密码再改回来 也可以登陆了