您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 质量控制/管理 > ping命令的设计与实现
设计报告课程计算机网络设计名称《ping命令的设计与实现》专业班级计科094同组人姓名同组人学号实验日期2013-04-10指导教师成绩2013年04月10日设计目的和要求1、实验目的:Ping命令向目的主机发送ICMPECHO—REQUEST请求并接收目的主机返回的响应报文,用来检验本地主机和远程的主机是否连接。2.实验要求:利用ICMP数据包,测试主机的连通性,通过课程设计,使学生熟悉ICMP报文结构,使学生对ICMP有更深的理解。要求:输出参考系统自带ping程序,命令行运行:pingip二、设计说明设计分析:使用原始套接字可以读写ICMP分组,利用原始套接字发送ICMP回显请求,并接收ICMP回显应答,通过icmp_send()发送ICMP回显示请求包,icmp_recv()接收ping目的主机的回复,并使用终端信号处理函数SIGINT处理信号,建立两个线程,一个用于发送数据,另一个用于接收响应数据,主程序等待两个线程运行完毕后再进行下一步动作。最后,主程序讲发送数据和接收的数据进行统计,并将结果打印出来。系统运行环境:虚拟机:Fedora14(linux操作系统)gcc设计中的重点和难点:ICMP数据包的打包和解包,以及从CRC16校验算法的分析实现输入和输出条件:在linux系统下运行ping在出现4个响应包后按Ctrl+c键停止发送。三、系统详细设计《Ping命令的设计与实现》Ping命令向目的主机发送ICMPECHO—REQUEST请求并接收目的主机返回的响应报文,用来检验本地主机和远程的主机是否连接。协议格式图1.1中已经对ICMP协议的报文格式进行了说明。Ping的客户端方式的类型为8,代码值为0,表示ICMP的回显请求。类型为0,代码为0是,是ICMP回显应答。检验和为16为的crc16的算法。078151631图1.1ICMP报文的数据格式图1.2所示为ping所使用的类型和代码格式。包含16位的标始符和16为的序列号。序列号是用于标识发送或者响应的序号,而标示符通常用于表明发送和接收此报的用户,一眼用进程的PID来识别。078151631图1.2ping的数据格式例如一个用户的进程PID为1000,发送了一个序列号为1的回显请求报文,当此报文被目的主机正确处理并返回后,可以用PID来识别是否为当前的用户,并且用序列号来识别哪个报文被返回,通过发送报文到目的主机并接受响应,可以计算发送和接收二者之间的时间差,来判断网络的状况。类型(8位)代码(8位)校验和(16位)此部分不同的类型和代码格式不同类型(8或0)代码(0)校验和标示符序列符占位字节如图1.3所示,ping程序一般按照图中的框架进行设计。主要分为发送数据和接收数据及计算时间差。发送数据对组织好的数据进行发送,接收数据从网络上接收数据并判断其合法性,例如判断是否本进程发出的报文等。图1.3ping程序的基本框架由于ICMP必须使用原始套接字进行设计,要手动设置IP的头部和ICMP的头部并进行校验。开始设置发送数据计算机发送数据校验和发送数据接收数据计算时速差解包判断正误结束校验和函数TCP/IP协议栈使用的校验算法是比较经典的,对16为的数据进行累加计算,并返回计算结果。需要注意的是对奇数个字节数据的计算,是将最后的有效数据作为最高位的字节,低字节填充了0。/*CRC16校验和计算icmp_cksum参数:data:数据len:数据长度返回值:计算结果,short类型*/staticunsignedshorticmp_cksum(unsignedchar*data,intlen){intsum=0;/*计算结果*/intodd=len&0x01;/*是否为奇数*/unsignedshort*value=(unsignedshort*)data;/*将数据按照2字节为单位累加起来*/while(len&0xfffe){sum+=*(unsignedshort*)data;data+=2;len-=2;}/*判断是否为奇数个数据,若ICMP报头为奇数个字节,会剩下最后一字节。*/if(odd){unsignedshorttmp=((*data)8)&0xff00;sum+=tmp;}sum=(sum16)+(sum&0xffff);sum+=(sum16);return~sum;}设置IP发送报文的头部ip头部格式:structip{#if__BYTE_ORDER==__LITTLE_ENDIAN/*如果为小端*/gnedintip_v:4;/*版本*/#endif#if__BYTE_ORDER==__BIG_ENDIAN/*如果为大端*/unsignedintip_v:4;/*版本*/unsignedintip_hl:4;/*头部长度*/#endifu_int8_tip_tos;/*TOS,服务类型*/u_shortip_len;/*总长度*/u_shortip_id;/*标识值*/u_shortip_off;*段偏移值*/u_int8_tip_ttl;/*TTL,生存时间*/u_int8_tip_p;/*协议类型*/u_shortip_sum;/*校验和*/structin_addrip_src,ip_dst;/*源地址和目的地址*/};设置ICMP发送报文的头部对于回显请求的ICMP报文,下面是ICMP结构简化形式:即仅包含消息类型、消息代码、校验和、数据报的ID、数据报的序列号即ICMP数据段几个部分。校验和的值在计算之前其他的值应该先进行填充,而校验和也需要设置为0来占位,然后在计算真正的校验和值。structicmp{u_int8_ticmp_type;/*消息类型*/u_int8_ticmp_code;/*消息类型的子码*/u_int16_ticmp_cksum;/*校验和*/union{u_charih_pptr;/*ICMP_PARAMPROB*/structin_addrih_gwaddr;/*网关地址*/structih_idseq/*显示数据报*/{u_int16_ticd_id;/*数据报ID*/u_int16_ticd_seq;/*数据报的序号*/}ih_idseq;}icmp_hun;#defineicmp_idicmp_hun.ih_idseq.icd_id#defineicmp_seqicmp_hun.ih_idseq.icd_sequnion{struct{u_int8_tid_data[1];/*数据*/}icmp_dun;#defineicmp_dataicmp_dun.id_data};ICMP回显得数据部分可以任意设置,但是以太网包的总长度不能小于以太网的最小值,即总长度不能小于46,由于IP头部为20字节,ICMP头部为8个字节,以太网头部占用14个字节,因此ICMP回显包的最小值为46-20-8-14=4个字节。ICMP回显请求的类型为8,即ICMP-ECHO。ICMP回显请求的代码值为0.ICMP回显请求的序列号是一个16位的值,通常由一个递增的值生成。ICMP回显请求的ID用于区别,通常用进程的PID填充。进行ICMP头部校验的代码如下:剥离ICMP接受报文的头部函数icmp_unpack()用于剥离IP头部,分析ICMP头部的值。判断是否为正确的ICMP报文,并打印结果。参数buf为剥去了以太网部分数据的IP数据报文,len为数据长度。可以利用IP头部的参数快速地跳ICMP报文部分,IP结构的ip_hl标识IP头部的长度,由于ip_hl标识的是4字节单位,所以需要乘以4来获得ICMP段的地址。获得ICMP数据段后,判断其类型是否为ICMP_ECHOREPLY,并核实其标识是否为本进程的PID。由于需要判断数据报文的往返时间,在本程序中需要先查找这个包发送时的时间,与当前时间进行计算后,可以得出本地主机与目标主机之间网络ICMP回显报文的差值。程序需要累加成功接收到的报文用于程序退出时的统计。/*设置ICMP报头*/staticvoidicmp_pack(structicmp*icmph,intseq,structtimeval*tv,intlength){unsignedchari=0;/*设置报头*/icmph-icmp_type=ICMP_ECHO;/*ICMP回显请求*/icmph-icmp_code=0;/*code值为0*/icmph-icmp_cksum=0;/*先将cksum值填写0,便于之后的cksum计算*/icmph-icmp_seq=seq;/*本报的序列号*/icmph-icmp_id=pid&0xffff;/*填写PID*/for(i=0;ilength;i++)icmph-icmp_data[i]=i;/*计算校验和*/icmph-icmp_cksum=icmp_cksum((unsignedchar*)icmph,length);}函数的返回值为-1时表示褚翠,其他值则正常。/*解压接收到的包,并打印信息*/staticinticmp_unpack(char*buf,intlen){inti,iphdrlen;structip*ip=NULL;structicmp*icmp=NULL;intrtt;ip=(structip*)buf;/*IP头部*/iphdrlen=ip-ip_hl*4;/*IP头部长度*/icmp=(structicmp*)(buf+iphdrlen);/*ICMP段的地址*/len-=iphdrlen;if(len8)/*判断长度是否为ICMP包*/{printf(ICMPpackets\'slengthislessthan8\n);return-1;}/*ICMP类型为ICMP_ECHOREPLY并且为本进程的PID*/if((icmp-icmp_type==ICMP_ECHOREPLY)&&(icmp-icmp_id==pid)){structtimevaltv_internel,tv_recv,tv_send;/*在发送表格中查找已经发送的包,按照seq*/pingm_pakcet*packet=icmp_findpacket(icmp-icmp_seq);if(packet==NULL)return-1;packet-flag=0;/*取消标志*/tv_send=packet-tv_begin;/*获取本包的发送时间*/gettimeofday(&tv_recv,NULL);/*读取此时时间,计算时间差*/tv_internel=icmp_tvsub(tv_recv,tv_send);rtt=tv_internel.tv_sec*1000+tv_internel.tv_usec/1000;/*打印结果,包含*ICMP段长度源IP地址包的序列号TTL时间差*/printf(%dbytefrom%s:icmp_seq=%uttl=%drtt=%dms\n,len,inet_ntoa(ip-ip_src),icmp-icmp_seq,ip-ip_ttl,,rtt);packet_recv++;/*接收包数量加1*/}elsereturn-1;}计算时间差由于需要评估网络状况,在发送数据报文的时候保存发送时间,接收到报文后,计算两个时刻之间的差值,生成了ICMP源主机和目标主机之间的网络状况的时间评估。发送报文发送报文函数是一个线程,每隔1s向目的主机发送一个ICMP回显请求报文,它在整个程序处于激活状态(alive为1)是一直发送报文。(1)获得当前的时间值,按照序列号packet_send将ICMP报文打包到缓冲区send_buff中后,发送到目的地址。发送成功后,记录发送报文的状态:序号seq为packet_send。标志flag为1,表示已经发送但是没有收到响应。发送时间为之前获得的时间。(2)每次发送成功后序号值会增加1,即packet_se
本文标题:ping命令的设计与实现
链接地址:https://www.777doc.com/doc-3649995 .html