2022年网络实验指导ICMP协议的分析与实现 .pdf
1 实验:ICMP 协议的分析与实现实验目的 分析 ICMP 报文,理解ICMP 协议在 Internet 网中的具体应用及其实现原理,深入了解TCP/IP 网络的容错控制;学会运用网络套接字Winsock 开发网络通信程序。实验内容 使用 Visual Studio C+6.0 和网络接口套接字Socket 进行 Windows 环境下的网络编程,运用原始嵌套字RAW_SOCKET 从 IP 层开始构造整个ICMP 报文,通过ICMP 协议所提供的回送请求(echo request)和回送应答(echo reply)这两种报文实现检测目的站的可达性与状态。1IP 报头、ICMP 报文的基本描述 IP协议并不能保证绝对的可靠,所以就设计了ICMP协议,进行差错报告.ICMP 消息使用IP 头作为基本控制.IP 头的格式如下:0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Version|IHL|Type of Service|Total Length|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Identification|Flags|Fragment Offset|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Time to Live|Protocol|Header Checksum|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Source Address|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Destination Address|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Version=4 IHL Internet头长 Type of Service=0 Total Length IP包的总长度 Identification,Flags,Fragment Offset 用于 IP 包分段 Time to Live IP包的存活时长 Protocol ICMP=1 Header Checksum 头校验和(检查整个IP 报头)Addresses 发送 Echo 消息的源地址是发送Echo reply消息的目的地址,相反,发送 Echo 消息的目的地址是发送Echo reply消息的源地址.名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 10 页 -2 Echo 或 Echo Reply 消息格式如下:0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Type|Code|Checksum|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Identifier|Sequence Number|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Data|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Type echo消息的类型为8 echo 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 char h_lenver;/4 位 IP 版本号+4 位首部长度unsigned char tos;/8 位服务类型TOS unsigned short total_len;/16 位 IP 包总长度(字节)unsigned short ident;/1 6 位标识,用于辅助IP 包的拆装,本实验不用,置零unsigned short frag_and_flags;/3 位标志位+13 位偏移位,也是用于 IP 包的拆装,本实验不用,置零unsigned char ttl;/8 位 IP 包生存时间TTL unsigned char proto;/8 位协议(TCP,UDP 或其他),本实验置ICMP,置为 1 unsigned short checksum;/16 位 IP 首部校验和,最初置零,等所有包头都填写正确后,计算并替换.unsigned int sourceIP;/32 位源 IP 地址unsigned int destIP;/32 位目的 IP 地址IP_HEADER;(2)ICMP 报头格式/定义 ICMP 首部名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 10 页 -3 typedef struct _icmphdr unsigned char i_type;/8 位类型,本实验用8:ECHO 0:ECHO REPLY unsigned char i_code;/8 位代码,本实验置零unsigned short i_cksum;/16 位校验和,从 TYPE 开始,直到最后一位用户数据,如果为字节数为奇数则补充一位unsigned short i_id;/识别号(一般用进程号作为识别号),用于匹配ECHO 和 ECHO REPLY 包unsigned short i_seq;/报文序列号,用于标记ECHO 报文顺序unsigned int timestamp;/时间戳ICMP_HEADER;3总体设计ICMP 协议中的发送、接收ICMP 回送请求报文,回送应答报文流程图。数据包太短?不是回送响?ID 不符合?输出数据报中的IP 地址填充 ICMP 数据报发送数据报接受数据报去掉IP 报头,获取ICMP 信息结束yy y N N N 开始名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 10 页 -4 4VC中网络套接字 Winsock 编程基础在 VC中进行 WINSOCK 的 API 编程开发的时候,需要在项目中使用下面三个文件,否则会出现编译错误。1WINSOCK.H:这是 WINSOCK API 的头文件,需要包含在项目中。2WSOCK32.LIB:WINSOCK API连接库文件。在使用中,一定要把它作为项目的非缺省的连接库包含到项目文件中去。3WINSOCK.DLL:WINSOCK 的动态连接库,位于WINDOWS 的安装目录下。几个基本的套接字:1、创 建 套 接 字 socket()功 能:使 用 前 创 建 一 个 新 的 套 接 字格 式:SOCKET PASCAL FAR socket(int 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_family;/AF_INET u_short sin_port;/16位 端 口 号,网 络 字 节 顺 序struct in_addr sin_addr;/32位 IP 地 址,网 络 字 节 顺 序char sin_zero8;/保 留 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、监 听 连 接 listen()功 能:用 于 面 向 连 接 服 务 器,表 明 它 愿 意 接 收 连 接。格 式:int PASCAL FAR listen(SOCKET s,int backlog);5、数 据 传 输 send()与 recv()名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 10 页 -5 功 能:数 据 的 发 送 与 接 收格 式: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);参 数:buf:指 向 存 有 传 输 数 据 的 缓 冲 区 的 指 针。6、多 路 复 用 select()功 能:用 来 检 测 一 个 或 多 个 套 接 字 状 态。格 式:int PASCAL FAR select(int nfds,fd_set FAR*readfds,fd_set FAR*writefds,fd_set FAR*exceptfds,const struct timeval FAR*timeout);参 数:readfds:指 向 要 做 读 检 测 的 指 针writefds:指 向 要 做 写 检 测 的 指 针exceptfds:指 向 要 检 测 是 否 出 错 的 指 针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);名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 10 页 -6/获得目标主机IP memset(&dest,0,sizeof(dest);/初始化 dest 结构 dest.sin_family=AF_INET;/填充 SOCKADDR_IN 结构内容 if(dest.sin_addr.s_addr=inet_addr(lpdest)=INADDR_NONE)if(hp=gethostbyname(lpdest)!=NULL)/目的主机名字不为空 memcpy(&(dest.sin_addr),hp-h_addr_list0,hp-h_length);dest.sin_family=hp-h_addrtype;printf(dest.sin_addr=%sn,inet_ntoa(dest.sin_addr);else CheckSockError(SOCKET_ERROR,gethostbyname();/创建 ICMP数据包 datasize+=sizeof(ICMP_HEADER);/包长 icmp_data=(char*)malloc(1024);/创建 icmp 数据报内存空间 recvbuf=(char*)malloc(1024);/接收 icmp 包缓冲区 if(!icmp_data)|(!recvbuf)CheckSockError(SOCKET_ERROR,malloc();memset(icmp_data,0,MAX_PACKET);/初始化 icmp_data FillICMPData(icmp_data,datasize);/填充 icmp 包printf(Pinging%s with%d bytes of data(timeout=%d ms):nn,inet_ntoa(dest.sin_addr),datasize,timeout);/发送与接收ICMP数据包 while(1)memset(recvbuf,0,MAX_PACKET);/初始化接受缓冲区static int nCount=0;/设置发送icmp 包的次数,一般为4 if(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(USHORT*)icmp_data,datasize);/计算校验和名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 10 页 -7 iErrorCode=sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest);/发送 icmp 数据报if(iErrorCode=SOCKET_ERROR)/错误检查 if(WSAGetLastError()=WSAETIMEDOUT)printf(timed outn);continue;CheckSockError(SOCKET_ERROR,sendto();if(iErrorCode i_type=ICMP_ECHO;/发送 ping /Request an ICMP echo icmp_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);/加上 icmp 包头/Place some junk in the buffer /memset(datapart,E,datasize-sizeof(ICMP_HEADER);/填充 datapart /计算检验和USHORT checksum(USHORT*buffer,int size)unsigned long cksum=0;while(size 1)cksum+=*buffer+;size-=sizeof(USHORT);if(size)cksum+=*(UCHAR*)buffer;cksum=(cksum 16)+(cksum&0 xffff);名师资料总结-精品资料欢迎下载-名师精心整理-第 8 页,共 10 页 -9 cksum+=(cksum 16);return(USHORT)(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=bytes iphdrlen=sizeof(unsigned long)*(iphdr-h_lenver&0 xf);/计算 ip 包头长度 tick=GetTickCount();if(bytes sin_addr);icmphdr=(ICMP_HEADER*)(buf+iphdrlen);if(icmphdr-i_type!=ICMP_ECHOREPLY)/不是回送响应(ping 应答),丢弃 printf(nonecho type%d recvdn,icmphdr-i_type);return;/Make sure this is an ICMP reply to something we sent!/if(icmphdr-i_id!=(USHORT)GetCurrentProcessId()/id号不符合,丢弃 printf(someone elses 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 命令。例如当程序检验地址为218.199.74.46的目标主机时,可以返回如下信息。名师资料总结-精品资料欢迎下载-名师精心整理-第 9 页,共 10 页 -10 名师资料总结-精品资料欢迎下载-名师精心整理-第 10 页,共 10 页 -