标题: tcpdump/libpcap过滤规则解析中的BUG 2005-03-14 16:19 今天收到"quickmouse@华中"的一封信,他想捕捉源端口为53/UDP或1233/UDP的报文 以及ARP报文。当时我说可以这样: tcpdump 'ip and udp and src port 53 or 1233' or arp 结果他回信说这个命令将捕捉部分TCP报文,我觉得很奇怪,感觉上不太可能,于是 执行了如下命令测试: telnet 53 这里上并未侦听53/TCP,于是将回送一个RST报文。我在前述tcpdump命令的 输出中并未看到这个RST报文。可耗子这样说,肯定不会空穴来风,他让我用-d确认 一下,结果看到这个输出: # tcpdump 'ip and udp and src port 53 or 1233' or arp -d (000) ldh [12] (001) jeq #0x800 jt 2 jf 17 (002) ldb [23] (003) jeq #0x11 jt 4 jf 10 (004) ldh [20] (005) jset #0x1fff jt 19 jf 6 (006) ldxb 4*([14]&0xf) (007) ldh [x + 14] (008) jeq #0x35 jt 18 jf 9 (009) jeq #0x4d1 jt 18 jf 19 (010) jeq #0x84 jt 12 jf 11 <== 0x84(132)不知从哪里来的 (011) jeq #0x6 jt 12 jf 19 (012) ldh [20] (013) jset #0x1fff jt 19 jf 14 (014) ldxb 4*([14]&0xf) (015) ldh [x + 14] (016) jeq #0x4d1 jt 18 jf 19 <== 源端口为1233/TCP的报文 (017) jeq #0x806 jt 18 jf 19 (018) ret #68 (019) ret #0 这个输出意味着上述命令将捕捉到源端口为1233/TCP的报文。用如下命令测试: telnet 1233 果然捕捉到一个RST报文。显然,耗子所说的解析BUG确实存在,最明显的是0x84不知 从哪里冒出来的。 现在检查如下过滤规则: # tcpdump 'udp src port 53 or 1233' or arp -d (000) ldh [12] (001) jeq #0x800 jt 2 jf 10 IP报文 (002) ldb [23] (003) jeq #0x11 jt 4 jf 12 UDP报文 (004) ldh [20] (005) jset #0x1fff jt 12 jf 6 第一个IP碎片 (006) ldxb 4*([14]&0xf) (007) ldh [x + 14] (008) jeq #0x35 jt 11 jf 9 源端口为53/UDP (009) jeq #0x4d1 jt 11 jf 12 源端口为1233/UDP (010) jeq #0x806 jt 11 jf 12 ARP报文 (011) ret #68 (012) ret #0 这次才是如我们所愿的过滤规则。两个教训,一是过滤规则写得长了,一些看似冗余 而无害的条件可能因BUG而变得有害,二是安全起见务必动用-d先期检查过滤规则。 不过,这个BUG可能是某些版本的tcpdump/libpcap相配合时出现的BUG: # tcpdump -V tcpdump version 3.8.3 libpcap version 0.8.3 真正解析过滤规则的部件是libpcap,而不是tcpdump。libpcap采用pcap_compile() 和pcap_setfilter()结合的办法屏蔽了各种链路层支持的不同。 在bash命令行上还可以这样写: # tcpdump \(udp src port 53 or 1233\) or arp 转义符\的出现是因为圆括号()对于bash本身有其它意义。 由于Ethereal/Winpcap使用类似的过滤规则,很可能存在类似的BUG,务必小心。在 Ethereal的对话框中并不支持变异的单引号,如下规则将报错: 'udp src port 53 or 1233' or arp Ethereal的对话框中圆括号没有类似bash元字符那样的特殊意义,因此也不需要转义 符,如下规则将报错: \(udp src port 53 or 1233\) or arp 在Ethereal中,只能按最原始的显示指定过滤规则: (udp src port 53 or 1233) or arp 2005-03-15 08:24 晚上又想了想,算不上是软件BUG,是我的BUG,看下面的注释: # tcpdump 'ip and udp and src port 53 or 1233' or arp -d (000) ldh [12] (001) jeq #0x800 jt 2 jf 17 IP报文 (002) ldb [23] (003) jeq #0x11 jt 4 jf 10 UDP报文 (004) ldh [20] (005) jset #0x1fff jt 19 jf 6 第一个IP碎片 (006) ldxb 4*([14]&0xf) (007) ldh [x + 14] (008) jeq #0x35 jt 18 jf 9 源端口为53/UDP (009) jeq #0x4d1 jt 18 jf 19 源端口为1233/UDP (010) jeq #0x84 jt 12 jf 11 0x84(132)与0x11(17)、0x06平行 (011) jeq #0x6 jt 12 jf 19 TCP报文 (012) ldh [20] (013) jset #0x1fff jt 19 jf 14 第一个IP碎片 (014) ldxb 4*([14]&0xf) (015) ldh [x + 14] (016) jeq #0x4d1 jt 18 jf 19 源端口为1233/TCP (017) jeq #0x806 jt 18 jf 19 ARP报文 (018) ret #68 (019) ret #0 or的优先级低于and,导致1233是单独的一个规则,不受"ip and udp and"的约束。 检查如下过滤规则: # tcpdump src port 1233 -d (000) ldh [12] (001) jeq #0x800 jt 2 jf 12 IP报文 (002) ldb [23] (003) jeq #0x84 jt 6 jf 4 0x84(132)与0x11(17)、0x06平行 (004) jeq #0x6 jt 6 jf 5 TCP报文 (005) jeq #0x11 jt 6 jf 12 UDP报文 (006) ldh [20] (007) jset #0x1fff jt 12 jf 8 第一个IP碎片 (008) ldxb 4*([14]&0xf) (009) ldh [x + 14] (010) jeq #0x4d1 jt 11 jf 12 源端口为1233/UDP或1233/TCP (011) ret #68 (012) ret #0