Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ICMP sockets (linux)

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

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?

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?

like image 751
Kei Nivky Avatar asked Nov 27 '11 23:11

Kei Nivky


People also ask

What is ICMP socket?

Linux have a special ICMP socket type you can use with: 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). This only works if a special sysctl is set.

What is ICMP in Linux?

DESCRIPTION top. This kernel protocol module implements the Internet Control Message Protocol defined in RFC 792. It is used to signal error conditions and for diagnosis.

Does Linux use ICMP?

With the Linux ping command, we can also access the time duration for sending and receiving responses from a network. It works by sending a series of Internet Control Message Protocol (ICMP) messages to the target host and waiting for an ICMP echo message from and to the host and Device.


1 Answers

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

  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).

This only works if a special sysctl is 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 -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:

#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 type must be 8 (ICMP_ECHO) and the ICMP code must be 0.

like image 119
nos Avatar answered Sep 29 '22 02:09

nos