ping

ping 这个命名来自于声呐探测,在网络上用来完成对网络连通性的探测,这个命名可以说是恰如其分了。
image.png
在上面的例子中,我使用 ping 命令探测了和新浪网的网络连通性。可以看到,每次显示是按照 seq 序列号排序显示的,一并显示的也包括 ttl(time to live),反映了两个 IP 地址之间传输的时间。最后还显示了 ping 命令的统计信息,如最小时间、平均时间等。

ping 命令的原理
ping 命令是基于一种叫做 ICMP 的协议开发的,ICMP 又是一种基于 IP 协议的控制协议,翻译为网际控制协议,其报文格式如下图:
image.png
ICMP 在 IP 报文后加入了新的内容,这些内容包括:

  • 类型:即 ICMP 的类型, 其中 ping 的请求类型为 8,应答为 0。
  • 代码:进一步划分 ICMP 的类型, 用来查找产生错误的原因。
  • 校验和:用于检查错误的数据。
  • 标识符:通过标识符来确认是谁发送的控制协议,可以是进程 ID。
  • 序列号:唯一确定的一个报文,前面 ping 名字执行后显示的 icmp_seq 就是这个值。

当我们发起 ping 命令时,ping 程序实际上会组装成如图的一个 IP 报文。报文的目的地址为 ping 的目标地址,源地址就是发送 ping 命令时的主机地址,同时按照 ICMP 报文格式填上数据,在可选数据上可以填上发送时的时间戳。

IP 报文通过 ARP 协议,源地址和目的地址被翻译成 MAC 地址,经过数据链路层后,报文被传输出去。当报文到达目的地址之后,目的地址所在的主机也按照 ICMP 协议进行应答。应答数据到达源地址之后,ping 命令可以通过再次解析 ICMP 报文,对比序列号,计算时间戳等来完成每个发送 - 应答的显示,最终显示的格式就像前面的例子中展示的一样。可以说,ICMP 协议为我们侦测网络问题提供了非常好的支持。

ifconfig

Windows 操作系统有一个 ipconfig 命令,用来显示当前的网络设备列表。事实上,Linux 也有一个对应的命令叫做 ifconfig,也是用来显示当前系统中的所有网络设备,通俗一点的说,就是网卡列表。

  1. [admin ~]$ ifconfig
  2. eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
  3. inet 10.58.10.73 netmask 255.255.255.0 broadcast 10.58.10.255
  4. inet6 fe80::f8ee:42ff:fe37:2500 prefixlen 64 scopeid 0x20<link>
  5. ether fa:ee:42:37:25:00 txqueuelen 1000 (Ethernet)
  6. RX packets 3104583392 bytes 545561457248 (508.0 GiB)
  7. RX errors 0 dropped 0 overruns 0 frame 0
  8. TX packets 3195822813 bytes 716497980476 (667.2 GiB)
  9. TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
  10. lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
  11. inet 127.0.0.1 netmask 255.0.0.0
  12. inet6 ::1 prefixlen 128 scopeid 0x10<host>
  13. loop txqueuelen 1 (Local Loopback)
  14. RX packets 248425004 bytes 30132525450 (28.0 GiB)
  15. RX errors 0 dropped 0 overruns 0 frame 0
  16. TX packets 248425004 bytes 30132525450 (28.0 GiB)
  17. TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

我稍微解释一下这里面显示的数据:

flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

这里显示的是网卡的状态,MTU 是最大传输单元的意思,表示链路层包的大小,1500 表示的是字节大小。

inet 10.58.10.73  netmask 255.255.255.0  broadcast 10.58.10.255
inet6 fe80::f8ee:42ff:fe37:2500  prefixlen 64  scopeid 0x20<link>

这里显示的是网卡的 IPv4 和 IPv6 地址,其中 IPv4 还显示了该网络的子网掩码(netmask)以及广播地址(broadcast)。在每个 IPv4 子网中,会有一个特殊地址被保留作为子网广播地址,比如这里的 10.58.10.255 就是这个子网的广播地址。当向这个地址发送请求时,就会向以太网网络上的一组主机发送请求。通常这种广播技术是用 UDP 来实现的。

netstat、lsof

在平时的工作中,我们最常碰到的问题就是某某进程对应的网络状况如何?是不是连接被打爆了?还是有大量的 TIME_WAIT 连接?netstat 命令可以帮助我们了解当前的网络连接状况,如果想知道当前所有的连接详情可以使用下面这行命令:

netstat -alepn

可能的结果为:
image.png
netstat 会把所有 IPv4 形态的 TCP,IPV6 形态的 TCP、UDP 以及 UNIX 域的套接字都显示出来。对于 TCP 类型来说,最大的好处是可以清楚地看到一条 TCP 连接的四元组(源地址、源端口、目的地地址和目的端口)。

例如这里的一条信息:

tcp        0      0 127.0.0.1:6379          127.0.0.1:47054         ESTABLISHED 1000       17586797   22220/./src/redis-s

它表达的意思是本地 127.0.0.1 的端口 6379 连上本地 127.0.0.1 的端口 47054,状态为 ESTABLISHED,本地进程号为 22220,进程为 redis。这在实战分析的时候非常有用,比如你可以很方便地知道,在某个时候是不是有很多 TIME_WAIT 的 TCP 连接导致端口号被占用光,以致新的连接分配不了。

除了显示 TCP 连接外,还会显式 UNIX 套接字信息。
image.png
UNIX 套接字的结果稍有不同,最关键的信息是 Path,它显示了本地套接字监听的文件路径,比如这条:

unix  2      [ ]         STREAM     CONNECTED     42737767 4745/java

这其实就是一个 java 进程在本地套接字的监听路径,4745 是进程号。实际上,netstat 命令可以选择的参数非常之多,这里只关注了几个简单的场景,你可以通过帮助命令或者查阅文档获得更多的信息。

lsof 的常见用途之一是帮助我们找出在指定的 IP 地址或者端口上打开套接字的进程,而 netstat 则告诉我们 IP 地址和端口使用的情况,以及各个 TCP 连接的状态。Isof 和 netstst 可以结合起来一起使用。比如说,我们可以通过 lsof 查看到底是谁打开了这个文件:

lsof -p 4745

下面这张图显示了该进程打开的本地文件套接字:
image.png
lsof 还有一个非常常见的用途。如果我们启动了一个服务器程序,发现这个服务器需要绑定的端口地址已经被占用,内核报出 address already in use 的出错信息,我们可以使用 lsof 找出正在使用该端口的那个进程。比如下面这个代码,就帮我们找到了使用 8080 端口的那个进程,从而帮助我们定位问题。

lsof -i :8080

tcpdump

tcpdump 这样的抓包工具对于网络编程而言是非常有用的,其具有非常强大的过滤和匹配功能。比如说我们可以指定某个网卡:

tcpdump -i eth0

再比如说指定来源:

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 的网络套接口,并向系统内核注册。当网卡接收到一个网络报文之后,它会遍历系统中所有已经被注册的网络协议,包括其中已经注册了的 AF_PACKET 网络协议。系统内核接下来就会将网卡收到的报文发送给该协议的回调函数进行一次处理,回调函数可以把接收到的报文完完整整地复制一份,假装是自己接收到的报文,然后交给 tcpdump 程序,进行各种条件的过滤和判断,再对报文进行解析输出。

下面这张图显示的是 tcpdump 的输出格式:
image.png
首先我们看到的是时间戳,之后类似 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 报文的细节进行显示,让我们可以看到每条报文的详细字节信息。这在对报文进行排查的时候很有用。