在使用套接字开发和测试过程中,总会碰到各种问题。学会对这些问题进行诊断和分析,其实需要不断地积累经验。而 Linux 平台下提供的各种网络工具,则为我们进行诊断分析提供了很好的帮助。
ping
在上面的例子中,我使用 ping 命令探测了和百度的网络连通性。可以看到,每次显示是按照 sequence 序列号排序显示的,一并显示的包括 TTL,反映了两个 IP 地址之间传输的时间。最后还显示了 ping 命令的统计信息,如最小时间、平均时间等。
那么 ping 命令的原理到底是什么呢?它是基于 TCP 还是 UDP 开发的?
都不是
ping 是基于 ICMP 协议开发的,ICMP 又是一种基于 IP 协议的控制协议,翻译为网际控制协议,其报文格式如下图:
ICMP 在 IP 报文后加入了新的内容,这些内容包括:
- 类型:即 ICMP 的类型, 其中 ping 的请求类型为 0,应答为 8。
- 代码:进一步划分 ICMP 的类型, 用来查找产生错误的原因。
- 校验和:用于检查错误的数据。
- 标识符:通过标识符来确认是谁发送的控制协议,可以是进程 ID。
- 序列号:唯一确定的一个报文,前面 ping 名字执行后显示的 icmp_seq 就是这个值。
当发起 ping 命令时,ping 程序会组装成如图的一个 IP 报文。报文的目的地址为 ping 的目标地址,源地址就是发送 ping 命令时的主机地址,同时按照 ICMP 报文格式填上数据,在可选数据上可以填上发送时的时间戳。
IP 报文通过 ARP 协议,源地址和目的地址被翻译成 MAC 地址,经过数据链路层后,报文被传输出去。当报文到达目的地址之后,目的地址所在的主机也按照 ICMP 协议进行应答。
应答数据到达源地址之后,ping 命令可以通过再次解析 ICMP 报文,对比序列号,计算时间戳等来完成每个发送 - 应答的显示,最终显示的格式就像前面的例子中展示的一样。
ICMP 协议为我们侦测网络问题提供了非常好的支持。另一种对路由的检测命令 Traceroute 也是通过 ICMP 协议来完成的。
ifconfig
先看第一行的括号,里面的每个标记都有各自的含义
- UP:表明接口已经启动并处于可用状态
- BROADCAST:接口支持广播地址功能
- MULTICAST:地址支持多播地址功能
而每个标志位对应二进制掩码中的一位:
flag就是这些标志位对应的二进制掩码相加的结果
第二行表示的是ipv4地址、子网掩码和广播地址
第三、四行表示的是ipv6地址和mac地址
netstat 和 lsof:对网络状况了如指掌
在工作中最常碰到的问题就是某某进程对应的网络状况如何?是不是连接被打爆了?还是有大量的 TIME_WAIT 连接?
netstat 可以帮助我们了解当前的网络连接状况,比如我想知道当前所有的连接详情,就可以使用下面这行命令:
netstat -alepn
netstat 会把所有 IPv4 形态的 TCP,IPV6 形态的 TCP、UDP 以及 UNIX 域的套接字都显示出来。
对于 TCP 类型来说,最大的好处是可以清楚地看到一条 TCP 连接的四元组(源地址、源端口、目的地地址和目的端口)。
例如这里的一条信息:
tcp 0 0 127.0.0.1:2379 127.0.0.1:52464 ESTABLISHED 0 27710 3496/etcd
它表达的意思是本地 127.0.0.1 的端口 52464 连上本地 127.0.0.1 的端口 2379,状态为 ESTABLISHED,本地进程为 etcd,进程为 3496。
这在实战分析时非常有用,比如你可以很方便地知道,在某个时候是不是有很多 TIME_WAIT 的 TCP 连接,导致端口号被占用光,以致新的连接分配不了。
当然,我们也可以只对 UNIX 套接字进行筛查。
netstat -x -alepn
UNIX 套接字的结果稍有不同,最关键的信息是 Path,这个信息显示了本地套接字监听的文件路径,比如这条:
unix 3 [ ] STREAM CONNECTED 23209 1400/dockerd /var/run/docker.sock
这就是 Docker 在本地套接字的监听路径。/var/run/docker.sock 是本地套接字监听地址,dockerd 是进程名称,1400 是进程号。
lsof 的常见用途之一是帮助我们找出在指定的 IP 地址或者端口上打开套接字的进程,而 netstat 则告诉我们 IP 地址和端口使用的情况,以及各个 TCP 连接的状态。Isof 和 netstst 可以结合起来一起使用。
比如说,我们可以通过 lsof 查看到底是谁打开了这个文件:
lsof /var/run/docker.sock
下面这张图显示了是 dockerd 打开了这个本地文件套接字:
lsof 还有一个非常常见的用途。如果我们启动了一个服务器程序,发现这个服务器需要绑定的端口地址已经被占用,内核报出“该地址已在使用”的出错信息,我们可以使用 lsof 找出正在使用该端口的那个进程。比如下面这个代码,就帮我们找到了使用 8080 端口的那个进程,从而帮助我们定位问题。
lsof -i :8080
抓包利器: tcpdump
tcpdump 具有非常强大的过滤和匹配功能。
比如说指定网卡:
tcpdump -i ens33
再比如说指定来源:
tcpdump src host hostname
我们再来一个复杂一点的例子。这里抓的包是 TCP,且端口是 80,包来自 IP 地址为 192.168.1.25 的主机地址。
tcpdump 'tcp and port 80 and src host 192.168.1.25'
tcpdump 在开启抓包时,会自动创建一个类型为 AF_PACKET 的网络套接口,并向内核注册。当网卡接收到网络报文后,它会遍历系统中所有已经被注册的网络协议。内核接下来就会将网卡收到的报文发送给该协议的回调函数进行一次处理,回调函数可以把接收到的报文完完整整地复制一份,假装是自己接收到的报文,然后交给 tcpdump 程序,进行各种条件的过滤和判断,再对报文进行解析输出。
下面这张图显示的是 tcpdump 的输出格式:
首先我们看到的是时间戳,之后类似 192.168.33.11.41388 > 192.168.33.11.6443 这样的,显示的是源地址(192.168.33.11.41388)到目的地址(192.168.33.11.6443);然后 Flags [ ] 是包的标志,[P] 表示是数据推送,比较常见的包格式如下:
- [S]:SYN,表示开始连接
- [.]:没有标记,一般是确认
- [P]:PSH,表示数据推送
- [F]:FIN,表示结束连接
- [R] :RST,表示重启连接
我们可以看到最后有几个数据,它们代表的含义如下:
- seq:包序号,就是 TCP 的确认分组
- cksum:校验码
- win:滑动窗口大小
- length:承载的数据(payload)长度 length,如果没有数据则为 0
此外,tcpdump 还可以对每条 TCP 报文的细节进行显示,让我们可以看到每条报文的详细字节信息。这在对报文进行排查的时候很有用。
总结
- ping 网络连通性的探测。
- ifconfig,显示当前系统中的所有网络设备。
- netstat 和 lsof 可以查看活动的连接状况。
- tcpdump 可以对各种奇怪的环境进行抓包,进而帮我们了解报文,排查问题。