0%

抓包工具 tcpdump

tcpdump 是一款灵活、功能强大的抓包工具,能有效地帮助排查网络故障问题。tcpdump 使用 libpcap 库来抓取网络报,这个库在几乎在所有的 Linux/Unix 中都有。本文主要介绍 tcpdump 的常见使用方法,关于其原理与实现可以参考 Tcpdump 原理与实现

命令格式

tcpdump 命令格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ tcpdump [ 选项 ] [ -c 数量 ] [ -i 网络接口 ] [ -w 文件名 ] [ 表达式 ]

# 常见选项如下
-i:指定监听的网络接口
-c:抓包次数
-D: 列出所有可用的网络接口
-n: 禁用域名解析,让 tcpdump 直接输出 IP 地址
-nn:直接以 IP 及 Port Number 显示,而非主机名与服务名称
-v: 会输出稍微详细一点的信息包括校验和 ttl 之类的
-vv: 更详细的信息,比如包括 NFS 的回包信息等
-vvv: 会尝试解析应用层协议,输出详细信息。二者组合就能完整的详细信息
-X: 以 16 进制格式输出数据包的内容, 不加该参数, 会只输出 iptcp/udp 头部信息
-A: 以 ASCII 值显示抓到的包, 比如和 MySQL 的交互时,可以通过 - A 查看包的文本内容
-r:从指定的文件中读取包
-w:输出信息保存到指定文件
-s: 设置每个数据包的大小;抓取数据包时默认抓取长度为 68 字节。加上 - s 0 后可以抓到完整的数据包
-t:在输出的每一行不打印时间戳
-tt: 打印时间戳,以秒为单位,使用格式 2019-02-02 10:37:37, 便于分析
-ttt: 打印时间戳,以微秒为单位,使用格式 2019-02-02 10:37:37.120297, 便于分析
-d:将匹配信息包的代码以人们能够理解的汇编格式给出;

在表达式中一般有如下几种类型的关键字:

type

1
2
3
4
host(缺省类型):	指明一台主机,如:host 10.215.20.13
net: 指定网络地址, net 10.215.20.0
port: 指明端口号, port 3306
portrange: 指明端口范围 ,portrange 22-125

direction

1
2
3
4
dst or src(缺省值) 指定源或者目标地址是 10.215.20.13 的流量包
src: src 10.9.51.13, 指定源地址是 10.9.51.13
dst: dst net 172.0.0.0, 指定目标网络地址是 172.0.0.0
dst and src 比如: src host 10.9.51.13 and dst host 10.215.20.13

protocol

1
2
3
4
5
6
协议的关键字:缺省值是监听所有协议的信息包
ip
arp
tcp
udp
icmp

逻辑运算

1
2
3
非 : ! , not
与 : && , and
或 : || , or

可以使用 and 或者 && 来将两个或多个条件组合起来:

1
tcpdump -i eth0 src 192.168.1.100 && port 22

可以使用 or 或者 || 检查是否匹配命令所列条件中的其中一条:

1
2
tcpdump -i eth0 src 192.168.1.100 or dst 192.168.1.50 and port 22
tcpdump -i eth0 port 443 or 80

可以用 not 或者 ! 表达不匹配某项条件时:

1
2
# 捕获 eth0 上除了 22 号端口的所有通讯
$ tcpdump -i eth0 src port not 22
1
2
3
4
5
# 抓取所有经过 bond0,目的地址是 10.10.1.254 或 10.10.1.200 端口是 80 的 TCP 数据
tcpdump -i bond0 '((tcp) and (port 80) and ((dst host 10.10.1.254) or (dst host 10.10.1.200)))'

# 抓取源 ip 是 172.16.1.59 且目的端口是 22,或源 ip 是 172.16.1.68 且目的端口是 80 的数据包。
tcpdump -i eth0 -vnn 'src host 172.16.1.59 and dst port 22' or 'src host 172.16.1.68 and dst port 80'

参数实例

1
tcpdump -i bond0 -tttt -s 0 -c 100  tcp and dst port ! 22 and src net 10.10.1.0/24 -w 20190131.pcap
  • tcp: 协议类型,用来过滤数据报的协议类型。
  • -i bond0 : 只抓取经过接口 bond0 的包
  • -tttt : 使用格式 2019-02-02 10:37:37.120297, 便于分析。
  • -s 0: 抓取数据包时默认抓取长度为 68 字节。加上 - s 0 后可以抓到完整的数据包
  • -c 100: 只抓取 100 个数据包
  • dst port ! 22: 不抓取目标端口是 22 的数据包
  • src net 10.10.1.0/24: 数据包的源网络地址为 10.10.1.0/24
  • -w 20190131.pcap: 保存成 pcap 文件中, 方便使用 wireshark 分析抓包结果

使用 tcpdump 抓包,需要管理员权限,因此下面的示例中绝大多数命令都是以 sudo 开头。

首先,先用 tcpdump -D 命令列出可以抓包的网络接口:

1
2
3
4
5
6
$ sudo tcpdump -D
1.eth0
2.virbr0
3.eth1
4.any (Pseudo-device that captures on all interfaces)
5.lo [Loopback]

抓取报文格式

tcpdump 能够抓取并解码多种协议类型的数据报文,如 TCP、UDP、ICMP 等等。虽然这里我们不可能介绍所有的数据报文类型,但可以分析下 TCP 类型的数据报文,来帮助你入门。更多有关 tcpdump 的详细介绍可以参考其 帮助手册。tcpdump 抓取的 TCP 报文看起来如下:

1
08:41:13.729687 IP 192.168.64.28.22 > 192.168.64.1.41916: Flags [P.], seq 196:568, ack 1, win 309, options [nop,nop,TS val 117964079 ecr 816509256], length 372

具体的字段根据不同的报文类型会有不同,但上面这个例子是一般的格式形式。

  • 第一个字段 08:41:13.729687 是该数据报文被抓取的系统本地时间戳。
  • IP 是网络层协议类型,这里是 IPv4,如果是 IPv6 协议,该字段值是 IP6。
  • 192.168.64.28.22 是源 ip 地址和端口号,紧跟其后的是目的 ip 地址和其端口号,这里是 192.168.64.1.41916。
  • 在源 IP 和目的 IP 之后,可以看到是 TCP 报文标记段 Flags [P.]。该字段通常取值如下:
标志类型 描述
S SYN Connection Start 发起连接标志
F FIN Connection Finish 关闭连接标志
P PUSH Data push 传送数据标志
R RST Connection reset 异常关闭连接
. ACK Acknowledgment 表示确认包

该字段也可以是这些值的组合,例如 [S.] 代表 SYN-ACK 数据包。

  • 接下来是该数据包中数据的序列号。对于抓取的第一个数据包,该字段值是一个绝对数字,后续包使用相对数值,以便更容易查询跟踪。例如此处 seq 196:568 代表该数据包包含该数据流的第 196 到 568 字节。
  • 接下来是 ack 值:ack 1。该数据包是数据发送方,ack 值为 1。在数据接收方,该字段代表数据流上的下一个预期字节数据,例如,该数据流中下一个数据包的 ack 值应该是 568。
  • 接下来字段是接收窗口大小 win 309,它表示接收缓冲区中可用的字节数,后跟 TCP 选项如 MSS(最大段大小)或者窗口比例值。更详尽的 TCP 协议内容请参考 Transmission Control Protocol(TCP) Parameters。
  • 最后,length 372 代表数据包有效载荷字节长度。这个长度和 seq 序列号中字节数值长度是不一样的。

检查数据包内容

在以上的示例中,我们只按数据包头部的信息来建立规则筛选数据包,例如源地址、目的地址、端口号等等。有时我们需要分析网络连接问题,可能需要分析数据包中的内容来判断什么内容需要被发送、什么内容需要被接收等。

tcpdump 提供了两个选项可以查看数据包内容,-X 以十六进制打印出数据报文内容,-A 打印数据报文的 ASCII 值。

例如,HTTP 请求报文内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
$ sudo tcpdump -i any -c10 -nn -A port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
13:02:14.871803 IP 192.168.122.98.39366 > 54.204.39.132.80: Flags [S], seq 2546602048, win 29200, options [mss 1460,sackOK,TS val 133625221 ecr 0,nop,wscale 7], length 0
E..<..@.@.....zb6.'....P...@......r............
............................
13:02:14.910734 IP 54.204.39.132.80 > 192.168.122.98.39366: Flags [S.], seq 1877348646, ack 2546602049, win 28960, options [mss 1460,sackOK,TS val 525532247 ecr 133625221,nop,wscale 9], length 0
E..<..@./..a6.'...zb.P..o..&...A..q a..........
.R.W....... ................
13:02:14.910832 IP 192.168.122.98.39366 > 54.204.39.132.80: Flags [.], ack 1, win 229, options [nop,nop,TS val 133625260 ecr 525532247], length 0
E..4..@.@.....zb6.'....P...Ao..'...........
.....R.W................
13:02:14.911808 IP 192.168.122.98.39366 > 54.204.39.132.80: Flags [P.], seq 1:113, ack 1, win 229, options [nop,nop,TS val 133625261 ecr 525532247], length 112: HTTP: GET / HTTP/1.1
E.....@.@..1..zb6.'....P...Ao..'...........
.....R.WGET / HTTP/1.1
User-Agent: Wget/1.14 (linux-gnu)
Accept: */*
Host: opensource.com
Connection: Keep-Alive
................
13:02:14.951199 IP 54.204.39.132.80 > 192.168.122.98.39366: Flags [.], ack 113, win 57, options [nop,nop,TS val 525532257 ecr 133625261], length 0
E..4.F@./.."6.'...zb.P..o..'.......9.2.....
.R.a....................
13:02:14.955030 IP 54.204.39.132.80 > 192.168.122.98.39366: Flags [P.], seq 1:643, ack 113, win 57, options [nop,nop,TS val 525532258 ecr 133625261], length 642: HTTP: HTTP/1.1 302 Found
E....G@./...6.'...zb.P..o..'.......9.......
.R.b....HTTP/1.1 302 Found
Server: nginx
Date: Sun, 23 Sep 2018 17:02:14 GMT
Content-Type: text/html; charset=iso-8859-1
Content-Length: 207
X-Content-Type-Options: nosniff
Location: https://opensource.com/
Cache-Control: max-age=1209600
Expires: Sun, 07 Oct 2018 17:02:14 GMT
X-Request-ID: v-6baa3acc-bf52-11e8-9195-22000ab8cf2d
X-Varnish: 632951979
Age: 0
Via: 1.1 varnish (Varnish/5.2)
X-Cache: MISS
Connection: keep-alive
<!DOCTYPE HTML PUBLIC"-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="https://opensource.com/">here</a>.</p>
</body></html>
................
13:02:14.955083 IP 192.168.122.98.39366 > 54.204.39.132.80: Flags [.], ack 643, win 239, options [nop,nop,TS val 133625304 ecr 525532258], length 0
E..4..@.@.....zb6.'....P....o..............
.....R.b................
13:02:15.195524 IP 192.168.122.98.39366 > 54.204.39.132.80: Flags [F.], seq 113, ack 643, win 239, options [nop,nop,TS val 133625545 ecr 525532258], length 0
E..4..@.@.....zb6.'....P....o..............
.....R.b................
13:02:15.236592 IP 54.204.39.132.80 > 192.168.122.98.39366: Flags [F.], seq 643, ack 114, win 57, options [nop,nop,TS val 525532329 ecr 133625545], length 0
E..4.H@./.. 6.'...zb.P..o..........9.I.....
.R......................
13:02:15.236656 IP 192.168.122.98.39366 > 54.204.39.132.80: Flags [.], ack 644, win 239, options [nop,nop,TS val 133625586 ecr 525532329], length 0
E..4..@.@.....zb6.'....P....o..............
.....R..................
10 packets captured
10 packets received by filter
0 packets dropped by kernel

这对定位一些普通 HTTP 调用 API 接口的问题很有用。当然如果是加密报文,这个输出也就没多大用了。

保存抓包数据

tcpdump 提供了保存抓包数据的功能以便后续分析数据包。例如,你可以夜里让它在那里抓包,然后早上起来再去分析它。同样当有很多数据包时,显示过快也不利于分析,将数据包保存下来,更有利于分析问题。

使用 -w 选项来保存数据包而不是在屏幕上显示出抓取的数据包:

1
2
3
4
5
$ sudo tcpdump -i any -c10 -nn -w webserver.pcap port 80
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
10 packets captured
10 packets received by filter
0 packets dropped by kernel

该命令将抓取的数据包保存到文件 webserver.pcap。后缀名 pcap 表示文件是抓取的数据包格式。正如示例中所示,保存数据包到文件中时屏幕上就没有任何有关数据报文的输出,其中 -c10 表示抓取到 10 个数据包后就停止抓包。如果想有一些反馈来提示确实抓取到了数据包,可以使用 -v 选项。

tcpdump 将数据包保存在二进制文件中,所以不能简单的用文本编辑器去打开它。使用 -r 选项参数来阅读该文件中的报文内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ tcpdump -nn -r webserver.pcap
reading from file webserver.pcap, link-type LINUX_SLL (Linux cooked)
13:36:57.679494 IP 192.168.122.98.39378 > 54.204.39.132.80: Flags [S], seq 3709732619, win 29200, options [mss 1460,sackOK,TS val 135708029 ecr 0,nop,wscale 7], length 0
13:36:57.718932 IP 54.204.39.132.80 > 192.168.122.98.39378: Flags [S.], seq 1999298316, ack 3709732620, win 28960, options [mss 1460,sackOK,TS val 526052949 ecr 135708029,nop,wscale 9], length 0
13:36:57.719005 IP 192.168.122.98.39378 > 54.204.39.132.80: Flags [.], ack 1, win 229, options [nop,nop,TS val 135708068 ecr 526052949], length 0
13:36:57.719186 IP 192.168.122.98.39378 > 54.204.39.132.80: Flags [P.], seq 1:113, ack 1, win 229, options [nop,nop,TS val 135708068 ecr 526052949], length 112: HTTP: GET / HTTP/1.1
13:36:57.756979 IP 54.204.39.132.80 > 192.168.122.98.39378: Flags [.], ack 113, win 57, options [nop,nop,TS val 526052959 ecr 135708068], length 0
13:36:57.760122 IP 54.204.39.132.80 > 192.168.122.98.39378: Flags [P.], seq 1:643, ack 113, win 57, options [nop,nop,TS val 526052959 ecr 135708068], length 642: HTTP: HTTP/1.1 302 Found
13:36:57.760182 IP 192.168.122.98.39378 > 54.204.39.132.80: Flags [.], ack 643, win 239, options [nop,nop,TS val 135708109 ecr 526052959], length 0
13:36:57.977602 IP 192.168.122.98.39378 > 54.204.39.132.80: Flags [F.], seq 113, ack 643, win 239, options [nop,nop,TS val 135708327 ecr 526052959], length 0
13:36:58.022089 IP 54.204.39.132.80 > 192.168.122.98.39378: Flags [F.], seq 643, ack 114, win 57, options [nop,nop,TS val 526053025 ecr 135708327], length 0
13:36:58.022132 IP 192.168.122.98.39378 > 54.204.39.132.80: Flags [.], ack 644, win 239, options [nop,nop,TS val 135708371 ecr 526053025], length 0
$

这里不需要管理员权限 sudo 了,因为此刻并不是在网络接口处抓包。

你还可以使用我们讨论过的任何过滤规则来过滤文件中的内容,就像使用实时数据一样。 例如,通过执行以下命令从源 IP 地址 54.204.39.132 检查文件中的数据包:

1
2
3
4
5
6
$ tcpdump -nn -r webserver.pcap src 54.204.39.132
reading from file webserver.pcap, link-type LINUX_SLL (Linux cooked)
13:36:57.718932 IP 54.204.39.132.80 > 192.168.122.98.39378: Flags [S.], seq 1999298316, ack 3709732620, win 28960, options [mss 1460,sackOK,TS val 526052949 ecr 135708029,nop,wscale 9], length 0
13:36:57.756979 IP 54.204.39.132.80 > 192.168.122.98.39378: Flags [.], ack 113, win 57, options [nop,nop,TS val 526052959 ecr 135708068], length 0
13:36:57.760122 IP 54.204.39.132.80 > 192.168.122.98.39378: Flags [P.], seq 1:643, ack 113, win 57, options [nop,nop,TS val 526052959 ecr 135708068], length 642: HTTP: HTTP/1.1 302 Found
13:36:58.022089 IP 54.204.39.132.80 > 192.168.122.98.39378: Flags [F.], seq 643, ack 114, win 57, options [nop,nop,TS val 526053025 ecr 135708327], length 0

参考资料