ICMP 套接字 (linux)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8290046/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 02:35:24  来源:igfitidea点击:

ICMP sockets (linux)

linuxsocketsicmp

提问by Kei Nivky

Is it possible to use ICMP sockets under the IP protocol? Maybe something like:

是否可以在 IP 协议下使用 ICMP 套接字?也许是这样的:

socket(PF_INET, <type>, IPPROTO_ICMP)?

socket(PF_INET, <type>, IPPROTO_ICMP)?

What should I put in the < type > field? I saw some examples using SOCK_RAW, but won't that prevent the OS from doing his job handling the IP protocol?

我应该在 < type > 字段中输入什么?我看到了一些使用 SOCK_RAW 的例子,但这不会阻止操作系统处理 IP 协议吗?

And another thing. How can the OS know to which process he should send the ICMP datagrams, since there are no ports involved with the protocol?

还有另一件事。由于协议不涉及端口,操作系统如何知道他应该将 ICMP 数据报发送到哪个进程?

采纳答案by Basile Starynkevitch

Yes it is possible, since the pingcommand does ICMP.

是的,这是可能的,因为该ping命令执行 ICMP。

To find out the syscalls involved, you can stracethat command (under root).

要找出所涉及的系统调用,您可以使用strace该命令(在 root 下)。

You could also glance into that command's source code, e.g. Debian's ping

您还可以查看该命令的源代码,例如Debian 的 ping

And there is the libopinglibrary to help you...

还有liboping库可以帮助您...

回答by nos

Linux have a special ICMP socket type you can use with:

Linux 有一个特殊的 ICMP 套接字类型,您可以使用它:

  socket(PF_INET, SOCK_DGRAM IPPROTO_ICMP);

This allows you to only send ICMP echo requests The kernel will handle it specially (match request/responses, fill in the checksum).

这允许你只发送ICMP echo请求,内核会专门处理(匹配请求/响应,填写校验和)。

This only works if a special sysctlis set. By default not even root can use this kind of socket. You specify the user groups that can access it. To allow root (group 0) to use ICMP sockets, do:

这仅在设置了特殊的 sysctl时才有效。默认情况下,即使是 root 也不能​​使用这种套接字。您指定可以访问它的用户组。要允许 root(组 0)使用 ICMP 套接字,请执行以下操作:

 sysctl -w net.ipv4.ping_group_range="0 0"

Here is an example program to demonstrate the very basic usage of sending an ICMP echo request:

这是一个示例程序,用于演示发送 ICMP 回显请求的非常基本的用法:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <sys/select.h>

//note, to allow root to use icmp sockets, run:
//sysctl -w net.ipv4.ping_group_range="0 0"

void ping_it(struct in_addr *dst)
{
    struct icmphdr icmp_hdr;
    struct sockaddr_in addr;
    int sequence = 0;
    int sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP);
    if (sock < 0) {
        perror("socket");
        return ;
    }

    memset(&addr, 0, sizeof addr);
    addr.sin_family = AF_INET;
    addr.sin_addr = *dst;

    memset(&icmp_hdr, 0, sizeof icmp_hdr);
    icmp_hdr.type = ICMP_ECHO;
    icmp_hdr.un.echo.id = 1234;//arbitrary id

    for (;;) {
        unsigned char data[2048];
        int rc;
        struct timeval timeout = {3, 0}; //wait max 3 seconds for a reply
        fd_set read_set;
        socklen_t slen;
        struct icmphdr rcv_hdr;

        icmp_hdr.un.echo.sequence = sequence++;
        memcpy(data, &icmp_hdr, sizeof icmp_hdr);
        memcpy(data + sizeof icmp_hdr, "hello", 5); //icmp payload
        rc = sendto(sock, data, sizeof icmp_hdr + 5,
                        0, (struct sockaddr*)&addr, sizeof addr);
        if (rc <= 0) {
            perror("Sendto");
            break;
        }
        puts("Sent ICMP\n");

        memset(&read_set, 0, sizeof read_set);
        FD_SET(sock, &read_set);

        //wait for a reply with a timeout
        rc = select(sock + 1, &read_set, NULL, NULL, &timeout);
        if (rc == 0) {
            puts("Got no reply\n");
            continue;
        } else if (rc < 0) {
            perror("Select");
            break;
        }

        //we don't care about the sender address in this example..
        slen = 0;
        rc = recvfrom(sock, data, sizeof data, 0, NULL, &slen);
        if (rc <= 0) {
            perror("recvfrom");
            break;
        } else if (rc < sizeof rcv_hdr) {
            printf("Error, got short ICMP packet, %d bytes\n", rc);
            break;
        }
        memcpy(&rcv_hdr, data, sizeof rcv_hdr);
        if (rcv_hdr.type == ICMP_ECHOREPLY) {
            printf("ICMP Reply, id=0x%x, sequence =  0x%x\n",
                            icmp_hdr.un.echo.id, icmp_hdr.un.echo.sequence);
        } else {
            printf("Got ICMP packet with type 0x%x ?!?\n", rcv_hdr.type);
        }
    }
}

int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf("usage: %s destination_ip\n", argv[0]);
        return 1;
    }
    struct in_addr dst;

    if (inet_aton(argv[1], &dst) == 0) {

        perror("inet_aton");
        printf("%s isn't a valid IP address\n", argv[1]);
        return 1;
    }

    ping_it(&dst);
    return 0;
}

Note that the kernel will reject and fail the sendto() call if the data sent does not have room for a proper ICMP header, and the ICMP typemust be 8 (ICMP_ECHO) and the ICMP code must be 0.

请注意,如果发送的数据没有空间容纳适当的 ICMP 标头,并且 ICMPtype必须为 8 (ICMP_ECHO) 并且 ICMP 代码必须为 0 ,则内核将拒绝并失败 sendto() 调用。