模拟ICMP过程

来源:互联网 发布:苹果mac os x 编辑:程序博客网 时间:2024/06/05 10:22

首先建立common头文件

//////////////////////////////////////////////////// comm.h文件// 包含一些公共函数#ifndef __COMM_H__#define __COMM_H__// 校验和的计算// 以16位的字为单位将缓冲区的内容相加,如果缓冲区长度为奇数,// 则再加上一个字节。它们的和存入一个32位的双字中USHORTchecksum(USHORT* buff, int size);BOOLSetTTL(SOCKET s, int nValue);BOOLSetTimeout(SOCKET s, int nTime, BOOL bRecv = TRUE);#endif // __COMM_H__

然后建立数据包头头文件

//////////////////////////////////////////////////// protoinfo.h文件/*定义协议格式定义协议中使用的宏 */#include <windows.h>#ifndef __PROTOINFO_H__#define __PROTOINFO_H__#define ETHERTYPE_IP    0x0800#define ETHERTYPE_ARP   0x0806typedef struct _ETHeader         // 14字节的以太头{UCHARdhost[6];// 目的MAC地址destination mac addressUCHARshost[6];// 源MAC地址source mac addressUSHORTtype;// 下层协议类型,如IP(ETHERTYPE_IP)、ARP(ETHERTYPE_ARP)等} ETHeader, *PETHeader;#define ARPHRD_ETHER 1// ARP协议opcodes#defineARPOP_REQUEST1// ARP 请求#defineARPOP_REPLY2// ARP 响应typedef struct _ARPHeader// 28字节的ARP头{USHORThrd;//硬件地址空间,以太网中为ARPHRD_ETHERUSHORTeth_type;//  以太网类型,ETHERTYPE_IP ??UCHARmaclen;//MAC地址的长度,为6UCHARiplen;//IP地址的长度,为4USHORTopcode;//操作代码,ARPOP_REQUEST为请求,ARPOP_REPLY为响应UCHARsmac[6];//源MAC地址UCHARsaddr[4];//源IP地址UCHARdmac[6];//目的MAC地址UCHARdaddr[4];//目的IP地址} ARPHeader, *PARPHeader;// 协议#define PROTO_ICMP    1#define PROTO_IGMP    2#define PROTO_TCP     6#define PROTO_UDP     17typedef struct _IPHeader// 20字节的IP头{    UCHAR     iphVerLen;      // 版本号和头长度(各占4位)    UCHAR     ipTOS;          // 服务类型     USHORT    ipLength;       // 封包总长度,即整个IP报的长度    USHORT    ipID;  // 封包标识,惟一标识发送的每一个数据报    USHORT    ipFlags;      // 标志    UCHAR     ipTTL;      // 生存时间,就是TTL    UCHAR     ipProtocol;     // 协议,可能是TCP、UDP、ICMP等    USHORT    ipChecksum;     // 校验和    ULONG     ipSource;       // 源IP地址    ULONG     ipDestination;  // 目标IP地址} IPHeader, *PIPHeader; // 定义TCP标志#define   TCP_FIN   0x01#define   TCP_SYN   0x02#define   TCP_RST   0x04#define   TCP_PSH   0x08#define   TCP_ACK   0x10#define   TCP_URG   0x20#define   TCP_ACE   0x40#define   TCP_CWR   0x80typedef struct _TCPHeader// 20字节的TCP头{USHORTsourcePort;// 16位源端口号USHORTdestinationPort;// 16位目的端口号ULONGsequenceNumber;// 32位序列号ULONGacknowledgeNumber;// 32位确认号UCHARdataoffset;// 高4位表示数据偏移UCHARflags;// 6位标志位//FIN - 0x01//SYN - 0x02//RST - 0x04 //PUSH- 0x08//ACK- 0x10//URG- 0x20//ACE- 0x40//CWR- 0x80USHORTwindows;// 16位窗口大小USHORTchecksum;// 16位校验和USHORTurgentPointer;// 16位紧急数据偏移量 } TCPHeader, *PTCPHeader;typedef struct _UDPHeader{USHORTsourcePort;// 源端口号USHORTdestinationPort;// 目的端口号USHORTlen;// 封包长度USHORTchecksum;// 校验和} UDPHeader, *PUDPHeader;#endif // __PROTOINFO_H__

common.cpp

//////////////////////////////////////////////////// comm.cpp文件#include <winsock2.h>#include "Ws2tcpip.h"#include "common.h"#include <windows.h>USHORT checksum(USHORT* buff, int size){unsigned long cksum = 0;while(size>1){cksum += *buff++;size -= sizeof(USHORT);}// 是奇数if(size){cksum += *(UCHAR*)buff;}// 将32位的chsum高16位和低16位相加,然后取反cksum = (cksum >> 16) + (cksum & 0xffff);cksum += (cksum >> 16);return (USHORT)(~cksum);}BOOL SetTTL(SOCKET s, int nValue){int ret = ::setsockopt(s, IPPROTO_IP, IP_TTL, (char*)&nValue, sizeof(nValue));return ret != SOCKET_ERROR;}BOOL SetTimeout(SOCKET s, int nTime, BOOL bRecv){int ret = ::setsockopt(s, SOL_SOCKET, bRecv ? SO_RCVTIMEO : SO_SNDTIMEO, (char*)&nTime, sizeof(nTime));return ret != SOCKET_ERROR;}

最后是主文件,接受ICMP回送应答报文。type为0  code为0

///////////////////////////////////////////// ping.cpp文件#include "protoinfo.h"#include "common.h"#include <iostream>#include <stdio.h>typedef struct icmp_hdr{    unsigned char   icmp_type;// 消息类型    unsigned char   icmp_code;// 代码    unsigned short  icmp_checksum;// 校验和// 下面是回显头    unsigned short  icmp_id;// 用来惟一标识此请求的ID号,通常设置为进程ID    unsigned short  icmp_sequence;// 序列号    unsigned long   icmp_timestamp; // 时间戳} ICMP_HDR, *PICMP_HDR;int main(){// 目的IP地址,即要Ping的IP地址char szDestIp[] = "115.25.217.12";// 127.0.0.1WSAData w;if(WSAStartup(MAKEWORD(2,2),&w)==SOCKET_ERROR)return -1;// 创建原始套节字SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);int time=1500;setsockopt(sRaw,SOL_SOCKET,SO_RCVTIMEO,(char *)&time,sizeof(int));// 设置接收超时// 设置目的地址SOCKADDR_IN dest;dest.sin_family = AF_INET;dest.sin_port = htons(0);dest.sin_addr.S_un.S_addr = inet_addr(szDestIp);// 创建ICMP封包char buff[sizeof(ICMP_HDR) + 32];ICMP_HDR* pIcmp = (ICMP_HDR*)buff;// 填写ICMP封包数据,请求一个ICMP回显pIcmp->icmp_type = 8;pIcmp->icmp_code = 0;pIcmp->icmp_id = (USHORT)::GetCurrentProcessId();pIcmp->icmp_checksum = 0;pIcmp->icmp_sequence = 0;// 填充数据部分,可以为任意memset(&buff[sizeof(ICMP_HDR)], 'E', 32);// 开始发送和接收ICMP封包USHORTnSeq = 0;char recvBuf[1024];SOCKADDR_IN from;int nLen = sizeof(from);while(TRUE){static int nCount = 0;int nRet;// ping次数if(nCount++ == 1000)break;pIcmp->icmp_checksum = 0;pIcmp->icmp_timestamp = ::GetTickCount();pIcmp->icmp_sequence = nSeq++;pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR) + 32);nRet = ::sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest));if(nRet == SOCKET_ERROR){printf(" sendto() failed: %d /n", ::WSAGetLastError());return -1;}nRet = ::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);if(nRet == SOCKET_ERROR){if(::WSAGetLastError() == WSAETIMEDOUT){printf(" timed out/n");continue;}printf(" recvfrom() failed: %d\n", ::WSAGetLastError());return -1;}// 下面开始解析接收到的ICMP封包int nTick = ::GetTickCount();if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR)){printf(" Too few bytes from %s \n", ::inet_ntoa(from.sin_addr));}// 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头// (ICMP_HDR*)(recvBuf + sizeof(IPHeader));ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); if(pRecvIcmp->icmp_type != 0)// 回显{printf(" nonecho type %d recvd \n", pRecvIcmp->icmp_type);printf("should quit\n");return -1;}if(pRecvIcmp->icmp_id != ::GetCurrentProcessId()){printf(" someone else's packet! \n");printf("should quit\n");return -1;}printf("从 %s 返回 %d 字节:\n", inet_ntoa(from.sin_addr),nRet);printf(" 数据包序列号 = %d. \t", pRecvIcmp->icmp_sequence);printf(" 延时大小: %d ms\n", nTick - pRecvIcmp->icmp_timestamp);printf(" \n");// 每一秒发送一次就行了::Sleep(1000);}return 0;}


0 0