Linux icmp 学习笔记 之二 icmp数据处理流程

在剖析icmp数据包处置工序预先阻止,我有以下几个问题:

1、为什么只为每个CPU创办一点钟套接字来发送ICMP音讯?,以防可以在不运用套接字的形势下发送ICMP音讯?

2、平的道德标准是什么?

3、TraceouTE的规律是什么?

一、IMCP协定的设定初值

1)的ICMP收执处置有或起作用设定初值

我们的了解ICMP协定是一点钟附加到IP层的3层协定。,3层协定是在IP数据做切片中回忆ICMP数据的协定。。和TCP、UDP亦TCP、UDP数据的4层协定回忆在IP数据包的数据做切片。。

怨恨ICMP和TCP等协定不属于相同的广泛分布LA。,但这都是在3层IP协定随后。,这将给ICMP、TCP的处置效能。因而在Linux,都是召集inet_add_protocol将其收执处置有或起作用中间定位的数据作曲添加到装饰inet_protos中去的(在起作用的三、四层收执在起作用的数据处置完全符合的知。

       的收执处置有或起作用ICMP的作曲的限界如次:

static const struct net_protocol icmp_protocol = {

    .handler =       icmp_rcv,

    .no_policy =   1,

    .netns_ok =     1,

};

当inet_init设定初值,更确切地说,iNETAdAdx协定称为TCP协定。、udp、icmp、与IGMP和静止PR中间定位的收执处置作曲的对齐,并保在装饰中。

当收执到的数据包的协定是ICMP时,更确切地说,将召集ICMPYRCV举行后续处置。。

2)  ICMP协定模块的设定初值

首要是召集有或起作用register_pernet_subsys(在起作用的该有或起作用的任务工序,请看。,将ICMP协定模块完全符合到广泛分布命令空白的,叫OPS -> init设定初值与协定中间定位的密码。。

向ICMP,其pernet_operations的限界如次:

static structpernet_operations __net_initdata icmp_sk_ops = {

       .init = icmp_sk_init,

       .exit = icmp_sk_exit,

};

在召集register_pernet_subsys将icmp协定模块完全符合到广泛分布命名空白的后,它将召集ICMPTHSKIN设定初值ICMP协定。,我们的剖析了ICMPSkySKIT。

该有或起作用首要造成以下效能:

/*

1、  为每一点钟cpu创办一点钟用于产生icmp数据包的socket

2、  设置必然的限度局限,包孕速率约束、收执数据包必须先具备的等。

*/

static int __net_init icmp_sk_init(作曲) net 广泛分布)

{

    int i, err;

依从的ICMPSK的空白的,主宰与CPU中间定位的套接字影响都回忆在ICMPSK SK装饰中。

    net-> =

           kzalloc(nr_cpu_ids *sizeof(作曲) sock *), GFP_KERNEL);

    if (网) 空)

           return -ENOMEM;

    为每个CPU创办一点钟原始套接字

    for_each_possible_cpu(i) {

           struct sock *sk;

           /*

创办一点钟 RAW 插座典型,召集(*SK)-> SKYPROT–>散列,将该socket从hash链表[RAW_HTABLE_SIZE]中剪下与该socket的关系

*/

           err =inet_ctl_sock_create(&sk, PF_INET,

                                   SOCK_RAW, IPPROTO_ICMP, 广泛分布)

           if (背面的) < 0)

                  goto fail;

           net->[i]= sk;

           /* Enough space FR2 64K ICMP packets, including

            * sk_buff struct 经费。

            */

           sk->sk_sndbuf =

                  (2 * ((64 *1024) + sizeof(作曲) sk_buff)));

           /*

            * Speedup sock_wfree()

            */

           sock_set_flag(SK),SOCK_USE_WRITE_QUEUE);

           inet_sk(SK))->pmtudisc= IP_PMTUDISC_DONT;

    }

    net->= 0;

    反照播送的索取 */

    net->= 1;

    /* 疏忽播送ICMP 背面的的恢复物

    net->ipv4.sysctl_icmp_ignore_bogus_error_responses= 1;

    net->= 1 * HZ; 限速值

/*举行速率限度局限的icmp数据包典型,最首要的是 unreachable 、source quench time exceeded 、parameter problem*/

    net->= 0x1818;

    net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr= 0;

    return 0;

fail:

    for_each_possible_cpu(i)

           inet_ctl_sock_destroy(网)[i]);

    kfree(网));

    return err;

}

怀疑:当新创办的套接字,为什么要将其从hash链表[RAW_HTABLE_SIZE]中剪下呢?

因我们的只运用因此套接字发送数据包。,用不着运用因此套接字收执数据包。。因而把它从哈希列表中剪下。。

为什么不立即的运用套接字收执ICMP音讯呢?,我的默认是,以防您运用套接字收执音讯,你需求在内核创办一点钟内核穿成串,用于侦听以防有数据抵达套接字。,此后改装一遍。

并立即的运用内核四层协定举行完全符合,处置收执到的ICMP音讯恰好是出恭。,和更少的内核资源的使用,因而为内核创办了套接字,收执处理或负责大致是采取四内核层造成。涂层创办的与ICMP中间定位的套接字将不家具。

二、ICMP协定的收执处置效能

ICMP收执处置效能是ICMPY-RCV,因此有或起作用的以下剖析。

首要效能:

1、  反省数据包的有理性

2、  如ICMP典型,

int icmp_rcv(作曲) sk_buff SKB)

{

       structicmphdr *icmph;

       structrtable *rt = skb_rtable(SKB)

       structnet *net = dev_net(RT);

       /*

       鉴于高扩展性的广泛分布安全体系作曲的保险单,胸部核作曲浊度

     在这里心不在焉剖析,成穹状弯曲。

       */

       if(!xfrm4_policy_check(NULL, XFRM_POLICY_IN, SKB) {

              structsec_path *sp = skb_sec_path(SKB)

              intnh;

              if(!(SP) && sp->xvec[sp->len – 1]->props.flags &

                             XFRM_STATE_ICMP))

                     gotodrop;

              if(!pskb_may_pull(SK)b, sizeof(*icmph) + sizeof(作曲) iphdr)))

                     gotodrop;

              nh= skb_network_offset(SKB)

              skb_set_network_header(SK)b,sizeof(*icmph));

              if(!xfrm4_policy_check_reverse(NULL, XFRM_POLICY_IN, SKB)

                     gotodrop;

              skb_set_network_header(SK)b,NH)

       }

       ICMP_INC_STATS_BH(net,ICMP_MIB_INMSGS);

       核实和物

       switch(SK)b->ip_summed) {

       caseCHECKSUM_COMPLETE:

              if(!csum_fold(SK)b->csum))

                     break;

              /*fall through */

       caseCHECKSUM_NONE:

              skb->csum= 0;

              if(__skb_checksum_complete(SKB)

                     gotoerror;

       }

       if(!pskb_pull(SK)b, sizeof(*icmph)))

              gotoerror;

       收到ICMP头

       icmph= icmp_hdr(SKB)

       ICMPMSGIN_INC_STATS_BH(net,icmph->type);

       /*

       向不供养的ICMP音讯,立即的丢掉

        */

       if(icmph->type > NR_ICMP_TYPES)

              gotoerror;

       /*

       断定以防打出的牌掉多播典型的icmp数据包

       1、只处置回响、timestamp、address_mask_request、address_mask_reply典型的多播icmp数据包

        */

       if(RTrt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) {

              /*

               *    RFC1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be

               *      silently ignored (我们的 let user decide with asysctl).

               *    RFC1122: 3.2.2.8 An ICMP_TIMESTAMP MAY be silently

               *      discarded if to broadcast/multicast.

               */

              if((icmph->type == ICMP_ECHO ||

                   icmph->type == ICMP_TIMESTAMP)&&

                 net->) {

                     gotoerror;

              }

              if(icmph->type != ICMP_ECHO &&

                  icmph->type != ICMP_TIMESTAMP &&

                  icmph->type != ICMP_ADDRESS &&

                  icmph->type != ICMP_ADDRESSREPLY) {

                     gotoerror;

              }

       }

       /*如icmp数据包典型,召集有重大意义的的处置有或起作用

       icmp_pointers[icmph->type].handler(SKB)

drop:

       kfree_skb(SKB)

       return0;

error:

       ICMP_INC_STATS_BH(net,ICMP_MIB_INERRORS);

       gotodrop;

}

向ICMP_pointers的限界如次:

/*

 *    This table is the definition of how wehandle ICMP。

 */

static const struct icmp_controlicmp_pointers[NR_ICMP_TYPES + 1] = {

       [ICMP_ECHOREPLY]= {

              .handler= icmp_discard,

       },

       [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_address,

       },

       [ICMP_ADDRESSREPLY]= {

              .handler= icmp_address_reply,

       },

};

流行的由内核处置的ICMP音讯具有ICMP-UnReA。、icmp_address、icmp_address_reply、icmp_timestamp、icmp_echo、icmp_redirect。

icmp_echo

/*

该效能首要是将ICMP的典型设置为ICMP-ECHECURPLY。,叫icmp_reply发送的包了

*/

static void icmp_echo(作曲) sk_buff SKB)

{

       structnet *net;

       net= dev_net(SK)b_dst(SK)b)->dev);

       if(!net->) {

              structicmp_bxm icmp_param;

                 =*icmp_hdr(SKB)

              .type= ICMP_ECHOREPLY;

                            = skb;

                   = 0;

                      =skb->len;

                     =sizeof(作曲) icmphdr);

              icmp_reply(&icmp_param,SKB)

       }

}

Timestamp

/*

设置工夫戳值,并将ICMP的典型设置为ICMPTyTimePracpPyPress,经过ICMPL应对发送

*/

static void icmp_timestamp(作曲) sk_buffSKB)

{

       structtimespec tv;

       structicmp_bxm icmp_param;

       /*

        *    Tooshort.

        */

       以防(SK)b->len < 4)

              gotoout_err;

       /*

        *    弗林 the current time as ms since midnight UT:

        */

       getnstimeofday(&tv);

       [1]= htonl((tv.tv_sec % 86400) * MSEC_PER_SEC +

                                    tv.tv_nsec / NSEC_PER_MSEC);

       [2]= [1];

       (SKB*Copype位(SKB), 0, &[0], 4))

              BUG();

          =*icmp_hdr(SKB)

       .type= ICMP_TIMESTAMPREPLY;

       .code= 0;

                     = skb;

            = 0;

              =0;

              =sizeof(作曲) icmphdr) + 12;

       icmp_reply(&icmp_param,SKB)

out:

       return;

out_err:

       ICMP_INC_STATS_BH(dev_net(SK)b_dst(SK)b)->dev),ICMP_MIB_INERRORS);

       gotoout;

}

Unreach 数据处置

效能:如ICMP无效装载数据的胜任,召集播送层的背面的处置有或起作用来处置

static void icmp_unreach(作曲) sk_buffSKB)

{

       structiphdr *iph;

       structicmphdr *icmph;

       inthash, protocol;

       conststruct net_protocol *ipprot;

       u32info = 0;

       structnet *net;

       net= dev_net(SK)b_dst(SK)b)->dev);

       /*

        *    Incompleteheader ?

        *   Onlychecks for the IP header, there should be an

        *    additionalcheck for longer headers in upper 程度。

        */

       if(!pskb_may_pull(SK)b, sizeof(作曲) iphdr)))

              gotoout_err;

       率先如愿以偿ICMP

       icmph= icmp_hdr(SKB)

       iph   = (作曲) iphdr *)skb->data;

       断定IP一号做切片完整的

       if(iph->ihl < 5) /* Mangled header, drop. */

              gotoout_err;

       只处置典型为3或12的数据包

       1、当典型为3时,就是密码是FRAG 需求的物

           a)当零碎不供养PMTU,报废要旨

           B)当零碎供养PMTU,召集IPHRTHFrack需求修正PMTU的值

       2、当典型12,经过ICMPH如愿以偿背面的偏移值(相向字组分类)。

       */

       if(icmph->type == ICMP_DEST_UNREACH) {

              switch(icmph->code & 15) {

              caseICMP_NET_UNREACH:

              caseICMP_HOST_UNREACH:

              caseICMP_PROT_UNREACH:

              caseICMP_PORT_UNREACH:

                     break;

              caseICMP_FRAG_NEEDED:

                     if() {

                            LIMIT_NETDEBUG(KERN_INFO”ICMP: %pI4: fragmentation needed and DF set.\n”,

                                          &iph->daddr);

                     }else {

                            info= ip_rt_frag_needed(net, iph,

                                                  ntohs(icmph->),

                                                  skb->dev);

                            if(!物)

                                   gotoout;

                     }

                     break;

              caseICMP_SR_FAILED:

                     LIMIT_NETDEBUG(KERN_INFO”ICMP: %pI4: Source Route Failed.\n”,

                                   &iph->daddr);

                     break;

              default:

                     break;

              }

              if(icmph->code > NR_ICMP_UNREACH)

                     gotoout;

       }else if (icmph->type == ICMP_PARAMETERPROB)

              info= ntohl(icmph->) >> 24;

       /*

        *    Throwit at our lower layers

        *

        *    RFC1122: 3.2.2 MUST extract the protocol ID from the passed

        *             头。

        *    RFC1122: 3.2.2.1 MUST pass ICMP unreach messages to the

        *             transport 层。

        *    RFC1122: 3.2.2.2 MUST pass ICMP time expired messages to

        *             transport 层。

        */

       /*

        *    反省 other end isnt violating RFC 1122. Some routers send

        *    bogusresponses to broadcast 帧。 If you see this message

        *    firstcheck your netmask matches at both ends, if it does then

        *    如愿以偿 other vendor to fix their kit.

        */

   /*

       向挥向地址是播送的icmp数据包,当你需求疏忽它,标记背面的

       疏忽数据包

       */

       if(!net->ipv4.sysctl_icmp_ignore_bogus_error_responses &&

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

              if(net_ratelimit())

                     printk(KERN_WARNING”%pI4 sent an invalid ICMP “

                                       “type %u, code %u “

                                       “error to a broadcast: %pI4 on%s\n”,

                            &ip_hdr(SK)b)->saddr,

                            icmph->type, icmph->code,

                            &iph->daddr,

                            skb->dev->name);

              gotoout;

       }

       /*

       检测icmp要旨中无效装载做切片内容音长以防大于胜任ip头部物扩大8八位位组

       发送ICMP背面的音讯时,ICMP数据做切片的值被设置为IP姓名牌物。 IP无效装载的前8个八位位组,

       可以断定TrSPO的涂数据播送背面的。

        */

       if(!pskb_may_pull(SK)b, iph->ihl * 4 + 8))

              gotoout;

       /*

       此刻IPH,是ICMP无效装载说得中肯IP姓名牌物,在icmp_rcv,SKB ->数据已对准ICMP音讯。

       无效装载做切片。

       */

       iph= (作曲) iphdr *)skb->data;

       获取播送层协定值

       protocol= iph->protocol;

       /*

       一号次召集RAWYICMP背面的,将背面的音讯发送到趣味源 sockte

        */

       raw_icmp_error(SK)b,protocol, 物);

       如协定值,找到一点钟合格的四级收执处置哈希装饰,

       召集其背面的处置有或起作用举行后续处置

       hash= protocol & (MAX_INET_PROTOS – 1);

       rcu_read_lock();

       ipprot= rcu_dereference(inet_protos[hash]);

       if(ipprot && ipprot->err_handler)

              ipprot->err_handler(SK)b,物);

       rcu_read_unlock();

out:

       return;

out_err:

       ICMP_INC_STATS_BH(net,ICMP_MIB_INERRORS);

       gotoout;

}

重定位处置:

效能:如ICMP中数据做切片的值,召集ip_rt_redirect,后续快速地流动(不熟悉路由重定位密码),对路由分叉的下一步剖析举行了朝外的剖析。。

static void icmp_redirect(作曲) sk_buffSKB)

{

       structiphdr *iph;

       包使合法化

       以防(SK)b->len < sizeof(作曲) iphdr))

              gotoout_err;

       if(!pskb_may_pull(SK)b, sizeof(作曲) iphdr)))

              gotoout;

       获取IP姓名牌物经过ICMP数据做切片举行

       iph= (作曲) iphdr *)skb->data;

       密码是 、ICMP_REDIR_NETTOS 、ICMP_REDIR_HOST 、ICMP_REDIR_HOSTTOS ,召集ip_rt_redirect 路由重定位处置

       结束电流(ICMPH-HDR(SKB)->密码 & 7) {

       caseICMP_REDIR_NET:

       caseICMP_REDIR_NETTOS:

              /*

               * As per RFC recommendations now handle it asa host 重定位。

               */

       caseICMP_REDIR_HOST:

       caseICMP_REDIR_HOSTTOS:

              ip_rt_redirect(ip_hdr(SK)b)->saddr,iph->daddr,

                            icmp_hdr(SK)b)->,

                            iph->saddr, skb->dev);

              break;

       }

out:

       return;

out_err:

       ICMP_INC_STATS_BH(dev_net(SK)b->dev),ICMP_MIB_INERRORS);

       gotoout;

}

Icmp_reply

在前面绍介ICMP 发射应答ICMP 工夫戳的应答工夫,这些有或起作用都由ICMPX应对召集来发送数据。,在这里是因此有或起作用的剖析

效能:

1、查找路由,查找终成泡影,立即的来回;显示证据二踩成

2、枯萎:使枯萎限度局限的ICPv4xxRimLim容许呼叫速率限度局限效能,容许发送时,使生效第三个踩,别的来回

3、召集ICMPthPuxy回答发送数据

/*

 *    Driving logic for building and sending ICMPmessages.

 */

static void icmp_reply(作曲) icmp_bxm*icmp_param, struct sk_buff SKB)

{

       structipcm_cookie ipc;

       structrtable *rt = skb_rtable(SKB)

       structnet *net = dev_net(RT);

       structsock *sk;

       structinet_sock *inet;

       __be32daddr;

       if(ip_options_echo(&icmp_param->replyopts, SKB)

              return;

       sk= icmp_xmit_lock(广泛分布)

       以防(SK) == 空)

              return;

       inet= inet_sk(SK));

       icmp_param->data.icmph.checksum= 0;

       inet->tos= IPH-HDR(SKB)-TOS

       daddr= ipc.addr = rt->rt_src;

       ipc.opt= NULL;

       ipc.shtx.flags= 0;

       if(icmp_param->) {

              ipc.opt= &icmp_param->replyopts;

              if()

                     daddr= icmp_param->

       }

       {

              structflowi fl = { .nl_u = { .ip4_u =

                                         { .daddr = daddr,

                                          .saddr= rt->rt_spec_dst,

                                          .tos= RT_TOS(ip_hdr(SK)b)->tos) } },

                                .proto = IPPROTO_ICMP };

              security_skb_classify_flow(SK)b,FL)

              以防(Ipou-RouthyOutUpPosikKEY(net), &rt, FL)

                     gotoout_unlock;

       }

       以防(ICMPv4xxRimLim容许(net), rt, icmp_param->,

                            icmp_param->))

              icmp_push_reply(icmp_param,&ipc, RT)

       ip_rt_put(RT);

out_unlock:

       icmp_xmit_unlock(SK));

}

a)速率把持效能ICMPv4xxRimLim容许

速率把持效能,首要是由两个有或起作用完整的icmpv4_xrlim_allow、xrlim_allow。

xrlim_allow是一点钟真正的限速运转;ICMPv4XXRIMLIAL是来回成的首要器,它是为了用不着枯萎:使枯萎限度局限的数据。,更确切地说,来回容许经过

效能:决定以防有工夫片用于数据播送,因此有或起作用的工序构成复杂。。

int xrlim_allow(作曲) dst_entry *dst, int超出的时间)

{

       unsignedlong now, token = dst->rate_tokens;

       intrc = 0;

       now= jiffies;

       token+= now – dst->rate_last;

       dst->rate_last= now;

       以防(预兆) > XRLIM_BURST_FACTOR * 超出的时间)

              token= XRLIM_BURST_FACTOR * timeout;

       以防(预兆) >= 超出的时间) {

              token-= timeout;

              rc= 1;

       }

       dst->rate_tokens= token;

       returnrc;

}

效能:决定数据以防被容许发送的数据

1、  向不供养的ICMP type典型,容许发送来回

2、  典型典型为ICMP-TestBuffunDelphi 密码是一点钟包icmp_frag_needed,容许发送

3、  因目的配件的巡回立基于,容许发送来回

4、  向静止典型的ICMP音讯,有重大意义的的包就是1将被限度局限。,向静止典型的数据包,立即的容许发送来回(即不限速)

static inline int icmpv4_xrlim_allow(作曲)net *net, struct rtable *rt,

              inttype, int 密码)

{

       structdst_entry *dst = &rt->

       intrc = 1;

       以防(典型) > NR_ICMP_TYPES)

              gotoout;

       /*Don”t limit PMTU 显示证据。 */

       以防(典型) == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)

              gotoout;

       /*No rate limit on loopback */

       if(dst->dev && (dst->dev->flags&IFF_LOOPBACK))

              gotoout;

       /*Limit if icmp type is enabled in ratemask. */

       以防(1) << type) & net->)

              rc= xrlim_allow(dst, net->);

out:

       returnrc;

}

b)数据播送

效能:

1、  召集ip_append_data,缓存数据

2、  召集IPH-FuluSupIdgIn帧立即的发送数据。

static void icmp_push_reply(作曲) icmp_bxm*icmp_param,

                         struct ipcm_cookie *ipc, struct rtable**RT)

{

       structsock *sk;

       structsk_buff *skb;

       推进插座

       sk= icmp_sk(dev_net((*RT)->));

       /*召集ip_append_data,缓存要发送到SK-> SKWRead Engy队列的数据

       并召集IP-PASS,发送数据

       Ip- AppEnthDI数据(SK), icmp_glue_bits, icmp_param,

                       icmp_param->data_len+icmp_param->head_len,

                        icmp_param->head_len,

                        ipc, rt, MSG_DONTWAIT) < 0)

              ip_flush_pending_frames(SK));

       elseif (SKB) = skb_peek(&sk->sk_write_queue)) != 空) {

              structicmphdr *icmph = icmp_hdr(SKB)

              __wsumcsum = 0;

              structsk_buff *skb1;

              skb_queue_walk(&sk->sk_write_queue,skb1) {

                     csum= csum_add(csum, skb1->csum);

              }

              csum= csum_partial_copy_nocheck((void *)&icmp_param->data,

                                          (炭) *)icmph,

                                           icmp_param->head_len, csum);

              icmph->checksum= csum_fold(csum);

              skb->ip_summed= CHECKSUM_NONE;

              ip_push_pending_frames(SK));

       }

}

icmp_send有或起作用

如从进入权和出口数据包终成泡影的处理或负责,下层协定将召集ICMPySead发送数据。,因此有或起作用的以下剖析

效能:发送ICMP error 数据包
无法发送ICMP error 数据包必须先具备的
1、传入的数据包,它们是多播数据包(计算机硬件或IP地址,不要发送ICMP error 数据包
2、参加比赛的人数据包有段,仅用于一号段的输出数据包,发送ICMP 背面的的数据包
3、进入权包自身是ICMP error典型的,不要发送ICMP数据包的进入权 error 

以防输出数据包使失望前述的必须先具备的,你需求发送一点钟ICMP的数据包 背面的典型数据
1、查找路由
2、路由查找成时,此后,将召集ICMPthPuxy回答。 发送数据

void icmp_send(作曲) sk_buff *skb_in, int type, int code, __be32 物)
{
struct iphdr *iph;
int room;
struct icmp_bxm icmp_param;
struct rtable *rt = skb_rtable(SK)b_in);
struct ipcm_cookie ipc;
__be32 saddr;
u8  tos;
struct net *net;
struct sock *sk;

if (!RT)
goto out;
net = dev_net(RT);

/*
*Find the original 头。 It is expected to be valid, of 快跑。
*Check this, icmp_send is called from the most obscure devices
*间或。
*/
iph = ip_hdr(SK)b_in);
/*
1、对用大锤打犬举行有理的反省,担保ipheader在sk_buff->head与sk_buff->tail暗中的范围内
*/
if ((u8 *)iph < skb_in->head ||
   (SK)b_in->network_header + sizeof(*iph)) > skb_in->tail)
goto out;

/*
*No replies to physical multicast/broadcast
*/
数据链路层的地址以防决定传入的P。,以防你停止
if (SK)b_in->pkt_type != PACKET_HOST)
goto out;

/*
*Now check at the protocol level
*/
/*
1、反省输出数据包以防播送、组播数据
*/
if (RTrt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
goto out;

/*
*Only reply to fragment 0. We byte re-order the constant
*mask for 赢利性。
*/
/*
1、向IP部门数据,仅将ICMP背面的物发送到一号部门字组分类
*/
if (iph->frag_off & htons(IP_OFFSET))
goto out;

/*
*If we send an ICMP error to an ICMP error a mess would 成果。
*/
/*
1、决定收执到的数据包以防是ICMP 背面的物包,以防ICMP背面的音讯不回包
*/
if (ICMP*影响[典型]背面的) {
/*
*We are an error, check if we are replying to an
*ICMP error
*/
if (iph->protocol == IPPROTO_ICMP) {
u8 _inner_type, *itp;

itp = skb_header_pointer(SK)b_in,
skb_network_header(SK)b_in) +
(iph->ihl << 2) +
offsetof(作曲) icmphdr,
典型) –
skb_in->data,
sizeof(_inner_type),
&_inner_type);
if (ITP) == 空)
goto out;

/*
*Assume any unknown ICMP type is an 背面的。 This
*isn”t specified by the RFC, but think about it..
*/
if (*itp > NR_ICMP_TYPES ||
  ICMP*影响[*ITP]背面的
goto out;
}
}

        结束软停止,并为套接字添加旋转锁,确保就是一点钟ICMP音讯被发送出去的同时。
sk = icmp_xmit_lock(广泛分布)
if (SK) == 空)
return;

/*
*Construct source address and 得到或获准进行选择。
*/

       /*
1、向挥向地址的当地的进入权数据包,当地的地址用作ICMP一组建议的源IP地址。
2、的挥向地址的数据包不回进入权,则如sysctl_icmp_errors_use_inbound_ifaddr的值来设置源ip地址
*/
saddr = iph->daddr;
if (!(RTrt_flags & RTCF_LOCAL)) {
struct net_device *dev = NULL;

rcu_read_lock();
if (RT &&
net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr)
dev = dev_get_by_index_rcu(net, rt->);

if (DEV)
saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK);
else
saddr = 0;
rcu_read_unlock();
}

      设置TOS值
tos = ICMP*影响[典型]背面的 ? ((iph->tos & IPTOS_TOS_MASK) |
  IPTOS_PREC_INTERNETCONTROL) :
 iph->tos;

if (ip_options_echo(&icmp_param.replyopts, skb_in))
goto out_unlock;

/*
*Prepare data for ICMP 头。
*/
设置ICMP的头物
.type= type;
.code= code;
. = info;
.checksum= 0;
 = skb_in;
= skb_network_offset(SK)b_in);
inet_sk(SK))->tos = tos;
ipc.addr = iph->saddr;
ipc.opt = &icmp_param.replyopts;
ipc.shtx.flags = 0;

{
struct flowi fl = {
.nl_u = {
.ip4_u = {
.daddr = icmp_param.replyopts.srr ?
icmp_param.replyopts.faddr :
iph->saddr,
.saddr = saddr,
.tos = RT_TOS(tos)
}
},
.proto = IPPROTO_ICMP,
.uli_u = {
.icmpt = {
.type = type,
.code = code
}
}
};
int err;
struct rtable *rt2;
/*xfrm 架构中间定位密码不默认XFRM密码,成穹状弯曲在这里立即的,开场白内核心不在焉翻开XFRM。
security_skb_classify_flow(SK)b_in, FL)

              当内核不翻开XFRM
a)以防心不在焉找到路由,立即的召集ICMPXXMITIX解锁,翻开软停止放开旋转锁定
B)以防找到路由,ICMPv4xxRimLIVE高的限速处理或负责。,并召集icmp_push_reply发送数据
if (__ip_route_output_key(net, &rt, FL)
goto out_unlock;

/* No need to clone since we”re just using its 地址。 */
rt2 = rt;
/*xfrm 架构中间定位密码,不懂XFRM密码,成穹状弯曲在这里立即的,开场白内核心不在焉翻开XFRM。
err = xfrm_lookup(net, (作曲) dst_entry **)&rt, &fl, NULL, 0);
switch (背面的)) {
case 0:
if (RT != rt2)
goto route_done;
break;
case -EPERM:
rt = NULL;
break;
default:
goto out_unlock;
}

if (xfrm_decode_session_reverse(SK)b_in, &fl, AF_INET))
goto relookup_failed;

if (inet_addr_type(net, ) == RTN_LOCAL)
err = __ip_route_output_key(net, &rt2, FL)
else {
struct flowi fl2 = {};
struct dst_entry *odst;

fl2.fl4_dst = ;
if (ip_route_output_key(net, &rt2, &fl2))
goto relookup_failed;

/* Ugh! */
odst = skb_dst(SK)b_in);
err = ip_route_input(SK)b_in, fl.fl4_dst, ,
    RT_TOS(tos), rt2->);

dst_release(&rt2->);
rt2 = skb_rtable(SK)b_in);
skb_dst_set(SK)b_in, odst);
}

if (背面的))
goto relookup_failed;

err = xfrm_lookup(net, (作曲) dst_entry **)&rt2, &fl, NULL,
 XFRM_LOOKUP_ICMP);
switch (背面的)) {
case 0:
dst_release(&rt->);
rt = rt2;
break;
case -EPERM:
goto ende;
default:
relookup_failed:
if (!RT)
goto out_unlock;
break;
}
}

route_done:
if (!icmpv4_xrlim_allow(net, rt, type, 密码))
goto ende;

/* RFC says return as much as we can without exceeding 576 八位位组。 */

room = dst_mtu(&rt->);
if (房间) > 576)
room = 576;
room -= sizeof(作曲) iphdr) + icmp_param.;
room -= sizeof(作曲) icmphdr);

= skb_in->len – ;
if ( > 房间)
= room;
= sizeof(作曲) icmphdr);

icmp_push_reply(&icmp_param, &ipc, RT)
ende:
ip_rt_put(RT);
out_unlock:
icmp_xmit_unlock(SK));
out:;
}

静止有或起作用:

static struct sock *icmp_sk(作曲) net 广泛分布)
{
/*
1、获取流行的CPU的家具形势 全袜,首要用于发送ICMP数据包
*/
return net->[smp_processor_id()];
}

/*
效能:
1、结束软停止
2、推进袜套的旋转锁
*/
static inline struct sock *icmp_xmit_lock(作曲) net 广泛分布)
{
struct sock *sk;

local_bh_disable();
/*
1、获取ICMP sock
2、袜套旋转锁,以防终成泡影,来回空值,以防成,回到袜套
*/
sk = icmp_sk(广泛分布)

if (不值得讨论的)!spin_trylock(&sk->))) {
/* This can happen if the output path signals a
* dst_link_failure() for an outgoing ICMP 小风趣的人.
*/
local_bh_enable();
return NULL;
}
return sk;
}
/*
效能:
1、翻开软停止
2、放开旋转锁定
*/
static inline void icmp_xmit_unlock(作曲) sock SK)
{
spin_unlock_bh(&sk->);
}

这么,ICMP数据收执与收执快速地流动剖析