真正搞明白一个问题不容易
实验目的
基于 packetdrill TCP 三次握手脚本,测试 TCP options 中 TSOPT 字段的由来,此次构造模拟的是客户端场景。
对于 TCP options,各类介绍资料已经数不胜数,本篇就不再赘述。
基础脚本
# cat tcp_3hs_007.pkt
// TCP 基础之三次握手
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0.01 < S. 0:0(0) ack 1 win 10000 <mss 1000>
+0 > . 1:1(0) ack 1
实验测试一
因为 > 表示预期协议栈会发送的数据包,所以内核协议栈自动构建发送 SYN 数据包。
+0 > S 0:0(0) <...>
// +0 本行代码执行时间相对于上一行代码的偏移时间。
// > ,表示预期协议栈会发送的数据包。
// 0:0(0) ,表示开始序号:结束序号(数据包长度)。
// <> 表示 TCP options,... 表示默认值。
模拟的是客户端场景,SYN 数据包自动构建的情况下,各字段因此无需自定义。
1.执行脚本
# packetdrill tcp_3hs_007.pkt
#
执行完成后退出。
2.捕获数据包
# tcpdump -i any port 8080 -nn
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
15:49:36.529121 tun0 Out IP 192.168.174.54.50762 > 192.0.2.1.8080: Flags [S], seq 1691568983, win 64240, options [mss 1460,sackOK,TS val 329810675 ecr 0,nop,wscale 7], length 0
15:49:36.629222 ? In IP 192.0.2.1.8080 > 192.168.174.54.50762: Flags [S.], seq 0, ack 1691568984, win 10000, options [mss 1000], length 0
15:49:36.629256 ? Out IP 192.168.174.54.50762 > 192.0.2.1.8080: Flags [.], ack 1, win 64240, length 0
15:49:36.629427 ? Out IP 192.168.174.54.50762 > 192.0.2.1.8080: Flags [F.], seq 1, ack 1, win 64240, length 0
15:49:36.629444 ? In IP 192.0.2.1.8080 > 192.168.174.54.50762: Flags [R.], seq 1, ack 1, win 10000, length 0
#
观察 tcpdump 抓包结果可以看到客户端 SYN TCP Options 包含 TSOPT ,因为此时在客户端开启了 Timestamps 支持,所以如上结果。
# sysctl -a | grep timestamps
net.ipv4.tcp_timestamps = 1
#
而服务器端响应的 SYN/ACK 不包含 TSOPT,因为此时服务器端属于 < 自行构造的数据包,在 <> TCP options 中只有 mss 1000,并没注入 TSOPT,所以此时 TCP 三次握手的结果,表明之后的 TCP 通讯将不支持 Timestamps 。
实验测试二
此时修改 SYN/ACK 的 TCP options ,同样增加 Timestamps 支持,同时考虑到双方的 Timestamps 匹配关系,脚本需修改如下:
# cat tcp_3hs_options_tsopt_001.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <mss 1460,sackOK,TS val 100 ecr 0,nop ,wscale 7>
+.1 < S. 0:0(0) ack 1 win 10000 <mss 1000, nop, nop, TS val 100 ecr 100>
+0 > . 1:1(0) ack 1 <nop,nop,TS val 200 ecr 100>
#
# packetdrill tcp_3hs_options_tsopt_001.pkt
#
观察 tcpdump 抓包结果可以看到确实 SYN TCP Options 中包含 TSOPT ,且服务器端响应的 SYN/ACK 同样也包含 TSOPT ,所以此时 TCP 三次握手的结果,表明之后的 TCP 通讯将支持 Timestamps ,因为双方均支持。
# tcpdump -i any port 8080 -nn
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
11:29:29.149147 tun0 Out IP 192.168.131.232.51364 > 192.0.2.1.8080: Flags [S], seq 1726783205, win 65535, options [mss 1460,sackOK,TS val 2146277730 ecr 0,nop,wscale 7], length 0
11:29:29.249271 tun0 In IP 192.0.2.1.8080 > 192.168.131.232.51364: Flags [S.], seq 0, ack 1726783206, win 10000, options [mss 1000,nop,nop,TS val 100 ecr 2146277730], length 0
11:29:29.249303 tun0 Out IP 192.168.131.232.51364 > 192.0.2.1.8080: Flags [.], ack 1, win 65535, options [nop,nop,TS val 2146277830 ecr 100], length 0
11:29:29.249436 tun0 Out IP 192.168.131.232.51364 > 192.0.2.1.8080: Flags [F.], seq 1, ack 1, win 65535, options [nop,nop,TS val 2146277830 ecr 100], length 0
11:29:29.249453 tun0 In IP 192.0.2.1.8080 > 192.168.131.232.51364: Flags [R.], seq 1, ack 1, win 10000, length 0
#
实验测试三
此时如果将客户端 Timestamps 支持关闭,则:
# sysctl -q net.ipv4.tcp_timestamps=0
#
# sysctl -a | grep timestamps
net.ipv4.tcp_timestamps = 0
#
仍然执行如下脚本,那么可以看到 tcpdump 抓包结果中客户端所发送的 SYN 不再包含 TSOPT ,那么不管服务器端本地的 net.ipv4.tcp_timestamps 是否启用(对于真实环境),在响应的 SYN/ACK 中都不会包含 TSOPT。此时 TCP 三次握手的结果,也表明之后的 TCP 通讯将不支持 Timestamps 。
# packetdrill tcp_3hs_007.pkt
#
# tcpdump -i any port 8080 -nn
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
11:34:32.709140 tun0 Out IP 192.168.100.137.46794 > 192.0.2.1.8080: Flags [S], seq 518444255, win 65535, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
11:34:32.809271 tun0 In IP 192.0.2.1.8080 > 192.168.100.137.46794: Flags [S.], seq 0, ack 518444256, win 10000, options [mss 1000], length 0
11:34:32.809309 tun0 Out IP 192.168.100.137.46794 > 192.0.2.1.8080: Flags [.], ack 1, win 65535, length 0
11:34:32.809433 tun0 Out IP 192.168.100.137.46794 > 192.0.2.1.8080: Flags [F.], seq 1, ack 1, win 65535, length 0
11:34:32.809452 tun0 In IP 192.0.2.1.8080 > 192.168.100.137.46794: Flags [R.], seq 1, ack 1, win 10000, length 0
#
测试脚本的服务器端 < 属于手工模拟,所以即便是注入了 TSOPT 字段,也非真实场景。
SYN TSOPT
SYN 中 TCP options 的 TSOPT,代码中是通过 tcp_syn_options 函数确定的,相关的代码流程简要说明如下:
sys_connect
|--inet_stream_connect
| |--tcp_v4_connect
| |--tcp_connect
| |--tcp_transmit_skb
| |--tcp_syn_options
tcp_connect -> tcp_transmit_skb -> __tcp_transmit_skb -> tcp_syn_options 构造 SYN 的 Options 各字段,其中涉及 TSOPT 。
/* Build a SYN and send it off. */
int tcp_connect(struct sock *sk)
{
...
/* Send off SYN; include data in Fast Open. */
err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) :
tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
if (err == -ECONNREFUSED)
return err;
...
}
EXPORT_SYMBOL(tcp_connect);
static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
gfp_t gfp_mask)
{
return __tcp_transmit_skb(sk, skb, clone_it, gfp_mask,
tcp_sk(sk)->rcv_nxt);
}
static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
int clone_it, gfp_t gfp_mask, u32 rcv_nxt)
{
...
if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) {
tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5);
}
...
}
如果启用了 TCP TSOPT(ipv4.sysctl_tcp_timestamps),则设置 Timestamps 选项标志,同时计算出 tsval 和 tsecr 的值。
static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
struct tcp_out_options *opts,
struct tcp_md5sig_key **md5)
{
...
if (likely(sock_net(sk)->ipv4.sysctl_tcp_timestamps && !*md5)) {
opts->options |= OPTION_TS;
opts->tsval = tcp_skb_timestamp(skb) + tp->tsoffset;
opts->tsecr = tp->rx_opt.ts_recent;
remaining -= TCPOLEN_TSTAMP_ALIGNED;
}
...
}
往期推荐
原文始发于微信公众号(Echo Reply):Wireshark & Packetdrill | TCP 三次握手之 TCP Options 字段 TSOPT
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论