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;
}