原始套接口的ping命令编程

原始套接口(SOCK_RAW)允许对较低层协议(如IP或ICMP)进行直接访问,常用于检验新的网络协议实现,也可用于测试新配置或安装的网络设备。创建一个原始套接口时,一般格式如下: 

SOCKET sockid =socket (AF_INET ,SOCK_RAW ,IPPROTO_ICMP ); //ICMP协议
SOCKET sockid =socket (AF_INET ,SOCK_RAW ,IPPROTO_IP ); //IP协议

以下是利用ICMP协议实现的PING命令:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <WINSOCK2.H>  
#pragma comment(lib,"ws2_32")  
#include <stdio.h>  
  
//--------------------①头部格式--------------  
/************************************************************************/  
/* IP头部                                                               */  
/************************************************************************/  
typedef struct IP_Header//20B的IP固定头部  
{  
    UCHAR ipVer;        //1B 4b版本号+4b头长度  
    UCHAR ipTOS;        //1B 服务类型  
    USHORT ipLength;    //2B 总长度(IP数据报的长度)  
    USHORT ipID;        //2B 标识(唯一标识发送的每一个数据报)  
    USHORT ipFlags;        //2B 3b标志+13b分段偏移  
    UCHAR ipTTL;        //1B 生存时间TTL  
    UCHAR ipProtocol;    //1B 上层协议标识(TCP、UDP、ICMP)      
    USHORT ipChecksum;    //2B 头部效验和  
    ULONG ipSource;        //4B 源IP地址  
    ULONG ipDestination;//4B 目的IP地址  
}IPHDR,*PIPHDR;  
/************************************************************************/  
/* ICMP头部                                                             */  
/************************************************************************/  
typedef struct ICMP_Header//8B的ICMP头部  
{  
    UCHAR icmp_type;    //1B 类型  
    UCHAR icmp_code;    //1B 代码  
    USHORT icmp_checksum;//2B 效验和  
    USHORT icmp_id;        //2B 标识符(通常设置为进程ID)  
    USHORT icmp_seq;    //2B 序号(0~3)  
}ICMPHDR,*PICMPHDR;  
//------------------------②函数声明-----------------------  
USHORT checksum(USHORT *buff,int size);  
int SetTimeout(SOCKET s,long nTime);  
//------------------------③main函数-----------------------  
int main()  
{  
    WSADATA wsaData;  
    if (WSAStartup(MAKEWORD(2,2),&wsaData)!=0)   
    {   
        printf("WSAStartup Failed,Error=【%d】\n",WSAGetLastError());   
        return 1;   
    }   
    else   
        printf("加载成功\n");   
    //----------创建原始套接字----------------  
    SOCKET sRaw=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);//创建原始套接字  
    //----------设置接收超时-----------------  
    struct timeval tv;  
    tv.tv_sec=1;  
    int F=SetTimeout(sRaw,tv.tv_sec);  
    if (F)  
    {  
        printf("setsockopt() Failed,Error=%d\n",WSAGetLastError());  
        return 1;  
    }  
    else  
        printf("设置接收超时为%d秒\n",tv.tv_sec);  
    //----------绑定-------------  
    struct sockaddr_in Sadd;  
    Sadd.sin_family=AF_INET;  
    Sadd.sin_port=htons(1111);  
    Sadd.sin_addr.s_addr=inet_addr("192.168.31.1");  
    if (bind(sRaw,(sockaddr*)&Sadd,sizeof(Sadd))==SOCKET_ERROR)  
    {  
        printf("bind() Failed,Error=%d\n",WSAGetLastError());  
        return 1;  
    }  
    else  
        printf("绑定成功,本地IP地址:【%s】,端口号:【%d】\n",inet_ntoa(Sadd.sin_addr),ntohs(Sadd.sin_port));  
    //---------目的IP地址(要ping的IP地址)--------------  
    struct sockaddr_in Dest;  
    Dest.sin_family=AF_INET;  
    Dest.sin_addr.s_addr=inet_addr("192.168.32.1");  
    //----------创建ICMP包----------------  
    char buff[sizeof(ICMPHDR)];        //char buff[sizeof(ICMPHDR)+32];  
    PICMPHDR pIcmp=(ICMPHDR*)buff;  
    // 1)填写ICMP包(公共部分)  
    pIcmp->icmp_type=8;  
    pIcmp->icmp_code=0;  
    pIcmp->icmp_id=(USHORT)GetCurrentProcessId();    //当前进程ID  
    //memset(&buff[sizeof(ICMPHDR)],'E',32);  
    //----------开始准备发送和接收ICMP封包--------------  
    char recvBuf[200];  
    struct sockaddr_in R_Dest;  
    int L_R_Dest=sizeof(struct sockaddr_in);  
    while (true)  
    {  
        static USHORT nSeq=0;    //seq由0开始  
        static int nCount=0;    //发送4个包  
        if (nCount++==4)  
            break;  
        //2)继续填写ICMP封包数据(可变部分)  
        pIcmp->icmp_checksum=0;  
        pIcmp->icmp_seq=nSeq++;  
        pIcmp->icmp_checksum=checksum((USHORT*)buff,sizeof(ICMPHDR));    //效验  
        //pIcmp->icmp_checksum=checksum((USHORT*)buff,sizeof(ICMPHDR)+32);  
        //-------------发送-------------  
        int time_send=GetTickCount();  
        int nRet=sendto(sRaw,buff,sizeof(ICMPHDR),0,(SOCKADDR*)&Dest,sizeof(Dest));  
        //int nRet=sendto(sRaw,buff,sizeof(ICMPHDR)+32,0,(SOCKADDR*)&Dest,sizeof(Dest));  
        if (nRet==SOCKET_ERROR)  
        {  
            printf("sendto() Failed,Error=%d\n",WSAGetLastError());  
            return 1;  
        }  
        //-------------接收---------------  
        int RRet=recvfrom(sRaw,recvBuf,sizeof(recvBuf),0,(sockaddr*)&R_Dest,&L_R_Dest);  
        int time_recv=GetTickCount();  
        if (nRet==SOCKET_ERROR)  
        {  
            if (WSAGetLastError()==WSAETIMEDOUT)  
            {  
                printf("timed out\n");  
                continue;  
            }  
            printf("recvfrom() Failed,Error=%d\n",WSAGetLastError());  
            return 1;  
        }  
        //------------解析----------------  
        PICMPHDR pRecvIcmp=(ICMPHDR*)(recvBuf+sizeof(IPHDR));  
        if (RRet<(nRet+sizeof(IPHDR)))  
            printf("从%s收到的数据过少\n",inet_ntoa(R_Dest.sin_addr));  
        if (pRecvIcmp->icmp_type!=0)    //不是应答  
        {  
            printf("这不是我要接收的应答报文,该报文类型为%d\n",pRecvIcmp->icmp_type);  
            return 1;  
        }  
        if (pRecvIcmp->icmp_id!=GetCurrentProcessId())  
        {  
            printf("这是别人的包\n");  
            return 1;  
        }  
        printf("%d bytes from %s,icmp_seq=%d,time:%d ms\n",RRet,inet_ntoa(R_Dest.sin_addr),pRecvIcmp->icmp_seq,time_recv-time_send);  
        Sleep(1000);  
    }  
    closesocket(sRaw);  
    WSACleanup();  
    return 0;  
}  
//---------------④效验函数-----------------  
USHORT checksum(USHORT *buff,int size)  
{  
    unsigned long cksum=0;  
    while (size>1)  
    {  
        cksum+=*buff++;  
        size-=2;  
    }  
    if (size==1)  
        cksum+=*(UCHAR*)buff;  
    cksum=(cksum>>16)+(cksum&0xffff);  
    cksum+=(cksum>>16);  
    USHORT ans=(USHORT)~cksum;  
    return (ans);  
}  
//---------------⑤接收超时函数---------------  
int SetTimeout(SOCKET s,long nTime)  
{  
    int ret=setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(char*)&nTime,sizeof(nTime));  
    return ret;  
}  
运行程序效果如下:

原始套接口的ping命令编程_第1张图片

 

你可能感兴趣的:(原始套接口的ping命令编程)