TCP->IP输出 之 ip_queue_xmit、ip_build_and_send_pkt、ip_send_unicast_reply
概述
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发送数据包;
源码分析
/* Note: skb->sk can be different from sk, in case of tunnels */
int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
{
struct inet_sock *inet = inet_sk(sk);
struct net *net = sock_net(sk);
struct ip_options_rcu *inet_opt;
struct flowi4 *fl4;
struct rtable *rt;
struct iphdr *iph;
int res; /* Skip all of this if the packet is already routed,
* f.e. by something like SCTP.
*/
rcu_read_lock();
inet_opt = rcu_dereference(inet->inet_opt);
fl4 = &fl->u.ip4; /* 获取skb中的路由缓存 */
rt = skb_rtable(skb); /* skb中有缓存则跳转处理 */
if (rt)
goto packet_routed; /* Make sure we can route this packet. */
/* 检查控制块中的路由缓存 */
rt = (struct rtable *)__sk_dst_check(sk, );
/* 缓存过期 */
if (!rt) {
__be32 daddr; /* Use correct destination address if we have options. */
/* 目的地址 */
daddr = inet->inet_daddr; /* 严格路由选项 */
if (inet_opt && inet_opt->opt.srr)
daddr = inet_opt->opt.faddr; /* If this fails, retransmit mechanism of transport layer will
* keep trying until route appears or the connection times
* itself out.
*/
/* 查找路由缓存 */
rt = ip_route_output_ports(net, fl4, sk,
daddr, inet->inet_saddr,
inet->inet_dport,
inet->inet_sport,
sk->sk_protocol,
RT_CONN_FLAGS(sk),
sk->sk_bound_dev_if);
/* 失败 */
if (IS_ERR(rt))
goto no_route; /* 设置控制块的路由缓存 */
sk_setup_caps(sk, &rt->dst);
} /* 将路由设置到skb中 */
skb_dst_set_noref(skb, &rt->dst); packet_routed:
/* 严格路由选项 &&使用网关,无路由 */
if (inet_opt && inet_opt->opt.is_strictroute && rt->rt_uses_gateway)
goto no_route; /* OK, we know where to send it, allocate and build IP header. */
/* 加入ip头 */
skb_push(skb, sizeof(struct iphdr) + (inet_opt ? inet_opt->opt.optlen : ));
skb_reset_network_header(skb); /* 构造ip头 */
iph = ip_hdr(skb);
*((__be16 *)iph) = htons(( << ) | ( << ) | (inet->tos & 0xff));
if (ip_dont_fragment(sk, &rt->dst) && !skb->ignore_df)
iph->frag_off = htons(IP_DF);
else
iph->frag_off = ;
iph->ttl = ip_select_ttl(inet, &rt->dst);
iph->protocol = sk->sk_protocol;
ip_copy_addrs(iph, fl4); /* Transport layer set skb->h.foo itself. */
/* 构造ip选项 */
if (inet_opt && inet_opt->opt.optlen) {
iph->ihl += inet_opt->opt.optlen >> ;
ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, );
} /* 设置id */
ip_select_ident_segs(net, skb, sk,
skb_shinfo(skb)->gso_segs ?: ); /* TODO : should we use skb->sk here instead of sk ? */
/* QOS等级 */
skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark; /* 输出 */
res = ip_local_out(net, sk, skb);
rcu_read_unlock();
return res; no_route:
/* 无路由处理 */
rcu_read_unlock();
IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
kfree_skb(skb);
return -EHOSTUNREACH;
}
int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk,
__be32 saddr, __be32 daddr, struct ip_options_rcu *opt)
{
struct inet_sock *inet = inet_sk(sk);
struct rtable *rt = skb_rtable(skb);
struct net *net = sock_net(sk);
struct iphdr *iph; /* Build the IP header. */
/* 构造ip头 */
skb_push(skb, sizeof(struct iphdr) + (opt ? opt->opt.optlen : ));
skb_reset_network_header(skb);
iph = ip_hdr(skb);
iph->version = ;
iph->ihl = ;
iph->tos = inet->tos;
iph->ttl = ip_select_ttl(inet, &rt->dst);
iph->daddr = (opt && opt->opt.srr ? opt->opt.faddr : daddr);
iph->saddr = saddr;
iph->protocol = sk->sk_protocol; /* 分片与否 */
if (ip_dont_fragment(sk, &rt->dst)) {
iph->frag_off = htons(IP_DF);
iph->id = ;
} else {
iph->frag_off = ;
__ip_select_ident(net, iph, );
} /* 选项 */
if (opt && opt->opt.optlen) {
iph->ihl += opt->opt.optlen>>;
ip_options_build(skb, &opt->opt, daddr, rt, );
} /* QOS优先级 */
skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark; /* Send it out. */
/* 输出 */
return ip_local_out(net, skb->sk, skb);
}
void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,
const struct ip_options *sopt,
__be32 daddr, __be32 saddr,
const struct ip_reply_arg *arg,
unsigned int len)
{
struct ip_options_data replyopts;
struct ipcm_cookie ipc;
struct flowi4 fl4;
struct rtable *rt = skb_rtable(skb);
struct net *net = sock_net(sk);
struct sk_buff *nskb;
int err;
int oif; /* 获取ip选项 */
if (__ip_options_echo(&replyopts.opt.opt, skb, sopt))
return; ipc.addr = daddr;
ipc.opt = NULL;
ipc.tx_flags = ;
ipc.ttl = ;
ipc.tos = -; /* 选项存在 */
if (replyopts.opt.opt.optlen) {
ipc.opt = &replyopts.opt; /* 源路由存在,设置下一跳ip地址为目的地址 */
if (replyopts.opt.opt.srr)
daddr = replyopts.opt.opt.faddr;
} /* 输出接口设置 */
oif = arg->bound_dev_if;
if (!oif && netif_index_is_l3_master(net, skb->skb_iif))
oif = skb->skb_iif; /* 查路由 */
flowi4_init_output(&fl4, oif,
IP4_REPLY_MARK(net, skb->mark),
RT_TOS(arg->tos),
RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol,
ip_reply_arg_flowi_flags(arg),
daddr, saddr,
tcp_hdr(skb)->source, tcp_hdr(skb)->dest,
arg->uid);
security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
rt = ip_route_output_key(net, &fl4);
if (IS_ERR(rt))
return; /* 根据skb更新sk的属性 */
inet_sk(sk)->tos = arg->tos; sk->sk_priority = skb->priority;
sk->sk_protocol = ip_hdr(skb)->protocol;
sk->sk_bound_dev_if = arg->bound_dev_if;
sk->sk_sndbuf = sysctl_wmem_default;
sk->sk_mark = fl4.flowi4_mark;
/* 数据追加到前一个skb或者新建skb后添加到发送队列 */
err = ip_append_data(sk, &fl4, ip_reply_glue_bits, arg->iov->iov_base,
len, , &ipc, &rt, MSG_DONTWAIT);
if (unlikely(err)) {
ip_flush_pending_frames(sk);
goto out;
} /* 如果发送队列有skb,则计算校验和,发送 */
nskb = skb_peek(&sk->sk_write_queue);
if (nskb) {
if (arg->csumoffset >= )
*((__sum16 *)skb_transport_header(nskb) +
arg->csumoffset) = csum_fold(csum_add(nskb->csum,
arg->csum));
nskb->ip_summed = CHECKSUM_NONE; /* 发送数据包 */
ip_push_pending_frames(sk, &fl4);
}
out:
ip_rt_put(rt);
}
TCP->IP输出 之 ip_queue_xmit、ip_build_and_send_pkt、ip_send_unicast_reply的更多相关文章
- TCP/IP协议栈在Linux内核中的运行时序分析
网络程序设计调研报告 TCP/IP协议栈在Linux内核中的运行时序分析 姓名:柴浩宇 学号:SA20225105 班级:软设1班 2021年1月 调研要求 在深入理解Linux内核任务调度(中断处理 ...
- TCP/IP协议学习(五) 基于C# Socket的C/S模型
TCP/IP协议作为现代网络通讯的基石,内容包罗万象,直接去理解理论是比较困难的:然而通过实践先理解网络通讯的理解,在反过来理解学习TCP/IP协议栈就相对简单很多.C#通过提供的Socket API ...
- TCP/IP详解 (转)
TCP/IP详解学习笔记(1)-基本概念 为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣经中 ...
- 『TCP/IP详解——卷一:协议』读书笔记——18
2013-08-27 15:44:52 第7章 Ping程序 7.1 引言 “ping”这个名字来源于声纳定为操作.Ping程序由Mike Muuss编写,目的是为了测试另一台主机是否可达.该程序发送 ...
- 『TCP/IP详解——卷一:协议』读书笔记——14
2013-08-25 11:32:06 第5章 RARP:逆地址解析协议 5.1 引言 具有本地磁盘的系统引导时,一般是从磁盘上的配置文件中读取IP地址.但是无盘机,如X终端或无盘工作站,则需要采用其 ...
- TCP/IP详解 笔记十四
TCP/IP协议(二) 连接的建立与终止 tcpdump -S输出TCP报文的格式 格式: 源>目的:标志 (标志就是tcp头部).标识首字符意义如下: 例如:telnet 某服务的输出(包括 ...
- TCP/IP详解
第一篇 TCPIP协议详解 第1章 TCPIP协议族 第2章 IP协议详解 第3章 TCP协议详解 第4章 TCP/IP通信案例:访问Internet上的Web服务器 一.TCP/IP协议族 TCP/ ...
- OSI七层&TCP&IP协议
OSI七层: OSI七层与ICP/IP概念层的对应: ICP/IP概念层上的网络设备: IP(Internet Protocol网际协议):计算机之间的通信 IP(网络协议)位于网络层,作用是把各种数 ...
- TCP IP详解(转)
大学学习网络基础的时候老师讲过,网络由下往上分为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. 网络七层协议简称OSI.TCP/IP刨除了物理层,并把上三层(会话层.表示层和应用层)统称 ...
- TCP/IP协议学习(四) 协议概述
生活中有舒适区,借口成为懒惰的护身符,学习也有舒适区,逃避便是阻止进步的最大障碍. 经过半年多嵌入式方面的工作和学习,我提高了很多,但同时我也对自己所面临的问题逐渐清晰: 1. 偏于实践,理论基础不牢 ...
随机推荐
- vue-cli 3.0不能用module.exports为undefined,导致第三方js都没有办法引用
新的 vue-cli 默认禁止了 commonjs 语法可以添加 babel 插件解决 yarn add @babel/plugin-transform-modules-commonjs -D 或 n ...
- java enum类自定义属性
enum类自定义属性 这就是enum比static静态变量好用的地方了,可以赋予每一个枚举值若干个属性,例如 实例1: public enum GasStationChannel { ZH(" ...
- Spring MVC之@RequestParam @RequestBody @RequestHeader 等详
Spring MVC之@RequestParam @RequestBody @RequestHeader 等详 引言: 接上一篇文章,对@RequestMapping进行地址映射讲解之后,该篇 ...
- shell脚本视频学习2
一.函数 1.函数格式 2.函数传入参数 3.手动输入函数中的参数 4.函数返回值 成功返回0,失败返回1 5.输入一个目录,判断目录是否存在,如果不存在则给出提示,如果存在则提示输入要创建的文件名, ...
- ado.net 断开 非断开
非断开 SqlConnection SqlCommand / SqlDataReader 接 断开 SqlConnection SqlDataAdapter / DataSet 接
- tftp client命令示例
tftp 192.168.1.1 -c put myfile theirfile tftp 192.168.1.1 -m binary -c put myfile theirfile The tftp ...
- 关于MySQL服务无法正常启动问题
使用mysql的时候,突然查看服务列表也找不到mysql服务 解决办法: 一.首先打开CMD,切换到MySql安装目录的MySql Server →bin目录下 运行如下命令(具体试个人安装的MySq ...
- 利用PyMySQL模块操作数据库
连接到数据库 import pymysql # 创建链接得到一个链接对象 conn = pymysql.Connect( host="127.0.0.1", # 数据库服务器主机地 ...
- C#在Oralce环境执行查询时报"Arithmetic operation resulted in an overflow"
问题描述:C#代码在Oralce环境执行分组求和的Sql时报错,提示“Arithmetic operation resulted in an overflow”,即算术运算导致溢出 (1).执行Sql ...
- git 清除所有历史记录
有些时候,git 仓库累积了太多无用的历史更改,导致 clone 文件过大.如果确定历史更改没有意义,可以采用下述方法清空历史, 1) 先 clone 项目到本地目录 (以名为 mylearning ...