实验七基于ICMP的简单程序实现.docx
- 文档编号:3935732
- 上传时间:2023-05-06
- 格式:DOCX
- 页数:17
- 大小:50.61KB
实验七基于ICMP的简单程序实现.docx
《实验七基于ICMP的简单程序实现.docx》由会员分享,可在线阅读,更多相关《实验七基于ICMP的简单程序实现.docx(17页珍藏版)》请在冰点文库上搜索。
实验七基于ICMP的简单程序实现
实验七基于ICMP的简单程序实现
【实验目的】
⑴学习套接字编程的基本流程;
⑵理解ICMP的工作原理;
【实验环境】
电脑、VC++6.0编程环境
【实验重点及难点】
重点:
使用原始套接字编写程序;
难点:
使用原始套接字编写程序。
【实验内容】
1、基础知识
⑴ICMP的作用:
ICMP是(InternetControlMessageProtocol)Internet控制报文协议。
它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。
控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。
关于ICMP报文类型可参考维基百科。
⑵IP的头部及ICMP头部、数据结构定义
IP头部:
typedefstruct_iphdr
{
//SupposetheBYTE_ORDERisLITTLE_ENDIAN
unsignedinth_len:
4;//头部长度
unsignedintversion:
4;//IP的版本号
unsignedchartos;//服务类型
unsignedshorttotal_len;//报长度
unsignedshortid;//标识符
unsignedshortfrag_offset;//分片偏移
unsignedcharttl;//生存时间
unsignedcharprotocol;//上层协议(TCP,UDP等)
unsignedshortchecksum;//IP校验和
unsignedintsourceIP;//源IP
unsignedintdestIP;//目标IP
}IpHeader;
//选项部分
typedefstruct_ipoptionhdr
{
unsignedcharcode;//选项类型
unsignedcharlen;//头部可选长度
unsignedcharptr;//偏移量
unsignedlongaddr[9];//记录经过的IP用作路由
}IpOptionHeader;
ICMP头部:
typedefstruct_icmphdr
{
BYTEi_type;//ICMP类型
BYTEi_code;//ICMPcode字段
USHORTi_cksum;//ICMP校验和
USHORTi_id;
USHORTi_seq;
ULONGtimestamp;
}IcmpHeader;
2、阅读程序,完成以下题目:
⑴画出程序的流程图,并解释每个模块的含义;
⑵运行程序,使用抓包工具捕获发送的数据包,并分析数据与程序对照。
参照实验指导书实验12ICMP协议应用中的设置ttl值的代码,将该程序修改为一个模拟tracert程序。
3、附录:
/*该程序使用原始套接字编写,默认ping本身IP地址,可以再VC6中设置参数
*格式为ping,也是可以使用-r选项,使用IP头部的选项字段记录
*经过的路由。
*/
//#include"stdafx.h"
#include
#include
#include
#include
#defineWIN32_LEAN_AND_MEAN
#pragmacomment(lib,"ws2_32.lib")
#defineIP_RECORD_ROUTE0x7
//IP头部的结构体
typedefstruct_iphdr
{
//SupposetheBYTE_ORDERisLITTLE_ENDIAN
unsignedinth_len:
4;//头部长度
unsignedintversion:
4;//IP的版本号
unsignedchartos;//服务类型
unsignedshorttotal_len;//报长度
unsignedshortid;//标识符
unsignedshortfrag_offset;//分片偏移
unsignedcharttl;//生存时间
unsignedcharprotocol;//上层协议(TCP,UDP等)
unsignedshortchecksum;//IP校验和
unsignedintsourceIP;//源IP
unsignedintdestIP;//目标IP
}IpHeader;
#defineICMP_ECHO8
#defineICMP_ECHOREPLY0
#defineICMP_MIN8//ICMP包头部最小长度
//ICMP头部结构体
//Thisisnotthestandardheader,butwereservespacefortime
typedefstruct_icmphdr
{
BYTEi_type;//ICMP类型
BYTEi_code;//ICMPcode字段
USHORTi_cksum;//ICMP校验和
USHORTi_id;
USHORTi_seq;
ULONGtimestamp;
}IcmpHeader;
//IP头部的选项部分
typedefstruct_ipoptionhdr
{
unsignedcharcode;//选项类型
unsignedcharlen;//头部可选长度
unsignedcharptr;//偏移量
unsignedlongaddr[9];//记录经过的IP用作路由
}IpOptionHeader;
#defineDEF_PACKET_SIZE32//包的默认长度
#defineMAX_PACKET1024//最大长度
#defineMAX_IP_HDR_SIZE60//IP头部的最大长度
BOOLbRecordRoute;//是否记录路由
intdatasize;//数据长度
char*lpdest;
//使用帮助函数
voidusage(char*progname)
{
printf("usage:
ping-r
printf("-rrecordroute");
printf("hostremotemachinetoping");
printf("datasizecanbeupto1KB");
ExitProcess(-1);
}
//用于填充ICMP的头部
voidFillICMPData(char*icmp_data,intdatasize)
{
IcmpHeader*icmp_hdr=NULL;
char*datapart=NULL;
icmp_hdr=(IcmpHeader*)icmp_data;
icmp_hdr->i_type=ICMP_ECHO;//ICMP回送请求类型为8
icmp_hdr->i_code=0;//code为0
icmp_hdr->i_id=(USHORT)GetCurrentProcessId();//填充当前进程号,用于返回响应的标示
icmp_hdr->i_cksum=0;
icmp_hdr->i_seq=0;
datapart=icmp_data+sizeof(IcmpHeader);
//使用字符填充数据包
memset(datapart,'E',datasize-sizeof(IcmpHeader));
}
//Function:
checksum
//计算校验和
USHORTchecksum(USHORT*buffer,intsize)
{
unsignedlongcksum=0;
while(size>1)
{
cksum+=*buffer++;
size-=sizeof(USHORT);
}
if(size)
{
cksum+=*(UCHAR*)buffer;
}
cksum=(cksum>>16)+(cksum&0xffff);
cksum+=(cksum>>16);
return(USHORT)(~cksum);
}
//Function:
DecodeIPOptions
//解析IP头部的选项部分,由于路径的路由记录
voidDecodeIPOptions(char*buf,intbytes)
{
IpOptionHeader*ipopt=NULL;
IN_ADDRinaddr;
inti;
HOSTENT*host=NULL;
ipopt=(IpOptionHeader*)(buf+20);
printf("RecordRoute:
");
for(i=0;i<(ipopt->ptr/4)-1;i++)
{
inaddr.S_un.S_addr=ipopt->addr[i];
if(i!
=0)printf("");
host=gethostbyaddr((char*)&inaddr.S_un.S_addr,sizeof(inaddr.S_un.S_addr),AF_INET);
if(host)
printf("(%-15s)%s",inet_ntoa(inaddr),host->h_name);
else
printf("(%-15s)",inet_ntoa(inaddr));
}
printf("\n");
return;
}
//Function:
DecodeICMPHeader
//从返回的IP数据中解析出ICMP的头部数据
voidDecodeICMPHeader(char*buf,intbytes,structsockaddr_in*from)
{
IpHeader*iphdr=NULL;
IcmpHeader*icmphdr=NULL;
unsignedshortiphdrlen;
DWORDtick;
staticinticmpcount=0;
iphdr=(IpHeader*)buf;
//头部字段乘以4
iphdrlen=iphdr->h_len*4;
tick=GetTickCount();
if((iphdrlen==MAX_IP_HDR_SIZE)&&(!
icmpcount))
DecodeIPOptions(buf,bytes);
if(bytes { printf("Toofewbytesfrom%s\n",inet_ntoa(from->sin_addr)); } icmphdr=(IcmpHeader*)(buf+iphdrlen); if(icmphdr->i_type! =ICMP_ECHOREPLY)//echotypemustbe0 { printf("non-echotype%drecvd\n",icmphdr->i_type); return; } //检查该返回的数据是否为上述程序发送的数据的响应 //InFillICMPDatafunctionweassignthei_idisGetCurrentProcessId() if(icmphdr->i_id! =(USHORT)GetCurrentProcessId()) { printf("someoneelse'spacket! \n"); return; } printf("%dbytesfrom%s",bytes,inet_ntoa(from->sin_addr)); printf("icmp_seq=%d.",icmphdr->i_seq); printf("time: %dms",tick-icmphdr->timestamp); printf("\n"); icmpcount++; return; } //Function: ValidateArgs //检查输入的参数是否合法 voidValidateArgs(intargc,char**argv) { bRecordRoute=FALSE; lpdest=NULL; datasize=DEF_PACKET_SIZE; for(inti=1;i { if((argv[i][0]=='-')||(argv[i][0]=='/')) { switch(tolower(argv[i][1])) { case'r': //记录经过的路由ping-r bRecordRoute=TRUE; break; default: usage(argv[0]); break; } } elseif(isdigit(argv[i][0])) datasize=atoi(argv[i]); else lpdest=argv[i]; } } // //Function: main //创建ICMP原始套接字,创建ICMP头部,添加合适的IP头部选项,发送ICMP数据包 //并等待响应,在等待响应式设置一个计时器,超时将放回。 否则,对收到的数据包 //进行解析。 intmain(intargc,char**argv) { WSADATAwsaData; SOCKETsockRaw=INVALID_SOCKET; structsockaddr_indest,from; intbread, fromlen=sizeof(from), timeout=1000, ret; charicmp_data[MAX_PACKET], recvbuf[MAX_PACKET]; unsignedintaddr=0; USHORTseq_no=0; structhostent*hp=NULL; IpOptionHeaderipopt; if(WSAStartup(MAKEWORD(2,2),&wsaData)! =0) { printf("WSAStartup()failed: %d\n",GetLastError()); return-1; } //检查用户输入的参数是否有效 ValidateArgs(argc,argv); //使用IPPROTO_ICMP创建一个原始套接字 sockRaw=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); //sock创建失败 if(sockRaw==INVALID_SOCKET) { printf("WSASocket()failed: %d\n",WSAGetLastError()); return-1; } //bRecordRoute=true表示需要记录经过的路由 if(bRecordRoute) { //为每一个ICMP包设置IP头部的选项部分。 memset(&ipopt,0,sizeof(ipopt)); ipopt.code=IP_RECORD_ROUTE;//设置记录路由选项 ipopt.ptr=4;//Pointtothefirstaddroffset ipopt.len=39;//选项长度 ret=setsockopt(sockRaw,IPPROTO_IP,IP_OPTIONS,(char*)&ipopt,sizeof(ipopt)); if(ret==SOCKET_ERROR) { printf("setsockopt(IP_OPTIONS)failed: %d\n",WSAGetLastError()); return-1; } } //设置发送或接收的超时时间 timeout=1000; bread=setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout)); if(bread==SOCKET_ERROR) { printf("setsockopt(SO_RCVTIMEO)failed: %d\n",WSAGetLastError()); return-1; } timeout=1000; bread=setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,sizeof(timeout)); if(bread==SOCKET_ERROR) { printf("setsockopt(SO_SNDTIMEO)failed: %d\n",WSAGetLastError()); return-1; } //根据需要解析域名为IP memset(&dest,0,sizeof(dest)); dest.sin_family=AF_INET; if((dest.sin_addr.s_addr=inet_addr(lpdest))==INADDR_NONE) { if((hp=gethostbyname(lpdest))! =NULL) { memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length); dest.sin_family=hp->h_addrtype; printf("dest.sin_addr=%s\n",inet_ntoa(dest.sin_addr)); } else { printf("gethostbyname()failed: %d\n",WSAGetLastError()); return-1; } } //创建ICMP包 datasize+=sizeof(IcmpHeader); memset(icmp_data,0,MAX_PACKET); //填充ICMP包 FillICMPData(icmp_data,datasize); //开始发送或者接收ICMP包 while (1) { staticintnCount=0; intbwrote; //设置发送ICMP包的数量,该程序设置为4个包 if(nCount++==4)break; ((IcmpHeader*)icmp_data)->i_cksum=0; ((IcmpHeader*)icmp_data)->timestamp=GetTickCount(); ((IcmpHeader*)icmp_data)->i_seq=seq_no++; ((IcmpHeader*)icmp_data)->i_cksum=checksum((USHORT*)icmp_data,datasize); bwrote=sendto(sockRaw,icmp_data,datasize,0,(structsockaddr*)&dest,sizeof(dest)); if(bwrote==SOCKET_ERROR) { if(WSAGetLastError()==WSAETIMEDOUT) { printf("timedout\n"); continue; } printf("sendto()failed: %d\n",WSAGetLastError()); return-1; } if(bwrote { printf("Wrote%dbytes\n",bwrote); } bread=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(structsockaddr*)&from,&fromlen); if(bread==SOCKET_ERROR) { if(WSAGetLastError()==WSAETIMEDOUT) { printf("timedout\n"); continue; } printf("recvfrom()failed: %d\n",WSAGetLastError()); return-1; } DecodeICMPHeader(recvbuf,bread,&from); Sleep(1000); } //Socket清理 if(sockRaw! =INVALID_SOCKET) closesocket(sockRaw); WSACleanup(); return0; }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验 基于 ICMP 简单 程序 实现
![提示](https://static.bingdoc.com/images/bang_tan.gif)