Search code examples
cpointerspingicmpraw-sockets

ICMP pointer and checksum


I reused the raw socket snippet from this site and removed the TCP part and added my ICMP part to request an echo. Both my machines are on the same LAN, running Ubuntu 32 bit. I gave the checksum function my ICMP-Pointer but it failed to calculate a correct checksum:

icmph->icmp_sum = csum( (unsigned short *) datagram + sizeof(struct iphdr), sizeof(struct icmp_header));

Then I tried out some raw numbers as offsets until it worked out:

icmph->icmp_sum = csum( (unsigned short *) datagram + 10, sizeof(struct icmp_header));

I even checked the position of the pointers and the size of the IP-header-struct:

Datagram-Pointer: 0xbff94680
IP-Pointer: 0xbff94680
ICMP-Pointer: 0xbff94694

As you can see, the ICMP-Pointer is 20 away from the Datagram- and IP-Pointer. Why will 10 do the job?

Thank you in advance.

PS: Here is the code:

**/*
    Raw TCP packets
    Silver Moon ([email protected])
*/
#include<stdio.h> //for printf
#include<string.h> //memset
#include<sys/socket.h>    //for socket ofcourse
#include<stdlib.h> //for exit(0);
#include<errno.h> //For errno - the error number
#include<netinet/tcp.h>   //Provides declarations for tcp header
#include<netinet/ip.h>    //Provides declarations for ip header


//ICMP Header
struct icmp_header
{
    u_char icmp_type;
    u_char icmp_code;
    u_short icmp_sum;
    u_short icmp_ident;
    u_short icmp_seq;
};

/*
    Generic checksum calculation function
*/
unsigned short csum(unsigned short *ptr,int nbytes) 
{
    register long sum;
    unsigned short oddbyte;
    register short answer;

    sum=0;
    while(nbytes>1) {
        sum+=*ptr++;
        nbytes-=2;
    }
    if(nbytes==1) {
        oddbyte=0;
        *((u_char*)&oddbyte)=*(u_char*)ptr;
        sum+=oddbyte;
    }

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

    return(answer);
}

int main (void)
{
    //Create a raw socket
    int s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);

    if(s == -1)
    {
        //socket creation failed, may be because of non-root privileges
        perror("Failed to create socket");
        exit(1);
    }

    //Datagram to represent the packet
    char datagram[sizeof(struct iphdr) + sizeof(struct icmp_header)] , source_ip[32];

    //zero out the packet buffer
    memset (datagram, 0, sizeof(struct iphdr) + sizeof(struct icmp_header));

    //IP header
    struct iphdr *iph = (struct iphdr *) datagram;

    //TCP header
    struct icmp_header *icmph = (struct icmp_header *) (datagram + sizeof (struct iphdr));
    struct sockaddr_in sin;


    //some address resolution
    strcpy(source_ip , "10.0.2.5");
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr ("10.0.2.4");

    //Fill in the IP Header
    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;
    iph->tot_len = sizeof (struct iphdr) + sizeof (struct icmp_header);
    iph->id = htonl (54321); //Id of this packet
    iph->frag_off = 0;
    iph->ttl = 255;
    iph->protocol = 1; //Use ICMP afterwards
    iph->check = 0;      //Set to 0 before calculating checksum
    iph->saddr = inet_addr ( source_ip );    //Spoof the source ip address
    iph->daddr = sin.sin_addr.s_addr;

    //Ip checksum
    iph->check = csum ((unsigned short *) datagram, iph->tot_len);

    //Fill in the ICMP Header
    icmph->icmp_type = 8; //Ping Request
    icmph->icmp_code = 0;
    icmph->icmp_sum = 0; //Set to 0 before calculating checksum 
    icmph->icmp_ident = 0x4142; //Just some Numbers
    icmph->icmp_seq = 0x4142; //Just some Numbers

    icmph->icmp_sum = csum( (unsigned short *) datagram + 10, sizeof(struct icmp_header));//? Why 10 and not sizeof(struct iphdr)=20?

    //For Debugging 
    printf("Datagram-Pointer: %p\n", (void*) datagram);  
    printf("IP-Pointer: %p\n", (void*) iph); 
    printf("ICMP-Pointer: %p\n\n", (void*) icmph);  
    printf("IP-Struct Size: %d\n",  sizeof(struct iphdr)); 

    //IP_HDRINCL to tell the kernel that headers are included in the packet
    int one = 1;
    const int *val = &one;
    if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
    {
        perror("Error setting IP_HDRINCL");
        exit(0);
    }

    //Senden
    if (sendto (s, datagram, iph->tot_len ,  0, (struct sockaddr *) &sin, sizeof (sin)) < 0)
    {
        perror("sendto failed");
    }
    //Data send successfully
    else
    {
        printf ("Packet Send. Length : %d \n" , iph->tot_len);
    }
    return 0;
}

//Complete**

Solution

  • sizeof gives you the size in bytes, not some arbitrary "word". That means you add an offset of 20 16-byte words in your first alternative.

    You should divide the result of sizeof by the size of unsigned short:

    icmph->icmp_sum = csum( (unsigned short *) datagram + sizeof(struct iphdr) / sizeof(unsigned short), sizeof(struct icmp_header));