现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

VC模拟ping发送ICMP数据包

2014-07-30 06:56 工业·编程 ⁄ 共 6286字 ⁄ 字号 暂无评论

1、程序源码

// comm.h文件

// 包含一些公共函数

#ifndef __COMM_H__

#define __COMM_H__

 

// 校验和的计算

// 以16位的字为单位将缓冲区的内容相加,如果缓冲区长度为奇数,

// 则再加上一个字节。它们的和存入一个32位的双字中

USHORT  checksum(USHORT* buff, int size); 

 

BOOL    SetTTL(SOCKET s, int nValue); 

BOOL    SetTimeout(SOCKET s, int nTime, BOOL bRecv = TRUE); 

 

#endif // __COMM_H__

[cpp] view plaincopyprint?

//////////////////////////////////////////////////

// protoinfo.h文件

 

/*

 

定义协议格式

定义协议中使用的宏

 

*/

 

#ifndef __PROTOINFO_H__

#define __PROTOINFO_H__

 

#define ETHERTYPE_IP    0x0800

#define ETHERTYPE_ARP   0x0806

 

typedef struct _ETHeader         // 14字节的以太头

UCHAR   dhost[6];           // 目的MAC地址destination mac address

UCHAR   shost[6];           // 源MAC地址source mac address

USHORT  type;               // 下层协议类型,如IP(ETHERTYPE_IP)、ARP(ETHERTYPE_ARP)等

} ETHeader, *PETHeader; 

 

#define ARPHRD_ETHER    1

 

// ARP协议opcodes

#define ARPOP_REQUEST   1       // ARP 请求  

#define ARPOP_REPLY     2       // ARP 响应

 

typedef struct _ARPHeader       // 28字节的ARP头

USHORT  hrd;                //  硬件地址空间,以太网中为ARPHRD_ETHER

USHORT  eth_type;           //  以太网类型,ETHERTYPE_IP ??

UCHAR   maclen;             //  MAC地址的长度,为6

UCHAR   iplen;              //  IP地址的长度,为4

USHORT  opcode;             //  操作代码,ARPOP_REQUEST为请求,ARPOP_REPLY为响应

UCHAR   smac[6];            //  源MAC地址

UCHAR   saddr[4];           //  源IP地址

UCHAR   dmac[6];            //  目的MAC地址

UCHAR   daddr[4];           //  目的IP地址

} ARPHeader, *PARPHeader; 

 

// 协议

#define PROTO_ICMP    1

#define PROTO_IGMP    2

#define PROTO_TCP     6

#define PROTO_UDP     17

 

typedef 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   0x80

 

typedef struct _TCPHeader       // 20字节的TCP头

USHORT  sourcePort;         // 16位源端口号

USHORT  destinationPort;    // 16位目的端口号

ULONG   sequenceNumber;     // 32位序列号

ULONG   acknowledgeNumber;  // 32位确认号

UCHAR   dataoffset;         // 高4位表示数据偏移

UCHAR   flags;              // 6位标志位

//FIN - 0x01

//SYN - 0x02

//RST - 0x04

//PUSH- 0x08

//ACK- 0x10

//URG- 0x20

//ACE- 0x40

//CWR- 0x80

 

USHORT  windows;            // 16位窗口大小

USHORT  checksum;           // 16位校验和

USHORT  urgentPointer;      // 16位紧急数据偏移量

} TCPHeader, *PTCPHeader; 

 

typedef struct _UDPHeader 

USHORT          sourcePort;     // 源端口号    

USHORT          destinationPort;// 目的端口号       

USHORT          len;            // 封包长度

USHORT          checksum;       // 校验和

} UDPHeader, *PUDPHeader; 

 

#endif // __PROTOINFO_H__

[cpp] view plaincopyprint?

//////////////////////////////////////////////////

// comm.cpp文件

 

#include <winsock2.h>

#include <windows.h>

#include "Ws2tcpip.h"

 

#include "comm.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; 

[cpp] view plaincopyprint?

///////////////////////////////////////////

// ping.cpp文件

 

#include "../common/initsock.h"

#include "../common/protoinfo.h"

#include "../common/comm.h"

 

#include <stdio.h>

 

CInitSock theSock; 

 

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[] = "119.147.15.13";  // 127.0.0.1

 

// 创建原始套节字

    SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 

 

// 设置接收超时

    SetTimeout(sRaw, 1000, TRUE); 

 

// 设置目的地址

    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封包

USHORT  nSeq = 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); 

return -1; 

        } 

 

if(pRecvIcmp->icmp_id != ::GetCurrentProcessId()) 

        { 

            printf(" someone else's packet! /n"); 

return -1; 

        } 

 

        printf("从 %s 返回 %d 字节:", inet_ntoa(from.sin_addr),nRet); 

        printf(" 数据包序列号 = %d. /t", pRecvIcmp->icmp_sequence); 

        printf(" 延时大小: %d ms", nTick - pRecvIcmp->icmp_timestamp); 

        printf(" /n"); 

 

// 每一秒发送一次就行了

        ::Sleep(1000); 

    } 

return 0; 

2、打开OmniPeek,运行程序并抓包分析截图如下:

OmniPeek抓包分析

给我留言

留言无头像?