1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
#include "ping.h"
static int difference_micro(struct ntptimeval *bBefore,
struct ntptimeval *aAfter)
{
return (signed long long) aAfter->time.tv_sec * 1000000ll +
(signed long long) aAfter->time.tv_usec -
(signed long long) bBefore->time.tv_sec * 1000000ll -
(signed long long) bBefore->time.tv_usec;
}
Ping::Ping(char *destination, unsigned int size, unsigned int timeout)
{
this->destination = destination;
this->size = size;
this->timeout = timeout;
this->id = getpid();
this->seq = 0;
}
PingReply Ping::Send()
{
PingReply result;
result.DAddr = destination;
result.error = "";
struct ntptimeval start;
ntp_gettime(&start);
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sock==-1)
{
result.error = strerror(errno);
return result;
}
timeval tv_timeout;
tv_timeout.tv_sec = 0;
tv_timeout.tv_usec = timeout*1000;
int rc = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv_timeout, sizeof(timeval));
if (rc==-1)
{
result.error = strerror(errno);
close(sock);
return result;
}
seq++;
char *send_buffer = (char*)malloc(size);
if (!send_buffer)
{
result.error = "(ping.cpp:29: malloc() failed. Unable to allocate memory.";
close(sock);
return result;
}
memset(send_buffer,0,size);
struct icmphdr IcmpHeader;
IcmpHeader.type = ICMP_ECHO;
IcmpHeader.code = 0;
IcmpHeader.checksum = 0;
IcmpHeader.un.echo.id = htons(id);
IcmpHeader.un.echo.sequence = htons(seq);
IcmpHeader.checksum = this->ip_checksum(&IcmpHeader, sizeof(struct icmphdr));
memcpy(send_buffer, &IcmpHeader, sizeof(struct icmphdr));
struct sockaddr_in sa_dest;
sa_dest.sin_family = AF_INET;
sa_dest.sin_addr.s_addr = inet_addr(result.DAddr);
rc = sendto(sock, send_buffer, size, 0, (struct sockaddr*)&sa_dest, sizeof(struct sockaddr_in));
if (rc==-1)
{
result.error = strerror(errno);
free(send_buffer);
close(sock);
return result;
}
char *recv_buffer = (char*)malloc(size);
if (!recv_buffer)
{
free(send_buffer);
close(sock);
return result;
}
struct sockaddr_in sa_reply;
socklen_t sa_size = sizeof(struct sockaddr_in);
rc = recvfrom(sock, recv_buffer, size, 0, (struct sockaddr*)&sa_reply, &sa_size);
struct ntptimeval elapsed;
ntp_gettime(&elapsed);
if (rc==-1)
{
result.error = "Request timed out";
free(recv_buffer);
free(send_buffer);
close(sock);
return result;
}
result.RAddr = inet_ntoa(sa_reply.sin_addr);
struct icmphdr *IcmpReplyHeader = (struct icmphdr*)(recv_buffer+20); // ICMP header is after the IP Header which is 20 bytes long
if (IcmpHeader.un.echo.id != IcmpReplyHeader->un.echo.id) { result.error = "Request timed out"; } // If we capture an ICMP echo reply that's not for our PID, discard it
if (IcmpHeader.un.echo.sequence != IcmpReplyHeader->un.echo.sequence) { result.error = "Request timed out"; } // If we capture an old ICMP echo reply, discard it
result.icmp_type = IcmpReplyHeader->type;
result.icmp_code = IcmpReplyHeader->code;
result.msec = difference_micro(&start,&elapsed)*0.001;
free(recv_buffer); free(send_buffer); close(sock);
return result;
}
unsigned short Ping::ip_checksum(void* vdata,size_t length) {
// Cast the data pointer to one that can be indexed.
char* data=(char*)vdata;
// Initialise the accumulator.
uint32_t acc=0xffff;
// Handle complete 16-bit blocks.
size_t i;
for (i=0;i+1<length;i+=2) {
uint16_t word;
memcpy(&word,data+i,2);
acc+=ntohs(word);
if (acc>0xffff) {
acc-=0xffff;
}
}
// Handle any partial block at the end of the data.
if (length&1) {
uint16_t word=0;
memcpy(&word,data+length-1,1);
acc+=ntohs(word);
if (acc>0xffff) {
acc-=0xffff;
}
}
// Return the checksum in network byte order.
return htons(~acc);
}
|