实验 ICMP协议的分析与实现
实验目的
分析ICMP报文理解ICMP协议在In ternet网中的具体应用及其实现原理深入了解 TCP/IP网络的容错控制学会运用网络套接字 Win sock开发网络通信程序。
实验内容
使用Visual Studio C++6.0和网络接口套接字Socket进行Windows环境下的网络编程运用原始嵌套字RAW_SOCKET从IP层开始构造整个ICMP报文通过ICMP协议所提供的回送请求(echo request)和回送应答(echoreply)这两种报文实现检测目的站的可达性与状态 。
1 . IP报头、 I C M P报文的基本描述
IP协议并不能保证绝对的可靠所以就设计了ICMP协议,进行差错报告.
ICMP消息使用IP头作为基本控制.
IP头的格式如下
0 1 2 3
01 23456789012345678901 2345678901
|Versio n| IHL|Type of Service|Total Len gth |
| Ide ntificati on |Flags| Fragme nt Offset |
|Time to Live|Protocol |Header Checksum|
| Source Address |
| Dest in ati on Address |
Version=4
IHL In ternet头长
Type of Service=0
Total Len gth IP 包的总长度
Identification, Flags, Fragment Offset 用于IP包分段
Timeto Live IP 包的存活时长
Protocol ICMP= 1
Header Checksum 头校验和(检查整个 IP报头)
Addresses发送Echo消息的源地址是发送 Echo reply消息的目的地址 相反 发送Echo
消息的目的地址是发送 Echo reply消息的源地址.
Echo或Echo Reply消息格式如下:
01 23
01 23456789012345678901 2345678901
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Type|Code|Checksum|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Identifier | Sequence Number|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Data|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Typeecho消息的类型为8echo reply 的消息类型为0.
Code=0
Checksum
为从TYPE开始到IP包结束的校验和也就是校验整个 ICMP报文
Identifier
如果code= 0, identifier 用来匹配echo和echo reply 消息
Sequence Number
如果code= 0, identifier 用来匹配echo和echo reply 消息
功能描述
收到echo消息必须回应 echo reply消息. identifier 和sequence number可能被发送echo的主机用来匹配返回的 echo reply消息.例如 identifier 可能用于类似于TCP或UDP的port用来标示一个会话,而sequence number会在每次发送echo请求后递增.收到echo的主机或路由器返回同一个值与之匹配
2数据结构
1 IP报头格式
//定义IP首部typedef struct_iphdr{unsigned charh_lenver; unsigned char tos; //4位IP版本号+4位首部长度unsigned short total_len; //8位服务类型TOSunsigned short ident; unsigned //16位IP包总长度字节short frag_and_flags; unsigned 〃 1 6位标识 用于辅助IP包的拆装 本实验不用 置零char ttl ; unsigned char proto; //3位标志位+13位偏移位,也是用于IP包的拆装,本实验不用,置零//8位IPunsigned short checksum; 包生存时间TTLunsigned int sourceIP; 〃8位协议TCP,UDP或其他 ,本实验置ICMP,置为1unsigned int destIP; //16位IP首部校验和最初置零等所有包头都填写正确后计算并替换. //32}IP_HEADER; 位源IP地址
2 ICMP报头格式 //32位目的IP地址
//定义ICMP首部
typedef struct_icmphdr{ 〃8位类型 本实验用8:ECHO 0:ECHO REPLYun sig ned char i_type; 〃8位代码,本实验置零un sig ned char i_code; 〃 16位校验和 从TYPE开始,直到最后一位用户数据 如果为字节数为奇数unsigned short i_cksum; 则补充一位
//识别号一般用进程号作为识别号 用于匹配ECHO和ECHO REPLY包unsigned short i_id; 〃报文序列号 用于标记ECHO报文顺序
//时间戳un sig ned short i_seq ;un sig ned int timestamp;
}ICMP_HEADER;
3总体设计
ICMP协议中的发送
4.VC中网络套接字Win sock编程基础
在VC中进行WINSOC的API编程开发的时候需要在项目中使用下面三个文件 否则会出现编译错误。
1 .WINSOCK.H:这是WINSOCKAP的头文件需要包含在项目中。
2 .WSOCK32.LIB:WINSOCK API连接库文件。在使用中一定要把它作为项目的非缺省的连接库包含到项目文件中去。
3. WINSOCK.DLL:WINSOC的动态连接库位于WINDOW的安装目录下。
几个基本的套接字
1 、 创建套接字——socket()功能使用前创建一个新的套接字
格式 SOCKET PASCAL FAR socket。 nt af, int type, int procotol);
参数 af:通信发生的区域type: 要建立的套接字类型procotol : 使用的特定协议
2、指定本地地址——bind()
功能 将套接字地址与所创建的套接字号联系起来。
格式 int PASCAL FAR bind(SOCKET s,const struct sockaddr FAR*name, int namelen);
参数 s:是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。 其它 没有错误 bind()
返回0 否则SOCKET_ERROR
地址结构说明struct sockaddr_in
{short sin_fami ly;//AF_INETu_short sin_port;//16 位端口号 网络字节顺序struct in_addr sin_addr;//32 位IP地址 网络字节顺序char sin_zero[8];// 保留
}
3 建立套接字连接-----------connect()和accept()
功能 共同完成连接工作
格式 int PASCAL FAR connect(SOCKET s,const struct sockaddr FAR*name, int namelen);
SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR*name, int FAR
*addrlen);
参数 同上
4、 监听连接——l isten()
功能用于面向连接服务器表明它愿意接收连接。格式 int PASCAL FAR l isten(SOCKET s, int backlog);
5、 数据传输——send()与recv()
功能 数据的发送与接收
格式 int PASCAL FAR send(SOCKET s,const char FAR*buf, int len, int flags);int PASCAL FAR recv(SOCKET s,const char FAR*buf, int len, int flags);
参数 b uf:指向存有传输数据的缓冲区的指针。
6、 多路复用——se lect()
功能 用来检测一个或多个套接字状态。
格式 int PASCAL FAR select(int nfds,fd_set FAR* readfds,fd_set FAR*writefds,fd_set FAR*exceptfds,const struct timeval FAR* timeout);
参数
7、 关闭套接字——closesocket()
功能关闭套接字s
格式 BOOL PASCAL FAR closesocket(SOCKET s);
5部分程序代码
//初始化SOCKET
WSADATA wsaData;iErrorCode=WSAStartup(MAKEWORD(2,2),&wsaData);CheckSockError(iErrorCode, "WSAStartup");sockRaw=socket(AF_INET,SOCK_RAW, IPPROTO_ICMP); // 原始套接字
CheckSockError(sockRaw, "socket");
//设置超时时间timeout=time;iErrorCode=setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));//时CheckSockError(iErrorCode, "SO_RCVTIMEO"); 设置接受延timeout=time;iErrorCode=setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,sizeof(timeout));//时
CheckSockError(iErrorCode, "SO_SNDTIMEO");
设置发送延//获得目标主机IP memset(&dest,0,sizeof(dest));//初始化dest结构dest.sin_fami ly=AF_I NET; // 填充SOCKADDR」结构内容if((dest.sin_addr.s_addr=inet_addr(lpdest))==INADDR_NONE)
{
if((hp=gethostbyname(lpdest)) !=NULL) // 目的主机名字不为空
{memcpy(&(dest.sin_addr),hp->h_addr_l ist[0],hp->h_length);dest.sin_fami ly=hp->h_addrtype;printf("dest.sin_addr=%s\n", inet_ntoa(dest.sin_addr));
}else
{
CheckSockError(SOCKET_ERROR, "gethostbyname()");
}
}
{
//创建ICMP数据包datasize+= // 包长sizeof(ICMP_HEADER); icmp_data= 创建icmp数据报内存空间
(char *)mal loc(1024); // recvbuf= 接收icmp包缓冲区
(char *)mal loc(1024); //if((! icmp_data) | | (!recvb uf))
CheckSockError(SOCKET_ERROR, "mal loc()");
}memset(icmp_data,0,MAX_PACKET); //初始化icmp_data Fi l l ICMPData(icmp_data,datasize); //
填充icmp包printf("Pinging%s with%d bytes of data(timeout=%dms):\n\n", inet_ntoa(dest.sin_addr),datasize,timeout );
//发送与接收ICMP数据包whi le(1)
{memset(recvbuf,0,MAX_PACKET); //初始化接受缓冲区static int nCount=0; // 设置发送icmp包的次数一般为4if(nCount++==4)break;
((ICMP_HEADER*)icmp_data)->i_cksum=0; // 初设校验和为0
((ICMP_HEADER*)icmp_data)->timestamp=GetTickCount(); // 获得目前时间
((ICMP_HEADER*)icmp_data)->i_seq=seq_no++; //icmp 数据报的序列号((ICMP_HEADER*)icmp_data)->i_cksum=checksum((USHO RT*)icmp_data,datasize);// 计算校验和iErrorCode=sendto(sockRaw, icmp_data,datasize,0,(structsockaddr*)&dest,sizeof(dest));//发送icmp数据报if(iErrorCode==SOCKET_ERROR) //错误检查
{if(WSAGetLastError()==WSAETIMEDO UT)
{printf("timed out\n");continue;
}
CheckSockError(SOCKET_ERROR, "sendto()");
} if(iErrorCode<datasize)
{printf("Wrote%d bytes\n", iErrorCode);
}int fromlen=sizeof(from);// 接受icmp包长度
iErrorCode=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(structsockaddr*)&from,&fromlen);//接受icmp包if(iErrorCode==SOCKET_ERROR)
{if(WSAGetLastError()==WSAETIMEDO UT)
{printf("timed out\n");continue;
}
CheckSockError(SOCKET_ERROR, "recvfrom()");
}DecodeICMPHeader(recvbuf, iErrorCode,&from); //分解icmp包头
Sleep(1000); //休眠一段时间
}
//SOCK错误处理程序void CheckSockError(int iErrorCode,char *pErrorMsg) {if(iErrorCode==SOCKET_ERROR)
{printf("%s Error:%d\n", pErrorMsg,GetLastError()); closesocket(sockRaw);
ExitProcess(0);
//填充数据void Fi l l ICMPData(char *icmp_data, int datasize)
{
ICMP_HEADER*icmp_hdr=NULL;char *datapart=NULL;icmp_hdr=(ICMP_HEADER*)icmp_data; icmp_hdr->i_type=
ICMP_ECHO; //发送ping//Request an ICMP echoicmp_hdr->i_code=0; //代码字段为0 icmp_hdr->i_id=
(USHORT)GetCurrentProcessId(); // icmp_hdr->i_cksum=0;icmp_hdr->i_seq=0; // 初始化序列号datapart=icmp_data+sizeof(ICMP_HEADER); ////
//Place somejunk in the buffer
// 加上icmp包头memset(datapart, 'E',datasize-sizeof(ICMP_HEADER)); // }
//计算检验和
unsigned long cksum=0;whi le(size> 1)
{cks u m+=*buffe r++;size-=sizeof(US HORT);
}if (size)
{cksu m+=*(UC HAR*)b uffer;
}cksum=(cksum>>16)+(cksum&0xffff);
cksum+=(cksum>>16); return(USHO RT)(~cksum); }
//ICMP解包程序void DecodeICMPHeader(char *buf, int bytes, struct sockaddr_in*from)
{
IP_HEADER*iphdr=NULL;
ICMP_HEADER*icmphdr=NULL; unsigned short iphdrlen;
DWORD tick;iphdr=(IP_HEADER*)buf;
//Number of 32-bit words*4=bytesiphdrlen=sizeof(unsigned long) * (iphdr->h_lenver&0xf); // 计算ip包头长度tick=GetTickCount();if(bytes<iphdrlen+ICMP_MIN) // 数据报太短丢弃
{printf("Too few bytes from%s\n", inet_ntoa(from->sin_addr));
}icmphdr=(ICMP_HEADER*)(buf+iphdrlen);if (icmphdr->i_type!=ICMP_ECHOREPLY) // 不是回送响应(ping应答)丢弃
{printf("nonecho type%d recvd\n", icmphdr->i_type); return;
}
//Make sure this is an ICMP reply to something we sent!
//if (icmphdr->i_id !=(USHORT)GetCurrentProcessId()) //id 号不符合丢弃
{printf("someone else‘s packet! \n");return;
}printf("%d bytes from%s:",bytes, inet_ntoa(from->sin_addr)); // 输出正在使用的ip地址printf(" icmp_seq=%d. ", icmphdr->i_seq); //printf(" time:%d输出序列号ms", tick- icmphdr->timestamp); //printf("\n"); 输出所用时间return;
}
6实验结果
该程序用来检验网络中的一台目标主机是否可达其功能相当与 Windows系统自带的ping命令。例如当程序检验地址为可以返回如下信息。
特网云为您提供高速、稳定、安全、弹性的云计算服务计算、存储、监控、安全,完善的云产品满足您的一切所需,深耕云计算领域10余年;我们拥有前沿的核心技术,始终致力于为政府机构、企业组织和个人开发者提供稳定、安全、可靠、高性价比的云计算产品与服务。公司名:珠海市特网科技有限公司官方网站:https://www.56dr.com特网云为您提供高速、稳定、安全、弹性的云计算服务 计算、存储、监控、安全,完善...
搬瓦工最近新增了荷兰机房中国联通(AS9929、AS4837)线路的VPS产品,选项为NL - China Unicom Amsterdam(ENUL_9),与日本软银和洛杉矶(DC06)CN2 GIA配置一致可以互换,属于高端系列,2.5Gbps-10Gbps大带宽,最低季付46.7美元起。搬瓦工VPS隶属于老牌IT7公司旗下,主要提供基于KVM架构VPS主机,数据中心包括美国洛杉矶、凤凰城、纽...
极光KVM创立于2018年,主要经营美国洛杉矶CN2机房、CeRaNetworks机房、中国香港CeraNetworks机房、香港CMI机房等产品。其中,洛杉矶提供CN2 GIA、CN2 GT以及常规BGP直连线路接入。从名字也可以看到,VPS产品全部是基于KVM架构的。极光KVM也有明确的更换IP政策,下单时选择“IP保险计划”多支付10块钱,可以在服务周期内免费更换一次IP,当然也可以不选择,...