ICMP网络控制报文协议

ICMP报文主要用于在网络系统中传递控制信息,如网络不可达,回显(ping),时间请求,掩码请求等。ICMP报文被IP层或者更高层(TCP/UDP)使用,一些用户进程也可以监听ICMP报文.

ICMP报文格式

 

8位类型码(type),8位代码(code),type决定大的分类,code进一步细分ICMP报文种类,

协议规定,ICMP数据部分必须包含出错协议数据包的IP报头+8Byte. 这样是为了能准把icmp报文传递给上层协议和进程

ICMP报文种类

最常见的是:目的不可达, 回显应答,每种类型的ICMP报文,又可以根据code字段进一步细分。

ICMP报文处理

输入处理

在af_inet初始化时,会注册协议为IPPROTO_ICMP的处理函数

static const struct net_protocol icmp_protocol = {

         .handler =         icmp_rcv,

         .err_handler = icmp_err,

         .no_policy =      1,

         .netns_ok =      1,

};

IP层通过判断protocol字段,调用icmp_rcv处理icmp报文,具体细分到每种icmp消息,又会有不同的处理函数,所有支持的icmp消息都定义在数组icmp_pointers中

static const struct icmp_control icmp_pointers[NR_ICMP_TYPES + 1] = {

        

/*回显应答处理函数*/

[ICMP_ECHOREPLY] = {

                   .handler = ping_rcv,

         },

   /*1-2是虚构的类型 */

         [1] = {

                   .handler = icmp_discard,

                   .error = 1,

         },

         [2] = {

                   .handler = icmp_discard,

                   .error = 1,

         },

   /*目的不可达差错报文处理 */

         [ICMP_DEST_UNREACH] = {

                   .handler = icmp_unreach,

                   .error = 1,

         },

   /*源端关闭*/

         [ICMP_SOURCE_QUENCH] = {

                   .handler = icmp_unreach,

                   .error = 1,

         },

  /*重定向 */

         [ICMP_REDIRECT] = {

                   .handler = icmp_redirect,

                   .error = 1,

         },

         [6] = {

                   .handler = icmp_discard,

                   .error = 1,

         },

         [7] = {

                   .handler = icmp_discard,

                   .error = 1,

         },

    /*回显 */

         [ICMP_ECHO] = {

                   .handler = icmp_echo,

         },

         [9] = {

                   .handler = icmp_discard,

                   .error = 1,

         },

         [10] = {

                   .handler = icmp_discard,

                   .error = 1,

         },

         [ICMP_TIME_EXCEEDED] = {

                   .handler = icmp_unreach,

                   .error = 1,

         },

         [ICMP_PARAMETERPROB] = {

                   .handler = icmp_unreach,

                   .error = 1,

         },

         [ICMP_TIMESTAMP] = {

                   .handler = icmp_timestamp,

         },

         [ICMP_TIMESTAMPREPLY] = {

                   .handler = icmp_discard,

         },

         [ICMP_INFO_REQUEST] = {

                   .handler = icmp_discard,

         },

         [ICMP_INFO_REPLY] = {

                   .handler = icmp_discard,

         },

    /*地址掩码 */

         [ICMP_ADDRESS] = {

                   .handler = icmp_discard,

         },

         [ICMP_ADDRESSREPLY] = {

                   .handler = icmp_discard,

         },

};

这里以目的不可达来分析

static void icmp_unreach(struct sk_buff *skb)

{

         const struct iphdr *iph;

         struct icmphdr *icmph;

         struct net *net;

         u32 info = 0;

 

         net = dev_net(skb_dst(skb)->dev);

   /*获取icmp报头 */

         icmph = icmp_hdr(skb);

         iph   = (const struct iphdr *)skb->data;

 

         if (icmph->type == ICMP_DEST_UNREACH) {

                   switch (icmph->code & 15) {

      /*这几种类型的ICMP不需要进行处理 */

                   case ICMP_NET_UNREACH:

                   case ICMP_HOST_UNREACH:

                   case ICMP_PROT_UNREACH:

                   case ICMP_PORT_UNREACH:

                            break;

                   case ICMP_FRAG_NEEDED:/*需要分片 */

                            if (ipv4_config.no_pmtu_disc) {

                                     LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"),

                                                      &iph->daddr);

                            } else {

                                     info = ntohs(icmph->un.frag.mtu);

                            }

                            break;

                   case ICMP_SR_FAILED:

                            LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: Source Route Failed\n"),

                                            &iph->daddr);

                            break;

                   default:

                            break;

                   }

                   if (icmph->code > NR_ICMP_UNREACH)

                            goto out;

         } else if (icmph->type == ICMP_PARAMETERPROB)/*参数出错 */

                   info = ntohl(icmph->un.gateway) >> 24;

 

        

 

         if (!net->ipv4.sysctl_icmp_ignore_bogus_error_responses &&

             inet_addr_type(net, iph->daddr) == RTN_BROADCAST) {

                   net_warn_ratelimited("%pI4 sent an invalid ICMP type %u, code %u error to a broadcast: %pI4 on %s\n",

                                          &ip_hdr(skb)->saddr,

                                          icmph->type, icmph->code,

                                          &iph->daddr, skb->dev->name);

                   goto out;

         }

    /* 进一步处理数据包*/

         icmp_socket_deliver(skb, info);

 

out:

         return;

out_err:

         ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);

         goto out;

}

 

static void icmp_socket_deliver(struct sk_buff *skb, u32 info)

{

         const struct iphdr *iph = (const struct iphdr *) skb->data;

         const struct net_protocol *ipprot;

         int protocol = iph->protocol;

  

   /*把icmp数据传递给ram socket,因为raw socket可能实现上层协议 */

         raw_icmp_error(skb, protocol, info);

         rcu_read_lock();

         ipprot = rcu_dereference(inet_protos[protocol]);

   /*传递给上层协议如:udp和tcp */

         if (ipprot && ipprot->err_handler)

                   ipprot->err_handler(skb, info);

         rcu_read_unlock();

}

输出处理

icmp的输出由函数icmp_send函数处理,但网络数据包处理出错时,由上层协议调用,

void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)

传递参数有type和code信息,处理完后,调用icmp_route_lookup把数据包发送到对端网络设备

你可能感兴趣的:(网络协议栈)