发送Ping(ICMP请求)并读取回复 [英] Send Ping (ICMP request) and read reply

查看:153
本文介绍了发送Ping(ICMP请求)并读取回复的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试发送ICMP(ping请求)并阅读回复。从现在开始,这是正常工作,但是当我想模拟TTL EXPIRED错误时( Ex:使用ttl = 2发送到google.com ) 我从来没有得到答案。但是我在
wireshark中看到了回复。 Windows内核是否在窃取我的数据包?如果是,那么为什么?我将套接字绑定到同一个interfaace,并设置了  SIO_RCVALL
套接字上的1选项。

I'm try to send an ICMP (ping request) and read the reply. From now this is properly working but when I want to simulate an TTL EXPIRED error (Ex : sending with a ttl=2 to google.com) I never get the answer. However I'm seeing the reply in wireshark. Is the windows kernel stealing me the packet ? If yes, then WHY ? I bind the socket to the same interfaace and also I set the SIO_RCVALL options to 1 on the socket.

这就是我所拥有的:

#include <winsock2.h> 
#include <ws2tcpip.h> 
#include <mstcpip.h>
#include <windows.h>
#include <stdint.h>
#include <vector>
#include <algorithm>

struct IPV4_HDR
{
    unsigned char ip_header_len : 4;
    unsigned char ip_version : 4;
    unsigned char ip_tos;
    unsigned short ip_total_length;
    unsigned short ip_id;

    unsigned char ip_frag_offset : 5;

    unsigned char ip_more_fragment : 1;
    unsigned char ip_dont_fragment : 1;
    unsigned char ip_reserved_zero : 1;

    unsigned char ip_frag_offset1;

    unsigned char ip_ttl;
    unsigned char ip_protocol;
    unsigned short ip_checksum;
    unsigned int ip_srcaddr;
    unsigned int ip_destaddr;
};

struct ICMP_HDR
{
    BYTE type;
    BYTE code;
    USHORT checksum;
    USHORT id;
    USHORT seq;
};

unsigned short compute_checksum(unsigned short* buffer, int size)
{
    unsigned long cksum = 0;
    while (size > 1)
    {
        cksum += *buffer++;
        size -= sizeof(unsigned short);
    }
    if (size)
    {
        cksum += *(char*)buffer;
    }

    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);
    return (unsigned short)(~cksum);
}

void send_receive_ping(SOCKET icmp_sock)
{
    std::vector<char> receive_buffer;
	receive_buffer.resize(65536);
	std::fill(receive_buffer.begin(), receive_buffer.end(), 0);
    char *Buffer = receive_buffer.data();
	
	int recv_bytes = 0;
    DWORD start_time = GetTickCount();
    bool first_time_in_loop = true;
    do
    {
        if ( (first_time_in_loop == true 
            || GetTickCount() - start_time > 5000))
        {
            OutputDebugString(L"Sending an ICMP packet....\n");
            send_icmp_packet(icmp_sock);

            first_time_in_loop = false;
            start_time = GetTickCount();
        }

        recv_bytes = recvfrom(icmp_sock, Buffer, 65536, 0, 0, 0);
        if (recv_bytes > 0)
        {
            // Handle received packet
        }
        else
        {
            break;
        }
    } while (recv_bytes > 0);
}

void send_icmp_packet(SOCKET icmp_sock)
{
    sockaddr_in sockaddr_in_dst = {};
    sockaddr_in_dst.sin_family = AF_INET;
    sockaddr_in_dst.sin_port = 0;
    sockaddr_in_dst.sin_addr.s_addr = inet_addr("184.150.168.247"); // google.com

    std::vector<char> send_buffer;
    send_buffer.resize(sizeof(IPV4_HDR) + sizeof(ICMP_HDR));
    std::fill(send_buffer.begin(), send_buffer.end(), 0);

    IPV4_HDR *ipv4_header = (IPV4_HDR *)send_buffer.data();
    ipv4_header->ip_header_len = 5;
    ipv4_header->ip_version = 4;
    ipv4_header->ip_tos = 16;
    ipv4_header->ip_total_length = htons( send_buffer.size() );
    ipv4_header->ip_id = htons(0);
    ipv4_header->ip_ttl = 64;
    //ipv4_header->ip_ttl = 2;
    ipv4_header->ip_protocol = IPPROTO_ICMP;
    ipv4_header->ip_srcaddr = dest.sin_addr.s_addr;
    ipv4_header->ip_destaddr = sockaddr_in_dst.sin_addr.s_addr;
    ipv4_header->ip_checksum = compute_checksum((unsigned short *)ipv4_header, sizeof(IPV4_HDR));

    static unsigned short seq = 0;
    ICMP_HDR *icmp_header = (ICMP_HDR *)(send_buffer.data() + sizeof(IPV4_HDR));
    icmp_header->type = 8;
    icmp_header->seq = seq++;
    icmp_header->id = 888;
    icmp_header->checksum = compute_checksum((unsigned short *)icmp_header, sizeof(ICMP_HDR));

    ret = sendto(icmp_sock, (char *)send_buffer.data(), send_buffer.size(),
        0, (sockaddr *)&sockaddr_in_dst, sizeof(sockaddr_in_dst));
}


int main()
{
    WSADATA ws;
    WSAStartup(MAKEWORD(2, 2), &ws);

    SOCKET icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);

    char hostname[256];
    gethostname(hostname, sizeof(hostname));

    hostent *local = gethostbyname(hostname);

    sockaddr_in source;
    memset(&source, 0, sizeof(source));
    memcpy(&source.sin_addr.s_addr, local->h_addr_list[0], sizeof(source.sin_addr.s_addr));
    source.sin_family = AF_INET;
    source.sin_port = 0;

    bind(icmp_sock, (sockaddr *)&source, sizeof(source));

    int recv_all_opt = 1;
    int ioctl_read = 0;
    WSAIoctl(icmp_sock, SIO_RCVALL, &recv_all_opt, sizeof(recv_all_opt), 0, 0, (LPDWORD)&ioctl_read, 0, 0);
	
    int ip_header_include = 1;
    setsockopt(icmp_sock, IPPROTO_IP, IP_HDRINCL, (char *)&ip_header_include, sizeof(ip_header_include));
	
    send_receive_ping(icmp_sock);

    closesocket(icmp_sock);
    WSACleanup();

    return 0;
}

我也试过使用两个不同的套接字:

I also tried using two different socket :


  1. 创建时使用( AF_INET,SOCK_RAW,IPPROTO_IP )进行阅读,使用  SIO_RCVALL  选项并在本地界面上绑定。
  2. 另一个( AF_INET,SOCK_RAW,IPPROTO_ICMP ),激活选项( IPPROTO_IP / IP_TTL = ON )和( IPPROTO_IP / IP_HDRINCL =关闭)并仅查找ICMP数据包。
  1. Created with (AF_INET, SOCK_RAW, IPPROTO_IP) for reading using SIO_RCVALL option and binding on local interface.
  2. The other with (AF_INET, SOCK_RAW, IPPROTO_ICMP), activate option (IPPROTO_IP/IP_TTL = ON) and ,(IPPROTO_IP/IP_HDRINCL = OFF) and seding only the ICMP packet.

但结果是最差的,我只是在阅读我传出的ICMP请求而且我从未得到任何MY回复,即使使用有效的TTL。但是我在Wireshark看到了它。为什么内核会窃取我的回复???



我想知道这是一个bug还是有些东西我没有正确使用?



我在Windows 7 PRO 64位SP1上运行,我是本地管理员和Visual Studio 2013.我的防火墙已关闭。



PS:在你问我之前;为什么不使用  IcmpSendEcho   API?因为我需要一些东西,我可以快速停下来并完全控制。 IcmpSendEcho仅在超时时工作,如果调用者请求停止操作,我不能等待x秒。
我也正在为Windows / Linux实施ping。

But the result is worst, I'm only reading my outgoing ICMP request and I never got any of MY reply, even with a valid TTL. However I'm seeing it in Wireshark. Why the kernel is stealing me the reply ???

I would like to know if it's a bug or maybe there something I don't use properly?

I'm running on Windows 7 PRO 64 bits SP1 and I'm a the local admin and Visual Studio 2013. My firewall is OFF.

PS : Before you ask me ; Why not use the IcmpSendEcho API ? Because I need something I can stop quickly and having full control. IcmpSendEcho is only working with a timeout and I can't wait x sec if caller request to stop the operation. Also I'm implementing a ping for Windows/Linux.

祝你好运

推荐答案

使用IPPROTO_RAW时,似乎recvfrom方法无法收到回复。

It seems that the recvfrom method cannot receive a reply when using IPPROTO_RAW.

使用IPPROTO_ICMP,您必须只发送一个ICMP数据包,而不是整个IP数据包。

With IPPROTO_ICMP you must only send an ICMP packet, not the whole IP packet.

接收时,您将获得整个IP数据包,并且必须提取ICMP回复。

When receiving however you'll get the whole IP packet and have to extract the ICMP reply.

请注意,您将获得发送给主机的所有ICMP数据包的副本,因此您必须对其进行过滤。

Note that you will get a copy of all ICMP packets sent to the host so you must filter them.

请查看
real ping

最好的问候,

Hart


这篇关于发送Ping(ICMP请求)并读取回复的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆