基于ICMP协议的ping命令,icmp协议 ping
一、ICMP协议分析ICMP:互联网控制报文协议。由于IP协议不是一个可靠的协议,它不能保证数据的成功传输。那么,我们如何确保数据的可靠传递呢?这里需要用到一个重要的协议模块ICMP(网络控制消息)协议。它传输错误消息和其他需要注意的信息,通常由IP层或更高层协议(TCP或UDP)使用。因此它通常被认为是IP层不可分割的一部分。其在IP数据消息中的封装如下:
ICMP的数据报文格式如下。所有消息的前4个字节都是相同的,其他的由于消息类型不同而不同。类型字段可以有15个不同的值来描述不同的ICMP消息。校验和字段包含整个ICMP消息,并使用与IP报头校验和相同的算法。有关详细信息,请搜索TCP/IP校验和算法。
不同类型的消息由类型字段和代码字段共同确定。下表显示了各种类型的ICMP消息。
根据上表,ICMP协议大致可以分为两类,一类是查询报文,一类是错误报文。查询由一对请求和响应定义,通常有以下用途:
Ping子网掩码查询(用于无盘工作站初始化自身时初始化子网掩码)时间戳查询(可用于同步时间),错误消息通常包含导致错误的IP数据报第一个片段的IP头(和选项),加上片段数据部分的前8个字节。RFC 792规范中定义的这8个字节包含了数据包传输层报头的所有解复用信息,这样传输层协议就可以将ICMP错误消息提交给正确的进程。
当IP包传输出现错误时,如主机不可达、端口不可达等。ICMP协议会将错误信息打包,然后发送回主机。给主机一个处理错误的机会,这也是基于IP层的协议能够安全的原因。从上面可以看出,互联网控制报文协议由8位错误类型、8位代码和16位校验和组成,前16位组成ICMP想要传输的信息。数据链路层可以发送的最大数据帧,即MTU(最大传输单位)是1500。很容易计算出ICMP协议在实际传输中的数据包为:20字节IP头,8字节ICMP头,1472字节(数据大小)。
虽然在大多数情况下,ICMP消息应该在错误的数据包传输中给出,但在特殊情况下,ICMP错误消息不会生成。如下
ICMP错误消息不会生成ICMP错误消息(传出IMCP查询消息)(防止IMCP的无限生成和传输)。目的地址是广播地址或多播地址的IP数据报。作为链路层的数据报广播。不是IP碎片化的第一块。源地址不是单一主机的数据报。也就是说,源地址不能是零地址、环回地址、广播地址或组播地址。
二、ping程序原理分析ping程序是Mike Muuss写的,用来测试另一台主机是否可达。现在它已经成为一个常用的网络状态检查工具。该程序向远程主机发送ICMP回应请求消息,并等待ICMP回应回复。借助ping的原理,出现了很多基于ping的网络扫描器,如nmap、arping、f ping、hp3等。因此,随着互联网安全意识的增强,可以设置一些提供访问控制策略的路由器和防火墙来过滤特定的ICMP消息请求。因此,无法通过简单的ping命令来判断远程主机是否在线。
Ping使用icmp协议,该协议向目的主机发送ICMP回应请求消息。ICMP协议规定,目的主机必须向源主机返回ICMP回应回复消息。如果源主机在一定时间内收到应答,则认为该主机是可到达的。的大多数TCP/IP实现都支持直接在内核中Ping服务器,ICMP回应请求和回应回复消息如下图所示。
ping的原理是使用类型代码为0的ICMP发送请求,而被请求的主机使用类型代码为8的ICMP进行响应。通过计算ICMP响应消息的数量和收发消息的时间差,可以判断当前的网络状态。往返时间的计算方法如下:ping命令发送ICMP报文时,将当前时间值存储在ICMP报文中并发送出去;当回复消息返回时,它使用当前时间值减去存储在ICMP消息数据中的时间值来计算往返时间。通过ping返回接收到的数据消息的字节大小、TTL值和往返时间。
Unix在执行ping程序时,将ICMP消息中的标识符字段设置为发送进程的id号。这样,即使ping程序的多个实例同时在同一台主机上运行,ping程序也可以识别返回的信息。
三。应用ICMP - TracerouteTraceroute是检测从主机到目的地的路由的一个重要和最方便的工具。如前所述,虽然ping工具也可以检测,但由于ip头的限制,ping无法完整记录其经过的路由器。所以Traceroute正好填补了这个空白。
Traceroute的原理非常非常有趣。在收到目的主机的IP后,它首先向目的主机发送一个TTL=1(还记得TTL是什么吗?),而第一个路由器收到这个数据包后,会自动将TTL减1,TTL变为0后,路由器会丢弃这个数据包,同时会生成一个主机无法到达的ICMP数据,上报给主机。主机收到这个数据报后,再向目的主机发送一个TTL=2的UDP数据报,然后刺激第二个路由器向主机发送ICMP数据报。依此类推,直到到达目的主机。这样,traceroute就获得了所有路由器的ip地址。从而避免了ip头只能记录有限的路由IP的问题。
有人要问了,我怎么知道UDP有没有到达目的主机?这涉及到一个技术问题。TCP和UDP协议都有一个端口号定义,而普通的网络程序只监控少数几个端口号较小的端口,比如80、23等等。Traceroute发送的UDP数据报端口号为30000(异常),所以到达目的主机时,目的主机只能向主机发送一个不可达端口的ICMP数据报。主人接到这个报告后,知道主人到了。所以说Traceroute是骗子一点都不为过。
Traceroute程序提供了一些非常有用的选项,甚至包括IP路由的选项。这些请参考man文档了解,这里就不赘述了。
第四,python实现ping程序方法。首先,简单实现了使用python脚本调用系统中的ping命令。
导入子流程
导入shlex
cmd=ping -c 1 www.baidu.com
args=shlex.split(cmd)
尝试:
subprocess.check_call(args,stdout=subprocess。管道,stderr=子进程。管道)
打印“百度服务器启动!”
除了子流程。CalledProcessError:
“打印”未能获得ping但是,在许多情况下,系统中的ping可执行文件是不可用或不可访问的。此时,您需要使用纯python检查脚本。下面是ICMP ping的python实现脚本。该脚本定义了一个Pinger类,它使用do_checksum()方法检查总和,使用send_ping()方法发送ping数据消息,使用receive_ping()方法接受ping数据消息,使用ping () main方法执行该类。下面是具体代码。
#定义Pinger类,初始化并创建实例。
导入操作系统
导入argparse
导入插座
导入结构
导入选择
导入时间
ICMP_ECHO_REQUEST=8 #特定于平台
DEFAULT_TIMEOUT=2
默认计数=4
类Pinger(对象):
Pings主机Pythonic方式
def __init__(self,target_host,count=DEFAULT_COUNT,timeout=DEFAULT_TIMEOUT):
self.target_host=目标主机
self.count=计数
self . time out=time out do _ checksum()方法定义如下,用于检查和验证总和。验证方法如下:
把校验和字段置为0将网间控制报文协议包(包括页眉和数据)以16位(2个字节)为一组,并将所有组相加(二进制求和)若高16位不为0,则将高16位与低16位反复相加,直到高16位的值为0,从而获得一个只有16位长度的值将此16位值进行按位求反操作,将所得值替换到校验和字段具体算法可以搜索:【TCP/IP】检验和算法。
定义do_checksum(自身,源字符串):
验证数据包完整性
总和=0
max _ count=(len(source _ string)/2)* 2
计数=0
同时计数最大计数:#分割数据每两比特(16位)为一组
val=ord(source _ string[count 1])* 256 ord(source _ string[count])
总和=总和值
总和=总和0xffffffff
计数=计数2
if max _ count len(source _ string):span #如果数据长度为基数,则将最后一位单独相加/span
sum=sum ord(source _ string[len(source _ string)-1])
总和=总和0xffffffff
总和=(总和16)(总和0xffff) #将高16位与低16位相加直到高16位为0
sum=sum (sum 16)
答案=~总和
答案=答案0xffff
答案=答案8 (答案80xff00)
返回答案#返回的是十进制整数下面是接受网间控制报文协议(互联网控制消息协议)类型码为8的网间控制报文协议(互联网控制消息协议)回应报文的方法。在未到达超时时间之前窝处于阻塞状态一直等待响应,当有数据传回时就接受响应,然后提取包含标识符身份的网间控制报文协议(互联网控制消息协议)报文首部和包含发送时间值的网间控制报文协议(互联网控制消息协议)内容部分,计算请求-响应的延时间隔。
def receive_ping(self,sock,ID,timeout):
从套接字接收平.
剩余时间=超时
虽然正确:
start_time=time.time()
readable=select.select([sock],[],[],time_remaining)
时间花费=(时间。时间()-开始时间)
如果可读[0]==[]: #超时
返回
time_received=time.time()
recv_packet,addr=sock.recvfrom(1024)
icmp_header=recv_packet[20:28]
类型、代码、校验和、packet_ID、sequence=struct.unpack(
bbHHh ,icmp_header
)
如果数据包ID==ID:
bytes _ In _ double=struct计算大小( d )
time_sent=struct.unpack(d ,recv _ packet[28:28 bytes _ In _ double])[0]
返回接收时间-发送时间
剩余时间=剩余时间-花费的时间
如果剩余时间=0:
返回下面定义的send_ping()方法,获取远程主机的域名服务器(域名服务器)主机名,然后使用结构体模块创建一个ICMP _回应_请求数据包,将查验请求的数据发送到目标主机。在此发送前也需要进行do_checksum()方法的校验。
def send_ping(self,sock,ID):
向目标主机发送砰命令
target _ addr=套接字。gethostbyname(self。目标_主机)
我的校验和=0
#创建一个校验和为0的虚拟海德尔.
header=struct.pack(bbHHh ,ICMP_ECHO_REQUEST,0,my_checksum,ID,1)
bytes _ In _ double=struct计算大小( d )
数据=(192字节双精度)* Q
data=struct.pack(d ,time.time())数据
#获取数据和伪报头的校验和。
我的校验和=self.do_checksum(头数据)
header=struct.pack(
bbHHh ,ICMP_ECHO_REQUEST,0,socket.htons(my_checksum),ID,1
)
数据包=标题数据
sock.sendto(packet,(target_addr,1))下面定义了一个ping_once()方法,向远程主机发送一次查验:将网间控制报文协议(互联网控制消息协议)协议传给插座()方法,创建一个原始的网间控制报文协议(互联网控制消息协议)套接字。由于砰程序需要使用袜子_RAW来构建数据包,所以需要根权限才能运行这个程序。因此,本程序需要使用根权限运行,下面的异常处理部分就是来负责未使用根运行时抛出的异常。
定义ping_once(自身):
超时时返回延迟(秒)或无。
icmp=套接字。getprotobyname( icmp )
尝试:
sock=socket.socket(插座.AF_INET,插座.SOCK_RAW,icmp)
除了socket.error,(errno,msg):
如果errno==1:
#不是超级用户,因此不允许操作
msg=ICMP消息只能从根用户进程发送
引发套接字.错误(消息)
除了例外,e:
"打印"异常:% s"%"(e)
my_ID=os.getpid()0xFFFF
self.send_ping(sock,my_ID)
delay=self.receive_ping(sock,my_ID,self.timeout)
sock.close()
返回延迟下面这个ping()是执行这个类的主要方法。在为循环中调用ping_once()方法,发送砰数据报文,并返回结果。
定义乒(自己):
运行砰进程
对于润智中的我(自我。计数):
打印" Ping % s . "% self.target _主机
尝试:
delay=self.ping_once()
除了socket.gaierror,e:
"打印“平失败。(套接字错误:" % s")"% e[1]
破裂
如果延迟==无:
"打印“平失败%ssec内超时% self.timeout
否则:
延迟=延迟* 1000
打印"在% 0.4毫秒内获得平”延迟
if __name__==__main__ :
parser=argparse .ArgumentParser(描述=Python ping )
解析器。add _ argument(-target-host ,action=store ,dest=target_host ,required=True)
given_args=parser.parse_args()
目标主机=给定参数。目标主机
pinger=Pinger(目标主机=目标主机)
pinger.ping()完整代码
参考参考1
参考2
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。