第68 章 ICMP
icmp(inter control message protocol,互联网控制报文协议)是一种用于在ipv4网络中发送控制消息的协议。icmp消息通常用于错误报告和诊断功能,帮助网络设备(如路由器和主机)报告错误情况并诊断网络问题。
icmp基于ip协议,但在数据报中使用不同的协议号(1)。icmp消息可以分为两类:
1 错误报告消息:这些消息用于报告错误情况。例如:
- 目的不可达(destination unreachable):当数据包无法到达目的地时,路由器会发送此消息,报告原因,如网络不可达、主机不可达、端口不可达等。
- 源抑制(source quench):当路由器因为拥塞而无法处理更多数据包时,它可以发送此消息给发送方,请求发送方减缓发送速度。
- 重定向(redirect):当路由器发现发送方可以通过其他路由器到达目的地时,它可以发送此消息,告知发送方使用更优路径。
2 信息请求与应答消息:这些消息用于诊断网络问题。例如:
- 回送请求(echo request):发送此消息以请求接收方回复一个回送应答(echo reply)消息。这通常用于测试网络连通性和延迟。
- 时间戳请求(timestamp request):发送此消息以请求接收方回复一个时间戳应答(timestamp reply)消息。这通常用于测量网络延迟和路径。
icmp协议对于网络管理和故障排查具有重要意义。然而,它也可能被滥用,如用于网络扫描和拒绝服务攻击(dos)等。因此,在某些情况下,防火墙可能会限制或过滤icmp消息。
通过使用icmp,网络设备可以检测并报告连接问题、网络拥塞、路由选择错误等问题,从而有助于网络管理和故障排查。
以下是icmp的主要作用:
1 错误报告:icmp可以用于报告数据包传输过程中的错误。当数据包无法到达目的地时,路由器会发送icmp错误消息通知发送方,说明无法交付数据包的原因,如网络不可达、主机不可达、端口不可达等。这有助于诊断网络问题并优化路由选择。
2 信息请求与应答:icmp可以用于诊断网络问题。例如,通过发送回送请求(echo request)消息,可以测试网络连通性;通过发送时间戳请求(timestamp request)消息,可以测量网络延迟和路径。
3 拥塞控制:icmp可以用于控制网络拥塞。当路由器因为拥塞而无法处理更多数据包时,它可以发送源抑制(source quench)消息给发送方,请求发送方减缓发送速度。
4 路径优化:icmp可以用于优化路由选择。当路由器发现发送方可以通过其他路由器到达目的地时,它可以发送重定向(redirect)消息,告知发送方使用更优路径。
以下是一个关于icmp的简单示例。假设有两台主机a和b,它们之间通过一个路由器r进行通信。
1 网络连通性测试:主机a想要测试与主机b之间的网络连通性。为此,它向主机b发送一个icmp回送请求(echo request)消息。
2 主机b回应:当主机b收到icmp回送请求消息时,它会回应一个icmp回送应答(echo reply)消息。
3 主机a收到回应:主机a收到主机b的回送应答消息后,可以确定与主机b之间的网络连接正常。主机a可以根据来回时间计算延迟,从而了解网络性能。
这个示例展示了icmp在诊断网络问题中的作用。通过发送和接收icmp消息,主机a可以测试与主机b之间的网络连通性,以及测量网络延迟。
以下是一个使用python的`socket`库发送和接收icmp回送请求/应答(echo request/reply)消息的示例。在这个例子中,我们将使用`socket`库的`af_i`地址族和`sock_raw`套接字类型来创建一个原始套接字,以便直接发送和接收icmp数据包。
```python
import socket
import struct
import time
构建icmp头部
def create_icmp_header(id=1, seq=1):
header = structpack(&39;bbhhh&39;, 8, 0, id, seq, 0)
return header
发送icmp回送请求
def send_echo_request(sock, target_ip):
target_addr = socketgethostbyname(target_ip)
icmp_header = create_icmp_header()
socksendto(icmp_header, (target_addr, 1))
接收icmp回送应答
def receive_echo_reply(sock):
recv_packet = sockrecvfrom(4096)
icmp_header, addr = recv_packet
解析icmp头部
type, code, checksum, id, seq = structunpack(&39;bbhhh&39;, icmp_header)
return addr, id, seq
主程序
def main():
icmp_socket = socketsocket(socketaf_i, socketsock_raw, socketgetprotobyname(&39;icmp&39;))
target_ip = input(&34;enter the target ip address: &34;)
send_echo_request(icmp_socket, target_ip)
print(&34;waiting for reply&34;)
addr, id, seq = receive_echo_reply(icmp_socket)
print(f&34;reply received from {addr} with id: {id}, sequence: {seq}&34;)
icmp_socketclose()
if __name__ == &39;__main__&39;:
main()
```
这个示例首先创建了一个原始套接字,用于发送和接收icmp数据包。然后,它向目标ip地址发送一个icmp回送请求,并等待回送应答。接收到应答后,它会打印出应答数据包的源地址、id和序列号。
需要注意的是,这个示例仅适用于具有root权限或具有cap__raw能力的用户。此外,在实际网络环境中,由于防火墙和网络地址转换(nat)等机制的影响,这个示例可能无法正常工作。在实际应用中,您可能需要使用专门的网络工具(如`ping`)或库(如`scapy`)来实现类似的功能。