#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/select.h>
struct tv32 {
uint32_t tv32_sec;
uint32_t tv32_usec;
};
#define MAXICMPLEN 76 // 8B header + (60B IP header + 8B DATA)
#define TV32_LEN (sizeof(struct tv32))
uint8_t icmp_type = ICMP_ECHO;
uint8_t icmp_type_rsp = ICMP_ECHOREPLY;
uint8_t outpackhdr[IP_MAXPACKET];
uint8_t *outpack;
int ident;
int datalen = 56;
/* counters */
long nsend = 1;
long nrecv = 0;
int interval = 1000;
/* timing */
double tmin = 99999999.0;
double tmax = 0.0;
double tsum = 0.0;
double tsumsq = 0.0;
struct addrinfo *dst;
int sock_icmp;
char dstIP[INET_ADDRSTRLEN];
void pinger(struct timeval *tv_send);
uint16_t in_cksum(uint16_t *h, int hlen);
struct timeval tvsub(struct timeval *tv1, struct timeval *tv2);
void pr_pack(uint8_t *packet, int len, struct sockaddr_in *from, struct timeval *tv_recv);
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: %s destination\n", argv[0]);
exit(EXIT_FAILURE);
}
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_RAW;
hints.ai_protocol = IPPROTO_ICMP;
int res;
if ((res = getaddrinfo(argv[1], NULL, &hints, &dst)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res));
exit(EXIT_FAILURE);
}
if ((sock_icmp = socket(dst->ai_family, dst->ai_socktype, dst->ai_protocol)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
struct sockaddr_in *dstAddr = (struct sockaddr_in *)dst->ai_addr;
inet_ntop(AF_INET, &dstAddr->sin_addr, dstIP, INET_ADDRSTRLEN);
printf("ping %s(%s)\n", dst->ai_canonname, dstIP);
outpack = outpackhdr + sizeof(struct ip);
ident = getpid() & 0xFFFF;
struct timeval tv_send;
pinger(&tv_send);
struct timeval intvl;
intvl.tv_sec = interval / 1000;
intvl.tv_usec = interval % 1000 * 1000;
uint8_t packet[IP_MAXPACKET];
memset(packet, 0, IP_MAXPACKET);
struct sockaddr_in from;
socklen_t fromlen = sizeof from;
int n, c;
while (1) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock_icmp, &rfds);
struct timeval now, timeout;
gettimeofday(&now, NULL);
timeout.tv_sec = tv_send.tv_sec + intvl.tv_sec - 1 - now.tv_sec;
timeout.tv_usec = tv_send.tv_usec + intvl.tv_usec + 1000000 - now.tv_usec;
while (timeout.tv_usec >= 1000000) {
timeout.tv_usec -= 1000000;
timeout.tv_sec++;
}
if (timeout.tv_sec < 0) {
timeout.tv_sec = timeout.tv_usec = 0;
}
n = select(sock_icmp + 1, &rfds, NULL, NULL, &timeout);
if (n < 0) {
perror("select");
continue;
}
if (n == 0) {
continue;
}
if (n == 1) {
if ((c = recvfrom(sock_icmp, packet, IP_MAXPACKET, 0, (struct sockaddr *)&from, &fromlen)) < 0) {
perror("recvfrom");
exit(EXIT_FAILURE);
}
struct timeval tv_recv;
gettimeofday(&tv_recv, NULL);
pr_pack(packet, c, &from, &tv_recv);
}
timeout.tv_usec += timeout.tv_sec * 1000000;
usleep(timeout.tv_usec);
pinger(&tv_send);
}
return 0;
}
void pinger(struct timeval *tv_send)
{
struct icmp *icp;
struct tv32 tv32;
int len, n;
icp = (struct icmp *)outpack;
icp->icmp_type = icmp_type;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_id = ident;
icp->icmp_seq = htons(nsend);
gettimeofday(tv_send, NULL);
tv32.tv32_sec = htonl(tv_send->tv_sec);
tv32.tv32_usec = htonl(tv_send->tv_usec);
memcpy(icp->icmp_data, &tv32, TV32_LEN);
len = ICMP_MINLEN + datalen;
icp->icmp_cksum = in_cksum((uint16_t *)icp, len);
if ((n = sendto(sock_icmp, icp, len, 0, dst->ai_addr, dst->ai_addrlen)) == -1) {
perror("sento");
exit(EXIT_FAILURE);
}
if (n != len) {
fprintf(stderr, "partial write %s %d(%d)\n", dstIP, len, n);
exit(EXIT_FAILURE);
}
nsend++;
}
void pr_pack(uint8_t *packet, int len, struct sockaddr_in *from, struct timeval *tv_recv)
{
struct ip *ip;
int hlen;
ip = (struct ip *)packet;
hlen = ip->ip_hl * 4;
if (len < hlen + ICMP_MINLEN) {
printf("packet too short");
return;
}
struct icmp *icp;
double triptime;
uint8_t *tp;
len -= hlen;
icp = (struct icmp *)(packet + hlen);
if (icp->icmp_type == icmp_type_rsp) {
if (icp->icmp_id != ident) {
return;
}
nrecv++;
triptime = 0.0;
struct tv32 tv32;
struct timeval tv_send;
struct timeval tv_diff;
tp = icp->icmp_data;
if (len - ICMP_MINLEN >= sizeof tv32) {
memcpy(&tv32, tp, sizeof(tv32));
tv_send.tv_sec = ntohl(tv32.tv32_sec);
tv_send.tv_usec = ntohl(tv32.tv32_usec);
tv_diff = tvsub(tv_recv, &tv_send);
triptime = tv_diff.tv_sec * 1000.0 + tv_diff.tv_usec / 1000.0;
tsum += triptime;
tsumsq += triptime * triptime;
if (triptime < tmin) {
tmin = triptime;
}
if (triptime > tmax) {
tmax = triptime;
}
}
}
char fromIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &from->sin_addr, fromIP, INET_ADDRSTRLEN);
printf(
"%d bytes from %s: icmp_seq=%u ttl=%d time=%.3f ms\n",
len,
fromIP,
ntohs(icp->icmp_seq),
ip->ip_ttl,
triptime
);
}
struct timeval tvsub(struct timeval *tv1, struct timeval *tv2)
{
struct timeval diff;
diff.tv_sec = tv1->tv_sec - 1 - tv2->tv_sec;
diff.tv_usec = tv1->tv_usec + 1000000 - tv2->tv_usec;
if (diff.tv_usec > 1000000) {
diff.tv_sec += 1;
diff.tv_usec -= 1000000;
}
return diff;
}
uint16_t in_cksum(uint16_t *h, int hlen)
{
int nleft, sum;
uint16_t *w;
uint16_t answer;
nleft = hlen;
sum = 0;
w = h;
while (nleft > 1) {
sum += *w;
w++;
nleft -= 2;
}
if (nleft == 1) {
sum += *(uint8_t *)w;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}