前言
ping是基于ICMP(Internet Control Message Protocol)协议实现的,而ICMP协议是在IP层实现的。
ping实际上是发起者发送一个Echo Request(type = 8)的,远程主机回应一个Echo Reply(type = 0)的过程。
为什么用ping不能测试某一个端口
刚开始接触网络的时候,可能很多人都有疑问,怎么用ping来测试远程主机的某个特定端口?
其实如果看下ICMP协议,就可以发现ICMP里根本没有端口这个概念,也就根本无法实现测试某一个端口了。
ICMP协议的包格式可以直接参考wiki: https://en.wikipedia.org/wiki/Ping_(networking_utility)#ICMP_packet
Ping如何计算请问耗时
在ping命令的输出上,可以看到有显示请求的耗时,那么这个耗时是怎么得到的呢?
1 | 64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=6.28 ms |
从Echo Request的格式里,看到不时间相关的东东,但是因为是Echo,即远程主机会原样返回Data数据,所以Ping的发起方把时间放到了Data数据里,当得到Echo Reply里,取到发送时间,再和当前时间比较,就可以得到耗时了。当然,还有其它的思路,比如记录每一个包的发送时间,当得到返回时,再计算得到时间差,但显然这样的实现太复杂了。
Ping如何区分不同的进程?
我们都知道本机IP,远程IP,本机端口,远程端口,四个元素才可以确定唯的一个信道。而ICMP里没有端口,那么一个ping程序如何知道哪些包才是发给自己的?或者说操作系统如何区别哪个Echo Reply是要发给哪个进程的?
实际上操作系统不能区别,所有的本机IP,远程IP相同的ICMP程序都可以接收到同一份数据。
程序自己要根据Identifier来区分到底一个ICMP包是不是发给自己的。在Linux下,Ping发出去的Echo Request包里Identifier就是进程pid,远程主机会返回一个Identifier相同的Echo Reply包。
可以接下面的方法简单验证:
启动系统自带的ping程序,查看其pid。
设定自己实现的ping程序的identifier为上面得到的pid,然后发Echo Request包。
可以发现系统ping程序会接收到远程主机的回应。
自己实现ping
自己实现ping要用到rawsocket,在linux下需要root权限。网上有很多实现的程序,但是有很多地方不太对的。自己总结实现了一个(最好用g++编绎):
1 |
|
安全相关的一些东东
尽管是很老的漏洞,但是也可以看出协议栈的实现也不是那么的靠谱。
- Ping flood http://en.wikipedia.org/wiki/Ping_flood
服务器关闭ping服务,默认是0,是开启:
1 | echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all |
总结
在自己实现的过程中,发现有一些蛋疼的地方,如
协议文档不够清晰,得反复对照;
有时候一个小地方处理不对,很难查bug,即使程序能正常工作,但也并不代表它是正确的;
用wireshark可以很方便验证自己写的程序有没有问题。