如何根据 tcpdump 中的主题名称过滤 MQTT 流量

How to filter MQTT traffic on base of topic name in tcpdump

我正在捕获 MQTT 流量以使用以下命令进行故障排除

 tcpdump -i team0 -w mqtt-trace.pcap src 10.x.x.x

但它会在几分钟内产生非常大的文件,我可以根据主题名称过滤 tcpdump

下面是tcp payload,我希望它只抓取有PKGCTRL/1/status/frequency的payload 或者tcpdump可以直接支持像wireshark这样的应用层协议的过滤器mqtt.topic == PKGCTRL/1/status/frequency

0000   00 13 95 36 2e ef 00 10 7e 07 87 3d 08 00 45 00   ...6....~..=..E.
0010   00 77 2e 0d 40 00 40 06 f6 78 0a 0b 80 f3 0a 0b   .w..@.@..x......
0020   80 f2 c3 6a 75 83 e4 f8 f7 7a 0a 89 67 76 50 18   ...ju....z..gvP.
0030   ea 60 59 f8 00 00 30 4d 00 1a 50 4b 47 43 54 52   .`Y...0M..PKGCTR
0040   4c 2f 31 2f 73 74 61 74 75 73 2f 66 72 65 71 75   L/1/status/frequ
0050   65 6e 63 79 0a 11 09 c2 7a 85 14 d0 71 37 16 12   ency....z...q7..
0060   06 08 01 10 01 18 00 12 1c 0a 0d 09 0b 46 25 75   .............F%u
0070   02 f2 48 40 10 21 18 00 11 00 60 76 14 d0 71 37   ..H@.!....`v..q7
0080   16 20 00 28 00                                    . .(.

按照@hardillb 的建议,改用 tshark。由于 tshark 的架构,您不能在显示过滤器的同时写入一个文件(这样会太慢)。

要获取您需要的信息,它看起来像这样

$ tshark -i team0 -f "src 10.x.x.x" \
  -Y "mqtt.topic == PKGCTRL/1/status/frequency" -T fields -e mqtt.topic
  • -i team0: 在接口 team0
  • 上过滤
  • -f "src 10.x.x.x":使用capture filter,和tcpdump的过滤一样。这将加快处理速度,因为它比显示过滤器(下一个项目符号)更快。
  • -Y "mqtt.topic == PKGCTRL/1/status/frequency":过滤匹配此显示过滤器的数据包
  • -T fields -e mqtt.topic:只输出mqtt.topic字段,因为那是目标信息。

-T fields 将输出由换行符垂直分隔的柱状数据。因为只有列,所以不会水平分隔,但默认是\t.

我认为之前接受的答案不一定会按照您的想法去做,甚至可能不会按照您的意愿去做。原始问题指出,“但它会在几分钟内产生非常大的文件,我可以根据主题名称过滤 tcpdump”吗

如果您试图限制捕获文件的大小,那么之前接受的答案不会这样做,因为它使用与最初提供的完全相同的捕获过滤器,即 src 10.x.x.x。这意味着您捕获的数据量与以前相同。仅仅因为没有指定捕获文件名并不意味着数据包没有被写入文件;他们是。在 tshark 的情况下,数据包被写入 临时 文件,该文件将继续增长直到捕获 session 终止,然后理想情况下它将被删除, 但不总是。临时文件的位置因 tshark 是 运行 的平台而异,但您应该可以通过 运行ning tshark -G folders | grep "^Temp".[= 轻松找到该目录。 28=]


现在,如果你想减少捕获文件的大小,或者你看到的数据包的数量,那么你应该能够修改 tcpdumptshark command-line 实现这一目标的论据。

首先,如果您不需要整个有效载荷,您可以应用 snaplen 在某个适当的值之后将数据包截短。这是使用 -s 选项完成的,它与任一捕获工具的选项相同。

好的,但是无论您决定是否应用 snaplen,如果您想根据特定主题名称进行过滤,很可能可以实现;但是,我在下面列出了一些注意事项。主要思想是使用 slice 运算符,[](参见 pcap-filter man page) to compare various bytes of the TCP payload to specific values. (NOTE: Neither tcpdump itself nor pcap-filter refers to this operator as the slice operator, but wireshark-filter,所以我也这样做。)所以过滤器应该:

  • 仅匹配数据包 to/from 特定主机,在本例中为 10.x.x.x
  • 仅匹配 MQTT 数据包(通常按端口号,我假设这是标准 tcp/1883 端口)
  • 仅匹配 QoS 为 0 的 PUBLISH 消息
  • 仅匹配主题长度为 26 字节的 PUBLISH 消息
  • 仅匹配主题为“PKGCTRL/1/status/frequency”
  • 的 PUBLISH 消息

这是一个 应该 有效的命令(至少在大多数情况下 -> 请参阅下面的注意事项):

tcpdump -i team0 -w mqtt-trace.pcap \
    "(src host 10.x.x.x) and \
    (tcp port 1883) and \
    ((tcp[20]&0xf6)=0x30) and \
    (tcp[22:2]=26) and \
    (tcp[24:4]=0x504b4743 and tcp[28:4]=0x54524c2f and \
     tcp[32:4]=0x312f7374 and tcp[36:4]=0x61747573 and \
     tcp[40:4]=0x2f667265 and tcp[44:4]=0x7175656e and tcp[48:2]=0x6379)"

从上面对所需过滤器的描述中,过滤器的每个单独组件为您做的事情应该很明显。

在这里,您最终会得到所需数据包的捕获文件,您可以在以后需要时参考该文件。如果您喜欢写入命名的捕获文件,您甚至可以将相同的过滤器应用于 tshark 解决方案,因为正如我之前解释的那样,无论您是否明确指定,tshark 都会将数据包写入文件有没有。

注意事项:

  • 过滤器假定 TCP headers 是 20 字节以简单说明解决方案,但事实可能并非如此。如果您想要一个更健壮的解决方案来适应任何 TCP header 大小,那么您需要从TCP header,这是在使用 ((tcp[12]&0xf0)>>4)*4 的过滤器中完成的,然后用该值替换切片运算符的偏移字段中出现的每个 20。因此,例如,tcp[22:2]=26 变为 tcp[(((tcp[12]&0xf0)>>4)*4)+2:2]=26,等等

  • 因为 MQTT 剩余消息长度字段是 variable-length 根据 MQTT3.1.1 section 2.2.3 Remaining Length 编码的,如上所提供的过滤器将仅对从 0 开始的剩余长度字段的值起作用到 127,即剩余长度字段 只能是一个字节 。鉴于本例中的主题是 26 个字节,主题长度本身是 2 个字节,这意味着过滤器将仅适用于 99 个字节或更少的 MQTT 消息有效负载 (127 - (2 + 26))。