本章主要说明Linux如何实现ICMP协议,针对每种ICMP消息类型,简要说明内核何时产生消息,以及当内核接收消息时又该如何处理。
ICMP报头:
前三个字段对所有icmp消息类型而言都相同:
类型和代码:这两个字段用于分辨出icmp消息类型。通常类型就足以区分,偶尔会需要代码进一步判断。
校验和:校验和包括icmp报头和有效载荷。所用算法和ip,udp,tcp一样。
其他字段取决于消息类型。
icmp消息分为两种:错误和查询。查询会使用报头的另外32位来定义两个字段:identifier(标识符)和sequence_number(序列号号码)
ICMP有效载荷:
所有ICMP错误消息在ICMP有效载荷中都包含相同的信息:触发ICMP消息传输的IP封包的IP报头,外加一部分IP有效载荷。所得IP封包不能超过576个字节。
上图a是触发icmp消息的报文。
ICMP常见类型:
ICMP_ECHO:用于测试远程主机是否可连接
ICMP_ECHOREPLY:当主机收到ICMP_ECHO时,会以ICMP_ECHOREPLY回复。
ICMP_SOURCE_QUENCH:已废弃。其功能现在由ECN(Early Congestion Notification 提前阻塞通知)机制替代。
ICMP_REDIRECT:当路由器检测到相邻主机正在使用次最佳路由时,会产生此类型。产生该消息的常见情况是入口封包必须从其所来的同一台设备上转发出去。此类型有下图所示的一些子类型:
ICMP_DEST_UNREACH:当ip封包无法到达目的,或者ip有效载荷无法传递到远程主机上的目的应用程序时,发送该类型的icmp报文给来源地。此类型有一些不同的子类型,如下图:
ICMP_TIME_EXCEEDED:当ip头的ttl变为0时,丢弃该包,并发送ICMP_TIME_EXCEEDED给来源地。下面是一些子类型:
ICMP_TIMESTAMP和ICMP_TIMESTAMPREPLY:ICMP_TIMESTAMP消息类型可用于要求远程主机提供时间戳,然后利用此时间戳使那些主机的时钟同步。
ICMP协议的应用程序:ping和traceroute
协议间彼此交互的范例:
IP协议:ip_local_deliver_finish函数会把入口ICMP消息传递给ICMP协议注册的接收函数icmp_rcv以及针对icmp协议注册的raw ip套接字(raw_v4_input)。传输请求会通过ip_append_data和ip_push_pending_frames函数交互给IP层
路由子系统:icmp消息通过icmp_reply和icmp_send传输的。这些函数会以ip_route_output_key函数查询路由表。
套接字层:当入口icmp消息携带错误指示时,套接字层会接收通知。(通过err_handler指针)
协议的初始化:
icmp协议无法编译成模块。初始化工作是建立一群套接字(每个cpu一个套接字),传输由内核所产生的icmp消息时会用到。这些套接字(类型位SOCK_RAW)及协议IPPROTO_ICMP不会插入到内核的套接字表中,因为入口icmp消息不应该以它为目的对象。
一些icmp结构:
struct icmphdr{
...
}//icmp头部
struct icmp_control{
...
}//icmp消息类型描述符
struct icmp_bxm{
...
}//该结构包含传输icmp消息所需的所有必要信息
以上这些结构的完整定义在net/ipv4/icmp.h中。
传输ICMP消息:
ICMP的两种消息错误和查询是由不同函数传输的:
icmp_send:当检测到特定情况时,,由内核使用,传输icmp错位信息。
icmp_reply:由icmp协议使用,回复需要做响应的入口ICMP请求消息。
这两个函数都会接收一个skb缓冲区作为输入。但是,icmp_send作为输入的缓冲区代表触发icmp消息传输的ip封包,而icmp_reply作为输入的缓冲区代表需要响应的入口icmp请求消息。
icmp_send函数:
该函数的参数介绍:
skb_in:和此错误相关的ip封包。
type
code:icmp报头中的类型和代码字段。
info:其他信息,如ICMP_REDIRECT消息所用的网关地址.
速率限制:
icmp消息在两个地方进行速率限制:
由路由程序:路由程序只对ICMP_DEST_UNREACH和ICMP_REDIRECT消息类型进行限制
由icmp程序:icmp程序可以对所有外出icmp消息类型进行速率限制。
上面两种速率限制差别是:路由程序是根据每个目的IP地址对icmp消息类型进行速率限制,而icmp程序根据每个源地址进行限制。也就是说,那些同时被icmp和路由程序限制的类型会被限制两次。
速率限制由icmpv4_xrlim_allow实现:
接收ICMP消息:
icmp_rcv函数由ip_local_deliver_finish所调用,来处理入口ICMP消息。
ICMP统计资料:
icmp协议的统计数据存储在icmp_mib数据结构内,内核按每个CPU存储统计数据。对每个统计数据而言,每个cpu都有两个实例,一个由在软件中断环境内执行的程序使用,另一个给不在软件中断的环境内执行的程序使用。
通过/proc文件系统调整:
icmp协议的所有调整参数可以在/proc/sys/net/ipv4下看到。
icmp_echo_ignore_broadcasts:当该标志设定时,传给广播地址的ICMP_ECHO消息会被忽略。
icmp_ignore_bogus_error_response:当该标志清掉时,带有广播目的IP地址的icmp错误消息会被忽略。
icmp_ratelimit:
icmp_ratemask:这两个变量由ICMP使用,以对外出icmp消息做速率限制。