ICMP сокеты (linux) - PullRequest
       10

ICMP сокеты (linux)

9 голосов
/ 28 ноября 2011

Можно ли использовать ICMP-сокеты по протоколу IP? Может быть что-то вроде:

socket(PF_INET, <type>, IPPROTO_ICMP)?

Что я должен поместить в поле ? Я видел несколько примеров использования SOCK_RAW, но не помешает ли это ОС выполнять свою работу по обработке протокола IP?

И еще одна вещь. Как ОС может знать, какому процессу он должен отправлять дейтаграммы ICMP, поскольку в протоколе нет портов, связанных с протоколом?

Ответы [ 2 ]

20 голосов
/ 20 ноября 2013

В Linux есть специальный тип сокета ICMP, который вы можете использовать с:

  socket(PF_INET, SOCK_DGRAM IPPROTO_ICMP);

Это позволяет отправлять только эхо-запросы ICMP. Ядро будет обрабатывать его специально (сопоставить запрос / ответы, заполнить контрольную сумму)..

Это работает, только если установлен специальный sysctl .По умолчанию даже root не может использовать этот тип сокета.Вы указываете группы пользователей, которые могут получить к нему доступ.Чтобы разрешить root (группа 0) использовать сокеты ICMP, выполните:

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

Вот пример программы, демонстрирующей самое простое использование отправки эхо-запроса 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;
}

Обратите внимание, что ядро ​​отклонит и не выполнит вызов sendto (), если отправленные данные не имеют места для правильного заголовка ICMP, и ICMP type должен быть равен 8 (ICMP_ECHO), а код ICMP должен быть равен 0.

8 голосов
/ 28 ноября 2011

Да, это возможно, поскольку команда ping выполняет ICMP.

Чтобы выяснить, какие системные вызовы задействованы, вы можете strace эту команду (под root).

Вы можететакже загляните в исходный код этой команды, например, ping Debian

И есть библиотека liboping , которая поможет вам ...

...