Could the Linux experts here give my program here a check, and
maybe offer some constructive criticism.

I've used sockets to drive my c programming forward, and
I think this is a good representation of where my coding
ability in c is currently.

It would be good to hear some opinions from more experienced
programmers.

The speed of checksum calculation isn't a concern in a ping program,
so I've taken the opportunity to use my own checksum function,
which means I'm completely to blame for the code.

(The Windows Version is closed source. )

Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
#include <stdint.h>
#include <errno.h>
#include <netdb.h>

/********************************************/
//
//          Basic ping program.
//
//
//
//
//          gemera        20th Aug 2013
//
/********************************************/

#define MAX_DATAGRAM 225                // max size of datagrams
#define DEFAULT_PINGS 5                 // default number of pings to send
#define IDENTIFIER 31137                // arbitrary default identifier for icmp messages
#define MAX_IDENTIFIER 65535            // max identifier value
#define ICMP_ECHO_REPLY 0               // icmp type identifier for echo reply
#define DESTINATION_UNREACHABLE 3       // icmp type identifier for destination unreachable error
#define ICMP_ECHO_REQUEST 8             // icmp type identifier for echo request
#define TIME_EXCEEDED 11                // icmp type identifier for time exceeded error
#define MAX_TTL 255                     // max ttl value
#define MAX_EXTRA_ARG_PAIRS 3            // max number of extra argument pairs
#define CARRY_BIT 0x10000                // used in checksum function

#define TRUE 1
#define FALSE 0

struct icmp_options             // own struct for icmp options to insert send time
{
    int32_t time_sec;
    int32_t time_usec;
};

static void check_command_line(const int argc,const char* const* const argv,int* ttl,unsigned* identifier,unsigned* no_of_pings);
static void init_addr_struct(struct sockaddr_in* target,const char* const* const argv);
static int get_socket(const int ttl_value);
static int send_echo_request(const int raw_socket,char* datagram,const struct sockaddr_in target,struct timeval time_start,const unsigned seq,const int ttl,const unsigned short int identifier);
static int recv_echo_reply(const int raw_socket,char* datagram,struct sockaddr_in* receive,struct timeval* time_end);
static void display_results(const char* datagram,struct timeval time_start,const struct timeval time_end,const struct sockaddr_in receive);
static unsigned short check_sum (const unsigned short* header, const int word_count);

int main(int argc, char** argv)
{
    char datagram[MAX_DATAGRAM];            // buffer for datagram
    struct sockaddr_in target = {0};        // address structure for sendto
    struct sockaddr_in receive = {0};       // address structure for recvfrom
    int raw_socket;
    unsigned int pings;
    unsigned int no_of_pings = DEFAULT_PINGS;
    unsigned identifier = IDENTIFIER;
    int ttl = MAX_TTL;
    struct timeval time_start = {0};        // to store send time
    struct timeval time_end = {0} ;            // to store recv time
    int recv_err_flag;                        // error flag for received messages
    int sent_err_flag;                        // error flag for sent messages
    int sent_count = 0;                        // count of sent messages
    int recv_count = 0;                        // count of received messages

    check_command_line(argc,(const char* const* const)argv,&ttl,&identifier,&no_of_pings);   // check command line arguments
    init_addr_struct(&target,(const char* const* const)argv);                                 // initialise target address struct
    raw_socket = get_socket(ttl);                                                           // initialise and get raw socket

    printf("\n\n");

    for(pings = 0;pings < no_of_pings;pings++)
    {
        sent_err_flag = FALSE;
        recv_err_flag = FALSE;

        sent_err_flag = send_echo_request(raw_socket,datagram,target,time_start,pings,ttl,identifier);  // send icmp echo request
        if(sent_err_flag == FALSE)
        {
            sent_count++;
        }

        recv_err_flag = recv_echo_reply(raw_socket,datagram,&receive,&time_end);             // receive echo reply
        if(recv_err_flag == FALSE)
        {
            display_results(datagram,time_start,time_end,receive);
            recv_count++;
        }

        sleep(1);
    }

    printf("\n\n\t Packets Sent: %d  Packets Received: %d  Packets Lost: %d\n\n", sent_count,recv_count,sent_count - recv_count);

    close(raw_socket);
    return 0;
}

static void check_command_line(const int argc,const char* const* const argv,int* ttl,unsigned* identifier,unsigned* no_of_pings)
{
    const char* const cmd_prefix[] = {"-t","-i","-n"};
    int found_flag[] = {FALSE,FALSE,FALSE};
    int i,j;

    if(argc < 2 || argc > 8 || (argc % 2 != 0))
    {
        fprintf(stderr," \n Usage: %s <target host or ip> [-t ttl value] [-i identifier] [-n no.of pings]\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    if(argc == 2)
    {
        return;
    }

    for(i = 3;i <= argc;i = i + 2)                               // deal with extra arguments in pairs
    {
        for(j = 0;j < MAX_EXTRA_ARG_PAIRS;j++)                     // 3 command prefix to search for
        {
            if((strcmp(argv[i - 1],cmd_prefix[j]) == 0))
            {
                if(j == 0 && found_flag[j] == FALSE)
                {
                    if((*ttl = atoi(argv[i])) > 0 && *ttl <= MAX_TTL)
                    {
                        found_flag[j] = TRUE;
                    }
                    else
                    {
                        fprintf(stderr,"\n Error: ttl range is 1 to %d.\n",MAX_TTL);
                        exit(EXIT_FAILURE);
                    }
                }
                else if(j == 0 && found_flag[j] == TRUE)
                {
                    fprintf(stderr,"\n Error: option has been duplicated.\n");
                    exit(EXIT_FAILURE);
                }

                if(j == 1 && found_flag[j] == FALSE)
                {
                    if((*identifier = atoi(argv[i])) >= 0 && *identifier <= MAX_IDENTIFIER)
                    {
                        found_flag[j] = TRUE;
                    }
                    else
                    {
                        fprintf(stderr,"\n Error: identifier range is 0 to %d.\n",MAX_IDENTIFIER);
                        exit(EXIT_FAILURE);
                    }
                }
                else if(j == 1 && found_flag[j] == TRUE)
                {
                    fprintf(stderr,"\n Error: option has been duplicated.\n");
                    exit(EXIT_FAILURE);
                }

                if(j == 2 && found_flag[j] == FALSE)
                {
                    if((*no_of_pings = atoi(argv[i])) > 0)
                    {
                        found_flag[j] = TRUE;
                    }
                    else
                    {
                        fprintf(stderr,"\n Error: number of pings must be greater than zero.\n");
                        exit(EXIT_FAILURE);
                    }
                }
                else if(j == 2 && found_flag[j] == TRUE)
                {
                    fprintf(stderr,"\n Error: option has been duplicated.\n");
                    exit(EXIT_FAILURE);
                }
            }
        }
    }
}

static void init_addr_struct(struct sockaddr_in* target,const char* const* const argv)
{
    struct hostent* host;                           // kernel takes care of ports
                                                    // and of source sockaddr_in
    target->sin_family = AF_INET;

    if((target->sin_addr.s_addr = inet_addr(argv[1])) == INADDR_NONE)        // check if target is ip or host name
    {
        if((host = gethostbyname(argv[1])) != NULL)
        {
            memcpy(&(target->sin_addr.s_addr),host->h_addr,host->h_length);
        }
        else
        {
            fprintf(stderr,"\n Error: invalid host name.\n");
            exit(EXIT_FAILURE);
        }
    }
}

static int get_socket(const int ttl_value)
{
    int ret_val;
    int raw_socket;
    struct timeval time_wait;

    time_wait.tv_sec = 1;                        // 1 second time wait
    time_wait.tv_usec = 0;

    raw_socket = socket(PF_INET,SOCK_RAW,IPPROTO_ICMP);
    if(raw_socket == -1)
    {
        perror(" Raw Socket Error");
        exit(EXIT_FAILURE);
    }

    ret_val = setsockopt(raw_socket,SOL_SOCKET,SO_SNDTIMEO,(void*)&time_wait,sizeof(time_wait));
    if(ret_val == -1)
    {                                            // set 1s blocking time on send
        perror(" SO_SNDTIMEO Error");
        exit(EXIT_FAILURE);
    }

    ret_val = setsockopt(raw_socket,SOL_SOCKET,SO_RCVTIMEO,(void*)&time_wait,sizeof(time_wait));
    if(ret_val == -1)
    {                                            // set 1s blocking time on recv
        perror(" SO_RCVTIMEO Error");
        exit(EXIT_FAILURE);
    }

    ret_val = setsockopt(raw_socket,IPPROTO_IP,IP_TTL,(char*)&ttl_value,sizeof(ttl_value));
    if(ret_val == -1)
    {                                            // set ip header ttl value
        perror(" IP_TTL Error");
        fprintf(stderr,"\n Unable to set IP_TTL\n");
        exit(EXIT_FAILURE);
    }

    return raw_socket;
}

static int send_echo_request(const int raw_socket,char* datagram,const struct sockaddr_in target,struct timeval time_start,const unsigned int seqs,const int ttl,const unsigned short int identifier)
{
    struct icmphdr* icmp_ptr;
    struct icmp_options* option_ptr;
    int ret_val;
    int err_flag = FALSE;

    gettimeofday(&time_start,NULL);     // save time datagram sent

    memset(datagram,0,MAX_DATAGRAM);

    icmp_ptr = (struct icmphdr*)datagram;
    option_ptr = (struct icmp_options*)(datagram + sizeof(struct icmphdr));

    option_ptr->time_sec = htonl(time_start.tv_sec);
    option_ptr->time_usec = htonl(time_start.tv_usec);

    icmp_ptr->type = ICMP_ECHO_REQUEST;
    icmp_ptr->code = 0;
    icmp_ptr->un.echo.id = htons(identifier);
    icmp_ptr->un.echo.sequence = htons(seqs);
    icmp_ptr->checksum = 0;
    icmp_ptr->checksum = check_sum((unsigned short const* const)datagram ,(sizeof(struct icmphdr) + sizeof(struct icmp_options))/2);

    ret_val = sendto(raw_socket,datagram,sizeof(struct icmphdr) + sizeof(struct icmp_options),0,(struct sockaddr*)&target,sizeof(target));
    if(ret_val == -1)
    {
        err_flag = TRUE;

        if(errno == EWOULDBLOCK)
        {
            fprintf(stderr,"\n Error: sendto timed out.\n");
        }
        else
        {
            perror(" sendto() error");
        }
    }

    return err_flag;
}

static int recv_echo_reply(const int raw_socket,char* datagram,struct sockaddr_in* receive,struct timeval* time_end)
{
    int err_flag = FALSE;
    int ret_val;
    unsigned int sze = sizeof(struct sockaddr_in);

    memset(datagram,0,MAX_DATAGRAM);

    ret_val = recvfrom(raw_socket,datagram,MAX_DATAGRAM,0,(struct sockaddr*)receive,&sze);
    if(ret_val == -1)
    {
        if(errno == EWOULDBLOCK)
        {
            fprintf(stderr,"\n Error: recvfrom timed out.\n");
        }
        else
        {
             perror(" recvfrom() error");
        }

        err_flag = TRUE;
    }

    gettimeofday(time_end,NULL);     // save time message received

    return err_flag;
}

static void display_results(const char* datagram,struct timeval time_start,const struct timeval time_end,const struct sockaddr_in receive)
{
    struct icmphdr* icmp_ptr;
    struct icmp_options* option_ptr;
    struct iphdr* ip_head_ptr;
    unsigned char ip_length;
    int message_length;
    long diff_usecs;
    long diff_secs;
    double round_trip_time;
    const char* const icmp_msg[] = {"Network Unreachable.","Host Unreachable.","Protocol Unreachable.","Port Unreachable.","Fragmentation Needed but DF bit set.",
                        "Source Route Failed.","Destination Network Unknown.","Destination Host Unknown.","Source Host Isolated",
                        "Destination Network Administratively Prohibited.","Destination Host Administratively Prohibited.",
                        "Network Unreachable For TOS.","Host Unreachable For TOS. ","Communications Administratively Prohibited.",
                        "Host Precedence Violation.","Precedence Cutoff In Effect."};

    ip_head_ptr = (struct iphdr*)datagram;     // check size of ip header
    ip_length = (ip_head_ptr->ihl) * 4;

    icmp_ptr = (struct icmphdr*)(datagram + ip_length);
    option_ptr = (struct icmp_options*)(datagram + ip_length + sizeof(struct icmphdr));

    if(icmp_ptr->type == ICMP_ECHO_REPLY)
    {
        message_length = sizeof(struct icmphdr) + sizeof(struct icmp_options);

        time_start.tv_sec  = ntohl(option_ptr->time_sec);                 // get delta time
        time_start.tv_usec = ntohl(option_ptr->time_usec);

        diff_secs = time_end.tv_sec - time_start.tv_sec;
        diff_usecs = time_end.tv_usec - time_start.tv_usec;
        diff_usecs = diff_secs*1000000 + diff_usecs;

        round_trip_time = (double)diff_usecs/1000;                         // time in ms

        printf(" %d bytes from %s id = %d: seq = %d ttl = %d rtt = %.3lf ms\n",message_length + ip_length,inet_ntoa(receive.sin_addr),ntohs(icmp_ptr->un.echo.id),ntohs(icmp_ptr->un.echo.sequence),ip_head_ptr->ttl,round_trip_time);
    }
    else if(icmp_ptr->type == DESTINATION_UNREACHABLE)
    {
        printf(" from %s : %s ttl = %d\n",inet_ntoa(receive.sin_addr),icmp_msg[icmp_ptr->code],ip_head_ptr->ttl);
    }
    else if(icmp_ptr->type == TIME_EXCEEDED)
    {
        if(icmp_ptr->code == 0)
        {
            printf(" from %s : Time Exceeded.TTL equals 0 during transit. ttl = %d\n",inet_ntoa(receive.sin_addr),ip_head_ptr->ttl);
        }
        else if(icmp_ptr->code == 1)
        {
            printf(" from %s : Time Exceeded.TTL equals 0 during reassembly. ttl = %d\n",inet_ntoa(receive.sin_addr),ip_head_ptr->ttl);
        }
        else
        {
             printf(" from %s : Time Exceeded. ttl = %d\n",inet_ntoa(receive.sin_addr),ip_head_ptr->ttl);
        }
    }
    else if(icmp_ptr->type == ICMP_ECHO_REQUEST)
    {
        printf(" from %s : Icmp Echo Request. ttl = %d\n",inet_ntoa(receive.sin_addr),ip_head_ptr->ttl);
    }
    else
    {
        printf(" from %s : Icmp Received. code = %u ttl = %d\n",inet_ntoa(receive.sin_addr),icmp_ptr->code,ip_head_ptr->ttl);
    }
}

static unsigned short check_sum(const unsigned short* const header, const int word_count)
{
    int i;
    uint32_t sum = 0;

    for(i = 0;i < word_count;i++)
    {
        sum += header[i];

        if(sum & CARRY_BIT)
        {
            sum = (sum ^ CARRY_BIT) + 1;
        }
    }

    return (unsigned short) ~sum;
}