Greetings!
I wrote a ping library for Linux using raw sockets to use in my application (piping ping is not an option) and it is working fine if I use it without threads.
However if I try to ping multiple hosts (each host in a separate thread) I get mixed responses in both threads, e.g. thread 1 pinging 192.168.1.100 and thread 2 pinging 192.168.1.200: sometimes the answers are: "ping to 192.168.1.100: reply from 192.168.1.200", etc. Is there any way to prevent raw sockets from capturing packets that are't sent through them ?
This is my code:
ping.h
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
|
#ifndef PING_H
#define PING_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/timex.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
typedef struct PingReply PingReply;
struct PingReply {
char *DAddr, *RAddr;
unsigned short icmp_type, icmp_code;
double msec;
char *error;
};
class Ping
{
public:
Ping(char *destination, unsigned int size, unsigned int timeout);
PingReply Send();
private:
unsigned int id, seq, size, timeout;
char *destination;
unsigned short ip_checksum(void* vdata,size_t length);
};
#endif // PING_H
|
Ping.cpp
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);
}
|
thread.h
1 2 3 4 5 6 7 8 9 10 11 12
|
#include <QThread>
#include "ping.h"
class Thread : public QThread
{
public:
void run();
void setParams(char *ip, int size, int timeout);
private:
Ping *p;
PingReply pr;
};
|
thread.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
#include "threads.h"
void Thread::run()
{
printf("Spawning thread...\n");
for (int i=0;i<7;i++) {
pr = p->Send();
if (strlen(pr.error)) { printf("%s\n",pr.error); }
else {
printf("Ping %s: Reply from %s;Response type: %d;Response code: %d;Delay: %.2f\n",
pr.DAddr, pr.RAddr, pr.icmp_type, pr.icmp_code, pr.msec);
}
}
printf("Thread finished...\n");
exec();
}
void Thread::setParams(char *ip, int size, int timeout)
{
p = new Ping(ip,size,timeout);
}
|
and
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
#include "threads.h"
int main(int argc, char **argv)
{
if (argc < 3) { printf("Usage: %s <size> <timeout> <IP1> [IP2]...\n",argv[0]); }
for (int i=3;i<argc;i++) {
Thread *t = new Thread;
printf("Thread at %p\n", t);
t->setParams(argv[i],atoi(argv[1]),atoi(argv[2]));
t->start();
}
sleep(5);
return 0;
}
|
|