Linux ICMP消息的产生与转换
ICMP在IP系统间传递差错和管理报文,是任何IP系统必须实现的组成部分。Linux 2.6.34中ICMP模块的实现在linux/icmp.h,net/icmp.h和ipv4/icmp.c中,导出了
icmp_err_convert数组和
icmp_send函数,供其它网络子系统使用。在其它网络子系统中,当检测到错误时,调用icmp_send产生并发送相应的ICMP差错消息到源主机;当源主机收到ICMP不可达差错消息,传递到原始套接字和传输层,而它们使用icmp_err_convert把对应的消息代码转换成套接字层比较容易理解的错误代码。在内核空间中可发送的ICMP消息包括查询应答和差错报文,下面总结了产生这两类消息的网络子系统(及函数)与错误转换。
应答消息
差错消息
错误转换
在这要注意,从ICMP_PORT_UNREACH到ECONNREFUSED的转换,不适用于TCP,原因已在 上节说明;而对于UDP的 未连接套接字,如果主机在线而端口没打开,调用sendto得不到ECONNREFUSED错误,但recvfrom会阻塞,这是因为虽然内核收到了ICMP差错,但没上报给应用进程。尽管如此,如果想得到ECONNREFUSED错误,那么可以写个ICMP守护进程,应用进程先把它的套接字描述符通过unix域套接口传递到ICMP守护进程,而守护进程使用raw socket来接收ICMP差错,再发给应用进程。
发送限速
不论一般差错消息还是重定向差错消息,发送限速针对的都是特定目标主机。
一般限速
在使用icmp_send发送差错消息(PMTU消息除外)时,为减少网络拥塞而限制了发送的速率,限速由xrlim_allow函数实现,定义在ipv4/icmp.c中。
1
#define
XRLIM_BURST_FACTOR
6
2
int
xrlim_allow(
struct
dst_entry
*
dst,
int
timeout)
3
{
4
unsigned long now, token = dst->rate_tokens;
5
int rc = 0;
6
7
now = jiffies;
8
token += now - dst->rate_last;
9
dst->rate_last = now;
10
if (token > XRLIM_BURST_FACTOR * timeout)
11
token = XRLIM_BURST_FACTOR * timeout;
12
if (token >= timeout)
{
13
token -= timeout;
14
rc = 1;
15
}
16
dst->rate_tokens = token;
17
return rc;
18
}

2

3



4

5

6

7

8

9

10

11

12



13

14

15

16

17

18

重定向限速
路由子系统使用ip_rt_send_redirect来发送重定向消息,定义在ipv4/route.c中,该函数内部调用icmp_send实现,在它的限速基础上,使用指数回退算法控制发送速率。
1
void
ip_rt_send_redirect
(
struct
sk_buff
*
skb)
2
{
3
struct rtable *rt = skb_rtable(skb);
4

2



3

4


5
6
/**//* No redirected packets during ip_rt_redirect_silence;
7
* reset the algorithm.
8
*/
9
if (time_after(jiffies, rt->u.dst.rate_last + ip_rt_redirect_silence))
10
rt->u.dst.rate_tokens = 0;
11
12
/**//* Too many ignored redirects; do not send anything
13
* set u.dst.rate_last to the last seen redirected packet.
14
*/
15
if (rt->u.dst.rate_tokens >= ip_rt_redirect_number)
{
16
rt->u.dst.rate_last = jiffies;
17
return;
18
}
19
20
/**//* Check for load limit; set rate_last to the latest sent
21
* redirect.
22
*/
23
if (rt->u.dst.rate_tokens == 0 || time_after(jiffies, (rt->u.dst.rate_last + (ip_rt_redirect_load << rt->u.dst.rate_tokens))))
{
24
icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway);
25
rt->u.dst.rate_last = jiffies;
26
++rt->u.dst.rate_tokens;
27

28
}
29
}

6


7

8

9

10

11

12


13

14

15



16

17

18

19

20


21

22

23


24

25

26

27


28

29
