加入收藏 | 设为首页 | 会员中心 | 我要投稿 PHP编程网 - 黄冈站长网 (http://www.0713zz.com/)- 数据应用、建站、人体识别、智能机器人、语音技术!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux内核分析 - 网络[十一]:ICMP模块

发布时间:2016-10-16 06:06:59 所属栏目:Linux 来源:站长网
导读:副标题#e# 内核版本:2.6.34 ICMP模块比较简单,要注意的是icmp的速率限制策略,向IP层传输数据ip_append_data()和 ip_push_pending_frames()。 在net/ipv4/af_inet.c中的inet_init()注册icmp协议,从这里也可以看出,ICMP模块是绑 定在IP模块之上的。inet_
副标题[/!--empirenews.page--]

内核版本:2.6.34

ICMP模块比较简单,要注意的是icmp的速率限制策略,向IP层传输数据ip_append_data()和 ip_push_pending_frames()。

在net/ipv4/af_inet.c中的inet_init()注册icmp协议,从这里也可以看出,ICMP模块是绑 定在IP模块之上的。inet_add_protocol()会将icmp_protocol加入到全局量inet_protos中。

if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)     
 printk(KERN_CRIT "inet_init: Cannot add ICMP protocoln");     
icmp_protocol定义如下:     
static const struct net_protocol icmp_protocol = {     
 .handler = icmp_rcv,     
 .no_policy = 1,     
 .netns_ok = 1,     
};

除了注册icmp协议,还要对icmp模块初始化,这部分由icmp_init()完成。

if (icmp_init() < 0)     
 panic("Failed to create the ICMP control socket.n");

icmp_init()函数做的事很简单,register_pernet_subsys(&icmp_sk_ops),而注册icmp网络子系统过程中会调用 icmp_sk_ops.init(即icmp_sk_init函数)来完成它的初始化,下面具体看icmp_sk_init()函数。

首先为net为配CPU数目 (nr_cpu_ids)个struct sock结构体空间,这里的net是全局的网络名,一般是init_inet。
net->ipv4.icmp_sk = kzalloc(nr_cpu_ids * sizeof(struct sock *), GFP_KERNEL);

每个CPU i,它的sock结构体位于net中的icmp_sk[i]。于每 个CPU i,初始化刚刚分配的icmp_sk[i]:

 -第一步,inet_ctl_sock_create()创建sk,并在net->ipv4.icmp_sk[i] = sk中将其赋值给icmp_sk[i]。

 -第二步:ICMP发送缓存区大小sk_sndbuf设置为128K

for_each_possible_cpu(i) { 

    
 struct sock *sk;     
 err = inet_ctl_sock_create(&sk, PF_INET,     
    SOCK_RAW, IPPROTO_ICMP, net);     
 if (err < 0)     
  goto fail;     
         
 net->ipv4.icmp_sk[i] = sk;     
 sk->sk_sndbuf =     
  (2 * ((64 * 1024) + sizeof(struct sk_buff)));     
 sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);     
 inet_sk(sk)->pmtudisc = IP_PMTUDISC_DONT;     
}

忽略发往广播地址的icmp echo报文;忽略发往广播地址的错误的响应报文;

net-

>ipv4.sysctl_icmp_echo_ignore_all = 0;     
net->ipv4.sysctl_icmp_echo_ignore_broadcasts = 1;     
net->ipv4.sysctl_icmp_ignore_bogus_error_responses = 1;

设置icmp处理速率,这里的ratelimit和ratemask参 数在后面限速处理时会具体用到。

net->ipv4.sysctl_icmp_ratelimit = 1 * HZ;     
net->ipv4.sysctl_icmp_ratemask = 0x1818;     
net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr = 0;

初始化工作完成后,还是从icmp的接收开始,icmp_rcv 完成icmp报文的处理。
取得icmp报头,此时skb->transport_header是在IP模块处理中的ip_local_deliver_finish() 将其设置为了指向icmp报头的位置。

icmph = icmp_hdr(skb);

根据icmp的类型type交由不同的处理函数去完成。

icmp_pointers[icmph->type].handler(skb);

icmp_pointers是在icmp.c中定义的全局量,部分如下:

static const struct icmp_control icmp_pointers[NR_ICMP_TYPES + 1] = {     
 [ICMP_ECHOREPLY] = {     
  .handler = icmp_discard,     
 },     
 [1] = {     
  .handler = icmp_discard,     
  .error = 1,     
 },     
……     
}

比如对于收到的icmp报文type为0或1(响应答复或目的不可达),协议栈要做的就是丢弃掉它 – icmp_discard()。下 面以icmp echo和icmp timestamp为例说明。

收到icmp echo报文执行icmp_echo()

icmp_param是回复时信息,它直接 拷贝了echo的ICMP报头icmp_hdr(skb),仅仅改变了报头的type = ICMP_ECHO_REPLY,然后调用icmp_reply()处理发送。

struct icmp_bxm icmp_param;     
icmp_param.data.icmph    = *icmp_hdr(skb);     
icmp_param.data.icmph.type = ICMP_ECHOREPLY;     
icmp_param.skb     = skb;     
icmp_param.offset    = 0;     
icmp_param.data_len    = skb->len;     
icmp_param.head_len    = sizeof(struct icmphdr);     
icmp_reply(&icmp_param, skb);

收到icmp timestamp报文后执行icmp_timestamp()

(编辑:PHP编程网 - 黄冈站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读