Question

I did a simple ping program , in that i created a buffer and in that only i filled the IP frame and icmp stucture and when i run the program it's working correctly .

I have looked the execution in Wireshark and the ethernet frame has been included correctly in the echo request with correct target MAC address (the MAC would probably found in arp cache), but ethernet frame has been created automatically even if I did not fill that in the buffer which I used in the sock_raw socket communication.

Another problem is my friend tried the same code , but when he checked in the Wireshark in the ethernet header the MAC address was 00.00.00.00.00.00 ,which means it will send to every device and the according device with the correct IP will reply , but its is not the case for me

and the code is here

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>


#define BUFFER_SIZE 100
#define DEFAULT_NUM_PACKETS 10

char buf[BUFFER_SIZE]; 
char *message = "\n ./output_file <sender_ip_addr> <dest_ip_addr> <no_of_reqs>\n \
        <no_of_req> = is the number of requests to send and 10 is the default and mentioning 0  is infinite\n";

void set_ip_layer_fields(struct icmphdr *icmp, struct ip *ip)
{
    // IP layer
    ip->ip_v = 4;
    ip->ip_hl = sizeof*ip >> 2;
    ip->ip_tos = 0;
    ip->ip_len = htons(sizeof(buf));
    ip->ip_id = 0;
    ip->ip_off = 0;
    ip->ip_ttl = 255;
    ip->ip_p = 1;
    ip->ip_sum = 0; 

    // ICMP Layer
    icmp->type = 8;
    icmp->code = 0;     
    icmp->checksum = htons(~(ICMP_ECHO << 8));  
}

int main(int argc, char *argv[])
{
    int s, i ;  
    struct ip *ip = (struct ip *)buf;
    struct icmphdr *icmp = (struct icmphdr *)(ip + 1);
    struct hostent *hp, *hp2;
    struct sockaddr_in dst;                               
    int num = DEFAULT_NUM_PACKETS;

    if(argc < 3)
    {
        fprintf(stdout, "%s\n",message);
        exit(1);
    }

    // If enough arguments supplied 
    if(argc == 4)
        num = atoi(argv[3]);

    // Loop based on the no of requests
    for(i = 1; num == 0 ? num == 0 : i <= num; i++)
    {
        memset(buf, 0, sizeof(buf));

        if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
        {
            perror("socket() error");
            exit(1);
        }

        if((hp = gethostbyname(argv[2])) == NULL)
        {
            if((ip->ip_dst.s_addr = inet_addr(argv[2])) == -1)
            {
                fprintf(stderr, "%s: unknown host.\n", argv[2]);
                exit(1);
            }
        }

        else
            memcpy(&ip->ip_dst.s_addr, hp->h_addr_list[0], hp->h_length);

        if((hp2 = gethostbyname(argv[1])) == NULL)
        {
            if((ip->ip_src.s_addr = inet_addr(argv[1])) == -1)
            {
                fprintf(stderr, "%s: unknown host\n", argv[1]);
                exit(1);
            }
        }
        else
            memcpy(&ip->ip_src.s_addr, hp2->h_addr_list[0], hp->h_length);

        set_ip_layer_fields(icmp, ip);

        dst.sin_addr = ip->ip_dst;
        dst.sin_family = AF_INET;

        if(sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&dst, sizeof(dst)) < 0)
        {
            fprintf(stderr, "error while sending\n");
        }
        else
            printf("request:%d sended successfully\n",i);

        close(s);

    }
    return 0;
}
Was it helpful?

Solution

The difference is how you declare your socket.

Using IPPROTO_RAW - The Ethernet header is provided for you and you provide everything from the IP header onwards.

Using htons(ETH_P_ALL) - You provide everything, including the Ethernet header.

More information is here: http://www.pdbuchan.com/rawsock/rawsock.html

Also using IPPROTO_RAW means that the option IP_HDRINCL is implicitly set which means that the IP is used from the IP header in your packet instead of the information placed in sendto.

Usually if you are specifying everything in a raw socket, you would use send instead of sendto as all of the information for sending is available in the header.

Read more about IP_HDRINCL here: http://man7.org/linux/man-pages/man7/raw.7.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top