网络协议分析神器 – tcpdump 简介及抓取 HTTP Header 实战
简介
t cpdump 网络嗅探器,将强大和简单结合到一个单一的命令行界面中,能够将网络中的报文抓取,输出到屏幕或者记录到文件中。
原理
了解 tcpdump 抓包原理
张彦飞allen,公众号:开发内功修炼用户态 tcpdump 如何实现抓到内核网络包的?
基本参数
参数 | 说明 |
---|---|
-i [interface] | 指定网卡(network interface) |
-w [flle] | 第一个 n 表示将地址解析为数字格式而不是主机名,第二个 N 表示将端口解析为数字格式而不是服务名 |
-n | 不显示 IP 地址 |
-X | hex and ASCII |
-A | ASCII(实际上是以人类可读懂的包进行显示) |
-XX | |
-v | 详细信息 |
-r | 读取文件而不是实时抓包 |
关键字 | |
type | host(主机名,域名,IP 地址), net, port, portrange |
direction | src, dst, src or dst , src and ds |
protocol | ether, ip,arp, tcp, udp, wlan |
捕获所有网络接口
tcpdump -D
按 IP 查找流量
最常见的查询之一 host,可以看到来往于 1.1.1.1 的流量。
tcpdump host 1.1.1.1
按源 / 目的 地址过滤
如果只想查看来自 / 向某方向流量,可以使用 src
和 dst
。
tcpdump src|dst 1.1.1.1
通过网络查找数据包
使用 net
选项,来要查找出 / 入某个网络或子网的数据包。
tcpdump net 1.2.3.0/24
使用十六进制输出数据包内容
hex
可以以 16 进制输出包的内容
tcpdump -c 1 -X icmp
查看特定端口的流量
使用 port
选项来查找特定的端口流量。
tcpdump port 3389
tcpdump src port 1025
查找端口范围的流量
tcpdump portrange 21-23
过滤包的大小
如果需要查找特定大小的数据包,可以使用以下选项。你可以使用 less
,greater
。
tcpdump less 32
tcpdump greater 64
tcpdump <= 128
捕获流量输出为文件
-w
可以将数据包捕获保存到一个文件中以便将来进行分析。这些文件称为 PCAP
(PEE-cap)文件,它们可以由不同的工具处理,包括 Wireshark
。
tcpdump port 80 -w capture_file
组合条件
tcpdump 也可以结合逻辑运算符进行组合条件查询
- **AND:**
and
or&&
- **OR:**
or
or||
- **EXCEPT:**
not
or!
tcpdump -i eth0 -nn host 220.181.57.216 and 10.0.0.1 # 主机之间的通讯 tcpdump -i eth0 -nn host 220.181.57.216 or 10.0.0.1 # 获取10.0.0.1与 10.0.0.9或 10.0.0.1 与10.0.0.3之间的通讯 tcpdump -i eth0 -nn host 10.0.0.1 and (10.0.0.9 or 10.0.0.3)
原始输出
并显示人类可读的内容进行输出包(不包含内容)。
tcpdump -ttnnvvS -i eth0
tcpdump -ttnnvvS -i eth0
IP 到端口
让我们查找从某个 IP 到端口任何主机的某个端口所有流量。
tcpdump -nnvvS src 10.5.2.3 and dst port 3389
去除特定流量
可以将指定的流量排除,如这显示所有到 192.168.0.2 的 非 ICMP 的流量。
tcpdump dst 192.168.0.2 and src net and not icmp
来自非指定端口的流量,如,显示来自不是 SSH 流量的主机的所有流量。
tcpdump -vv src mars and not dst port 22
选项分组
在构建复杂查询时,必须使用单引号 '
。单引号用于忽略特殊符号 ()
,以便于使用其他表达式(如 host, port, net 等)进行分组。
tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)'
过滤 TCP 标记位
TCP RST
The filters below find these various packets because tcp[13] looks at offset 13 in the TCP header, the number represents the location within the byte, and the !=0 means that the flag in question is set to 1, i.e. it’s on.
tcpdump 'tcp[13] & 4!=0'
tcpdump 'tcp[tcpflags] == tcp-rst'
TCP SYN
tcpdump 'tcp[13] & 2!=0'
tcpdump 'tcp[tcpflags] == tcp-syn'
同时忽略 SYN 和 ACK 标志的数据包
tcpdump 'tcp[13]=18'
TCP URG
tcpdump 'tcp[13] & 32!=0'
tcpdump 'tcp[tcpflags] == tcp-urg'
TCP ACK
tcpdump 'tcp[13] & 16!=0'
tcpdump 'tcp[tcpflags] == tcp-ack'
TCP PSH
tcpdump 'tcp[13] & 8!=0'
tcpdump 'tcp[tcpflags] == tcp-push'
TCP FIN
tcpdump 'tcp[13] & 1!=0'
tcpdump 'tcp[tcpflags] == tcp-fin'
查找 http 包
查找 user-agent
信息
tcpdump -vvAls0 | grep 'User-Agent:'
查找只是 GET
请求的流量
tcpdump -vvAls0 | grep 'GET'
查找 http 客户端 IP
tcpdump -vvAls0 | grep 'Host:
查询客户端 cookie
tcpdump -vvAls0 | grep 'Set-Cookie|Host:|Cookie:'
查找 DNS 流量
tcpdump -vvAs0 port 53
查找对应流量的明文密码
tcpdump port http or port ftp or port smtp or port imap or port pop3 or port telnet -lA | egrep -i -B5 'pass=|pwd=|log=|login=|user=|username=|pw=|passw=|passwd= |password=|pass:|user:|username:|password:|login:|pass |user '
关于抓包节点和抓包设备
如何抓取有用的包,以及如何找到对应的接口,有以下建议:
- 抓包节点:通常情况下会在源端和目的端两端同时抓包,观察数据包是否从源端正常发出,目的端是否接收到数据包并给源端回包,以及源端是否正常接收到回包。如果有丢包现象,则沿网络链路上各节点抓包排查。例如,A 节点经过 c 节点到 B 节点,先在 AB 两端同时抓包,如果 B 节点未收到 A 节点的包,则在 c 节点同时抓包。
- 抓包设备:对于 Kubernetes 集群中的 Pod,由于容器内不便于抓包,通常视情况在 Pod 数据包经过的 veth 设备,docker0 网桥,CNI 插件设备(如 cni0,flannel.1 etc..)及 Pod 所在节点的网卡设备上指定 Pod IP 进行抓包。选取的设备根据怀疑导致网络问题的原因而定,比如范围由大缩小,从源端逐渐靠近目的端,比如怀疑是 CNI 插件导致,则在 CNI 插件设备上抓包。从 pod 发出的包逐一经过 veth 设备,cni0 设备,flannel0,宿主机网卡,到达对端,抓包时可按顺序逐一抓包,定位问题节点。
实战
容器环境中,有某个 http 服务无法正常访问,需要定位在 ingress 转发请求的时候,是否带上了指定 “X-Spa-Base-Path: xxxx” 的 header。
准备
安装 tcpdump
yum install -y tcpdump
确认抓取的 pod 和网卡信息
先获取 ingress 的的 ip 地址(建议先改成1副本)
kubectl -n kube-system get pod -o wide |grep ingress-controllerbr
假设我们获取到的 IP 是:192.168.1.1
获取业务 Pod 所在的节点和名称
假设我们获取到的节点是 node-1,名称是 portal-service-abcd。
进入到 Pod 的命名空间
在 node-1 节点执行如下命令进 入pod,此种方法可以使用宿主机的命令,所以可以直接调用之前安装好的 tcpdump(具体可以参考:Linxu 小技巧 – 使用 nsenter 在主机上调试容器)。
NAME=portal-service-abcd; PID=`docker ps | grep $NAME | grep pause | awk '{print $1}' | xargs docker inspect -f {{.State.Pid}}`; nsenter -n --target $PID
获取网卡名称
一般容器网络生成的网卡名称基本上是固定的,例如 eth0。
抓包
先执行以下命令开始抓包:
tcpdump -i eth0 -s 0 -A "tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420" and host 192.168.1.1
参数解析
参数 | 含义 | 详细说明 |
---|---|---|
-i eth0 |
指定网卡接口 | 捕获通过接口 eth0 的流量。 |
-s 0 |
设置捕获数据包大小为无限制 | 捕获完整的数据包,而不是默认的 68 字节。 |
-A |
以 ASCII 格式显示数据包内容 | 便于查看文本内容,适用于分析 HTTP 流量等。 |
"tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420" |
过滤包含 "GET " 的 TCP 数据包 |
详细说明: 1. tcp[12:1] 读取 TCP 头部的第 12 字节(数据偏移字段)。 2. & 0xf0 提取高 4 位,表示 TCP 头长度。 3. >> 2 转为字节偏移量。 4. = 0x47455420 匹配 ASCII "GET " 的十六进制编码。 |
and host 192.168.1.1 |
限定流量的来源或目的 IP 为 192.168.1.1 |
捕获与指定主机通信的流量,进一步缩小分析范围。 |
访问服务,然后对抓到的包进行分析。