`

rawsocket发送icmp包

 
阅读更多
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;

#define PING_DATA_LEN 56

//ICMP消息头部
struct ICMPHeader
{
    unsigned char type;//消息类型
    unsigned char code;//消息代码
    unsigned short checksum;//校验和
    union{
        struct{
            unsigned short id;
            unsigned short sequence;
        }echo;
        unsigned int gateway;
        struct{
            unsigned short unsed;
            unsigned short nextmtu;
        }frag; //pmtu实现
    }un;
    unsigned char data[0];//ICMP数据占位符
};

struct IPHeader
{
    unsigned char headerLen:4;
    unsigned char version:4;
    unsigned char tos; //服务类型
    unsigned short totalLen; //总长度
    unsigned short id; //标识
    unsigned short flagOffset; //3位标志+13位片偏移
    unsigned char ttl; //TTL
    unsigned char protocol; //协议
    unsigned short checksum; //首部检验和
    unsigned int srcIP; //源IP地址
    unsigned int dstIP; //目的IP地址
};


//This function calculates the 16-bit one's complement sum
//of the supplied buffer (ICMP) header
unsigned short checksum(unsigned short* buffer, int size)
{
    unsigned long cksum = 0;

    while (size > 1)
    {
        cksum += *buffer++;
        size -= sizeof(unsigned short);
    }

    if (size)
    {
        cksum += *(unsigned char*)buffer;
    }

    cksum = (cksum>>16) + (cksum & 0xffff);
    cksum += (cksum>>16);

    return (unsigned short)(~cksum);
}

//校验和算法
unsigned short cal_chksum(unsigned short *addr,int len)
{
    int nleft=len;
    int sum=0;
    unsigned short *w=addr;
    unsigned short answer=0;

    //把ICMP报头二进制数据以2字节为单位累加起来
    while(nleft>1)
    {
        sum+=*w++;
        nleft-=2;
    }

    //若ICMP报头为奇数个字节,会剩下最后一字节。把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加
    if( nleft==1)
    {
        *(unsigned char *)(&answer)=*(unsigned char *)w;
        sum+=answer;
    }
    sum=(sum>>16)+(sum&0xffff);
    sum+=(sum>>16);
    answer=~sum;
    return answer;
}

//ip数字转字符串
void ip_ll_to_str(long long ip_num,char* ip_str)
{
    unsigned int iptok1 = (ip_num & 0xFF000000) >> 24;
    unsigned int iptok2 = (ip_num & 0x00FF0000) >> 16;
    unsigned int iptok3 = (ip_num & 0x0000FF00) >> 8;
    unsigned int iptok4 = ip_num & 0x000000FF;
    char ip[32];
    bzero(ip,sizeof(ip));
    snprintf(ip,sizeof(ip),"%d.%d.%d.%d",iptok1,iptok2,iptok3,iptok4);
    strcpy(ip_str,ip);
}

//发送ICMP报文
void send_icmp_packet(int sockfd,sockaddr_in* dst_addr,int pid,bool build_ip_protocl)
{
    char sendBuf[1024] = "";

    int totalLen = sizeof(IPHeader) + sizeof(ICMPHeader)+PING_DATA_LEN;
    int pos = 0;
    if(build_ip_protocl)
    {
        IPHeader* ipHeader = (IPHeader *)sendBuf;
        ipHeader->headerLen = sizeof(IPHeader)>>2;
        ipHeader->version = IPVERSION;
        //服务类型
        ipHeader->tos = 0;
        ipHeader->totalLen = htons(totalLen);
        ipHeader->id=0;
        //设置flag标记为0
        ipHeader->flagOffset=0;
        //运用的协议为ICMP协议
        ipHeader->protocol=IPPROTO_ICMP;
        //一个封包在网络上可以存活的时间
        ipHeader->ttl=255;
        //目的地址
        ipHeader->dstIP = dst_addr->sin_addr.s_addr;
        pos = sizeof(IPHeader);
    }

    ICMPHeader *icmpHeader = (ICMPHeader*)(sendBuf+pos);
    icmpHeader->type = ICMP_ECHO;
    icmpHeader->code = 0;
    icmpHeader->un.echo.id = pid;

    //计算校验和
    icmpHeader->checksum = cal_chksum( (unsigned short *)icmpHeader,totalLen);

    IPHeader* ipHeader = (IPHeader *)sendBuf;
    char ipHeaderStr[256] = "";
    char srcIPStr[64] = "",dstIPStr[64]="";
    ip_ll_to_str(ipHeader->srcIP,srcIPStr);
    ip_ll_to_str(ipHeader->dstIP,dstIPStr);
    snprintf(ipHeaderStr,sizeof(ipHeaderStr),"request ip header info: version:%d,tos:%d,protocol:%d,ttl:%d,srcIP:%s,dstIP:%s",ipHeader->version,ipHeader->tos,ipHeader->protocol,ipHeader->ttl,srcIPStr,dstIPStr);
    cout << ipHeaderStr << endl;

    if(sendto(sockfd,sendBuf,totalLen,0,(struct sockaddr *)dst_addr,sizeof(*dst_addr))<0){
        perror("sendto error");
    }
}


//接收解析ICMP报文
void parse_icmp_packet(int sockfd,int pid)
{
    sockaddr_in cliaddr;
    bzero(&cliaddr,sizeof(cliaddr));
    socklen_t cliLen = sizeof(cliaddr);
    char recvBuf[256] = "";
    int recvLen = recvfrom(sockfd,recvBuf,sizeof(recvBuf),0,(sockaddr*)&cliaddr,&cliLen);
    if( recvLen <0)
    {
        if(errno==EINTR){
            return;
        }
        printf("recvfrom error:%s",strerror(errno));
        return;
    }

    IPHeader *ipHeader = (IPHeader*)recvBuf;
    char ipHeaderStr[256] = "";
    char srcIPStr[64] = "",dstIPStr[64]="";
    ip_ll_to_str(ntohl(ipHeader->srcIP),srcIPStr);
    ip_ll_to_str(ntohl(ipHeader->dstIP),dstIPStr);
    snprintf(ipHeaderStr,sizeof(ipHeaderStr),"response ip header info: version:%d,tos:%d,protocol:%d,ttl:%d,srcIP:%s,dstIP:%s",ipHeader->version,ipHeader->tos,ipHeader->protocol,ipHeader->ttl,srcIPStr,dstIPStr);
    cout << ipHeaderStr << endl;

    int ipHeaderLen = sizeof(IPHeader);
    ICMPHeader *icmpHeader = (ICMPHeader *)(recvBuf+sizeof(IPHeader));  //越过ip报头,指向ICMP报头
    int icmpLen = recvLen - ipHeaderLen;
    //小于ICMP报头长度则不合理
    if( icmpLen < 8)
    {
        printf("ICMP packets/'s length is less than 8/n");
        exit(1);
    }
    //确保所接收的是我所发的的ICMP的回应
    if(icmpHeader->type!=ICMP_ECHOREPLY || icmpHeader->un.echo.id!=pid){
        exit(1);
    }
    char icmpStr[256] = "";
    snprintf(icmpStr,sizeof(icmpStr),"%d byte from %s: icmp_seq=%d,ttl=%dms",icmpLen,inet_ntoa(cliaddr.sin_addr),icmpHeader->un.echo.sequence,ipHeader->ttl);
    cout << icmpStr << endl;
}

int main()
{
    int sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
    if( sockfd < 0)
    {
        cout << strerror(errno) << endl;
        return -1;
    }

    //扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答
    int bufSize=50*1024;
    setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&bufSize,sizeof(bufSize) );

    //是否自己构造ip协议头
    bool build_ip_protocol = true;

    if(build_ip_protocol)
    {
        int on = 1;
        setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));
    }

    //用主机名或ip地址都可以
    //www.baidu.com
    char dst_str[32] = "220.181.112.244";
    sockaddr_in dst_addr;
    bzero(&dst_addr,sizeof(dst_addr));
    dst_addr.sin_family=AF_INET;
    dst_addr.sin_addr.s_addr = inet_addr(dst_str);

    if( dst_addr.sin_addr.s_addr == INADDR_NONE)
    {
        hostent *host=gethostbyname(dst_str);
        if(host==NULL) //是主机名
        {
            cout << "gethostbyname error:" << strerror(errno) <<endl;
            return -1;
        }
        memcpy( (char *)&dst_addr.sin_addr,host->h_addr,host->h_length);
    }

    //获取main的进程id,用于设置ICMP的标志符
    int pid=getpid();

    //发送ICMP报文
    send_icmp_packet(sockfd,&dst_addr,pid,build_ip_protocol);

    cout << "PING " << dst_str << "(" << inet_ntoa(dst_addr.sin_addr) << ") " << PING_DATA_LEN << " bytes data in ICMP packets" << endl;

    //解析所有ICMP报文
    parse_icmp_packet(sockfd,pid);
    close(sockfd);
    return 0;

}


//这里的srcIP是0.0.0.0,但实际发出去的报文是我本地的ip,也就是说如果你没设置IP,则系统会自动给你加上IP
request ip header info: version:4,tos:0,protocol:1,ttl:255,srcIP:0.0.0.0,dstIP:244.112.181.220
PING 220.181.112.244(220.181.112.244) 56 bytes data in ICMP packets
response ip header info: version:4,tos:0,protocol:1,ttl:45,srcIP:220.181.112.244,dstIP:172.16.96.52
64 byte from 220.181.112.244: icmp_seq=0,ttl=45ms

分享到:
评论

相关推荐

    利用RawSocket实现简单的Ping程序 ICMP协议

    4. **发送ICMP报文**:使用`sendto()`函数将构造好的ICMP报文发送到目标主机。 5. **接收ICMP响应**:使用`recvfrom()`函数等待接收ICMP回显应答。因为是Raw Socket,所以会收到完整的ICMP报文,包括回显应答的类型...

    raw socket发送报文

    在计算机网络编程中,"raw socket发送报文"是一个高级话题,主要涉及到网络协议栈的底层操作。Raw sockets允许程序员直接操作数据链路层(如Ethernet或PPP)的数据包,而不是通过传输层协议(如TCP或UDP)进行通信。...

    RawSend_RawSocket基于MAC发送_sock_raw_C++_原始Socket发送_

    在本主题中,“RawSend_RawSocket基于MAC发送_sock_raw_C++_原始Socket发送_”指的是使用C++语言通过原始套接字(Raw Socket)向指定MAC地址发送以太网数据帧的过程。以下将详细介绍这一技术及其相关知识点。 1. **...

    raw socket来实现ip报文的发送

    Raw Socket提供了一种直接访问网络底层协议(如IP、ICMP等)的能力,允许程序员创建不依赖于任何特定传输协议(如TCP或UDP)的数据包。在Linux或Windows系统中,可以使用`socket()`函数创建一个RAW类型套接字,通常...

    linux下raw_socket模拟ping程序

    linux下c语言中使用raw_socket模拟ping程序,给目标服务器发送ICMP包,并能接受和统计所发的包

    多线程实现无连接的Raw Socket通信.rar

    在本项目中,多线程用于同时处理多个Raw Socket通信任务,比如接收和发送数据可能由不同的线程负责。在Windows上,可以使用`CreateThread()`函数创建新线程,或者使用`std::thread`库(如果项目使用C++11及以上版本...

    raw socket demo

    在压缩包文件名称列表中,"rawsocket"可能是源代码文件或相关资源文件的名称。实际操作时,可以解压该文件,查看源代码以了解如何在具体编程语言中实现raw socket的功能。 总结,raw sockets提供了一种直接与网络...

    raw socket 大合集

    原始套接字常用于实现ping程序,通过发送ICMP回显请求报文并接收响应,检测网络连通性。它也可以用于执行traceroute,通过发送不同TTL值的ICMP报文来跟踪数据包在网络中的路径。 ### 5. TCP/IP嗅探 原始套接字可...

    发送ICMP_EACHO请求报文

    在C语言中实现发送ICMP_ECHO请求报文是一项技术挑战,因为这涉及到低级别的网络编程。下面将详细讲解这个过程涉及的关键知识点: 1. **网络编程基础知识**:首先,你需要理解TCP/IP协议栈的基本结构,包括网络接口...

    用RawSocket实现的Sniffer的C++Builder源程序

    标题中的“用RawSocket实现的Sniffer的C++Builder源程序”揭示了这是一个使用C++Builder编程环境,通过Raw Socket技术开发的网络嗅探器(Sniffer)项目。网络嗅探器是一种工具,用于捕获并分析网络上的数据包,这...

    封装并发送ICMP数据包

    "封装并发送ICMP数据包"这个主题涉及到的是通过编程手段实现对ICMP报文的构造和发送,通常在诊断网络连接、测量网络延迟等方面有广泛应用,如我们熟知的`ping`命令。 在C或C++中实现这个功能,你需要了解以下关键...

    原始套接字发送iCMP自定义头部版本

    在这个主题中,“原始套接字发送iCMP自定义头部版本”指的是利用原始套接字来创建和发送带有自定义头部的ICMP(Internet Control Message Protocol,互联网控制消息协议)数据包。 ICMP是IP协议的一部分,主要用于...

    原始套接字透析之Raw Socket基础.pdf

    创建并绑定Raw Socket后,就可以通过`sendto()`或`recvfrom()`函数发送和接收IP数据包。需要注意的是,由于Raw Socket不处理协议层的封装和解封装,因此需要程序员自己处理这些细节,例如TCP头、UDP头或ICMP头的构建...

    raw_socket_ping.zip_PEP_RAW_ping程序_raw ping_raw_socket 写ping

    它通过发送ICMP回显请求报文到目标主机,然后等待并解析响应的回显应答报文,以此来判断网络连接是否畅通。 **Raw Socket的介绍** Raw Socket是操作系统提供的一个接口,允许用户直接操作网络协议栈,绕过高层的TCP...

    原始套接字透析之Raw Socket基础 (2).docx

    一旦Socket与地址绑定,就可以开始使用Raw Socket发送和接收IP包。发送数据时,可以使用`sendto`或`send`函数,接收则可以使用`recvfrom`或`recv`。需要注意的是,由于原始套接字不涉及协议层的处理,因此发送的数据...

    利用ICMP数据包探测网络中的活动主机 VC++

    探测过程通常涉及发送ICMP回显请求到目标网络,然后接收或监听回应,以此判断主机是否在线。 在VC++中实现这个功能,首先需要包含必要的头文件,如`winsock2.h`和`ws2tcpip.h`,并链接`ws2_32.lib`库。接下来,需要...

    原始套接字透析之Raw Socket基础.docx

    一旦Raw Socket创建并绑定,就可以使用`sendto()`或`recvfrom()`函数发送和接收IP包。需要注意的是,由于Raw Socket绕过了传输层,所以开发者需要自己处理诸如校验和、序列号等传输层协议细节。同时,Raw Socket权限...

    精选_使用原始套接字Raw Socket实现数据包嗅探_源码打包

    源码打包中的`rawsocket_test`文件很可能是包含了一个示例程序,用于演示如何使用原始套接字进行数据包嗅探。这个程序可能包括了以下部分: 1. **套接字创建**:调用`socket()`函数创建一个原始套接字。 2. **设置...

    Socket Raw通信.zip

    Raw Socket允许程序员访问网络协议栈的较低层,如IP或ICMP,从而可以直接构建和发送这些协议的数据包。这种直接控制能力使得开发人员能够构建出更灵活和特定需求的网络应用。然而,这也意味着更多的责任,比如需要...

Global site tag (gtag.js) - Google Analytics