1、 工欲善其事必先利其器:学会使用各种工具
Linux 平台下提供的各种网络工具,则为我们进行诊断分析提供了很好的帮助。在这一讲里,我将会选择几个重点的工具逐一介绍。
1.1、必备工具: ping
“ping”: 这个命名来自于声呐探测,在网络上用来完成对网络连通性的探测;
$ ping www.sina.com.cn
PING www.sina.com.cn (202.102.94.124) 56(84) bytes of data.
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=1 ttl=63 time=8.64 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=2 ttl=63 time=11.3 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=3 ttl=63 time=8.66 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=4 ttl=63 time=13.7 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=5 ttl=63 time=8.22 ms
64 bytes from www.sina.com.cn (202.102.94.124): icmp_seq=6 ttl=63 time=7.99 ms
^C
--- www.sina.com.cn ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5006ms
rtt min/avg/max/mdev = 7.997/9.782/13.795/2.112 ms
上面的例子中,使用 ping 命令探测了和新浪网的网络连通性。
- 每次显示是按照 sequence 序列号排序显示的;
- TTL(time to live),反映了两个 IP 地址之间传输的时间;
- 还显示了 ping 命令的统计信息,如最小时间、平均时间等。
ping 命令的原理到底是什么呢?它是基于 TCP 还是 UDP 开发的?都不是。
ping 是基于一种叫做 ICMP 的协议开发的,ICMP 又是一种基于 IP 协议的控制协议,翻译为网际控制协议,其报文格式如下图:
ICMP 在 IP 报文后加入了新的内容,这些内容包括:
**类型:**即 ICMP 的类型, 其中 ping 的请求类型为 8,应答为 0;
**代码:**进一步划分 ICMP 的类型, 用来查找产生错误的原因;
**校验和:**用于检查错误的数据;
**标识符:**通过标识符来确认是谁发送的控制协议,可以是进程 ID;
**序列号:**唯一确定的一个报文,前面 ping 名字执行后显示的 icmp_seq 就是这个值。
- 当我们发起 ping 命令时,ping 程序实际上会组装成如图的一个 IP 报文。报文的目的地址为 ping 的目标地址,源地址就是发送 ping 命令时的主机地址,同时按照 ICMP 报文格式填上数据,在可选数据上可以填上发送时的时间戳。
- IP 报文通过 ARP 协议,源地址和目的地址被翻译成 MAC 地址,经过数据链路层后,报文被传输出去。当报文到达目的地址之后,目的地址所在的主机也按照 ICMP 协议进行应答。(之所以叫做协议,是因为双方都会遵守这个报文格式,并且也会按照格式进行发送 - 应答。)
- 应答数据到达源地址之后,ping 命令可以通过再次解析 ICMP 报文,对比序列号,计算时间戳等来完成每个发送 - 应答的显示,最终显示的格式就像前面的例子中展示的一样。
ICMP 协议为我们侦测网络问题提供了非常好的支持。
1.2、基本命令: ifconfig
Windows 有一个 ipconfig 命令,用来显示当前的网络设备列表。
Linux 有一个对应的命令叫做 ifconfig,也用来显示当前系统中的所有网络设备,通俗一点的说,就是网卡列表。
vagrant@ubuntu-xenial-01:~$ ifconfig
cni0 Link encap:Ethernet HWaddr 0a:58:0a:f4:00:01
inet addr:10.244.0.1 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::401:b4ff:fe51:bcf9/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
RX packets:2133 errors:0 dropped:0 overruns:0 frame:0
TX packets:2216 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:139381 (139.3 KB) TX bytes:853302 (853.3 KB)
docker0 Link encap:Ethernet HWaddr 02:42:93:0f:f7:11
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:93ff:fe0f:f711/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:653 errors:0 dropped:0 overruns:0 frame:0
TX packets:685 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:49542 (49.5 KB) TX bytes:430826 (430.8 KB)
enp0s3 Link encap:Ethernet HWaddr 02:54:ad:ea:60:2e
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::54:adff:feea:602e/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:7951 errors:0 dropped:0 overruns:0 frame:0
TX packets:4123 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5081047 (5.0 MB) TX bytes:385600 (385.6 KB)
解释显示的数据:
Link encap:Ethernet HWaddr 02:54:ad:ea:60:2e
是一个以太网设备,MAC 地址为 02:54:ad:ea:60:2e。
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::54:adff:feea:602e/64 Scope:Link
显示的是网卡的 IPv4 和 IPv6 地址,其中 IPv4 还显示了该网络的子网掩码以及广播地址。
广播地址:
- 在每个 IPv4 子网中,有一个特殊地址被保留作为子网广播地址,比如这里的 10.0.2.255 就是这个子网的广播地址。
- 当向这个地址发送请求时,就会向以太网网络上的一组主机发送请求。
- 广播(broadcast)的技术,是用 UDP 来实现的。
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
这里显示的是网卡的状态,MTU 是最大传输单元的意思,表示的是链路层包的大小。1500 表示的是字节大小。
Metric:
Linux 在一台主机上可以有多个网卡设备,很可能有这么一种情况,多个网卡可以路由到目的地。
举例: 在同时有无线网卡和有线网卡的情况下,网络连接是从哪一个网卡设备上出去的?Metric 就是用来确定多块网卡的优先级的,数值越小,优先级越高,1 为最高级。
1.3、netstat 和 lsof:对网络状况了如指掌
ifconfig
我们最常碰到的问题就是某某进程对应的网络状况如何?是不是连接被打爆了?还是有大量的 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 Socket -x -alepn
UNIX 套接字的结果稍有不同,最关键的信息是 Path,这个信息显示了本地套接字监听的文件路径,比如这条:
unix 3 [ ] STREAM CONNECTED 23209 1400/dockerd /var/run/docker.sock
/var/run/docker.sock 是本地套接字监听地址,dockerd 是进程名称,1400 是进程号。
lsof
作用:帮助我们找出在指定的 IP 地址或者端口上打开套接字的进程;
netstat 则告诉我们 IP 地址和端口使用的情况,以及各个 TCP 连接的状态。
通过 lsof 查看到底是谁打开了这个文件:
lsof /var/run/docker.sock
这张图显示了是 dockerd 打开了这个本地文件套接字:
lsof 还有一个非常常见的用途:
如果我们启动了一个服务器程序,发现这个服务器需要绑定的端口地址已经被占用,内核报出“该地址已在使用”的出错信息;
可以使用 lsof 找出正在使用该端口的那个进程:
lsof -i :8080
1.4、抓包利器: tcpdump
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'
如果我们对 TCP 协议非常熟悉,还可以写出这样的 tcpdump 命令:
tcpdump 'tcp and port 80 and tcp[13:1]&2 != 0'
tcp[13:1]: TCP 头部开始处偏移为 13 的字节,如果这个值为 2,说明设置了 SYN 分节,当然,我们也可以设置成其他值来获取希望类型的分节。(注意,这里的偏移是从 0 开始算起的,tcp[13]其实是报文里的第 14 个字节。)
tcpdump抓包过程:
- tcpdump 在开启抓包的时候,会自动创建一个类型为 AF_PACKET 的网络套接口,并向系统内核注册。
- 当网卡接收到一个网络报文之后,它会遍历系统中所有已经被注册的网络协议,包括其中已经注册了的 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;
小结
- ping 可以用来帮助我们进行网络连通性的探测。
- ifconfig,用来显示当前系统中的所有网络设备。
- netstat 和 lsof 可以查看活动的连接状况。
- tcpdump 可以对各种奇怪的环境进行抓包,进而帮我们了解报文,排查问题。
思考题
1、你知道 tcpdump 这个工具还可以对 UDP 包进行抓包处理吗?
很简单 指定端口即可;
2、netstat 输出时,监听状态的套接字所对应的 Foreign Address 显示的 . 表示的是什么意思呢?
这个套接字正在监听端口等待连接进来,允许任何地址、任何端口来建立连接。