Thread: Linux raw socket programming

  1. #1
    Registered User
    Join Date
    Sep 2008
    Posts
    14

    Linux raw socket programming

    Hi,
    I am trying to write a program similar to ping in linux but a simplified version so i can learn the sockets programming under linux.
    I have had a look at the ping.c from the iputils package under linux but i cannot understand how everything is working in this file.
    All want to do is send a icmp echo to an IP address and display a result when i get a echo reply back.
    After i can do this i would like to start adding more features such as setting IP TOS bits and etc....but i just need a simple example to get started.
    I also had a look at Beej's guide but it discusses only SOCK_STREAM and SOCK_DGRAM.
    Can someone let me know where i can get some simple examples of raw socket programming for linux.....

    Is anyone is able to share a example so i can have look at this and get started.

    Thanks

  2. #2
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    I posted an awesome link once... lemme see if I can find it again.

  3. #3
    Registered User
    Join Date
    Sep 2008
    Posts
    14
    Quote Originally Posted by master5001 View Post
    I posted an awesome link once... lemme see if I can find it again.
    Thanks......hope you can find the link.....

    thanks

  4. #4
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    I am feverishly searching to no avail thus far. I think I posted it on the network programming board... lemme try to do a search of posts made by me there.

  5. #5
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    this is not what I wanted, but it looks simple enough to follow.

  6. #6
    Registered User
    Join Date
    Sep 2008
    Posts
    14
    Hi,
    I found the following example in the networking forum regarding a simple ping to demonstrate the use of Linux raw sockets example. See the code below.
    I was able to compile and run this program on linux box.
    I would like to start changing some of the program to better understand how this works.
    Now i would like to know where abouts in this program is the source IP address set, i.e. here the output when i run this program:

    e.g. without any arguments:
    Code:
    ./pinger
    
    Usage: pinger [destination] <-s [source]>
    Destination must be provided
    Source is optional
    e.g. with only one IP address:
    Code:
    ./pinger 192.168.1.254
    Source address: 127.0.1.1
    Destination address: 192.168.1.254
    Sent 4 byte packet to 192.168.1.254
    e.g. with both source and destination IP address:
    Code:
    ./pinger 192.168.1.254 -s 192.168.1.64
    Source address: 192.168.1.64
    Destination address: 192.168.1.254
    Sent 4 byte packet to 192.168.1.254
    Received 4 byte reply from 192.168.1.254:
    ID: 26882
    TTL: 64
    As you can see that in the case if you do not specific a source IP address the program sets the source IP address to be 127.0.1.1...this is of course not good and which is also not routable, so you never get any echo reply's back. As you can see that this program states that the source address is optional but i believe it must be specified in order for this program to work.

    First thing i would like to do is able to set the source IP address to be the IP address of eth0.
    So how can i write a piece of code just to get the IP address of eth0 and than which socket function do i call to set the IP address to be the IP address of eth0.
    Is it also possible that we can let the linux kernel decide the source IP address and how do i do this?

    Here is the actual code:
    Code:
    /*
     *    pinger.c 
     *    This is a ping imitation program 
     *    It will send an ICMP ECHO packet to the server of 
     *    your choice and listen for an ICMP REPLY packet
     *    Have fun!
     */
    /*
     *    pinger.c 
     *    This is a ping imitation program 
     *    It will send an ICMP ECHO packet to the server of 
     *    your choice and listen for an ICMP REPLY packet
     *    Have fun!
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <linux/ip.h>
    #include <linux/icmp.h>
    #include <string.h>
    #include <unistd.h>
    
    
    char dst_addr[15];
    char src_addr[15];
    
    unsigned short in_cksum(unsigned short *, int);
    void parse_argvs(char**, char*, char* );
    void usage();
    char* getip();
    
    int main(int argc, char* argv[])
    {
        struct iphdr* ip;
        struct iphdr* ip_reply;
        struct icmphdr* icmp;
        struct sockaddr_in connection;
        char* packet;
        char* buffer;
        int sockfd;
        int optval;
        int addrlen;
        
        if (getuid() != 0)
        {
    	fprintf(stderr, "%s: root privelidges needed\n", *(argv + 0));
    	exit(EXIT_FAILURE);
        }
    
        parse_argvs(argv, dst_addr, src_addr);
        printf("Source address: %s\n", src_addr);
        printf("Destination address: %s\n", dst_addr);
        
        /*
         * allocate all necessary memory
        */
        ip = malloc(sizeof(struct iphdr));
        ip_reply = malloc(sizeof(struct iphdr));
        icmp = malloc(sizeof(struct icmphdr));
        packet = malloc(sizeof(struct iphdr) + sizeof(struct icmphdr));
        buffer = malloc(sizeof(struct iphdr) + sizeof(struct icmphdr));
        /****************************************************************/
        
        ip = (struct iphdr*) packet;
        icmp = (struct icmphdr*) (packet + sizeof(struct iphdr));
        
        /*  
         *	here the ip packet is set up except checksum
         */
        ip->ihl			= 5;
        ip->version			= 4;
        ip->tos			= 0;
        ip->tot_len			= sizeof(struct iphdr) + sizeof(struct icmphdr);
        ip->id			= htons(random());
        ip->ttl			= 255;
        ip->protocol		= IPPROTO_ICMP;
        ip->saddr			= inet_addr(src_addr);
        ip->daddr			= inet_addr(dst_addr);
    
        
        if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
        {
    	perror("socket");
    	exit(EXIT_FAILURE);
        }
        
        /* 
         *	IP_HDRINCL must be set on the socket so that
         *	the kernel does not attempt to automatically add
         *	a default ip header to the packet
         */
        
        setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(int));
        
        /*
         *	here the icmp packet is created
         *	also the ip checksum is generated
         */
        icmp->type			= ICMP_ECHO;
        icmp->code			= 0;
        icmp->un.echo.id		= 0;
        icmp->un.echo.sequence	= 0;
        icmp->checksum 		= 0;
        icmp-> checksum		= in_cksum((unsigned short *)icmp, sizeof(struct icmphdr));
        
        ip->check			= in_cksum((unsigned short *)ip, sizeof(struct iphdr));
        
        connection.sin_family = AF_INET;
        connection.sin_addr.s_addr = inet_addr(dst_addr);
        
        /*
         *	now the packet is sent
         */
        
        sendto(sockfd, packet, ip->tot_len, 0, (struct sockaddr *)&connection, sizeof(struct sockaddr));
        printf("Sent %d byte packet to %s\n", sizeof(packet), dst_addr);
        
        /*
         *	now we listen for responses
         */
        addrlen = sizeof(connection);
        if (recvfrom(sockfd, buffer, sizeof(struct iphdr) + sizeof(struct icmphdr), 0, (struct sockaddr *)&connection, &addrlen) == -1)
        {
    	perror("recv");
        }
        else
        {
    	printf("Received %d byte reply from %s:\n", sizeof(buffer), dst_addr);
            ip_reply = (struct iphdr*) buffer;
    	printf("ID: %d\n", ntohs(ip_reply->id));
    	printf("TTL: %d\n", ip_reply->ttl);
        }
        close(sockfd);
        return 0;
    }
    
    void parse_argvs(char** argv, char* dst, char* src)
    {
        int i;
        if(!(*(argv + 1))) 
        {
    	/* there are no options on the command line */
    	usage();
    	exit(EXIT_FAILURE);	
        }
        if (*(argv + 1) && (!(*(argv + 2)))) 
        {
    	/* 
    	 *   only one argument provided
    	 *   assume it is the destination server
    	 *   source address is local host
    	 */
    	strncpy(dst, *(argv + 1), 15);
    	strncpy(src, getip(), 15);
    	return;
        }
        else if ((*(argv + 1) && (*(argv + 2))))
        {
    	/* 
    	 *    both the destination and source address are defined
    	 *    for now only implemented is a source address and 
    	 *    destination address
    	 */
    	strncpy(dst, *(argv + 1), 15);
    	i = 2;
    	while(*(argv + i + 1))
    	{
    	    if (strncmp(*(argv + i), "-s", 2) == 0)
    	    {
    		strncpy(src, *(argv + i + 1), 15);
    		break;
    	    }
    	    i++;
    	}
    
        }
    }
    
    void usage()
    {
        fprintf(stderr, "\nUsage: pinger [destination] <-s [source]>\n");
        fprintf(stderr, "Destination must be provided\n");
        fprintf(stderr, "Source is optional\n\n");
    }
    
    char* getip()
    {
        char buffer[256];
        struct hostent* h;
        
        gethostname(buffer, 256);
        h = gethostbyname(buffer);
        
        return inet_ntoa(*(struct in_addr *)h->h_addr);
        
    }
    /*
     * in_cksum --
     * Checksum routine for Internet Protocol
     * family headers (C Version)
     */
    unsigned short in_cksum(unsigned short *addr, int len)
    {
        register int sum = 0;
        u_short answer = 0;
        register u_short *w = addr;
        register int nleft = len;
        /*
         * Our algorithm is simple, using a 32 bit accumulator (sum), we add
         * sequential 16 bit words to it, and at the end, fold back all the
         * carry bits from the top 16 bits into the lower 16 bits.
         */
        while (nleft > 1)
        {
    	  sum += *w++;
    	  nleft -= 2;
        }
        /* mop up an odd byte, if necessary */
        if (nleft == 1)
        {
    	  *(u_char *) (&answer) = *(u_char *) w;
    	  sum += answer;
        }
        /* add back carry outs from top 16 bits to low 16 bits */
        sum = (sum >> 16) + (sum & 0xffff);		/* add hi 16 to low 16 */
        sum += (sum >> 16);				/* add carry */
        answer = ~sum;				/* truncate to 16 bits */
        return (answer);
    }

  7. #7
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    that's a good article master5001. I've just about got the hang of local SOCK_STREAM and am curious about the difference between the different styles.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Moved to Networking/Device Communication.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675
    parse_argvs extracts the command line parameters.
    In the event there's only a single argument, getip() retrieves the IP address of the system running the program and uses that as the source IP address.

  10. #10
    Registered User
    Join Date
    Sep 2008
    Posts
    14
    Quote Originally Posted by rags_to_riches View Post
    parse_argvs extracts the command line parameters.
    In the event there's only a single argument, getip() retrieves the IP address of the system running the program and uses that as the source IP address.
    Hi,
    thanks for your reply.
    But the IP address it gets from the system is wrong i.e. in the case if you do not specific a -s 192.168.1.1 it assumes that the source is 127.0.1.1, here is the example when i execute this program without supplying any source address:
    Code:
    ./pinger 192.168.1.254
    Source address: 127.0.1.1
    Destination address: 192.168.1.254
    Sent 4 byte packet to 192.168.1.254
    On this system i have a eth0 card with a 192.168.1.1 IP address, i want to set software set the source to be 192.168.1.1 instead of 127.0.1.1. How do i do this.....i know this will mean change to the getip() function...but how?

    Thanks

  11. #11
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    You'd probably have to set your hostname to something other than "localhost".

    Edit: Actually, you can't do sethostname unless you're root, so never mind. On my (mac) machine, what you've got works and gethostent() gives me localhost 127.0.0.1; maybe, if gethostbyname isn't working for you, try gethostent instead?
    Last edited by tabstop; 10-07-2008 at 08:55 PM.

  12. #12
    Registered User
    Join Date
    Sep 2008
    Posts
    14
    Hi,
    All i would like to do is write a function that can retrieve all the IP address's that are currently configured on my system and than use one of those IP address as a source IP address.
    So how do i go about writing a small c program that can retrieve all the interfaces and IP addresses on the system.

    Thanks in advance.....

  13. #13
    geek Whiteghost's Avatar
    Join Date
    Aug 2005
    Posts
    19
    How would i do raw socket programming on unbuntu linux do i have to be in main root?
    Fedora 9 gcc 4.3

  14. #14
    Registered User jeffcobb's Avatar
    Join Date
    Dec 2009
    Location
    Henderson, NV
    Posts
    875
    There is a way to do this, I need to dig through the archives but need time as the code is obscure,,,,,hopefully someone else will answer before I finish with my searches
    C/C++ Environment: GNU CC/Emacs
    Make system: CMake
    Debuggers: Valgrind/GDB

  15. #15
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Take your time . . . it's already been over a year, I doubt another few hours or days will make much difference.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Raw socket
    By like_no_other in forum Networking/Device Communication
    Replies: 4
    Last Post: 03-28-2009, 02:05 PM
  2. socket programming in c or c++ in linux...
    By pk20 in forum Linux Programming
    Replies: 5
    Last Post: 02-12-2009, 01:54 AM
  3. Raw serial control in Linux
    By crepincdotcom in forum Networking/Device Communication
    Replies: 2
    Last Post: 09-12-2008, 12:17 PM
  4. pthread and socket porting from Linux to Windows
    By mynickmynick in forum C Programming
    Replies: 2
    Last Post: 07-18-2008, 06:57 AM
  5. socket programming in linux
    By crazeinc in forum C Programming
    Replies: 1
    Last Post: 05-27-2005, 07:40 PM