参考资料

<<linux内核网络栈源代码情景分析>>

icmp协议

在实现的过程中, ICMP协议工作再IP协议之上,但又不与TCP协议工作再一级,而是在下一级,在一般ICMP模块处理完后,还需要进一步调用TCP协议进行处理。该协议的目的主要是用于通报错误或者探测远端主机信息。

ICMP的主要流程

icmp_send函数

该函数主要是发送一个ICMP错误通报数据包


/** Send an ICMP message in response to a situation**   Fixme: Fragment handling is wrong really.*/void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, struct device *dev)
{struct sk_buff *skb;struct iphdr *iph;int offset;struct icmphdr *icmph;int len;struct device *ndev=NULL;  /* Make this =dev to force replies on the same interface */unsigned long our_addr;int atype;/**    Find the original IP header.*/iph = (struct iphdr *) (skb_in->data + dev->hard_header_len);                 // 获取ip头部信息/**  No replies to MAC multicast*/if(skb_in->pkt_type!=PACKET_HOST)                                          // 如果数据包目的地址不是指向本地的单播地址则直接返回                                return;/**  No replies to IP multicasting*/atype=ip_chk_addr(iph->daddr);                                               // 检查原数据包最终目的地址类型,如果是一个多波或者广播地址则不满足产品ICMP数据包的条件if(atype==IS_BROADCAST || atype==IS_MULTICAST)return;/**  Only reply to first fragment.*/if(ntohs(iph->frag_off)&IP_OFFSET)                                            // 检查是否是第一个数据包分片,return;/**  We must NEVER NEVER send an ICMP error to an ICMP error message*/if(type==ICMP_DEST_UNREACH||type==ICMP_REDIRECT||type==ICMP_SOURCE_QUENCH||type==ICMP_TIME_EXCEEDED)   // 检查原数据包是否是一个ICMP错误报文,{/**  Is the original packet an ICMP packet?*/if(iph->protocol==IPPROTO_ICMP)                                                                    // 检查{icmph = (struct icmphdr *) ((char *) iph +4 * iph->ihl);/**  Check for ICMP error packets (Must never reply to*  an ICMP error).*/if (icmph->type == ICMP_DEST_UNREACH ||icmph->type == ICMP_SOURCE_QUENCH ||icmph->type == ICMP_REDIRECT ||icmph->type == ICMP_TIME_EXCEEDED ||icmph->type == ICMP_PARAMETERPROB)return;}}icmp_statistics.IcmpOutMsgs++;/**  This needs a tidy.  */switch(type)                                                                  // 更新统计{case ICMP_DEST_UNREACH:icmp_statistics.IcmpOutDestUnreachs++;break;case ICMP_SOURCE_QUENCH:icmp_statistics.IcmpOutSrcQuenchs++;break;case ICMP_REDIRECT:icmp_statistics.IcmpOutRedirects++;break;case ICMP_ECHO:icmp_statistics.IcmpOutEchos++;break;case ICMP_ECHOREPLY:icmp_statistics.IcmpOutEchoReps++;break;case ICMP_TIME_EXCEEDED:icmp_statistics.IcmpOutTimeExcds++;break;case ICMP_PARAMETERPROB:icmp_statistics.IcmpOutParmProbs++;break;case ICMP_TIMESTAMP:icmp_statistics.IcmpOutTimestamps++;break;case ICMP_TIMESTAMPREPLY:icmp_statistics.IcmpOutTimestampReps++;break;case ICMP_ADDRESS:icmp_statistics.IcmpOutAddrMasks++;break;case ICMP_ADDRESSREPLY:icmp_statistics.IcmpOutAddrMaskReps++;break;}        /** Get some memory for the reply. */len = dev->hard_header_len + sizeof(struct iphdr) + sizeof(struct icmphdr) +sizeof(struct iphdr) + 32; /* amount of header to return */                            // 获取总的数据长度skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC);                                        // 申请内存if (skb == NULL) {icmp_statistics.IcmpOutErrors++;return;}skb->free = 1;/**  Build Layer 2-3 headers for message back to source. */our_addr = dev->pa_addr;                                                              // 获取原数据网络设备的地址 从哪里来发回哪里去if (iph->daddr != our_addr && ip_chk_addr(iph->daddr) == IS_MYADDR)our_addr = iph->daddr;offset = ip_build_header(skb, our_addr, iph->saddr,&ndev, IPPROTO_ICMP, NULL, len,skb_in->ip_hdr->tos,255);                                                    // 建立头部 if (offset < 0)                                                                          // 创建失败{icmp_statistics.IcmpOutErrors++;skb->sk = NULL;kfree_skb(skb, FREE_READ);return;}/* * Re-adjust length according to actual IP header size. */skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8;                    // 创建头部并初始化/**  Fill in the frame*/icmph = (struct icmphdr *) (skb->data + offset);icmph->type = type;icmph->code = code;icmph->checksum = 0;icmph->un.gateway = info; /* This might not be meant for this form of the union but it willbe right anyway */memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8);icmph->checksum = ip_compute_csum((unsigned char *)icmph,sizeof(struct icmphdr) + sizeof(struct iphdr) + 8);                // 计算ICMP校验值/** Send it and free it once sent.*/ip_queue_xmit(NULL, ndev, skb, 1);                                                      // 数据发送出去
}

该函数主要是发送一个ICMP错误通报数据包,ICMP错误通报数据包一般在接受到远端一个不合法的数据包后产生的,用于通知远端发生错误的原因。

icmp_unreach函数

处理ICMP错误类型,处理相关错误后就调用传输层协议处理tcp_err等

/* * Handle ICMP_UNREACH and ICMP_QUENCH. */static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb)
{struct inet_protocol *ipprot;struct iphdr *iph;unsigned char hash;int err;err = (icmph->type << 8) | icmph->code;                                         // 获取错误值iph = (struct iphdr *) (icmph + 1);                                           // 获得头部switch(icmph->code & 7)                                                       // 查看具体的错误类型{case ICMP_NET_UNREACH:break;case ICMP_HOST_UNREACH:break;case ICMP_PROT_UNREACH:printk("ICMP: %s:%d: protocol unreachable.\n",in_ntoa(iph->daddr), ntohs(iph->protocol));break;case ICMP_PORT_UNREACH:break;case ICMP_FRAG_NEEDED:printk("ICMP: %s: fragmentation needed and DF set.\n",in_ntoa(iph->daddr));break;case ICMP_SR_FAILED:printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));break;default:break;}/**  Get the protocol(s). */hash = iph->protocol & (MAX_INET_PROTOS -1);                                 // 获取协议位置索引/**  This can't change while we are doing it. */ipprot = (struct inet_protocol *) inet_protos[hash];                       // 获取传输层协议函数操作集while(ipprot != NULL) {struct inet_protocol *nextip;nextip = (struct inet_protocol *) ipprot->next;/* * Pass it off to everyone who wants it. */if (iph->protocol == ipprot->protocol && ipprot->err_handler) {ipprot->err_handler(err, (unsigned char *)(icmph + 1),iph->daddr, iph->saddr, ipprot);                      // 调用协议错误处理函数处理}ipprot = nextip;}kfree_skb(skb, FREE_READ);                                                    // 释放数据
}

主要就是调用传输协议层处理错误信息,让上层应用处理。

icmp_redirect函数

该函数主要是处理重定向报文。

/**  Handle ICMP_REDIRECT. */static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb,struct device *dev, unsigned long source)
{struct rtable *rt;struct iphdr *iph;unsigned long ip;/**   Get the copied header of the packet that caused the redirect*/iph = (struct iphdr *) (icmph + 1);                                         // 初始化为原数据包IP首部ip = iph->daddr;                                                             // 初始化为发送数据包的远端IP地址switch(icmph->code & 7)                                                   // 获取重定向类型进行处理 {case ICMP_REDIR_NET:/** This causes a problem with subnetted networks. What we should do*   is use ICMP_ADDRESS to get the subnet mask of the problem route*    and set both. But we don't..*/
#ifdef not_a_good_ideaip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),                 // 网络重定向直接添加路由表项ip, 0, icmph->un.gateway, dev,0, 0);break;
#endifcase ICMP_REDIR_HOST:                                                     // 主机重定向/** Add better route to host.*  But first check that the redirect*  comes from the old gateway..*   And make sure it's an ok host address* (not some confused thing sending our*   address)*/rt = ip_rt_route(ip, NULL, NULL);                                    // 获取ip对应的路由表内容if (!rt)break;if (rt->rt_gateway != source || ip_chk_addr(icmph->un.gateway))     // 不可是多播地址或者广播地址break;printk("redirect from %s\n", in_ntoa(source));ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),ip, 0, icmph->un.gateway, dev,0, 0);                           // 修改原表项 break;case ICMP_REDIR_NETTOS:case ICMP_REDIR_HOSTTOS:printk("ICMP: cannot handle TOS redirects yet!\n");break;default:break;}/** Discard the original packet*/kfree_skb(skb, FREE_READ);
}

该函数处理有重定向ICMP数据包,这种数据包由路由器发送,用于通知本地到达某个网络或主机的一个更好的路由路径,路由器为特定的目的主机发送重定向消息将主机重定向到一个更优的路由器或者告诉主机目的主机实际上是在同一个链路的邻居节点上。

icmp_echo函数

该函数主要是是回复一个Echo应答数据包。

/**  Handle ICMP_ECHO ("ping") requests. */static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,unsigned long saddr, unsigned long daddr, int len,struct options *opt)
{struct icmphdr *icmphr;struct sk_buff *skb2;struct device *ndev=NULL;int size, offset;icmp_statistics.IcmpOutEchoReps++;icmp_statistics.IcmpOutMsgs++;size = dev->hard_header_len + 64 + len;               // mac首部长度 加上IP数据负载长度 返回该数据skb2 = alloc_skb(size, GFP_ATOMIC);                     // 申请内存if (skb2 == NULL)                                          // 如果申请失败则计数返回{icmp_statistics.IcmpOutErrors++;kfree_skb(skb, FREE_READ);return;}skb2->free = 1;/* Build Layer 2-3 headers for message back to source */offset = ip_build_header(skb2, daddr, saddr, &ndev,IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);          // 建立头部信息if (offset < 0)                                             // 如果出错则计数释放空间返回{icmp_statistics.IcmpOutErrors++;printk("ICMP: Could not build IP Header for ICMP ECHO Response\n");kfree_skb(skb2,FREE_WRITE);kfree_skb(skb, FREE_READ);return;}/**    Re-adjust length according to actual IP header size. */skb2->len = offset + len;                                   // 设置长度/**  Build ICMP_ECHO Response message. */icmphr = (struct icmphdr *) (skb2->data + offset);             // 获取偏移的返回值 并初始化memcpy((char *) icmphr, (char *) icmph, len);icmphr->type = ICMP_ECHOREPLY;icmphr->code = 0;icmphr->checksum = 0;icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);/**   Ship it out - free it when done */ip_queue_xmit((struct sock *)NULL, ndev, skb2, 1);            // 发送数据/**  Free the received frame*/kfree_skb(skb, FREE_READ);
}

该函数主要是处理Echo请求,ping应用程序使用Echo ICMP请求数据包探测远端主机的可达性。对于Echo请求的处理是咋接受到一个远端地址指向本地的数据包后,回复一个Echo应答数据包

icmp_rcv函数

该函数是ICMP协议总入口函数,作用类似与tcp_rcv,当IP模块发现上层使用ICMP协议时,即调用icmp_rcv函数将数据包上传,该函数就将对这个具体类型的数据类型调用不同的处理函数进行处理。

/* * Deal with incoming ICMP packets. */int icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,unsigned long daddr, unsigned short len,unsigned long saddr, int redo, struct inet_protocol *protocol)
{struct icmphdr *icmph;unsigned char *buff;/**  Drop broadcast packets. IP has done a broadcast check and ought one day*    to pass on that information.*/icmp_statistics.IcmpInMsgs++;/**    Grab the packet as an icmp object*/buff = skb1->h.raw;                                                  // 获取IP模块传入的数据icmph = (struct icmphdr *) buff;                                         // 转换成 icmph类型数据/** Validate the packet first */if (ip_compute_csum((unsigned char *) icmph, len))                      // 计算校验和 如果不对则出错返回{/* Failed checksum! */icmp_statistics.IcmpInErrors++;printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr));kfree_skb(skb1, FREE_READ);return(0);}/**    Parse the ICMP message */if (ip_chk_addr(daddr) != IS_MYADDR)                                  // 检查ICMP报文最终目的IP地址是否为本地地址{if (icmph->type != ICMP_ECHO)                                        // 如果不是ICMP_ECHO则返回{icmp_statistics.IcmpInErrors++;kfree_skb(skb1, FREE_READ);return(0);}daddr=dev->pa_addr;                                              // 设置地址}switch(icmph->type) {case ICMP_TIME_EXCEEDED:icmp_statistics.IcmpInTimeExcds++;icmp_unreach(icmph, skb1);                                          // 设置不可达return 0;case ICMP_DEST_UNREACH:icmp_statistics.IcmpInDestUnreachs++;icmp_unreach(icmph, skb1);return 0;case ICMP_SOURCE_QUENCH:icmp_statistics.IcmpInSrcQuenchs++;icmp_unreach(icmph, skb1);return(0);case ICMP_REDIRECT:icmp_statistics.IcmpInRedirects++;icmp_redirect(icmph, skb1, dev, saddr);                           // 设置为重定向return(0);case ICMP_ECHO: icmp_statistics.IcmpInEchos++;icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt);                 // 发送icmp_echo响应报文return 0;case ICMP_ECHOREPLY:icmp_statistics.IcmpInEchoReps++;kfree_skb(skb1, FREE_READ);return(0);case ICMP_TIMESTAMP:icmp_statistics.IcmpInTimestamps++;icmp_timestamp(icmph, skb1, dev, saddr, daddr, len, opt);           // 时间戳return 0;case ICMP_TIMESTAMPREPLY:icmp_statistics.IcmpInTimestampReps++;kfree_skb(skb1,FREE_READ);return 0;/* INFO is obsolete and doesn't even feature in the SNMP stats */case ICMP_INFO_REQUEST:icmp_info(icmph, skb1, dev, saddr, daddr, len, opt);                // 获取信息return 0;case ICMP_INFO_REPLY:skb1->sk = NULL;kfree_skb(skb1, FREE_READ);return(0);case ICMP_ADDRESS:icmp_statistics.IcmpInAddrMasks++;icmp_address(icmph, skb1, dev, saddr, daddr, len, opt);     return 0;case ICMP_ADDRESSREPLY:/** We ought to set our netmask on receiving this, but *    experience shows it's a waste of effort.*/icmp_statistics.IcmpInAddrMaskReps++;kfree_skb(skb1, FREE_READ);return(0);default:icmp_statistics.IcmpInErrors++;kfree_skb(skb1, FREE_READ);return(0);}/*NOTREACHED*/kfree_skb(skb1, FREE_READ);return(-1);
}

该函数主要检查ICMP首部中类型,先检查数据包合法性,然后判断是什么类型报文类型再调用对应处理函数。

总结

ICMP协议实现起来主要按照协议格式实现,ICMP报文主要分为两种报文:错误通知报文以及查询报文,Echo请求和应答,地址掩码请求和应答等属于查询报文,而重定向目的端不可达属于错误通知报文。本文主要学习了ICMP的操作函数。由于本人才疏学浅,如有错误请批评指正。

Linux内核网络栈1.2.13-icmp.c概述相关推荐

  1. Linux内核网络栈1.2.13-socket.c函数概述

    参考资料 <<linux内核网络栈源代码情景分析>> socket常用函数概述 根据socket提供的常用的库函数,socket,read,write等函数, 执行的过程 in ...

  2. Linux内核网络栈1.2.13-route.c概述

    参考资料 <<linux内核网络栈源代码情景分析>> route路由表概述 在IP协议的实现中,只要发送数据包都要查询路由表,选择合适的路由选项,确定下一站的地址,并构造MAC ...

  3. Linux内核--网络栈实现分析(二)--数据包的传递过程--转

    转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的&qu ...

  4. Linux内核--网络栈实现分析(一)--网络栈初始化--转

    转载地址 http://blog.csdn.net/yming0221/article/details/7488828 作者:闫明 本文分析基于内核Linux Kernel 1.2.13 以后的系列博 ...

  5. linux内核网络初始化,Linux内核--网络栈实现分析

    本文分析基于内核Linux Kernel 1.2.13 以后的系列博文将深入分析Linux内核的网络栈实现原理,这里看到曹桂平博士的分析后,也决定选择Linux内核1.2.13版本进行分析. 原因如下 ...

  6. Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7497260 更多请看专栏,地址 ...

  7. Linux内核网络栈1.2.13-tcp.c概述

    参考资料 <<linux内核网络栈源代码情景分析>> af_inet.c文件中调用函数在协议层的实现 本文主要根据在af_inet.c文件中根据初始化不同的协议,来调用不同的协 ...

  8. Linux内核网络栈1.2.13-af_inet.c概述

    参考资料 <<linux内核网络栈源代码情景分析>> socket常用函数继续调用分析 根据socket提供的常用库函数,socket.read和write等函数,继续往下一层 ...

  9. Linux内核网络栈1.2.13-网卡设备的初始化流程

    参考资料 <<linux内核网络栈源代码情景分析>> 网卡设备的初始化 本文主要描述一下网卡设备的整个初始化的过程,该过程主要就是根据设备的硬件信息来获取与传输网络数据,注册相 ...

最新文章

  1. ceph 面试_终于有人把Ceph分布式存储讲清楚了!
  2. 币对交易所_比特币向1万4大涨,OK交易所的比特币为什么反而贬值7折?
  3. [java进阶]1.Java读取txt文件和写入txt文件
  4. mount: unknown filesystem type 'ntfs'(转载)
  5. 2015.12.23 OC中的字符串(NSStringNSMutableString) 数组(NSArrayNSMutableArray)
  6. sublime突然中文乱码
  7. [蓝桥杯][历届试题]小朋友排队(树状数组)
  8. 【代码笔记】Web-HTML-列表
  9. 数据分析实战项目:SQL分析淘宝用户行为
  10. 9106w android7,三星note4 SM-N9106W原厂刷机包4.4.4/5.0.1rom线刷包Root驱动
  11. Arm 架构的过程调用标准
  12. 【EMC专题】共模和差模
  13. 新猿木子李:0基础学python培训教程 Python操作日期
  14. SHR和SAR移位指令
  15. 三重积分平均值_二重积分或者三重积分里面如果积分区域关于坐标轴对称比如积分区域是一个圆或者球,就只用求第一象限或卦...
  16. 益阳安化高考2021成绩查询,2021年安化县高考状元名单资料,今年安化县高考状元多少分...
  17. Double 保留至小数点后两位
  18. 使用 Mac 位置定位服务的应用的操作方法
  19. linux网络掉线频繁怎么设置,解决ssh登录的时候,没操作总是会自动掉线的设置办法,100%有效...
  20. 常量的定义与使用与变量的定义与使用

热门文章

  1. 联合南京大学,爱奇艺智能论文入选顶会CVPR 2021
  2. 我佛了!用KNN实现验证码识别,又 Get 到一招!
  3. IJCAI 2020灭霸式拒稿,AI审稿是否更公平?
  4. 中文NLP的分词真有必要吗?李纪为团队四项任务评测一探究竟 | ACL 2019
  5. 实战:CNN+BLSTM+CTC的验证码识别从训练到部署 | 技术头条
  6. 维基百科联手谷歌翻译,结果“惨不忍睹”!
  7. 通俗理解PCA降维作用
  8. 前沿 | DeepMind 最新研究——神经算术逻辑单元,有必要看一下!
  9. 像教光学一样在高中教深度学习?怼过LeCun的Google大牛认为这事有出路
  10. 简历写的好,就赢了90%的人了