【问题标题】:Send Icmp message but receive nothing?发送 Icmp 消息但什么也没收到?
【发布时间】:2018-03-05 00:46:40
【问题描述】:

我想发送 ICMP 消息并接收回显。我花了一周时间研究原始套接字并搜索参考。然后我写下面的代码保证发送ICMP消息成功,因为'socket'和'send'的返回是ok的(不是负1)。不幸的是,“recvfrom”什么也没收到,下面是我在 Windows 上的简洁代码:

#define _WIN32_WINNT 0x501

#include <stdio.h>
#include <ws2tcpip.h>
#include <winsock2.h>

#include <fcntl.h>
#include <unistd.h>

#include <string.h>
#include <sys/stat.h>

#pragma comment(lib,"Ws2_32.lib")

WSADATA wsaData;
int main(int args, char *argv[]){
    WSAStartup(MAKEWORD(2,2),&wsaData);

    //**************icmp book******************/
    typedef struct icmp_hdr {
        unsigned char icmp_type;
        unsigned char icmp_code;
        unsigned short icmp_checksum;
        unsigned short icmp_id;
        unsigned short icmp_sequence;
        unsigned long icmp_timestamp;
    } ICMP_HDR;

    ICMP_HDR *icmp = NULL;
    char buffer[sizeof(ICMP_HDR) + 32];

    icmp = (ICMP_HDR *)buffer; //

    icmp->icmp_type = 8;
    icmp->icmp_code = 0;
    icmp->icmp_id = 1314;
    icmp->icmp_checksum = sizeof(ICMP_HDR) + 32;
    icmp->icmp_sequence = 0;
    icmp->icmp_timestamp = GetTickCount();

    memset(&buffer[sizeof(ICMP_HDR)],'@',32);

    int my_socket = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);

    struct in_addr my_addr;
    my_addr.s_addr = inet_addr("127.0.0.1");
    // inet_aton(argv[1],&my_addr);

    struct sockaddr_in sa_t;

    sa_t.sin_family = AF_INET;
    sa_t.sin_port = htons(0);
    sa_t.sin_addr = my_addr;


    int p = sendto(my_socket,buffer,sizeof(ICMP_HDR)+32,0,(struct sockaddr *)&sa_t, sizeof(sa_t));

    printf("return : %d\n ------s: %d\n",p,my_socket);

    int tmp_add = sizeof(sa_t);

    char buf[1024];

    recvfrom(my_socket,buf,1024,0,(struct sockaddr *)&sa_t,&tmp_add);

    printf("receive is:  %s\n",buf);

    return 1;

}

当我编译并运行这段代码时,我的期望是“recvfrom”函数会接收到任何东西。

但是“recvfrom”中没有任何内容,我需要两天时间,在这里谢谢。

【问题讨论】:

  • 获取tour、阅读How to Askminimal reproducible example。您发布的代码不是 MCVE。你的目标是什么操作系统?
  • 您的防火墙状态如何?您正在向本地主机发送 ICMP 回显命令,它可能已配置为不回复这些命令。
  • 您也没有检查 WSAStartup() 调用的返回值。 sendto() 调用返回什么值?
  • @jwdonahue 谢谢。第一:当我 ping 127.0.0.1 时它工作正常第二:正如我所说,'sendto()' 的返回不是减一,实际上它返回 268,再次感谢。
  • 既然您似乎正在发送 ICMP 回显请求,那么标准 ping 命令 1) 是否有效并且 2) 在接收端显示数据包?我的猜测是内核正在拦截 ICMP 8 消息并在它到达用户空间之前对其进行处理。或许尝试其他 ICMP 消息类型?

标签: c sockets


【解决方案1】:

首先,您需要确保编译器不会填充您的结构来尝试对齐 4、8 或 16 字节地址边界 (#pragma pack) 上的所有内容,然后您必须正确对齐结构并正确计算校验和。在响应方面,您不能将缓冲区视为字符串,它可能包含非 ASCII 数据。此外,网络字节顺序并不总是与任一主机架构上的字节顺序相同,因此您应该始终在发送/接收之前进行转换。

这行得通。我会留给你弄清楚响应中额外的 20 个左右字节是什么。

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <ws2tcpip.h>
#include <winsock2.h>

#include <fcntl.h>

#include <string.h>
#include <sys/stat.h>

#pragma comment(lib,"Ws2_32.lib")

#define STRING_LENGTH 32

//**************icmp book******************/
#pragma pack(push, 1)
typedef struct icmp_msg {
    uint8_t type;
    uint8_t code;
    uint16_t checksum;
    uint16_t id;
    uint16_t sequence;
    uint32_t timestamp;
    uint8_t tail[STRING_LENGTH + 2];
} ICMP_MSG;
#pragma pack(pop)

// malloc() is gauranteed to provide suitable alignement.
ICMP_MSG *ping = NULL;

void PrintMsg(ICMP_MSG *msg)
{
    printf("Ping:\n\tId: %u16\n\tSequence: %u16\n\tTimestamp: %lu\n\tString: ", ntohs(msg->id), ntohs(msg->sequence), ntohl(msg->timestamp));

    char *it = (char*)&(msg->tail[0]);
    char *it_end = (char*)msg + sizeof(ICMP_MSG);
    while (it < it_end)
    {
        char *hexString[65] = { 0 };
        _itoa_s(*it, (char*)hexString, 32, 16);
        printf("0x%s,", (char*)hexString);
        it++;
    }

    printf("\n\n");
}

// Ripped from https://github.com/pocoproject/poco/blob/develop/Net/src/ICMPPacketImpl.cpp#L98 and cleaned-up.
uint16_t Checksum(uint16_t *addr, size_t len)
{
    size_t nleft = len;
    uint16_t* w = addr;
    uint16_t answer;
    int32_t sum = 0;

    while (nleft > 1)
    {
        sum += *w++;
        nleft -= sizeof(uint16_t);
    }

    if (nleft == 1)
    {
        uint16_t u = 0;
        *(uint8_t*)(&u) = *(uint8_t*)w;
        sum += u;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum & 0xffff;

    return answer;
}

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

    ping = malloc(sizeof(ICMP_MSG));

    ping->type = 8;
    ping->code = 0;
    ping->id = htons(1314);
    ping->checksum = 0;  // Must be zero prior to checksum calculation.
    ping->sequence = 0;
    ping->timestamp = htonl(GetTickCount());

    // 0x40 == '@' in ASCII
    memset(ping->tail, 0x40, STRING_LENGTH);
    // NUL pad the last two bytes fo the string.
    ping->tail[STRING_LENGTH] = '\0';
    ping->tail[STRING_LENGTH + 1] = '\0';

    ping->checksum = Checksum((uint16_t*)ping, sizeof(ICMP_MSG));

    int res;
    int my_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);


    struct in_addr my_addr;
    my_addr.s_addr = inet_addr("127.0.0.1");
    //res = inet_pton(AF_INET, "127.0.0.1", &my_addr.s_addr);

    struct sockaddr_in sa_t;

    sa_t.sin_family = AF_INET;
    sa_t.sin_port = htons(0);
    sa_t.sin_addr = my_addr;

    PrintMsg(ping);

    res = sendto(my_socket, (const char*)ping, sizeof(ICMP_MSG), 0, (struct sockaddr *)&sa_t, sizeof(sa_t));

    printf("Socket: %d\nsendto returned: %d\n", my_socket, res);

    int tmp_add = sizeof(sa_t);

    ICMP_MSG *resp = calloc(1, 2048 /*sizeof(ICMP_MSG)*/);

    res = recvfrom(my_socket, (char*)resp, 2048, 0, (struct sockaddr *)&sa_t, &tmp_add);

    printf("recvfrom returned: %d\n", res);
    if (SOCKET_ERROR == res)
    {
        res = WSAGetLastError();
        printf("WSAGetLastError() returned: %d (", res);
        switch (res)
        {
            case WSAEMSGSIZE: 
                printf("WSAMSGSIZE)\n");
                break;
            default:
                printf("Unexpected)\n");
                break;
        }
    }

    PrintMsg(resp);

    system("pause");

    free(ping);
    free(resp);

    return 1;

}

【讨论】:

  • 效果很好,非常感谢详细的分步说明!谢谢?
  • 很高兴它有帮助。实际上,我自己需要复习一下。顺便说一句,您可以通过Checksum(resp) 运行响应消息,并且由于设置了校验和字段,因此它的总和应该正好为零。这就是您验证发送的任何内容在传输过程中没有出现乱码的方式。
猜你喜欢
  • 2012-06-09
  • 2013-02-08
  • 1970-01-01
  • 1970-01-01
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多