我试图理解碎片的概念:
我有两台具有公共 IP 的虚拟机,连接到交换机。
tracepath 显示数据包未通过网关
来自 vm1:尝试向 vm2 发送 65507 字节的 icmp。
ping -M want -s 65507 vm2
但在 vm2 上的 tcpdump 输出中显示
tcpdump -evvv icmp
12:48:44.635551 42:43:30:b4:89:0c (oui Unknown) > b6:7a:6b:7d:54:32 (oui Unknown), ethertype IPv4 (0x0800), length 1514: (tos 0x0, ttl 64, id 10843, offset 1480, flags [+], proto ICMP (1), length 1500)
VM1 > VM2: icmp
12:48:44.635568 42:43:30:b4:89:0c (oui Unknown) > b6:7a:6b:7d:54:32 (oui Unknown), ethertype IPv4 (0x0800), length 1514: (tos 0x0, ttl 64, id 10843, offset 2960, flags [+], proto ICMP (1), length 1500)
VM1 > Vm2: icmp
12:48:44.635572 42:43:30:b4:89:0c (oui Unknown) > b6:7a:6b:7d:54:32 (oui Unknown), ethertype IPv4 (0x0800), length 1514: (tos 0x0, ttl 64, id 10843, offset 4440, flags [+], proto ICMP (1), length 1500)
VM1>VM2 icmp
12:48:44.635575 42:43:30:b4:89:0c (oui Unknown) > b6:7a:6b:7d:54:32 (oui Unknown), ethertype IPv4 (0x0800), length 1514: (tos 0x0, ttl 64, id 10843, offset 5920, flags [+], proto ICMP (1), length 1500)
VM1>VM2: icmp
12:48:44.635578 42:43:30:b4:89:0c (oui Unknown) > b6:7a:6b:7d:54:32 (oui Unknown), ethertype IPv4 (0x0800), length 1514: (tos 0x0, ttl 64, id 10843, offset 7400, flags [+], proto ICMP (1), length 1500)
**Vm1 > VM2**: icmp
12:48:44.635581 42:43:30:b4:89:0c (oui Unknown) > b6:7a:6b:7d:54:32 (oui Unknown), ethertype IPv4 (0x0800), length 1514: (tos 0x0, ttl 64, id 10843, offset 8880, flags [+], proto ICMP (1), length 1500)
重复此操作 31 次,直到完全收到。完整粘贴:http://pastebin.com/cnQhn8dK
那么为什么看起来接收的总数据量是1500*31=46500 字节发生了什么事65507-46500=19007 字节。
有人可以澄清这一点吗?
答案1
是的,使用带 -s 选项的 tcpdump,结果现在正确了。正如您的结果,我们计算了 45 个数据包。44 个数据包有 1500 字节,1 个数据包有 415 字节。
44*1500+415=66415
66415-65507 = 908
908/45=20加8
您可以看到,每个数据包为第一个数据包添加 20 字节的 ip 头 + 8 字节的 icmp 头。
答案2
补充一下Gnouc 的回答。
您正在发送65507
字节数据。这不包括 8 个字节的 ICMP 标头。
最常见的 MTU 大小是1500
。此大小占第 3 层大小,这就是您所看到的,ethertype IPv4 (0x0800), length 1514:
这意味着总帧大小实际上是 1514 字节。这 14 个字节占以太网报头。每个 mac 地址 12 个字节 + 2 个用于类型。
IP 标头的最小和最常见大小为 20 字节(最大为 60 字节)。
因此我们有
1514 bytes - Ethernet header = 1500 bytes
1500 - IP header = 1480 bytes
1480 - ICMP header = 1472 bytes
您最多可以发送 1472 个字节而不会产生碎片。
但是 IP 对数据包进行分段的方式不需要为每个数据包发送报头,而只需要发送第一个数据包的报头。
经过分段后,在 MTU 为 1500 的情况下您可以发送的最大字节数为 1480 字节。
我们知道您的数据的总大小以及您可以发送的最大数据量。
因此,至少需要65507 / 1480 ~= 44.2 packets
。即 44 个 1514 的数据包,然后最后一个数据包的大小小于其一半。
其余的数据包怎么样了?
但为什么是 31 个数据包?这全在于你捕获的缓冲区大小。在 tcpdump 的末尾,你应该看到
31 packets captured
57 packets received by filter
14 packets dropped by kernel
如果您添加捕获的数据包 + 内核丢弃的数据包,您将得到正确的答案,这就是-s
它改变快照长度的原因。
-s 从每个数据包中截取 snaplen 字节的数据,而不是默认的 65535 字节。由于快照有限而被截断的数据包在输出中用“[|proto]”表示,其中 proto 是发生截断的协议级别的名称。请注意,获取更大的快照会增加处理数据包所需的时间,并有效地减少数据包缓冲量。这可能会导致数据包丢失。您应该将 snaplen 限制为可捕获您感兴趣的协议信息的最小数字。将 snaplen 设置为 0 会将其设置为默认值 65535,以便与最近的旧版本的 tcpdump 向后兼容。
为什么内核首先丢弃数据包?
再次引用 tcpdump 手册页
内核丢弃的数据包(这是由于缓冲区空间不足而由运行 tcpdump 的操作系统中的数据包捕获机制丢弃的数据包数量,如果操作系统将该信息报告给应用程序;如果没有,则将报告为 0)。