icmp and host 1.1.1.1 这个就是包过滤的表达式了,icmp 表示只抓取 icmp 协议,host 1.1.1.1 表示只抓取 src ip 或者 dst ip 是 1.1.1.1 的包。这种包过滤表达式其实是 pcap-filter(7)3 提供的,所以要想看语法是怎么定义的,看 pcap-filter 的文档就可以了。pcap-filter 支持的语法很灵活,能做的事情很多,基本上想抓什么样的包都可以写出来。但是我们没有必要把所有的语法都记住,因为常用的抓包都是比较简单的。可以找一个 tcpdump exmaple4 看一下,基本就够用了。其次,我们一般不会直接从 tcpdump 就分析出来问题原因,所以这个语法最重要的作用是把我们想要的包抓到,然后为了抓包性能更高,抓包文件更小,我们想要对抓包定义的更精确一些。其实,多抓一些包也没有什么问题,如果不确定怎么过滤出来 TCP SYN+ACK 的包,那不妨就把所有的 SYN 包全抓到,然后再用 Wireshark 这种工具来分析吧。最后,我们现在有 AI 了,用 AI 来写 pcap-filter 也是一个不错的方法,因为这种语法难写,但是很容易验证正确性。
Tcpdump 一些常用的其他参数如下:
-n 不解析主机名和端口号,保留原始的数字
-v, -vv, -vvv v 越多表示输出的信息越详细
-c 5 表示抓到 5 个包之后就退出
-e 显示二层的 link layer header,这样就可以看到 MAC 地址了
-Q 可以指定抓包方向,可以选的有 in, out, inout
-A 可以展示包的内容,tcpdump 默认是只根据不同的协议展示 header 信息的。在线上排查问题的时候,我们往往需要通过特殊请求的关键字来定位到单个请求的情况进行排查,这样 -A 展示出来包的内容就格外有用。
通过 -A 参数来抓取特定的 HTTP 请求
这里分享一个特殊的技巧,就是发标记请求来定位问题。比如 A 通过 B 代理发请求给 C,现在网络不通,我们要定位 B 收到了请求没有,才知道是 B 的问题还是 C 的问题。但是 B 本身就有很多线上流量,怎么知道 A 发送的请求到达 B 了没有呢?我们可以在 B 进行 tcpdump:tcpdump -i eth0 tcp | grep asdf123 -A 10,然后我们从 A 发送一个请求:curl http://host-C.com/asdf123。asdf123 就是我们在请求里面放上的标记,如果 B 能够正常转发,我们就可以 match 到这个请求。当然了,这种技巧只适用于 HTTP 这种明文协议。
Wireshark 离线分析
有些问题很难直接在 tcpdump 的终端分析出来问题,比如涉及 sequence number 分析的,重传分析之类的,我们需要人工对比 seq number,真是一项费眼睛的工作!所以如上所说,我们也经常在机器上用 tcpudmp 抓包保存成 .pcap 文件,下载到本地用 Wireshark 分析。Wireshark 就可以自动根据 sequence number 告诉我们重传等信息了!
从网络设备的工作原理上讲也是这样,一个 IP 包从 src 传到 dst,中间经过了各种各样的网络设备,那么从 src 发出到 dst 收到,中间的网络设备修改了这个包的什么内容,才能一环接一环把它送到目的地呢?
交换机:不会修改任何内容,只查找自己的 mac 地址表转发走;
路由器:会根据 dst IP 查询下一跳应该发给哪一个 IP,但是下一跳的 IP 不会添加到数据包中。路由器会获取到下一跳对应的 MAC 地址,把数据包的 dst MAC 修改成下一跳的 MAC 然后转发出去。可以认为,此路由器出口 IP 和下一跳路由器入口的 IP 是在同一个子网中,所以每跳三层转发都是在同一个子网内的转发,即「所有的转发都是二层转发」。一般来说,路由器会有很多个物理接口,每一个物理接口都有 IP,互相连接的两个路由器的接口是在同一个子网(也有某些特殊的 P2P 网络可以不在同子网,甚至不需要配置 IP就能完成三层路由)。
不通路由的之间的接口是在同一个 LAN 下,所以也是基于二层转发
可以看出来,如果不考虑 NAT 这种会改变 IP 的设备,三层及以上的内容(除了 TTL 会被路由器修改)的内容是基本不会改变的。而二层内容几乎每一次都在改变。