tcpdump命令

通俗的说,tcpdump是一个抓包工具,用于抓取互联网上传输的数据包。
形象的说,tcpdump就好比是国家海关,驻扎在出入境的咽喉要道,凡是要入境和出境的集装箱,海关人员总要打开箱子,看看里面都装了点啥。
学术的说,tcpdump是一种嗅探器(sniffer),利用以太网的特性,通过将网卡适配器(NIC)置于混杂模式(promiscuous)来获取传输在网络中的信息包。

【抓人生中的第一个包】

要用tcpdump抓包,请记住,一定要切换到root账户下,因为只有root才有权限将网卡变更为“混杂模式”。
然后呢,就是用ifconfig的方法查看好你的服务器的网卡名称。(我的叫做eth0,一般都是这个名字)

# tcpdump -i eth0 -nn -X ‘port 53′ -c 1

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
19:48:33.285838 IP 116.255.245.206.47940 > 8.8.8.8.53: 22768+ A? www.baidu.com. (31)
0x0000: 4500 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t… 0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 …..D.5.’.WX… 0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 ………www.bai 0x0030: 6475 0363 6f6d 0000 0100 01 du.com….. 1 packets captured 1 packets received by filter 0 packets dropped by kernel [/shell] 在此,我重点解释下这个命令: -i选项:
是interface的含义,是指我们有义务告诉tcpdump希望他去监听哪一个网卡。这在我们一台服务器有多块网卡时很有必要。

-nn选项:
意思是说当tcpdump遇到协议号或端口号时,不要将这些号码转换成对应的协议名称或端口名称。比如,众所周知21端口是FTP端口,我们希望显示21,而非tcpdump自作聪明的将它显示成FTP。

-X选项:
告诉tcpdump命令,需要把协议头和包内容都原原本本的显示出来(tcpdump会以16进制和ASCII的形式显示),这在进行协议分析时是绝对的利器。

‘port 53’:
这是告诉tcpdump不要看到啥就显示啥。我们只关心源端口或目的端口是53的数据包,其他的数据包别给我显示出来。

-c选项:
是Count的含义,这设置了我们希望tcpdump帮我们抓几个包。我设置的是1,所以tcpdump不会帮我再多抓哪怕一个包回来。

 

从我的理解来看,tcpdump可以分为三大部分内容,第一是“选项”,第二是“过滤表达式”,第三是“输出信息”。
下面的文章,会分别从这三个方面来重点介绍。

tcpdump的选项,我还真是专门数了一下,总共有50个之多。在这个系列文章中,实在很难全都覆盖,我尽量挑选重要且常用的选项来讲解,
一些不常用的选项,大家抽空可以自己去了解一下。

在《第一招》中讲到了-i选项、-nn选项、-c选项、-X选项。今天,我们继续介绍另外两个重要的选项,即-e选项和-l选项。

【-e选项】- 增加以太网帧头部信息输出

闲言少叙,直接给出例子,大家就能看到区别了:

# tcpdump -i eth0 -c 1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
16:36:18.490875 IP 116.255.245.206.snapenetio > 61.135.169.73.56503: Flags [P.], seq 990974500:990974696, ack 4001909970, win 270, length 196
# tcpdump -i eth0 -c 1 -e
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
16:36:09.910296 00:15:5d:f5:4b:37 (oui Unknown) > 00:00:0c:07:ac:15 (oui Cisco), ethertype IPv4 (0x0800), length 250: 116.255.245.206.snapenetio > 61.135.169.73.56503: Flags [P.], seq 990973640:990973836, ack 4001909762, win 270, length 196

 

如果还是看不懂,说明你忘记了以太网协议的头格式,我再这里补充下,帮大家回忆回忆:

struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主机的地址 */
u_char ether_shost[ETHER_ADDR_LEN]; /* 源主机的地址 */
u_short ether_type; /* IP? ARP? RARP? etc */
};

在刚才加-e选项的输出中,会发现有oui Unknown的字样,这oui是什么东东呢?在这里顺便科普一下咯:
OUI,即Organizationally unique identifier,是“组织唯一标识符”,在任何一块网卡(NIC)中烧录的6字节MAC地址中,前3个字节体现了OUI,其表明了NIC的制造组织。通常情况下,该标识符是唯一的。

【-l选项】- 使得输出变为行缓冲

-l选项的作用就是将tcpdump的输出变为“行缓冲”方式,这样可以确保tcpdump遇到的内容一旦是换行符即将缓冲的内容输出到标准输出,以便于利用管道或重定向方式来进行后续处理。

众所周知,Linux/UNIX的标准I/O提供了全缓冲、行缓冲和无缓冲三种缓冲方式。标准错误是不带缓冲的,终端设备常为行缓冲,而其他情况默认都是全缓冲的。

大家在使用tcpdump时,有时会有这样的需求:“对于tcpdump输出的内容,提取每一行的第一个域,即”时间域”,并输出出来,为后续统计所用”,这种场景下,我们就需要使用到-l来将默认的全缓冲变为行缓冲了。

# tcpdump -i eth0 -l |awk ‘{print $1}’
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
22:56:57.571680
22:56:57.572103
22:56:57.599515
22:56:57.706286
22:56:57.888108
22:56:57.888296
22:56:57.973546
22:56:57.973768
22:56:57.975660
22:56:58.019052
22:56:58.019318
^C146 packets captured
161 packets received by filter
0 packets dropped by kernel

 

如果不加-l选项,那么只有全缓冲区满,才会输出一次,这样不仅会导致输出是间隔不顺畅的,而且当你ctrl-c时,很可能会断到一行的半截,损坏统计数据的完整性。

 

【-t选项】- 输出时不打印时间戳

这个选项非常好理解,直接看例子吧:

# tcpdump -i eth0 -c 1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
01:52:10.433391 ARP, Request who-has 116.255.247.61 tell 116.255.247.125, length 64
# tcpdump -i eth0 -c 1 -t
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
ARP, Request who-has 116.255.245.35 tell 116.255.245.62, length 64

 

【-v选项】- 输出更详细的信息

加了-v选项之后,在原有输出的基础之上,你还会看到tos值、ttl值、ID值、总长度、校验值等。
至于上述值的含义,需要你专门去研究下IP头、TCP头的具体协议定义咯。

# tcpdump -i eth0 -c 1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
01:53:09.162644 IP 116.255.245.206.snapenetio > 221.223.255.234.54198: Flags [P.], seq 1443040202:1443040398, ack 3517061690, win 353, length 196
# tcpdump -i eth0 -c 1 -v
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
01:52:57.902894 IP (tos 0x10, ttl 64, id 18293, offset 0, flags [DF], proto TCP (6), length 172)
116.255.245.206.snapenetio > 221.223.255.234.54198: Flags [P.], cksum 0x4dd9 (correct), seq 1443039302:1443039434, ack 3517061430, win 353, length 132

 

【-F选项】- 指定过滤表达式所在的文件

还记得我们在第一招中所提到的命令么,我帮助大家回忆一下:

 tcpdump -i eth0 -nn -X ‘port 53′ -c 1

这里面的’port 53’便叫做“过滤表达式”,用于设置我们抓包的条件的,只有满足过滤条件的网络包,才会被抓过来。
当这个过滤条件非常复杂,或者我们需要将一个过滤条件固化下来复用的时候,就会想到把它存到一个文本文件中。
此时,-F选项就会派上用场。请看例子:

# cat filter.txt
port 53
# tcpdump -i eth0 -c 1 -t -F filter.txt
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
IP 116.255.245.206.54357 > ns.sc.cninfo.net.domain: 36720+ A? www.baidu.com. (31)
1 packets captured
6 packets received by filter
0 packets dropped by kernel

 

我们建立了一个filter.txt文本文件来存储过滤表达式,然后通过-F来指定filter.txt,这样tcpdump就会心知肚明地读取filter.txt中的内容作为过滤条件。

 

做过网络流量分析的同学,或许都有一个共同的需求,那就是“流量保存”和“流量回放”,这就恰好对应了今天要讲解的-w选项和-r选项。

“流量保存”就是把抓到的网络包能存储到磁盘上,保存下来,为后续使用。

“流量回放”就是把历史上的某一时间段的流量,重新模拟回放出来,用于流量分析。

【-w选项】- 将流量保存到文件中

# tcpdump -i eth0 -w flowdata
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
^C327 packets captured
327 packets received by filter
0 packets dropped by kernel

 

通过上面的例子可以看到,通过-w选项将流量都存储在了flowdata文件中了。大家是否有兴趣less下flowdata,看看里面都是什么东东?

# less flowdata
“flowdata” may be a binary file. See it anyway?

 

悲剧,原来都是二进制格式的,无法直接通过文本方式查看。嗯,买了个关子,把真像告诉大家吧!

tcpdump的-w方式是把raw packets(原始网络包)直接存储到文件中了,也就是存储的都是结构体形式,而非是分析之后的文本格式的信息,因此大家是无法直接通过less命令查看的。

那么,怎么查看呢?大家想必也想到了,就是用-r选项。

【-r选项】- 读取raw packets文件

# tcpdump -r flowdata
reading from file flowdata, link-type EN10MB (Ethernet)
16:43:36.202443 IP 116.255.245.206.snapenetio > 61.135.169.73.52414: Flags [P.], seq 4082702792:4082702924, ack 3248983965, win 291, length 132
16:43:36.222033 IP 61.135.169.73.52414 > 116.255.245.206.snapenetio: Flags [.], ack 132, win 61, length 0
16:43:36.277407 IP 116.255.245.62 > ospf-all.mcast.net: OSPFv2, Hello, length 48
16:43:36.370846 ARP, Request who-has 116.255.245.203 tell 116.255.245.254, length 64
16:43:36.521947 ARP, Request who-has 116.255.245.203 tell 116.255.245.253, length 64
16:43:36.635472 ARP, Request who-has 116.255.245.214 tell 116.255.245.253, length 64

 

其实上面的命令就是在不知不觉中进行了“流量回放”,你会发现网络包被“抓”的速度都按照历史进行了回放,真像一个“时光机”啊!

由于是按raw packets来存储的,所以你完全可以使用-e、-l和过滤表达式来对输出信息进行控制,十分方便。

 

可以给tcpdump传送“过滤表达式”来起到网络包过滤的作用,而且可以支持传入单个或多个过滤表达式,从这一点来说tcpdump还是很大肚能容的。
当你传入的过滤表达式含有shell通配符时,别忘使用单引号把表达式括起来,以防shell自作主张的把含有通配符的表达式先进行了解释和通配。

如果你希望自己研究“过滤表达式”,没问题,我会告诉你如何“进门”,方法就是:

man pcap-filter

你会发现,过滤表达式大体可以分成三种过滤条件,“类型”、“方向”和“协议”,这三种条件的搭配组合就构成了我们的过滤表达式。

【我只想抓UDP的包,不想被TCP的包打扰】

# tcpdump -i eth0 -c 10 ‘udp’
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
11:25:20.801612 IP 116.255.245.48.54808 > 229.111.112.12.csd-mgmt-port: UDP, length 4
11:25:20.802120 IP 116.255.245.206.54313 > ns.sc.cninfo.net.domain: 5256+ PTR? 12.112.111.229.in-addr.arpa. (45)
11:25:21.145126 IP ns.sc.cninfo.net.domain > 116.255.245.206.54313: 5256 NXDomain 0/0/0 (45)
11:25:21.145315 IP 116.255.245.206.46658 > ns.sc.cninfo.net.domain: 15551+ PTR? 48.245.255.116.in-addr.arpa. (45)
11:25:21.153966 IP 116.255.245.43.62220 > 229.111.112.12.csd-mgmt-port: UDP, length 4
11:25:21.180135 IP 116.255.245.61.hsrp > all-routers.mcast.net.hsrp: HSRPv0-hello 20: state=active group=21 addr=116.255.245.33
11:25:21.231151 IP ns.sc.cninfo.net.domain > 116.255.245.206.46658: 15551 NXDomain 0/0/0 (45)
11:25:21.231430 IP 116.255.245.206.46158 > ns.sc.cninfo.net.domain: 31924+ PTR? 69.2.139.61.in-addr.arpa. (42)
11:25:21.277087 IP ns.sc.cninfo.net.domain > 116.255.245.206.46158: 31924 1/0/0 PTR ns.sc.cninfo.net. (72)
11:25:21.277824 IP 116.255.245.206.42656 > ns.sc.cninfo.net.domain: 806+ PTR? 206.245.255.116.in-addr.arpa. (46)
10 packets captured
20 packets received by filter
0 packets dropped by kernel

 

举这个例子,是为了说明tcpdump具有根据网络包的协议来进行过滤的能力,我们还可以把udp改为ether、ip、ip6、arp、tcp、rarp等。
或许你会提问“为啥这些协议里没有应用层协议呢?”,其实理由很简单,应用层协议非基础类网络协议,经常会新增或淘汰,tcpdump不会深入到应用层部分去智能解析。所以,你现在看到的tcpdump支持的protocol都是应用层以下的。

【我想专门查看这个源机器和那个目的机器之间的网络包,不想被其他无关的机器打扰】

这个其实很简单,也很直观,只要设置src(source)和dst(destination)就好了,而且方便的是,tcpdump还支持使用and和or来进行搭配组合呢!
如果没有设置的话,默认是src or dst。

# tcpdump -i eth0 ‘dst 8.8.8.8’
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
13:21:23.281978 IP 116.255.245.206 > google-public-dns-a.google.com: ICMP echo request, id 23081, seq 1, length 64
13:21:24.286663 IP 116.255.245.206 > google-public-dns-a.google.com: ICMP echo request, id 23081, seq 2, length 64
13:21:25.288612 IP 116.255.245.206 > google-public-dns-a.google.com: ICMP echo request, id 23081, seq 3, length 64
^C
3 packets captured
5 packets received by filter
0 packets dropped by kernel

 

【我只想查目标机器端口是53或80的网络包,其他端口的我不关注】

# tcpdump -i eth0 -c 3 ‘dst port 53 or dst port 80’
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
13:29:04.530130 IP 114.255.192.96.29832 > 116.255.245.206.http: Flags [S], seq 3169042560, win 5840, options [mss 1460,sackOK,TS val 2949111416 ecr 0], length 0
13:29:04.530660 IP 116.255.245.206.43211 > ns.sc.cninfo.net.domain: 40188+ PTR? 206.245.255.116.in-addr.arpa. (46)
13:29:04.548589 IP 114.255.192.96.29832 > 116.255.245.206.http: Flags [.], ack 3709396068, win 5840, options [nop,nop,TS val 2949111475 ecr 1601243970], length 0
3 packets captured
10 packets received by filter
0 packets dropped by kernel

 

我们可以设置过滤类型,上面例子中我们使用了port这个类型,就是来指定端口。当然,tcpdump还支持如下的类型:
1 host:指定主机名或IP地址,例如’host roclinux.cn’或’host 202.112.18.34′
2 net :指定网络段,例如’arp net 128.3’或’dst net 128.3′
3 portrange:指定端口区域,例如’src or dst portrange 6000-6008′

如果我们没有设置过滤类型,那么默认是host。

 

接下来会讲解tcpdump的过滤表达式,这次思路很简单,就是直接举例子,其实就是man tcpdump中的例子,很直观,很受用。

【例子1】- 我想抓到那些通过eth0网卡的,且来源是roclinux.cn服务器或者目标是roclinux.cn服务器的网络包

tcpdump -i eth0 'host roclinux.cn'

【例子2】- 我想抓通过eth0网卡的,且roclinux.cn和baidu.com之间通讯的网络包,或者,roclinux.cn和qiyi.com之间通讯的网络包

tcpdump -i eth0 'host roclinux.cn and (baidu.com or qiyi.com)'

【例子3】- 我想获取使用ftp端口和ftp数据端口的网络包

tcpdump 'port ftp or ftp-data'

大家是不是会有一个疑问“这个ftp、ftp-data到底对应哪个端口?除了ftp/ftp-data,还有哪些服务名称我可以直接用呢?”

嗯,这是个好问题,答案现在揭晓咯。

在Linux系统中,/etc/services这个文件里面,就存储着所有知名服务和传输层端口的对应关系。这个对应关系是由IANA组织 (the Internet Assigned Numbers Authority,互联网数字分配机构)来全权负责的,你可以到这个链接http://www.iana.org/assignments/port-numbers通过Web方式查到。

如果你直接把/etc/services里的ftp对应的端口值从21改为了8888,那么tcpdump就会去抓端口含有8888的网络包了。

【例子4】- 我想获取roclinux.cn和baidu.com之间建立TCP三次握手中第一个网络包,即带有SYN标记位的网络包,另外,目的主机不能是qiyi.com

tcpdump 'tcp[tcpflags] & tcp-syn != 0 and not dst host qiyi.com'

这个语句看着比较复杂,其实如果要把这段解释清楚的确不容易,需要你具备计算机网络专业知识才行。这个我会安排一章来讲。

【例子5】- 打印IP包长超过576字节的网络包

tcpdump 'ip[2:2] > 576'

【例子6】- 打印广播包或多播包,同时数据链路层不是通过以太网媒介进行的。

tcpdump 'ether[0] & 1 = 0 and ip[16] >= 224'

最后三个例子,或许你看得有些晕头转向,没关系,先有个感官认识,看完接下来的几篇文章后,相信保证你就明白了:)

 

最后的3个例子比较复杂,大家可能会感觉云里雾里。我们现在回顾下这三个例子,然后今天的第七招就是教大家如何看懂他们!

tcpdump 'tcp[tcpflags] & tcp-syn != 0 and not dst host qiyi.com'
tcpdump 'ip[2:2] > 576'
tcpdump 'ether[0] & 1 = 0 and ip[16] >= 224'

==

在讲解前,需要你熟练掌握ETHER/IP/TCP/UDP等协议的包头定义格式,能够知道每一位的作用和含义。

有关ETHER的,可以参考《计算机网络协议包头赏析-以太网》;
有关IP的,可以参考《计算机网络协议包头赏析-IP》;
有关TCP的,可以参考《计算机网络协议包头赏析-TCP》;
有关UDP的,可以参考《计算机网络协议包头赏析-UDP》。

==

磨刀不误砍柴工,如果本系列的前六篇文章你都仔细看过了,同时,几种协议的包格式也都了然于心了,那我们就切入正题,介绍下过滤表达式的高级语法。

其实我们需要着重讲解的只有一种语法,即proto [ expr : size],只要掌握了这个语法格式,相信大家就能看懂上面的三个稀奇古怪的表达式了。

proto就是protocol的缩写,表示这里要指定的是某种协议的名称,比如ip、tcp、ether。其实proto这个位置,总共可以指定的协议类型有15个之多,包括:

  • ether – 链路层协议
  • fddi – 链路层协议
  • tr – 链路层协议
  • wlan – 链路层协议
  • ppp – 链路层协议
  • slip – 链路层协议
  • link – 链路层协议
  • ip
  • arp
  • rarp
  • tcp
  • udp
  • icmp
  • ip6
  • radio

expr用来指定数据报偏移量,表示从某个协议的数据报的第多少位开始提取内容,默认的起始位置是0;而size表示从偏移量的位置开始提取多少个字节,可以设置为1、2、4。

如果只设置了expr,而没有设置size,则默认提取1个字节。比如ip[2:2],就表示提取出第3、4个字节;而ip[0]则表示提取ip协议头的第一个字节。

在我们提取了特定内容之后,我们就需要设置我们的过滤条件了,我们可用的“比较操作符”包括:>,<,>=,<=,=,!=,总共有6个。

好,掌握了上面内容之后,我可以很负责任的告诉你,你已经掌握了tcpdump过滤表达式的最重要语法了。我们先来小试牛刀,看一个例题:

ip[0] & 0xf != 5

IP协议的第0-4位,表示IP版本号,可以是IPv4(值为0100)或者IPv6(0110);第5-8位表示首部长度,单位是“4字节”,如 果首部长度为默认的20字节的话,此值应为5,即”0101″。ip[0]则是取这两个域的合体。0xf中的0x表示十六进制,f是十六进制数,转换成8 位的二进制数是“0000 1111”。而5是一个十进制数,它转换成8位二进制数为”0000 0101″。

有了上面这些分析,大家应该可以很清楚的知道,这个语句中!=的左侧部分就是提取IP包首部长度域,如果首部长度不等于5,就满足过滤条件。言下之意也就是说,要求IP包的首部中含有可选字段。

大家可能已经有所体会,在写过滤表达式时,你需要把协议格式完全背在脑子里,才能把表达式写对。可这对大多数人来说,可能有些困难。为了让 tcpdump工具更人性化一些,有一些常用的偏移量,可以通过一些名称来代替,比如icmptype表示ICMP协议的类型域、icmpcode表示 ICMP的code域,tcpflags则表示TCP协议的标志字段域。

更进一步的,对于ICMP的类型域,可以用这些名称具体指代:icmp-echoreply, icmp-unreach, icmp-sourcequench, icmp-redirect, icmp-echo, icmp-routeradvert, icmp-routersolicit, icmp-timxceed, icmp-paramprob, icmp-tstamp, icmp-tstampreply, icmp-ireq, icmp-ireqreply, icmp-maskreq, icmp-maskreply。

而对于TCP协议的标志字段域,则可以细分为tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg。

如果一个过滤表达式有多个过滤条件,那么就需要使用逻辑符了,其中,!或not都可以表示“否定”,&&与and都可以表示“与”,而||与or都可以表示“或”。

==

好了,大功告成,如果你真的仔细阅读并掌握了上面的内容,我相信你有能力自己来分析下面这三个语句,而且一定能明白它们的“言下之意”的!

tcpdump 'tcp[tcpflags] & tcp-syn != 0 and not dst host qiyi.com'
tcpdump 'ip[2:2] > 576'
tcpdump 'ether[0] & 1 = 0 and ip[16] >= 224'


还记得上面里的“人生中第一次抓包”么,我们这次就来好好剖析剖析这第一个包。
# tcpdump -i eth0 -nn -X 'port 53' -c 1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
19:48:33.285838 IP 116.255.245.206.47940 > 8.8.8.8.53: 22768+ A? www.baidu.com. (31)
0x0000: 4500 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01 du.com.....
1 packets captured
1 packets received by filter
0 packets dropped by kernel

【第2行】

“tcpdump: verbose output suppressed, use -v or -vv for full protocol decode”

这一行,是一句贴心的提醒,简单易懂,就是说你的命令里没有用到-v和-vv,如果希望看到更全的输出内容,可以使用这两个选项。
如果你有兴趣,可以试试,看看加上-vv后,到底输出内容里会多些什么。

【第3行】

“listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes”

这一句表示我们监听的是通过eth0这个NIC设备的网络包,且它的链路层是基于以太网的,要抓的包大小限制是65535字节。

包大小限制值可以通过-s选项来设置,如果你要追求高性能,建议把这个值调低,这样可以有效避免在大流量情况下的丢包现象。

【第4行】

“19:48:33.285838 IP 116.255.245.206.47940 > 8.8.8.8.53: 22768+ A? www.baidu.com. (31)”

“19:48:33.285838”,分别对应着这个包被抓到的“时”、“分”、“秒”、“微妙”。

“IP”,表示这个包在网络层是IP包。

“116.255.245.206.47940”,表示这个包的源IP为116.255.245.206,源端口为47940。

“>”,这个大于号表示数据包的传输方向。

“8.8.8.8.53“,表示这个包要发向的目的端IP是8.8.8.8,目标端口为53,也就是我们熟知的DNS服务端口。

“22768+ A? www.baidu.com. (31)“,这是DNS协议的内容,即请求www.baidu.com的A纪录。

【第5/6/7/8行】

0x0000: 4500 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

接下来便是IP包的内容了,是除去了以太网之后剩下的内容,其中左侧红色字体部分是十六进制内容,右侧天蓝色字体部分是相应的ASCII码内容。
如果想看懂上面这些十六进制数字,前提就是大家要对IP、TCP/UDP的包格式很熟悉才可以。正好Linux大棚有一个系列文章就是来介绍这些重要协议:

计算机网络协议包头赏析-以太网
计算机网络协议包头赏析-IP
计算机网络协议包头赏析-TCP
计算机网络协议包头赏析-UDP

好,下面我们就来给大家解读下那些“晦涩的十六进制数字”。

0x0000: 45】00 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

如上,最外层是IP数据包,最开始的一个字节(8bit)中,前4bit表示IP的版本,此处为4,表示这是一个IPv4版本的IP包;后4bit 表示这IP包的首部长度,此处的数字是5,由于单位是“4字节”,因此可以计算得出这个IP包的首部长度是固定的20字节。如下绿色字体部分都是IP数据 包的首部部分:

0x0000: 【4500 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808】 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

如下所示,在IP版本和首部长度之后,接下来的一个字节(8bit)是“00”,这是IP协议的服务类型域(TOS),由于已经很少使用,因此此处被置为00。

0x0000: 45【00】003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

如下所示,后面的2字节(16bit)是“003b”,表示整个IP包的总长度(首部长度+数据长度),单位是字节,因此可以知道这个IP包的总长度是59字节(0x3b需要转换为十进制)。你可以数数下面的红色部分,应该是正好59个字节

0x0000: 4500 【003b】 c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

再向下的2字节(16bit)是“标识域”,如果IP包的大小超过了数据链路层的MTU限制,就需要对IP包进行分拆,此时就要用这个域来表示哪些包在分拆前是同一组的。此处的标识域值为0xc341。

0x0000: 4500 003b 【c341】 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

再继续向向下看,便是3bit的标志位,最高位为保留位,中间一位为DF(don’t fragment),最低位为MF(more fragments),可以看到这三位是用来控制IP拆包后的组装所用。由于此包没有拆包,因此这三位都被置为0,如下所示:

0x0000: 4500 003b c341 【0】000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

和上面的标志位配合的是紧接着的13bit的片便宜,但由于本包没有拆包,因此此域也无用,因此为0。

0x0000: 4500 003b c341 【0000】 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

如下,紧接着是8bit的TTL(Time To Live,即生存周期),此包的值为0x40,换算成十进制是64,这表明这个网络包,如果经过了超过64个中间路由节点,则认为目的地不可达,中间路由器会将此包抛弃掉。

0x0000: 4500 003b c341 0000 【40】11 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

继续的8bit为协议域,用于指代上一层协议类型。此处的值为0x11,对应十进制的17,而17是UDP协议的代号,因此可以知道这个网络包所用的传输层协议是UDP,而非TCP(TCP的协议号是6、TCMP的协议号是1)。

0x0000: 4500 003b c341 0000 40【11】 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

接下来的2个字节表示IP首部校验和,此处计算出来的结果是3c93。

0x0000: 4500 003b c341 0000 4011 【3c93】 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

如下,再往下就是大家非常熟悉的4字节的IP源地址,即“74 ff f5 ce”,转换成IP地址则为116.255.245.206。

0x0000: 4500 003b c341 0000 4011 3c93 【74ff f5ce】 E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

以此类推,再下面的4字节则为IP目的地址,即“08 08 08 08”,转换成IP地址则为8.8.8.8,这是著名的Google-DNS服务器地址。

0x0000: 4500 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808】 bb44 0035 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

至此,网络层IP协议的首部20字节已经分析完了。再向下我们即将进入传输层UDP协议的包分析阶段。

相对于TCP包来说,UDP包的首部还是比较简单的,总共只有8个字节,如下绿色部分便是UDP首部部分:

0x0000: 4500 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 【bb44 0035 0027 b457】 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

UDP首部的前2个字节(如下棕色部分)为源端口,此处为“bb 44”,即47940;而接下来的2字节(如下粉色部分)为目的端口,值为“00 35”,即53(DNS服务)。

0x0000: 4500 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 【bb44】 【0035】 0027 b457 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

而接下来的2字节(如下棕色部分)则表示UDP包的总长度(报头+数据部分),此处的值为“00 27”,这算成十进制则为39,就表示此UDP包的总长度为39字节,减去首部的8字节外,还有31字节来存储真正要传输的数据。

0x0000: 4500 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 【0027】 【b457】 58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01             du.com.....

而UDP首部的最后两个字节(如上粉色部分)则是校验和部分,此处的值为0xb457。

再向下的部分(绿色部分),则是应用层协议的内容(本例中是DNS协议)了,如果你有兴趣,可以继续了解下DNS协议、HTTP协议、FTP协议等众多的应用层协议,这非常有利于大家在学习协议、追查网络问题时,透过现象看本质。

0x0000: 4500 003b c341 0000 4011 3c93 74ff f5ce E..;.A..@.<.t...
0x0010: 0808 0808 bb44 0035 0027 b457 【58f0 0100 .....D.5.'.WX...
0x0020: 0001 0000 0000 0000 0377 7777 0562 6169 .........www.bai
0x0030: 6475 0363 6f6d 0000 0100 01】             du.com.....


[秘籍一]

使用-A选项,则tcpdump只会显示ASCII形式的数据包内容,不会再以十六进制形式显示;

[秘籍二]

使用-XX选项,则tcpdump会从以太网部分就开始显示网络包内容,而不是仅从网络层协议开始显示。

[秘籍三]

使用如下命令,则tcpdump会列出所有可以选择的抓包对象。

# tcpdump -D
1.eth0
2.any (Pseudo-device that captures on all interfaces)
3.lo

[秘籍四]

如果想查看哪些ICMP包中“目标不可达、主机不可达”的包,请使用这样的过滤表达式:

icmp[0:2]==0x0301

[秘籍五]

要提取TCP协议的SYN、ACK、FIN标识字段,语法是:

tcp[tcpflags] & tcp-syn
tcp[tcpflags] & tcp-ack
tcp[tcpflags] & tcp-fin

[秘籍六]

要提取TCP协议里的SYN-ACK数据包,不但可以使用上面的方法,也可以直接使用最本质的方法:

tcp[13]==18

[秘籍七]

如果要抓取一个区间内的端口,可以使用portrange语法:

tcpdump -i eth0 -nn 'portrange 52-55' -c 1  -XX

发表评论

电子邮件地址不会被公开。 必填项已用*标注