tcpdump是一款强大的网络抓包工具,它使用libpcap库来抓取网络数据包,这个库在几乎在所有的 Linux/Unix 中都有。熟悉 tcpdump 的使用能够帮助你分析调试网络数据,本文将通过一个个具体的示例来介绍它在不同场景下的使用方法。不管你是系统管理员,程序员,云原生工程师还是 yaml 工程师,掌握 tcpdump 的使用都能让你如虎添翼,升职加薪。

1. 基本语法和使用方法

tcpdump的常用参数如下:
$ tcpdump -i eth0 -nn -s0 -v port 80复制代码-i: 选择要捕获的接口,通常是以太网卡或无线网卡,也可以是vlan或其他特殊接口。如果该系统上只有一个网络接口,则无需指定。-nn: 单个 n 表示不解析域名,直接显示 IP;两个 n 表示不解析域名和端口。这样不仅方便查看 IP 和端口号,而且在抓取大量数据时非常高效,因为域名解析会降低抓取速度。-s0: tcpdump 默认只会截取前96字节的内容,要想截取所有的报文内容,可以使用-s number,number就是你要截取的报文字节数,如果是 0 的话,表示截取报文全部内容。-v: 使用-v,-vv和-vvv来显示更多的详细信息,通常会显示更多与特定协议相关的信息。port 80: 这是一个常见的端口过滤器,表示仅抓取80端口上的流量,通常是 HTTP。
额外再介绍几个常用参数:
-p: 不让网络接口进入混杂模式。默认情况下使用 tcpdump 抓包时,会让网络接口进入混杂模式。一般计算机网卡都工作在非混杂模式下,此时网卡只接受来自网络端口的目的地址指向自己的数据。当网卡工作在混杂模式下时,网卡将来自接口的所有数据都捕获并交给相应的驱动程序。如果设备接入的交换机开启了混杂模式,使用-p选项可以有效地过滤噪声。-e: 显示数据链路层信息。默认情况下 tcpdump 不会显示数据链路层信息,使用-e选项可以显示源和目的 MAC 地址,以及 VLAN tag 信息。例如:$ tcpdump -n -e -c 5 not ip6 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on br-lan, link-type EN10MB (Ethernet), capture size 262144 bytes 18:27:53.619865 24:5e:be:0c:17:af > 00:e2:69:23:d3:3b, ethertype IPv4 (0x0800), length 1162: 192.168.100.20.51410 > 180.176.26.193.58695: Flags [.], seq 2045333376:2045334484, ack 3398690514, win 751, length 1108 18:27:53.626490 00:e2:69:23:d3:3b > 24:5e:be:0c:17:af, ethertype IPv4 (0x0800), length 68: 220.173.179.66.36017 > 192.168.100.20.51410: UDP, length 26 18:27:53.626893 24:5e:be:0c:17:af > 00:e2:69:23:d3:3b, ethertype IPv4 (0x0800), length 1444: 192.168.100.20.51410 > 220.173.179.66.36017: UDP, length 1402 18:27:53.628837 00:e2:69:23:d3:3b > 24:5e:be:0c:17:af, ethertype IPv4 (0x0800), length 1324: 46.97.169.182.6881 > 192.168.100.20.59145: Flags [P.], seq 3058450381:3058451651, ack 14349180, win 502, length 1270 18:27:53.629096 24:5e:be:0c:17:af > 00:e2:69:23:d3:3b, ethertype IPv4 (0x0800), length 54: 192.168.100.20.59145 > 192.168.100.1.12345: Flags [.], ack 3058451651, win 6350, length 0 5 packets captured复制代码

显示 ASCII 字符串

-A表示使用ASCII字符串打印报文的全部数据,这样可以使读取更加简单,方便使用grep等工具解析输出内容。-X表示同时使用十六进制和ASCII字符串打印报文的全部数据。这两个参数不能一起使用。例如:
$ tcpdump -A -s0 port 80复制代码

抓取特定协议的数据

后面可以跟上协议名称来过滤特定协议的流量,以UDP 为例,可以加上参数 udp 或protocol 17,这两个命令意思相同。
$ tcpdump -i eth0 udp $ tcpdump -i eth0 proto 17复制代码
同理,tcp 与protocol 6意思相同。

抓取特定主机的数据

使用过滤器host可以抓取特定目的地和源 IP 地址的流量。
$ tcpdump -i eth0 host 10.10.1.1复制代码
也可以使用src或dst只抓取源或目的地:
$ tcpdump -i eth0 dst 10.10.1.20复制代码

将抓取的数据写入文件

使用 tcpdump 截取数据报文的时候,默认会打印到屏幕的默认输出,你会看到按照顺序和格式,很多的数据一行行快速闪过,根本来不及看清楚所有的内容。不过,tcpdump 提供了把截取的数据保存到文件的功能,以便后面使用其他图形工具(比如 wireshark,Snort)来分析。
-w选项用来把数据报文输出到文件:
$ tcpdump -i eth0 -s0 -w test.pcap复制代码

行缓冲模式

如果想实时将抓取到的数据通过管道传递给其他工具来处理,需要使用-l选项来开启行缓冲模式(或使用-c选项来开启数据包缓冲模式)。使用-l选项可以将输出通过立即发送给其他命令,其他命令会立即响应。
$ tcpdump -i eth0 -s0 -l port 80 | grep 'Server:'复制代码

组合过滤器

过滤的真正强大之处在于你可以随意组合它们,而连接它们的逻辑就是常用的与/AND/&&、或/OR/||和非/not/!。
and or && or or || not or !复制代码

2. 过滤器

关于 tcpdump 的过滤器,这里有必要单独介绍一下。
机器上的网络报文数量异常的多,很多时候我们只关系和具体问题有关的数据报(比如访问某个网站的数据,或者 icmp 超时的报文等等),而这些数据只占到很小的一部分。把所有的数据截取下来,从里面找到想要的信息无疑是一件很费时费力的工作。而tcpdump 提供了灵活的语法可以精确地截取关心的数据报,简化分析的工作量。这些选择数据包的语句就是过滤器(filter)!

Host 过滤器

Host 过滤器用来过滤某个主机的数据报文。例如:
$ tcpdump host 1.2.3.4复制代码
该命令会抓取所有发往主机1.2.3.4或者从主机1.2.3.4发出的流量。如果想只抓取从该主机发出的流量,可以使用下面的命令:
$ tcpdump src host 1.2.3.4复制代码

Network 过滤器

Network 过滤器用来过滤某个网段的数据,使用的是CIDR 模式。可以使用四元组(x.x.x.x)、三元组(x.x.x)、二元组(x.x)和一元组(x)。四元组就是指定某个主机,三元组表示子网掩码为255.255.255.0,二元组表示子网掩码为255.255.0.0,一元组表示子网掩码为255.0.0.0。例如,
抓取所有发往网段192.168.1.x或从网段192.168.1.x发出的流量:
$ tcpdump net 192.168.1复制代码
抓取所有发往网段10.x.x.x或从网段10.x.x.x发出的流量:
$ tcpdump net 10复制代码
和Host 过滤器一样,这里也可以指定源和目的:
$ tcpdump src net 10复制代码
也可以使用 CIDR 格式:
$ tcpdump src net 172.16.0.0/12复制代码

Proto 过滤器

Proto 过滤器用来过滤某个协议的数据,关键字为proto,可省略。proto 后面可以跟上协议号或协议名称,支持icmp,igmp,igrp,pim,ah,esp,carp,vrrp,udp和tcp。因为通常的协议名称是保留字段,所以在于 proto 指令一起使用时,必须根据 shell 类型使用一个或两个反斜杠(/)来转义。Linux 中的 shell 需要使用两个反斜杠来转义,MacOS 只需要一个。
例如,抓取 icmp 协议的报文:
$ tcpdump -n proto \\icmp # 或者 $ tcpdump -n icmp复制代码

Port 过滤器

Port 过滤器用来过滤通过某个端口的数据报文,关键字为port。例如:
$ tcpdump port 389复制代码

3. 理解 tcpdump 的输出

截取数据只是第一步,第二步就是理解这些数据,下面就解释一下 tcpdump 命令输出各部分的意义。
21:27:06.995846 IP (tos 0x0, ttl 64, id 45646, offset 0, flags [DF], proto TCP (6), length 64) 192.168.1.106.56166 > 124.192.132.54.80: Flags [S], cksum 0xa730 (correct), seq 992042666, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 663433143 ecr 0,sackOK,eol], length 0 21:27:07.030487 IP (tos 0x0, ttl 51, id 0, offset 0, flags [DF], proto TCP (6), length 44) 124.192.132.54.80 > 192.168.1.106.56166: Flags [S.], cksum 0xedc0 (correct), seq 2147006684, ack 992042667, win 14600, options [mss 1440], length 0 21:27:07.030527 IP (tos 0x0, ttl 64, id 59119, offset 0, flags [DF], proto TCP (6), length 40) 192.168.1.106.56166 > 124.192.132.54.80: Flags [.], cksum 0x3e72 (correct), ack 2147006685, win 65535, length 0复制代码
最基本也是最重要的信息就是数据报的源地址/端口和目的地址/端口,上面的例子第一条数据报中,源地址 ip 是192.168.1.106,源端口是56166,目的地址是124.192.132.54,目的端口是80。>符号代表数据的方向。
此外,上面的三条数据还是 tcp 协议的三次握手过程,第一条就是SYN报文,这个可以通过Flags [S]看出。