tcpdump
TCP/IP协议
- tcpdump查看三次握手
- TCP报文到达确认(ACK)机制
- TCP协议的一些认识及实践:http://www.cnblogs.com/geekma/archive/2012/10/23/2735944.html
- UDP防火墙穿透原理与分析
- Wireshark抓包常见问题解析:
-
TcpNoDelay降低通信延迟
- http://babyhe.blog.51cto.com/1104064/1393725
-
http://blog.csdn.net/huang_xw/article/details/7340241
TcpNoDelay=false,为启用nagle算法,也是默认值。 Nagle算法的立意是良好的,避免网络中充塞小封包,提高网络的利用率。但是当Nagle算法遇到delayed ACK悲剧就发生了。Delayed ACK的本意也是为了提高TCP性能,跟应答数据捎带上ACK,同时避免糊涂窗口综合症,也可以一个ack确认多个段来节省开销。悲剧发生在这种情况,假设一端发送数据并等待另一端应答,协议上分为头部和数据,发送的时候不幸地选择了write-write,然后再read,也就是先发送头部,再发送数据,最后等待应答。 实验模型: 发送端(客户端) write(head); write(body); read(response); 接收端(服务端) read(request); process(request); write(response); 这里假设head和body都比较小,当默认启用nagle算法,并且是第一次发送的时候,根据nagle算法,第一个段head可以立即发送,因为没有等待确认的段;接收端(服务端)收到head,但是包不完整,继续等待body达到并延迟ACK;发送端(客户端)继续写入body,这时候nagle算法起作用了,因为head还没有被ACK,所以body要延迟发送。这就造成了发送端(客户端)和接收端(服务端)都在等待对方发送数据的现象: 发送端(客户端)等待接收端ACK head以便继续发送body; 接收端(服务端)在等待发送方发送body并延迟ACK,悲剧的无以言语。 这种时候只有等待一端超时并发送数据才能继续往下走。
tcpdump使用范例
# 调试keepalived/lvs时,抓包分析: # 把125.70.148.77改成你自己的IP,避免远程登录的流量。 tcpdump -n -i bond0 -s 0 -l -w - 'not host 125.70.148.77'|strings|more tcpdump -n -i bond0 -s 0 -l -w - 'not host 125.70.148.77' tcpdump -n -i bond0 -s 0 -l 'not host 125.69.149.152'|grep VRRP # 截取eth1,端口号6881的tcp数据包。数据文件保存为test.pcap。 tcpdump -w test.pcap -i eth1 tcp port 6881 # 同时截取udp端口号33210和33220的数据包呢? tcpdump -w test.pcap -i eth1 tcp port 6881 or udp \( 33210 or 33220 \) # 显示指定目标ip的、端口不为80、2222的流量 tcpdump -i eth0 'dst 118.123.242 and ! port 80 and ! port 2222' tcpdump -i eth0 'dst 118.123.242 and ( port 80 or port 8008)' # 读取数据包文件(-nn不做名称转换,r读取包): tcpdump -nnr test.pcap # 添加 -tttt 选项使时间戳格式更加可读。 tcpdump -ttttnnr test.pcap # 读取数据包中所有内容,转换为文本形式 tcpdump -ttttnnr test.pcap -w -|strings|more cat test.pcap|tcpdump -ttttnr -|more cat test.pcap|tcpdump -s 0 -X -ttttnr -|more # 显示数据内容 tcpdump -s 0 -X -ttttnnr test.pcap|more # 有些版本的tcpdump允许指定端口范围,下述指令为针对一定端口范围截取数据。 tcpdump tcp portrange 20-24 # 显示所有非80、2222的tcp流量: tcpdump -i eth0 'tcp and ! port 80 and ! port 2222' # 显示dns流量: tcpdump -i eth1 'udp port 53' # 显示所有ipv4 http、端口为80的流量? tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' # 显示所有到202.54.1.5的FTP会话: tcpdump -i eth1 'dst 202.54.1.5 and (port 21 or 20)' # 显示192.168.1.5的所有http会话: tcpdump -ni eth0 'dst 192.168.1.5 and tcp and port http' # 记录到文件(记录可使用wireshark查看): tcpdump -n -i eth1 -s 0 -w output.txt src or dst port 80 # 抓取nginx与fcgi之间的数据包: tcpdump -i lo -w - -s 1500 '(src host 127.0.0.1 and (src or dst port 8004 or src or dst port 8005))'|strings # 指定主机、端口 tcpdump -n -s 0 -i wlan0 -w - 'host 115.182.65.216 and port 8005' tcpdump -n -s 0 -i wlan0 -w - 'host 118.123.242.105 and port 9099' tcpdump -nn -s 0 -i eth0 -w tcp5010_20131021_1.tcpdump tcp port 5010
TCPDUMP的使用
命令格式
-
tcpdump采用命令行方式,它的命令格式为:
tcpdump [ -adeflnNOpqStvx ] [ -c 数量 ] [ -F 文件名 ] [ -i 网络接口 ] [ -r 文件名] [ -s snaplen ] [ -T 类型 ] [ -w 文件名 ] [表达式 ]
选项介绍
选项 | 说明 |
---|---|
-a | 将网络地址和广播地址转变成名字; |
-d | 将匹配信息包的代码以人们能够理解的汇编格式给出; |
-dd | 将匹配信息包的代码以c语言程序段的格式给出; |
-ddd | 将匹配信息包的代码以十进制的形式给出; |
-e | 在输出行打印出数据链路层的头部信息; |
-f | 将外部的Internet地址以数字的形式打印出来; |
-l | 使标准输出变为缓冲行形式; |
-n | 不把网络地址转换成名字; |
-t | 在输出的每一行不打印时间戳; |
-v | 输出一个稍微详细的信息,例如在ip包中可以包括ttl和服务类型的信息; |
-vv | 输出详细的报文信息; |
-c | 在收到指定的包的数目后,tcpdump就会停止; |
-F | 从指定的文件中读取表达式,忽略其它的表达式; |
-i | 指定监听的网络接口; |
-r | 从指定的文件中读取包(这些包一般通过-w选项产生); |
-w | 直接将包写入文件中,并不分析和打印出来; |
-T | 将监听到的包直接解释为指定的类型的报文,常见的类型有rpc(远程过程调用)和snmp(简单网络管理协议) |
表达式
表达式是一个逻辑表达式,tcpdump利用它作为过滤报文的条件,如果一个报文满足表达式的条件,则这个报文将会被捕获。如果没有给出任何条件,则网络上所有的信息包将会 被截获。
在表达式中一般如下几种类型的关键字:
- 类型:host,net,port
一种是关于类型的关键字,主要包括host,net,port, 例如 host 210.27.48.2,指明 210.27.48.2是一台主机,net 202.0.0.0 指明202.0.0.0是一个网络地址,port 23 指明端口号是23。如果没有指定类型,缺省的类型是host.
- 传输方向:src,dst,dst or src,dst and src
第二种是确定传输方向的关键字,主要包括src,dst,dst or src,dst and src,这些关键字指明了传输的方向。举例说明,src 210.27.48.2 ,指明ip包中源地址是210.27.48.2 , dst net 202.0.0.0 指明目的网络地址是202.0.0.0 。如果没有指明方向关键字,则缺省是src or dst关键字。
- 协议:fddi,ip,arp,rarp,tcp,udp等
第三种是协议的关键字,主要包括fddi,ip ,arp,rarp,tcp,udp等类型。Fddi指明是在FDDI(分布式光纤数据接口网络)上的特定的网络协议,实际上它是"ether"的别名,fddi和e ther具有类似的源地址和目的地址,所以可以将fddi协议包当作ether的包进行处理和分析。
其他的几个关键字就是指明了监听的包的协议内容。如果没有指定任何协议,则tcpdump将会监听所有协议的信息包。
- 其他重要关键字
除了这三种类型的关键字之外,其他重要的关键字如下:gateway, broadcast,less,greater。
- 逻辑运算符
还有三种逻辑运算,
- 非运算 'not ' '! '
- 与运算 'and','&&'
- 或运算 'or' ,'||'
这些关键字可以组合起来构成强大的组合条件来满足人们的需要,下面举几个例子来说明。
例子
- 想要截获所有210.27.48.1 的主机收到的和发出的所有的数据包:
tcpdump host 210.27.48.1
- 想要截获主机210.27.48.1 和主机210.27.48.2 或210.27.48.3的通信,使用命令
tcpdump host 210.27.48.1 and \(210.27.48.2 or 210.27.48.3\)
- 如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包,使用命令:
tcpdump ip host 210.27.48.1 and ! 210.27.48.2
- 如果想要获取主机210.27.48.1接收或发出的telnet包,使用如下命令:
tcpdump tcp port 23 host 210.27.48.1
输出结果介绍
下面我们介绍几种典型的tcpdump命令的输出信息
- 数据链路层头信息
使用命令tcpdump --e host ice
ice 是一台装有linux的主机,她的MAC地址是0:90:27:58:af:1a
H219是一台装有SOLARIC的SUN工作站,它的MAC地址是8:0:20:79:5b:46
命令的输出结果如下所示:
21:50:12.847509 eth0 < 8:0:20:79:5b:46 0:90:27:58:af:1a ip 60: h219.33357 > ice.telnet 0:0(0) ack 22535 win 8760 (DF)
分析:
-
21:50:12
是显示的时间 -
847509
是ID号 -
eth0 <
表示从网络接口eth0 接受该数据包 -
eth0 >
表示从网络接口设备发送数据包 -
8:0:20:79:5b:46
是主机H219的MAC地址,它表明是从源地址H219发来的数据包 -
0:90:27:58:af:1a
是主机ICE的MAC地址,表示该数据包的目的地址是ICE -
ip
是表明该数据包是IP数据包, -
60
是数据包的长度, -
h219.33357 > ice.telnet
表明该数据包是从主机H219的33357端口发往主机ICE的TELNET(23)端口 -
ack 22535
表明对序列号是222535的包进行响应 -
win 8760
表明发送窗口的大小是8760
- ARP包的TCPDUMP输出信息
使用命令#tcpdump arp
得到的输出结果是:
22:32:42.802509 eth0 > arp who-has route tell ice (0:90:27:58:af:1a) 22:32:42.802902 eth0 < arp reply route is-at 0:90:27:12:10:66 (0:90:27:58:af:1a)
分析:
-
22:32:42
时间戳 -
802509
ID号 -
eth0 >
表明从主机发出该数据包 -
arp
表明是ARP请求包 -
who-has route tell ice
表明是主机ICE请求主机ROUTE的MAC地址 -
0:90:27:58:af:1a
是主机ICE的MAC地址。
- TCP包的输出信息
用TCPDUMP捕获的TCP包的一般输出信息是:
src > dst: flags data-seqno ack window urgent options
-
src > dst
表明从源地址到目的地址 -
flags
是TCP包中的标志信息,S 是SYN标志, F(FIN), P(PUSH) , R(RST) "."(没有标记) -
data-seqno
是数据包中的数据的顺序号 -
ack
是下次期望的顺序号 -
window
是接收缓存的窗口大小 -
urgent
表明数据包中是否有紧急指针 -
options
是选项
- UDP包的输出信息
用TCPDUMP捕获的UDP包的一般输出信息是:
route.port1 > ice.port2: udp lenth
UDP十分简单,上面的输出行表明从主机ROUTE的port1端口发出的一个UDP数据包到主机ICE的port2端口,类型是UDP, 包的长度是lenth
wireshark查看
要让wireshark能分析tcpdump的包,关键的地方是 -s 参数, 还有要保存为-w文件,例如下面的例子:
./tcpdump -i eth0 -s 0 -w SuccessC2Server.pcap host 192.168.1.20 # 抓该主机的所有包,在wireshark中过滤 ./tcpdump -i eth0 'dst host 239.33.24.212' -w raw.pcap # 抓包的时候就进行过滤
wireshark的过滤,很简单的,比如:
tcp.port eq 5541 ip.addr eq 192.168.2.1
过滤出来后, 用fllow tcp 查看包的内容。
其他
混杂模式
-
device eth0/eth1 entered promiscuous mode
-
message日志中提示:
kernel: device eth0 entered promiscuous mode kernel: device eth0 left promiscuous mode
- 网卡进入了混杂模式。一般对通信进行抓包分析时进入混杂模式(tcpdump)。(默认网卡启用了混杂模式的)
-
关闭混杂模式:
ifconfig eth0 -promisc
-
启用混杂模式:
ifconfig eth0 promisc
-
message日志中提示:
- TCP协议的KeepAlive机制与HeartBeat心跳包:http://www.nowamagic.net/academy/detail/23350382
- TCP Keepalive HOWTO:http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/
TCP协议状态
TCP的状态 (SYN, FIN, ACK, PSH, RST, URG)
在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.
其中,对于我们日常的分析有用的就是前面的五个字段。它们的含义是:
- SYN 表示建立连接
- FIN 表示关闭连接
- ACK 表示响应
- PSH 表示有 DATA数据传输
- RST 表示连接重置
其中,ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应,如果只是单个的一个SYN,它表示的只是建立连接。
TCP的几次握手就是通过这样的ACK表现出来的。
但SYN与FIN是不会同时为1的,因为前者表示的是建立连接,而后者表示的是断开连接。
RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。
一般地,当出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接;而当出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接。
PSH为1的情况,一般只出现在DATA内容不为0的包中,也就是说PSH为1表示的是有真正的TCP数据包内容被传递。
TCP的连接建立和连接关闭,都是通过请求-响应的模式完成的。
概念补充-TCP三次握手
TCP(Transmission Control Protocol)传输控制协议
TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:
-
位码即tcp标志位,有6种标示:
- SYN(synchronous建立联机)
- ACK(acknowledgement 确认)
- PSH(push传送)
- FIN(finish结束)
- RST(reset重置)
- URG(urgent紧急)
- Sequence number(顺序号码)
- Acknowledge number(确认号码)
- 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机; 第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包; 第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认; 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。