/**
** TMETRIC - An aid to discovering the bandwidth on a route to a host
**
** Copyright (C) 2000 Michael Bacarella
**
** This program is available to you under the terms of the
** GNU General Public License.
**
** Contact: mike@bacarella.com for bugfixes, comments, etc.
**
** NOTE: This program uses type 'double' for it's calculations.
** Weird errors occur on systems with really old FPUs.
** I will make no effort to do this support myself.
** Patches are welcome, of course.
**
** VERSION: 0.5
**/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#define SEND_BUFFER_SIZE 64000
#define RECV_BUFFER_SIZE 64000
typedef unsigned char ng_uchar;
typedef unsigned short ng_ushort;
typedef unsigned long ng_ulong;
char *progname;
extern char *optarg;
char *se(void)
{
return strerror(errno);
}
int getproto(char *proto)
{
struct protoent *pe = getprotobyname(proto);
if (pe == NULL) {
fprintf(stderr, "%s: getprotobyname(%s): %s\n",
progname, proto, se());
exit(1);
}
return pe->p_proto;
}
/* chksum - shamelessly lifted from ping.c and butchered */
ng_ushort chksum(ng_ushort *addr, size_t len)
{
size_t nleft = len;
int sum = 0;
ng_ushort *w = addr;
ng_ushort answer;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(ng_uchar *)(&answer) = *(ng_uchar *)w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
/* genpkts - sit in a loop generating icmp echo request
* packets, size based on criteria */
void genpkts(int rs, pid_t cproc, struct sockaddr_in *sin,
size_t range_start, size_t range_end, size_t range_res,
size_t delay, size_t verbose)
{
char sbuffer[SEND_BUFFER_SIZE];
struct icmp *icmp;
struct timeval *tv;
size_t i, len = range_start + 8;
size_t rstep = (range_end - range_start) / range_res;
icmp = (struct icmp *)sbuffer;
for (i = 0; i < range_res; i++) {
memset(icmp, 0, sizeof(struct icmp));
len += rstep;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_id = cproc;
icmp->icmp_seq = i;
gettimeofday((struct timeval *)icmp->icmp_data, NULL);
icmp->icmp_cksum = chksum((u_short *)icmp, len);
if (verbose > 0)
puts("genpkts(): sending packet");
if (sendto(rs, sbuffer, len, 0, (struct sockaddr *)sin,
sizeof(struct sockaddr)) == -1) {
if (errno != EINTR) {
printf("%s: sendto: %s\n", progname, se());
break;
}
}
if (verbose > 2)
printf("generate_packet(): sleeping "
"for %d secs\n", delay);
sleep(delay);
}
if (verbose > 2)
printf("%s: terminating packet processor thread\n", progname);
kill(getppid(), SIGINT);
if (verbose > 2)
printf("%s: exiting\n", progname);
exit(0);
}
void pravg(double bps)
{
bps = bps * 8.0; /* convert it to bits-per-second before printing */
if (bps <= 1024)
printf("%5f bps",bps);
else if (bps <= 1024*1024)
printf("%5.2f kbps", bps/1024);
else if (bps <= 1024*1024*1024)
printf("%5.2f Mbps", bps/(1024*1024));
else
printf("%5.2f Gbps", bps/(1024*1024*1024));
}
/* procpkt - receive icmp packet, update statistics */
void procpkt(char *rbuffer, size_t len, pid_t cproc, size_t verbose,
double *highest, double *lowest, double *sum, size_t *total)
{
struct ip *ip;
struct icmp *icmp;
struct timeval tv, *ts;
double elapsed, bps;
sigset_t blockmask, allowmask;
gettimeofday(&tv, NULL);
if (verbose > 1)
puts("procpkt(): handling icmp packet");
ip = (struct ip *)rbuffer;
icmp = (struct icmp *)(rbuffer + (ip->ip_hl << 2));
if (icmp->icmp_type != ICMP_ECHOREPLY) {
if (verbose > 1)
puts("procpkt(): not one of our packets");
return;
}
if (icmp->icmp_id != cproc) {
if (verbose > 1)
puts("procpkt(): not one of our packets");
return;
}
if (verbose > 0)
puts("procpkt(): received reply");
ts = (struct timeval *)icmp->icmp_data;
elapsed = (double)(tv.tv_sec + (double)tv.tv_usec/1000000)
- (double)(ts->tv_sec + (double)ts->tv_usec/1000000);
if (elapsed == 0.0) {
puts("floating point error -- elapsed time cannot be zero!");
return;
}
printf("%d byte probe: (seq %d ping: %2.4f secs): ",
len, icmp->icmp_seq, elapsed);
bps = (double)((double)len/(double)elapsed);
/* block sigint-- */
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGINT);
sigprocmask(SIG_BLOCK, &blockmask, &allowmask);
/* compute new stats for SIGINT handler */
*sum += (double)bps;
(*total)++;
if (bps > *highest)
*highest = bps;
if (bps < *lowest)
*lowest = bps;
/* --unblock sigint (restore old behavior) */
sigprocmask(SIG_SETMASK, &allowmask, NULL);
pravg(bps);
putchar('\r');
fflush(stdout);
}
void prhelp(void)
{
fprintf(stderr, "usage: %s [options] <hostname>\n"
"\t-s start of packet size range\n"
"\t-e end of packet size range\n"
"\t-r resolution of range\n"
"\t-d delay between each packet\n"
"\t-v verbosity. use many v's for more verbosity\n"
"\t-h this information\n"
"example: %s -s 1000 -e 5000 -r 5 host\n"
"\tsends 5 packets, stepping from 1000 "
"to 5000 bytes at host\n",
progname, progname);
exit(0);
}
int main (int argc, char *argv[])
{
int rs;
struct sockaddr_in sin;
struct hostent *he;
pid_t cproc;
char rbuffer[RECV_BUFFER_SIZE], ch, *host;
size_t range_start, range_res, range_end,
verbose, size, total, delay;
double highest, lowest, sum;
void sigint(int s) {
printf("\ntmetric stats avg/min/max: ");
if (total) {
pravg((double)sum/total);
putchar(' ');
pravg(lowest);
putchar(' ');
pravg(highest);
putchar('\n');
} else
puts("0/0.0/0.0/0.0");
if (verbose > 2)
printf("%s: packet processor terminating\n", progname);
kill(cproc,SIGKILL);
exit(0);
}
total = verbose = 0;
highest = sum = 0.0;
lowest = 1000000000;
progname = argv[0];
delay = 1;
range_start = range_res = 256;
range_end = SEND_BUFFER_SIZE;
if (argc < 2)
prhelp();
while ((ch = getopt(argc, argv, "hs:e:r:d:v")) != -1) {
switch (ch) {
case 's':
range_start = atol(optarg);
break;
case 'e':
range_end = atol(optarg);
break;
case 'r':
range_res = atol(optarg);
break;
case 'd':
delay = atol(optarg);
break;
case 'v':
verbose++;
break;
case 'h':
default:
prhelp();
break;
}
}
if (range_start+8 >= range_end) {
fprintf(stderr, "error: %s: unusable ranges specified\n",
progname);
exit(1);
}
if (delay == 0) {
fprintf(stderr, "error: %s: delay cannot be zero\n",
progname);
exit(1);
}
if (range_res < 2) {
fprintf(stderr, "error: %s: resolution cannot be less than 2\n",
progname);
exit (1);
}
host = argv[argc-1];
if ((rs = socket(AF_INET, SOCK_RAW, getproto("icmp"))) == -1) {
fprintf(stderr, "%s: socket(): %s\n", progname, se());
exit(1);
}
size = RECV_BUFFER_SIZE;
setsockopt(rs, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
setsockopt(rs, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size));
setuid(getuid());
sin.sin_family = AF_INET;
if ((he = gethostbyname(host)) == NULL) {
perror("gethostbyname()");
exit(1);
}
memcpy(&sin.sin_addr, he->h_addr, he->h_length);
if ((cproc = fork()) == 0) {
genpkts(rs, getpid(), &sin, range_start,
range_end, range_res, delay, verbose);
exit(0);
}
signal(SIGINT, sigint);
if (cproc == -1) {
fprintf(stderr, "%s: fork(): %s\n", progname, se());
exit(1);
}
while (1) {
int n = recvfrom(rs, rbuffer, sizeof(rbuffer),
0, (struct sockaddr *)&sin, &size);
if (n < 0) {
if (errno == EINTR)
continue;
fprintf(stderr, "%s: recvfrom(): %s\n", progname, se());
}
procpkt(rbuffer, n, cproc, verbose, &highest,
&lowest, &sum, &total);
}
exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1