重学TCP协议(3) 端口号及MTU、MSS | 小册免费学

263 阅读6分钟

1. 端口相关的命令

1.1 查看端口是否打开

使用 nc 和 telnet 这两个命令可以非常方便的查看到对方端口是否打开或者网络是否可达。如果对端端口没有打开,使用 telnet 和 nc 命令会出现 "Connection refused" 错误

1.2 查看监听端口的进程

  1. 使用 netstat
sudo netstat -ltpn | grep :22

image.png 2. 使用 lsof

因为在 linux 上一切皆文件,TCP socket 连接也是一个 fd。因此使用 lsof 也可以

sudo lsof -n -P -i:22

其中 -n 表示不将 IP 转换为 hostname,-P 表示不将 port number 转换为 service name,-i:port 表示端口号为 22 的进程

1.3 查看进程监听的端口

  1. 使用 netstat
sudo netstat -atpn | grep 22

image.png

  1. 使用 lsof
sudo lsof -n -P -p 1333 | grep TCP
  1. /proc/pid

每个进程启动后,会生成/proc/pid目录,里面存在一个fd目录,fd 目录表示进程打开的所有的文件,cd 到那个目录,fd 为 0,1,2的分别表示标准输入stdin(0)、标准输出stdout(1)、错误输出stderr(2)。fd 为 3 表示 nc 监听的套接字的inode ,通过这个 inode 号来找改 socket 的信息。

image.png cat /proc/net/tcp存有tcp连接的信息,根据inode可以找到特定的socket信息

image.png

2. 临时端口号是如何分配的

有两种典型的使用方式会生成临时端口:

  • 调用 bind 函数不指定端口
  • 调用 connect 函数

3. 最大传输单元(Maximum Transmission Unit, MTU)

数据链路层传输的帧大小是有限制的,不能把一个太大的包直接塞给链路层,这个限制被称为「最大传输单元(Maximum Transmission Unit, MTU)」以太网的帧最小的帧是 64 字节,除去 14 字节头部和 4 字节 CRC 字段,有效荷载最小为 46 字节。最大的帧是 1518 字节,除去 14 字节头部和 4 字节 CRC,有效荷载最大为 1500,这个值就是以太网的 MTU。

image.png

MTU=1500的由来

最早的以太网工作方式:载波多路复用/冲突检测(CSMA/CD),因为网络是共享的,即任何一个节点发送数据之前,先要侦听线路上是否有数据在传输,如果有,需要等待,如果线路可用,才可以发送。

假设A发出第一个bit位,到达B,而B也正在传输第一个bit位,于是产生冲突,冲突信号得让A在完成最后一个bit位之前到达A,这个一来一回的时间间隙slot time是57.6μs。在10Mbps的网络中,在57.6μs的时间内,能够传输576个bit,所以要求以太网帧最小长度为576个bits,从而让最极端的碰撞都能够被检测到。这个576bit换算一下就是72个字节,去掉8个字节的前导符和帧开始符,所以以太网帧的最小长度为64字节。

为什么标准以太网帧长度上限为1518字节?

IP头total length为两个byte,理论上IP packet可以有65535 byte,加上Ethernet Frame头和尾,可以有65535 +14 + 4 = 65553 byte。如果在10Mbps以太网上,将会占用共享链路长达50ms,这将严重影响其它主机的通信,特别是对延迟敏感的应用是无法接受的。由于线路质量差而引起的丢包,发生在大包的概率也比小包概率大得多,所以大包在丢包率较高的线路上不是一个好的选择。但是如果选择一个比较小的长度,传输效率又不高,拿TCP应用来说,如果选择以太网长度为218byte,TCP payload = 218 - Ethernet Header - IP Header - TCP Header = 218 - 18 - 20 - 20 = 160 byte那有效传输效率= 160 / 218 = 73%而如果以太网长度为1518,那有效传输效率= 1460 / 1518 = 96%通过比较,选择较大的帧长度,有效传输效率更高,而更大的帧长度同时也会造成上述的问题,于是最终选择一个折衷的长度:1518 byte ! 对应的IP packet 就是 1500 byte,这就是最大传输单元MTU的由来。

4. IP 分段

IPv4 数据报的最大大小为 65535 字节,这已经远远超过了以太网的 MTU,而且有些网络还会开启巨帧(Jumbo Frame)能达到 9000 字节。 当一个 IP 数据包大于 MTU 时,IP 会把数据报文进行切割为多个小的片段(小于 MTU),使得这些小的报文可以通过链路层进行传输

image.png 利用 IP 包分片的策略,有一种对应的网络攻击方式IP fragment attack,就是一直传More fragments = 1的包,导致接收方一直缓存分片,从而可能导致接收方内存耗尽。

IP分片实例

// 在cmd 下ping 网关10.103.240.1
// ping 命令: -l表示数据长度 -n ping次数 -f设置DF标志表示不能分片
ping 10.103.240.1 -l 5000 -n 1

image.png 由上图可知,每个分片都包含off=xxx,ID=4858信息,接收方依据这两个值,把ID相同的分片按照off值(偏移量)进行重组的。

下图所示的最后一个分片,即第2716号包,它包含了一个More fragment = 0的Flag,表示最后一个分片,因此接收方可以开始重组。

image.png 而其他的分片比如第2715号包包含一个More fragment = 1的Flag,如下图所示,因此接收方知道后续还有更多的分片,所以先缓存着不重组。IP分片导致的网络攻击方式就是持续发送More fragment为1的包,导致接收方一直缓存分片,从而耗尽内存。

image.png

5. TCP 最大段大小(Max Segment Size,MSS)

TCP 为了避免被发送方分片,会主动把数据分割成小段再交给网络层,最大的分段大小称之为 MS(MaxSegment Size)。

MSS = MTU - IP header头大小 - TCP 头大小

这样一个 MSS 的数据恰好能装进一个 MTU 而不用分片。

TCP在建立连接时进行三次握手,前两个握手包中双方互相声明自己的MSS,客户端声明MSS=8960,服务器端声明了MSS=1460。三次握手之后,客户端的MTU值比服务器端大,如果发送一个9000字节的包过去可能被分片或丢弃。因此客户端会把自己的MSS也降到1460字节。

6. 为什么有时候抓包看到的单个数据包大于 MTU

image.png 原因是这个分片不是发生在IP协议上,而是在TCP协议上,有个东西叫tso(tcp segment offload),意思是如果网卡支持tso,操作系统发送大的tcp包时,不需要消耗CPU来计算分片,而是将整个包发送到网卡,由网卡的NPU来进行分片处理。而抓包软件抓的仅仅是CPU处理后的信息,也就是说在发送方抓的时候还没到网卡就被CPU抓了,而真正的分片是由后面的网卡才分片

参考资料

《深入理解 TCP 协议:从原理到实战》