有时,在对副本集的运行状况进行故障排除时,我想要专门过滤掉心跳包,跟踪它们的发出情况以及后续的答复(或缺少答复),而不会受到在副本集之间流动的其他数据的干扰。
不幸的是,这些数据包的结构与普通的命令/查询和响应非常相似。尽管Wireshark有解析器可以让我进入 MongoDB 有线协议,我不能使用该技术让 tcpdump 在源头过滤掉数据包。
那么,问题是 - 如何在 tcpdump 中过滤 MongoDB 副本集心跳?
答案1
构建于这个文件首先,我们需要确定我们的识别特征是什么,以便过滤器成功并仅挑选出心跳。然后,我们需要获取该标识符的十六进制表示形式。从出站心跳本身开始(本质上只是一个查询/命令),它是一个管理命令并包含以下字符串:
replSetHeartBeat = 0x7265706c536574486561727446265174 (16 bytes)
现在我们有了识别字符串,我们需要弄清楚在 TCP 内部查找的位置。偏移量的计算方式如下:
TCP 的 32 字节让你达到MongoDB 有线协议, 进而:
- 4字节-消息长度
- 4 个字节 - 请求 ID
- 4 字节 - 响应
- 4 个字节 - 操作码
- 4 个字节 - 标志
- 11 个字节 - 集合名称(在这种情况下始终相同,但一般可能会有所不同)
- 4 个字节 - numtoskip
- 4 个字节 - numtoreturn
- 4 个字节 - 文档长度
- 1 个字节 - 类型
因此总偏移量为: (32+4+4+4+4+4+11+4+4+4+1) = 76 字节
因此你会认为需要的是这样的:
sudo tcpdump -i eth0 'tcp[76:16] = 0x7265706c536574486561727446265174'
遗憾的是,tcpdump 一次仅允许最多 4 个字节匹配,因此您实际上需要将其分解为 4 x 4 字节块,然后使用逻辑 AND 来组合匹配:
sudo tcpdump -i eth0 '(tcp[76:4] = 0x7265706c) and (tcp[80:4] = 0x53657448) and (tcp[84:4] = 0x65617274) and (tcp[88:4] = 0x62656174)'
这涵盖了心跳的出站部分,但回复呢?
值得庆幸的是,心跳上的答复更容易匹配 - 我们正在寻找文档的 rs:true 部分,并且它转换如下,可以轻松地放入 4 个字节中:
rs : true = 0x72730001 (4 bytes)
以类似的方式计算偏移量(唯一的实际区别是 8 字节游标 ID 而不是 11 字节集合名称)我们得到 73 字节偏移量,从而得到以下过滤器:
sudo tcpdump -i eth0 'tcp[73:4] = 0x72730001'
最后,让我们把所有这些放在一起,并添加一些我喜欢的 tcpdump 选项。最后我们得到这个命令:
sudo tcpdump -Xs0 -nnpi eth0 -w heartbeats.pcap '((tcp[76:4] = 0x7265706c) and (tcp[80:4] = 0x53657448) and (tcp[84:4] = 0x65617274) and (tcp[88:4] = 0x62656174)) or tcp[73:4] = 0x72730001'
(在 Mac OS X 和 Linux 上使用 MongoDB 2.4.4 测试成功)
当然,这也可以更普遍地应用,你只需要制定出适当的匹配标准、偏移量和字节匹配。
作为参考,您可以使用相同的标准,但语法略有不同,在 Wireshark 中测试此类过滤。上述标准的等效 Wireshark 过滤器是:
tcp[76:16]==72:65:70:6c:53:65:74:48:65:61:72:74:62:65:61:74
tcp[73:4]==72:73:00:01