tcpdump 命令简介

[ tcpdump linux ]

联调接口是每个 Web 开发者必备技能,前端以 Chrome Developer Tools 为主,浏览器发出的请求都可以通过 Network 捕获。

服务端开发远程调用 Http 接口时,可以通过 Http 代理软件观察请求的内容,比如 Fiddler,Charles。 然而,能这样做的前提条件是服务端的开发者处于 GUI 环境,如果你处于 Linux 命令行环境,就需要 tcpdump 之类的命令来捕捉 Http 流量。

基本选项

tcpdump 可以捕捉指定网卡的流量,在控制台输出,保存到文件;还可以不监听,直接解析文件内容并输出。

其中常用的选项:

tcpdump [ -c count ] 
        [ -C file_size ] [ -G rotate_seconds ] [ -F file ]
        [ -i interface ] [ -j tstamp_type ] [ -m module ] [ -M secret ]
        [ --number ] [ -Q in|out|inout ]
        [ -r file ] [ -V file ] [ -s snaplen ] [ -T type ] [ -w file ]
        [ -W filecount ]
        [ -E spi@ipaddr algo:secret,...  ]
        [ -y datalinktype ] [ -z postrotate-command ] [ -Z user ]
        [ --time-stamp-precision=tstamp_precision ]
        [ --immediate-mode ] [ --version ]
        [ expression ]

各中含义如下:

输出格式

来看看 tcpdump 如何工作,比如我们想通过它来抓取本地访问 www.baidu.com 的数据。

tcpdump 'port 80 and host www.baidu.com'

然后通过 curl www.baidu.com 就会看到 tcpdump 如下输出:

### 三次握手建立 TCP 链接
23:05:19.720766 IP [local-ip].55405 > 180.101.49.12.http: Flags [S], seq 2801727995, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
23:05:19.731660 IP 180.101.49.12.http > [local-ip].55405: Flags [S.], seq 1189577036, ack 2801727996, win 8192, options [mss 1452,nop,nop,sackOK,nop,wscale 5], length 0
23:05:19.731678 IP [local-ip].55405 > 180.101.49.12.http: Flags [.], ack 1, win 229, length 0

### 本地向服务端发起 http 请求
23:05:19.731705 IP [local-ip].55405 > 180.101.49.12.http: Flags [P.], seq 1:78, ack 1, win 229, length 77: HTTP: GET / HTTP/1.1

### 服务端向本地 ack,并发送 response
23:05:19.742859 IP 180.101.49.12.http > [local-ip].55405: Flags [.], ack 78, win 908, length 0
23:05:19.744004 IP 180.101.49.12.http > [local-ip].55405: Flags [P.], seq 1:1441, ack 78, win 908, length 1440: HTTP: HTTP/1.1 200 OK

### 本地向服务端 ack
23:05:19.744016 IP [local-ip].55405 > 180.101.49.12.http: Flags [.], ack 1441, win 251, length 0

### 服务端继续向本地发送数据
23:05:19.744019 IP 180.101.49.12.http > [local-ip].55405: Flags [P.], seq 1441:2782, ack 78, win 908, length 1341: HTTP

### 本地向服务端 ack
23:05:19.744022 IP [local-ip].55405 > 180.101.49.12.http: Flags [.], ack 2782, win 274, length 0

### 数据接收完毕后,本地主动关闭链接,向服务端发起 FIN,并且 ack 自己已无数据发送; 1/4
23:05:19.744139 IP [local-ip].55405 > 180.101.49.12.http: Flags [F.], seq 78, ack 2782, win 274, length 0
### 服务端继续向本地发送数据
23:05:19.752994 IP 180.101.49.12.http > [local-ip].55405: Flags [P.], seq 1441:2782, ack 78, win 908, length 1341: HTTP
### 本地向服务端 ack
23:05:19.753001 IP [local-ip].55405 > 180.101.49.12.http: Flags [.], ack 2782, win 274, options [nop,nop,sack 1 {1441:2782}], length 0
### 服务度端向本地 ack; 2/4
23:05:19.755279 IP 180.101.49.12.http > [local-ip].55405: Flags [.], ack 79, win 908, length 0
### 服务端向本地发送 FIN; 3/4
23:05:19.755289 IP 180.101.49.12.http > [local-ip].55405: Flags [F.], seq 2782, ack 79, win 908, length 0
### 本地向服务端 ack; 4/4
23:05:19.755299 IP [local-ip].55405 > 180.101.49.12.http: Flags [.], ack 2783, win 274, length 0
### 服务端重置链接
23:05:22.765004 IP 180.101.49.12.http > [local-ip].55405: Flags [R], seq 1189579819, win 0, length 0

其中每一列的含义如下:

另外,通过观察还能得出 ack num 和 seq num 有以下关系:

  1. 在建立链接和关闭链接的过程中,ack 对方发送的数据时:ack num = seq num + 1
  2. 在发送数据的过程中,ack num = 上一次发送的 seq 范围的最后一个字节
  3. 在其它情况下,packet 会带上(重复)上一次本方的 ack num

另外通过实验,可以实现 3 次挥手。Flags 分别是:

  1. F
  2. F.
  3. .

表达式

这是 tcpdump 的黑魔法。

tcpdump 用表达式过滤 packet,只输出表达式为 true 的 packet。

表达式由一个或多个表达元(primitive) 组成。一个表达元通常由一个或多个修饰符(qualifiers) 后跟一个名字或数字表示的 id 组成(即, ‘qualifiers id’).

有三种不同类型的修饰符: type, dir 以及 proto.

type 修饰符

type 修饰符指定 id 所代表的对象类型, id 可以是名字也可以是数字.

可选的对象类型有: host, net, port 以及 portrange。 host 表明 id 表示主机, net 表明 id 是网络, port 表明 id 是端口而 portrange 表明 id 是一个端口范围。如:

如果不指定 type 修饰符, id 默认的修饰符为 host.

dir 修饰符

dir 修饰符描述 id 所对应的传输方向, 即发往 id 还是从 id 接收(而 id 到底指什么需要看其前面的 type 修饰符). 可取的方向为: src, dst, src 或 dst, src 并且 dst. 如:

如果不指定 dir 修饰符, id 默认的修饰符为 src 或 dst.

proto 修饰符

proto 修饰符描述 id 所属的协议. 可选的协议有: ether, fddi, tr, wlan, ip, ip6, arp, rarp, decnet, tcp 以及 upd.

如:

如果不指定 proto 修饰符, 则默认为与相应 type 匹配的修饰符. 例如:

由于 tcpdump 直接通过数据链路层的 BSD 数据包过滤器或 DLPI(datalink provider interface, 数据链层提供者接口) 来直接获得网络数据包, 其可抓取的数据包可涵盖上层的各种协议, 包括 arp, rarp, icmp(因特网控制报文协议), ip, ip6, tcp, udp, sctp(流控制传输协议)。

对于修饰符后跟 id 的格式, 可理解为, type id 是对包最基本的过滤条件: 即对包相关的主机, 网络, 端口的限制; dir 表示对包的传送方向的限制; proto表示对包相关的协议限制)

表达元之间还可以通过关键字 and, or 以及 not 进行连接, 从而可组成比较复杂的条件表达式. 比如:host foo and not port ftp and not port ftp-data,其过滤条件为: 数据包的主机为 foo, 并且端口不是 ftp(端口21) 和 ftp-data(端口20)。

常用端口和名字的对应可在 linux 系统中的 /etc/service 文件中找到。

为了表示方便, 同样的修饰符可以被省略, 如 ‘tcp dst port ftp or ftp-data or domain 与以下的表达式含义相同 tcp dst port ftp or tcp dst port ftp-data or tcp dst port domain`. 其过滤条件可理解为: 包的协议为 tcp, 目的端口为 ftp 或 ftp-data 或 domain(端口53).

借助括号以及操作符, 可把表达元组合在一起使用 (由于括号是 shell 的特殊字符, 所以在 shell 脚本或终端中使用时必须对括号进行转义, 即’(‘ 与’)’需要分别表达成’(’ 与 ‘)’).

操作符:

  1. 否定操作 (!not)
  2. 与操作(&&and)
  3. 或操作(||or)

否定操作符的优先级别最高. 与操作和或操作优先级别相同, 并且二者的结合顺序是从左到右. 要注意的是, 表达 ‘与操作’ 时, 需要显式写出 ‘and’ 操作符, 而不只是把前后表达元并列放置(nt: 二者中间的 ‘and’ 操作符不可省略).

如果一个标识符前没有关键字, 则表达式的解析过程中最近用过的关键字(往往也是从左往右距离标识符最近的关键字)将被使用.

比如 not host vs and acenot host vs and host ace 的精简表达,而不是 not (host vs or ace)

前两者表示:所需数据包不是来自或发往 host vs, 而是来自或发往 ace,而后者表示数据包只要不是来自或发往 vs 或 ac 都符合要求)

整个条件表达式可以被当作一个单独的字符串参数也可以被当作空格分割的多个参数传入 tcpdump, 后者更方便些.

通常, 如果表达式中包含元字符(如正则表达式中的 ‘*’, ‘.’ 以及 shell 中的 ‘(‘等字符),最好还是使用单独字符串的方式传入. 这时, 整个表达式需要被单引号括起来, 作为一个字符串被解析。

常用 tcpdump 的表达元

以上的几个 host 表达式之前可以添加以下关键字: ip, arp, rarp, 以及 ip6. 比如: ip host [host], 也可以表达为: ether proto ip and host [host]

这种表达式在 ip 之前需要有 \ 来转义,因为 ip 对 tcpdump 来说已经是一个关键字

如果数据包的网关地址是 host, 则与此对应的条件表达式为真。 需要注意的是, 这里的网关地址是指以太网地址, 而不是IP 地址。

以上关于 port 的选项都可以在其前面添加关键字: tcp 或者 udp, 比如: tcp src port [port] 这将使 tcpdump 只抓取源端口是 port 的 tcp 数据包。

表达式实战 - HTTP 示例

  1. To monitor HTTP traffic including request and response headers and message body:

    tcpdump -A -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
    
  2. To monitor HTTP traffic including request and response headers and message body from a particular source:

    tcpdump -A -s 0 'src example.com and tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
    
  3. To monitor HTTP traffic including request and response headers and message body from local host to local host:

    tcpdump -A -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' -i lo
    
  4. To only include HTTP requests, modify tcp port 80 to tcp dst port 80 in above commands

  5. Capture TCP packets from local host to local host

    tcpdump -i lo
    
  6. 如果这台机器上建立的连接非常之多,我们需要限定抓取 本机(192.168.1.200) 上,4000 端口的流量,可以:

    tcpdump -A -s 0 -n '(src host 192.168.1.200 and port 4000) or (dst host 192.168.1.200 and dst port 4000)' 
    
  7. 如果想抓取来自本机的 curl 请求的流量,则需要声明 -i lo

_REF

<<<EOF

Disqus is loading...