前文介绍了TCP Fast Open的原理和使用,这次用tcpdump工具将TCP消息抓一下,看看是具体怎样实现的。
首先要了解一下IP报头和TCP报头的格式,这样才能根据协议对应报头内容:
接下来我们看一下第一次连接的时候的抓包内容:
抓包命令:
tcpdump -i lo tcp port 5555 -Xnn
SYN+空Cookie:
16:38:18.614264 IP 127.0.0.1.52868 > 127.0.0.1.5555: Flags [S], seq 2089549908, win 43690, options [mss 65495,sackOK,TS val 49428115 ecr 0,nop,wscale 7,tfo cookiereq,nop,nop], length 0
0x0000: 4500 0040 4:版本 | 5:IP报文长度,5*32=5*4字节=5*2个4位16进制=10个4位16进制 | 00服务类型 | 0040:总长度64个字节=32个4位16进制
36c7 4000 36c7:标识,IP报头的序列号 | 010:0未使用,1允许分段,0没有更多分段在传输 | 后面的0表示偏移,这里偏移是0
4006 05ef 40:生存时间 | 06:TCP协议 | 05ef:报头校验和
7f00 0001 7f00 0001:源IP地址7f.00.00.01=127.0.0.1
0x0010: 7f00 0001 7f00 0001:目的IP地址7f.00.00.01=127.0.0.1 | ***因为IP报头长度是10个4位16进制,所以到此,IP报头结束,下面是TCP报头
-------------------------------------------------------------------------------------------------------------------------------------------------------
ce84 15b3 ce84:源端口52868 | 15b3:目的端口5555
7c8c 0054 7c8c 0054:请求端序列号
0000 0000 0000 0000:请求端确认号0
0x0020: b002 aaaa b:数据偏移,TCP头部长度,10*32字节=10*2个4位16进制=20个4位16进制,刚好到结束,也就是该消息没有数据 | 保留6位0 |
000010:URG(0),ACK(0),PSH(0),RST(0),SYN(1),FIN(0) | aaaa:窗口43690
fe34 0000 fe34:校验和 | 0000:紧急指针
0204 ffd7 02:TCP选项,kind=2 最大报文段长度(MSS)选项 | 04:length=4 | ffd7:最大报文段长度65495(mss)
0402 080a 04:TCP选项,kind=4 选择性确认(Selective Acknowledgment,SACK)选项 | 02:length=2 |
08:TCP选项,kind=8 时间戳选项 | 0a:length=10 也就是后面8个字节都是属于这个选项的
0x0030: 02f2 3693 02f2 3693:时间戳值
0000 0000 0000 0000:时间戳回显应答
0103 0307 01:空操作,一般用于填充4字节整数倍 |
03:TCP选项,kind=3 窗口扩大因子选项:假设TCP头部中的接收通告窗口大小是N,窗口扩大因子(移位数)是M,那么TCP报文段的实际接收通告窗口大小是N*2M,或者说N左移M位。|
03:length=3 | 07:位移数,也就是上面的M,那么实际通告窗口大小就是aaaa*2*7
2202 0101 22:kind=22 fto选项 | 02:length | 0101:nop,nop,fto的cookie值一定要后面加上个nopnop,而实际cookie的长度是length-2,参考 https://tools.ietf.org/html/rfc7413#section-4.1.2
SYN+ACK+Cookie
16:38:18.614275 IP 127.0.0.1.5555 > 127.0.0.1.52868: Flags [S.], seq 2825442661, ack 2089549909, win 43690, options [mss 65495,sackOK,TS val 49428115 ecr 49428115,nop,wscale 7,tfo cookie 32faf79caff51cde,nop,nop], length 0
0x0000: 4500 0048 4:版本 | 5:IP报文长度,5*32=5*4字节=5*2个4位16进制=10个4位16进制 | 00服务类型 | 0048:总长度72个字节=36个4位16进制
0000 4000 0000:标识,IP报头的序列号 | 010:0未使用,1允许分段,0没有更多分段在传输 | 后面的0表示偏移,这里偏移是0
4006 3cae 40:生存时间 | 06:TCP协议 | 3cae:报头校验和
7f00 0001 7f00 0001:源IP地址7f.00.00.01=127.0.0.1
0x0010: 7f00 0001 7f00 0001:目的IP地址7f.00.00.01=127.0.0.1
-------------------------------------------------------------------------------------------------------------------------------------------------------
15b3 ce84 15b3:源端口5555 | ce84:目的端口52868
a868 d565 a868 d565:响应端序列号
7c8c 0055 7c8c 0055:响应端确认号
0x0020: d012 aaaa d:数据偏移,TCP头部长度,13*32字节=13*2个4位16进制=26个4位16进制,刚好到结束,也就是该消息没有数据 | 保留6位0 |
010010:URG(0),ACK(1),PSH(0),RST(0),SYN(1),FIN(0) | aaaa:窗口43690
fe3c 0000 fe30:校验和 | 0000:紧急指针
0204 ffd7 02:TCP选项,kind=2 最大报文段长度(MSS)选项 | 04:length=4 | ffd7:最大报文段长度65495(mss)
0402 080a 04:TCP选项,kind=4 选择性确认(Selective Acknowledgment,SACK)选项 | 02:length=2 |
08:TCP选项,kind=8 时间戳选项 | 0a:length=10 也就是后面8个字节都是属于这个选项的
0x0030: 02f2 3693 02f2 3693:时间戳值
02f2 3693 02f2 3693:时间戳回显应答
0103 0307 01:空操作,一般用于填充4字节整数倍 |
03:TCP选项,kind=3 窗口扩大因子选项:假设TCP头部中的接收通告窗口大小是N,窗口扩大因子(移位数)是M,那么TCP报文段的实际接收通告窗口大小是N*2M,或者说N左移M位。|
03:length=3 | 07:位移数,也就是上面的M,那么实际通告窗口大小就是aaaa*2*7
220a 32fa 22:kind=22 fto选项 | 0a:length |
0x0040: f79c aff5
1cde 0101 32fa f79c aff5 1cde: cookie | 0101 :nop,nop
ACK+Data,这里发送的数据是"Hello, tcp fast open"
16:38:18.614284 IP 127.0.0.1.52868 > 127.0.0.1.5555: Flags [P.], seq 1:21, ack 1, win 342, options [nop,nop,TS val 49428115 ecr 49428115], length 20
0x0000: 4500 0048 4:版本 | 5:IP报文长度,5*32=5*4字节=5*2个4位16进制=10个4位16进制 | 00服务类型 | 0048:总长度72个字节=26个4位16进制
36c8 4000 36c8:标识,IP报头的序列号,比第一次发送的序列号+1 | 010:0未使用,1允许分段,0没有更多分段在传输 | 后面的0表示偏移,这里偏移是0
4006 05e6 40:生存时间 | 06:TCP协议 | 05e6:报头校验和
7f00 0001 7f00 0001:源IP地址7f.00.00.01=127.0.0.1
0x0010: 7f00 0001 7f00 0001:目的IP地址7f.00.00.01=127.0.0.1
-------------------------------------------------------------------------------------------------------------------------------------------------------
ce84 15b3 ce84:源端口52868 | 15b3:目的端口5555
7c8c 0055 7c8c 0055:请求端序列号
a868 d566 a868 d566:请求端确认序列号
0x0020: 8018 0156 8:数据偏移,TCP头部长度,8*32字节=8*2个4位16进制=16个4位16进制 | 保留6位0 |
011000:URG(0),ACK(1),PSH(1)表示含有数据,RST(0),SYN(0),FIN(0) | 0156:窗口342
fe3c 0000 fe3c:校验和 | 0000:紧急指针
0101 080a 01:空操作,一般用于填充4字节整数倍 | 01:空操作,一般用于填充4字节整数倍 | 08:TCP选项,kind=8 时间戳选项 | 0a:length=10 也就是后面8个字节都是属于这个选项的
02f2 3693 02f2 3693:时间戳值
0x0030: 02f2 3693 02f2 3693:时间戳回显应答
4865 6c6c Hell
6f2c 2074 o,.t
6370 2066 cp.f
0x0040: 6173 7420 6f70 656e ast.open
断开请求后,看下一次请求的情况(由于很多内容是一样的,这里就不再详细解析了,只解析一些关键的数据):
SYN+Cookie+Data
16:38:28.615169 IP 127.0.0.1.52870 > 127.0.0.1.5555: Flags [S], seq 1871838175:1871838195, win 43690, options [mss 65495,sackOK,TS val 49430615 ecr 0,nop,wscale 7,tfo cookie 32faf79caff51cde,nop,nop], length 20
0x0000: 4500 005c bb88 4000 4006 8111 7f00 0001 E..\..@.@.......
0x0010: 7f00 0001
-------------------------------------------------------------------------------------------------------------------------------------------------------
ce86 15b3
6f91 fbdf 6f91 fbdf:请求端序列号1871838175
0000 0000 ........o.......
0x0020: d002 aaaa fe50 0000 0204 ffd7 0402 080a .....P..........
-------------------------------------------------------------------------------------------------------------------------------------------------------
0x0030: 02f2 4057 02f2 4057:时间戳值
0000 0000 0000 0000:时间戳回显应答
0103 0307 01:空操作,一般用于填充4字节整数倍 |
03:TCP选项,kind=3 窗口扩大因子选项:假设TCP头部中的接收通告窗口大小是N,窗口扩大因子(移位数)是M,那么TCP报文段的实际接收通告窗口大小是N*2M,或者说N左移M位。|
03:length=3 | 07:位移数,也就是上面的M,那么实际通告窗口大小就是aaaa*2*7
220a 32fa 22:kind=22 fto选项 | 0a:length | 32fa
0x0040: f79c aff5 1cde 0101 f79c aff5 1cde : cookie | 0101: nop, nop
4865 6c6c 6f2c 2074 Hello,.t
0x0050: 6370 2066 6173 7420 6f70 656e cp.fast.open
SYN+ACK
16:38:28.615198 IP 127.0.0.1.5555 > 127.0.0.1.52870: Flags [S.], seq 2175465761, ack 1871838196, win 43690, options [mss 65495,sackOK,TS val 49430615 ecr 49430615,nop,wscale 7], length 0
0x0000: 4500 003c 0000 4000 4006 3cba 7f00 0001
0x0010: 7f00 0001
-------------------------------------------------------------------------------------------------------------------------------------------------------
15b3 ce86
81aa f921 81aa f921:请求端序列号2175465761
6f91 fbf4 6f91 fbf4:请求端确认号1871838196 = 1871838175 + 21
0x0020: a012 aaaa a:数据偏移,TCP头部长度,10*32字节=10*2个4位16进制=20个4位16进制,刚好到结束,也就是该消息没有数据 | 保留6位0 |
010010:URG(0),ACK(1),PSH(0),RST(0),SYN(1),FIN(0) | aaaa:窗口43690
fe30 0000 0204 ffd7 0402 080a .....0..........
0x0030: 02f2 4057 02f2 4057 0103 0307 ..@W..@W....
ACK
16:38:28.615198 IP 127.0.0.1.5555 > 127.0.0.1.52870: Flags [S.], seq 2175465761, ack 1871838196, win 43690, options [mss 65495,sackOK,TS val 49430615 ecr 49430615,nop,wscale 7], length 0
0x0000: 4500 003c 0000 4000 4006 3cba 7f00 0001
0x0010: 7f00 0001
-------------------------------------------------------------------------------------------------------------------------------------------------------
15b3 ce86
81aa f921 81aa f921:请求端序列号2175465761
6f91 fbf4 6f91 fbf4:请求端确认号1871838196 = 1871838175 + 21
0x0020: a012 aaaa a:数据偏移,TCP头部长度,10*32字节=10*2个4位16进制=20个4位16进制,刚好到结束,也就是该消息没有数据 | 保留6位0 |
010010:URG(0),ACK(1),PSH(0),RST(0),SYN(1),FIN(0) | aaaa:窗口43690
fe30 0000 0204 ffd7 0402 080a .....0..........
0x0030: 02f2 4057 02f2 4057 0103 0307 ..@W..@W....
看完这些内容,整个TFO的流程会更加清晰。