468 lines
10 KiB
C
468 lines
10 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)spray.c 1.1 94/10/31 Copyr 1988 Sun Micro";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1985 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <rpc/rpc.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <netdb.h>
|
|
#include <rpcsvc/spray.h>
|
|
|
|
#define DEFBYTES 100000 /* default numbers of bytes to send */
|
|
#define MAXPACKETLEN 1514
|
|
|
|
char *adrtostr();
|
|
char *host;
|
|
struct in_addr adr;
|
|
int lnth, cnt;
|
|
int icmp;
|
|
|
|
main(argc, argv)
|
|
char **argv;
|
|
{
|
|
int err, i;
|
|
int delay = 0;
|
|
int psec, bsec;
|
|
int buf[SPRAYMAX/4];
|
|
struct hostent *hp;
|
|
struct sprayarr arr;
|
|
struct spraycumul cumul;
|
|
double delta;
|
|
|
|
if (argc < 2)
|
|
usage();
|
|
|
|
cnt = -1;
|
|
lnth = SPRAYOVERHEAD;
|
|
while (argc > 1) {
|
|
if (argv[1][0] == '-') {
|
|
switch (argv[1][1]) {
|
|
case 'd':
|
|
if (argc < 3)
|
|
usage();
|
|
delay = atoi(argv[2]);
|
|
if (delay < 0)
|
|
usage();
|
|
argc--;
|
|
argv++;
|
|
break;
|
|
case 'i':
|
|
icmp++;
|
|
break;
|
|
case 'c':
|
|
if (argc < 3)
|
|
usage();
|
|
cnt = atoi(argv[2]);
|
|
if (cnt <= 0)
|
|
usage();
|
|
argc--;
|
|
argv++;
|
|
break;
|
|
case 'l':
|
|
if (argc < 3)
|
|
usage();
|
|
lnth = atoi(argv[2]);
|
|
if (lnth <= 0)
|
|
usage();
|
|
argc--;
|
|
argv++;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
} else {
|
|
if (host)
|
|
usage();
|
|
else
|
|
host = argv[1];
|
|
}
|
|
argc--;
|
|
argv++;
|
|
}
|
|
if (host == NULL)
|
|
usage();
|
|
if (isinetaddr(host)) {
|
|
adr.s_addr = htonl(inet_addr(host));
|
|
host = adrtostr(adr);
|
|
} else {
|
|
if ((hp = gethostbyname(host)) == NULL) {
|
|
fprintf(stderr, "%s is unknown host name\n", host);
|
|
exit(1);
|
|
}
|
|
adr.s_addr = *((u_long *)hp->h_addr);
|
|
}
|
|
if (icmp)
|
|
doicmp(adr, delay);
|
|
if (cnt == -1)
|
|
cnt = DEFBYTES/lnth;
|
|
if (lnth < SPRAYOVERHEAD)
|
|
lnth = SPRAYOVERHEAD;
|
|
else if (lnth >= SPRAYMAX)
|
|
lnth = SPRAYMAX;
|
|
if (lnth <= MAXPACKETLEN && lnth % 4 != 2)
|
|
lnth = ((lnth + 5)/4) * 4 - 2;
|
|
arr.lnth = lnth - SPRAYOVERHEAD;
|
|
arr.data = buf;
|
|
printf("sending %d packets of lnth %d to %s ...", cnt, lnth, host);
|
|
fflush(stdout);
|
|
|
|
if (err = mycallrpc(adr, SPRAYPROG, SPRAYVERS, SPRAYPROC_CLEAR,
|
|
xdr_void, NULL, xdr_void, NULL)) {
|
|
fprintf(stderr, "SPRAYPROC_CLEAR ");
|
|
clnt_perrno(err);
|
|
return;
|
|
}
|
|
for (i = 0; i < cnt; i++) {
|
|
callrpcnowait(adr, SPRAYPROG, SPRAYVERS, SPRAYPROC_SPRAY,
|
|
xdr_sprayarr, &arr, xdr_void, NULL);
|
|
if (delay > 0)
|
|
slp(delay);
|
|
}
|
|
if (err = mycallrpc(adr, SPRAYPROG, SPRAYVERS, SPRAYPROC_GET,
|
|
xdr_void, NULL, xdr_spraycumul, &cumul)) {
|
|
fprintf(stderr, "SPRAYPROC_GET ");
|
|
fprintf(stderr, "%s ", host);
|
|
clnt_perrno(err);
|
|
return;
|
|
}
|
|
delta = 1000000.0 * cumul.clock.tv_sec + cumul.clock.tv_usec;
|
|
printf("\n\tin %.1f seconds elapsed time,\n", delta/1000000.0);
|
|
if (cumul.counter < cnt) {
|
|
printf("\t%d packets (%.2f%%) dropped\n",
|
|
cnt - cumul.counter,
|
|
100.0 * (cnt - cumul.counter)/cnt);
|
|
psec = (1000000.0 * cnt) / delta;
|
|
bsec = (lnth * 1000000.0 * cnt) / delta;
|
|
printf("Sent:\t%d packets/sec, ", psec);
|
|
if (bsec > 1024)
|
|
printf("%.1fK", bsec / 1024.0);
|
|
else
|
|
printf("%d", bsec);
|
|
printf(" bytes/sec\n");
|
|
psec = (1000000.0 * cumul.counter) / delta;
|
|
bsec = (lnth * 1000000.0 * cumul.counter) / delta;
|
|
printf("Rcvd:\t%d packets/sec, ", psec);
|
|
if (bsec > 1024)
|
|
printf("%.1fK", bsec / 1024.0);
|
|
else
|
|
printf("%d", bsec);
|
|
printf(" bytes/sec\n");
|
|
} else {
|
|
printf("\tno packets dropped\n");
|
|
psec = (1000000.0 * cumul.counter) / delta;
|
|
bsec = (lnth * 1000000.0 * cumul.counter) / delta;
|
|
printf("\t%d packets/sec, ", psec, bsec);
|
|
if (bsec > 1024)
|
|
printf("%.1fK", bsec / 1024.0);
|
|
else
|
|
printf("%d", bsec);
|
|
printf(" bytes/sec\n");
|
|
}
|
|
exit(0);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/*
|
|
* like callrpc, but with addr instead of host name
|
|
*/
|
|
mycallrpc(addr, prognum, versnum, procnum, inproc, in, outproc, out)
|
|
struct in_addr addr;
|
|
xdrproc_t inproc, outproc;
|
|
char *in, *out;
|
|
{
|
|
struct sockaddr_in server_addr;
|
|
enum clnt_stat clnt_stat;
|
|
struct timeval timeout, tottimeout;
|
|
|
|
static CLIENT *client;
|
|
static int socket = RPC_ANYSOCK;
|
|
static int oldprognum, oldversnum, valid;
|
|
static struct in_addr oldadr;
|
|
|
|
if (valid && oldprognum == prognum && oldversnum == versnum &&
|
|
adr.s_addr == oldadr.s_addr) {
|
|
/* reuse old client */
|
|
} else {
|
|
close(socket);
|
|
socket = RPC_ANYSOCK;
|
|
if (client) {
|
|
clnt_destroy(client);
|
|
client = NULL;
|
|
}
|
|
timeout.tv_usec = 0;
|
|
timeout.tv_sec = 10;
|
|
bzero(&server_addr, sizeof (server_addr));
|
|
bcopy(&adr, &server_addr.sin_addr, sizeof (adr));
|
|
server_addr.sin_family = AF_INET;
|
|
if ((client = clntudp_create(&server_addr, prognum,
|
|
versnum, timeout, &socket)) == NULL)
|
|
return ((int) rpc_createerr.cf_stat);
|
|
valid = 1;
|
|
oldprognum = prognum;
|
|
oldversnum = versnum;
|
|
oldadr = adr;
|
|
}
|
|
tottimeout.tv_sec = 25;
|
|
tottimeout.tv_usec = 0;
|
|
clnt_stat = clnt_call(client, procnum, inproc, in,
|
|
outproc, out, tottimeout);
|
|
/*
|
|
* if call failed, empty cache
|
|
*/
|
|
if (clnt_stat != RPC_SUCCESS)
|
|
valid = 0;
|
|
return ((int) clnt_stat);
|
|
}
|
|
|
|
callrpcnowait(adr, prognum, versnum, procnum, inproc, in, outproc, out)
|
|
struct in_addr adr;
|
|
xdrproc_t inproc, outproc;
|
|
char *in, *out;
|
|
{
|
|
struct sockaddr_in server_addr;
|
|
enum clnt_stat clnt_stat;
|
|
struct timeval timeout, tottimeout;
|
|
|
|
static CLIENT *client;
|
|
static int socket = RPC_ANYSOCK;
|
|
static int oldprognum, oldversnum, valid;
|
|
static struct in_addr oldadr;
|
|
|
|
if (valid && oldprognum == prognum && oldversnum == versnum &&
|
|
oldadr.s_addr == adr.s_addr) {
|
|
/* reuse old client */
|
|
} else {
|
|
close(socket);
|
|
socket = RPC_ANYSOCK;
|
|
if (client) {
|
|
clnt_destroy(client);
|
|
client = NULL;
|
|
}
|
|
timeout.tv_usec = 0;
|
|
timeout.tv_sec = 0;
|
|
bzero(&server_addr, sizeof (server_addr));
|
|
bcopy(&adr, &server_addr.sin_addr, sizeof (adr));
|
|
server_addr.sin_family = AF_INET;
|
|
if ((client = clntudp_create(&server_addr, prognum,
|
|
versnum, timeout, &socket)) == NULL)
|
|
return ((int) rpc_createerr.cf_stat);
|
|
valid = 1;
|
|
oldprognum = prognum;
|
|
oldversnum = versnum;
|
|
oldadr = adr;
|
|
}
|
|
tottimeout.tv_sec = 0;
|
|
tottimeout.tv_usec = 0;
|
|
clnt_stat = clnt_call(client, procnum, inproc, in,
|
|
outproc, out, tottimeout);
|
|
/*
|
|
* if call failed, empty cache
|
|
* since timeout is zero, normal return value is RPC_TIMEDOUT
|
|
*/
|
|
if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_TIMEDOUT) {
|
|
fprintf(stderr, "spray: send error ");
|
|
clnt_perrno(clnt_stat);
|
|
valid = 0;
|
|
}
|
|
return ((int) clnt_stat);
|
|
}
|
|
|
|
char *
|
|
adrtostr(adr)
|
|
struct in_addr adr;
|
|
{
|
|
struct hostent *hp;
|
|
char *inet_ntoa();
|
|
|
|
hp = gethostbyaddr(&adr, sizeof (adr), AF_INET);
|
|
if (hp == NULL)
|
|
return (inet_ntoa(adr));
|
|
else
|
|
return (hp->h_name);
|
|
}
|
|
|
|
slp(usecs)
|
|
{
|
|
struct timeval tv;
|
|
|
|
tv.tv_sec = usecs / 1000000;
|
|
tv.tv_usec = usecs % 1000000;
|
|
select(32, 0, 0, 0, &tv);
|
|
}
|
|
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/ip_icmp.h>
|
|
#include <signal.h>
|
|
|
|
#define MAXICMP (2082 - IPHEADER) /* experimentally determined to be max */
|
|
#define IPHEADER 34 /* size of ether + ip header */
|
|
#define MINICMP 8 /* minimum icmp length */
|
|
|
|
struct timeval tv1, tv2;
|
|
int pid, rcvd;
|
|
int die(), done();
|
|
|
|
doicmp(adr, delay)
|
|
struct in_addr adr;
|
|
{
|
|
char buf[MAXICMP];
|
|
struct icmp *icp = (struct icmp *)buf;
|
|
int i, s;
|
|
int fromlen;
|
|
struct sockaddr_in to, from;
|
|
|
|
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
|
|
perror("ping: socket");
|
|
exit(1);
|
|
}
|
|
if (lnth >= IPHEADER + MINICMP)
|
|
lnth -= IPHEADER;
|
|
else
|
|
lnth = MINICMP;
|
|
if (lnth > MAXICMP) {
|
|
fprintf(stderr, "%d is max packet size\n",
|
|
MAXICMP + IPHEADER);
|
|
exit(1);
|
|
}
|
|
if (cnt == -1)
|
|
cnt = DEFBYTES/(lnth+IPHEADER);
|
|
to.sin_family = AF_INET;
|
|
to.sin_port = 0;
|
|
to.sin_addr = adr;
|
|
icp->icmp_type = ICMP_ECHO;
|
|
icp->icmp_code = 0;
|
|
icp->icmp_cksum = 0;
|
|
icp->icmp_id = 1;
|
|
icp->icmp_seq = 1;
|
|
icp->icmp_cksum = in_cksum(icp, lnth);
|
|
|
|
printf("sending %d packets of lnth %d to %s ...",
|
|
cnt, lnth + IPHEADER, host);
|
|
fflush(stdout);
|
|
|
|
if ((pid = fork()) < 0) {
|
|
perror("ping: fork");
|
|
exit(1);
|
|
}
|
|
if (pid == 0) { /* child */
|
|
sleep(1); /* wait a second to give parent time to recv */
|
|
for (i = 0; i < cnt; i++) {
|
|
if (sendto(s, icp, lnth, 0, &to, sizeof (to)) != lnth) {
|
|
fprintf(stderr, "\n");
|
|
perror("ping: sendto");
|
|
kill (pid, SIGKILL);
|
|
exit(1);
|
|
}
|
|
if (delay > 0)
|
|
slp(delay);
|
|
}
|
|
sleep(1); /* wait for last echo to get thru */
|
|
exit(0);
|
|
}
|
|
|
|
if (pid != 0) { /* parent */
|
|
signal(SIGCHLD, done);
|
|
signal(SIGINT, die);
|
|
rcvd = 0;
|
|
for (i = 0; ; i++) {
|
|
fromlen = sizeof (from);
|
|
if (recvfrom(s, buf, sizeof (buf), 0,
|
|
&from, &fromlen) < 0) {
|
|
perror("ping: recvfrom");
|
|
continue;
|
|
}
|
|
if (i == 0)
|
|
gettimeofday(&tv1, 0);
|
|
else if (i == cnt - 1)
|
|
gettimeofday(&tv2, 0);
|
|
rcvd++;
|
|
}
|
|
}
|
|
}
|
|
|
|
in_cksum(addr, len)
|
|
u_short *addr;
|
|
int len;
|
|
{
|
|
register u_short *ptr;
|
|
register int sum;
|
|
u_short *lastptr;
|
|
|
|
sum = 0;
|
|
ptr = (u_short *)addr;
|
|
lastptr = ptr + (len/2);
|
|
for (; ptr < lastptr; ptr++) {
|
|
sum += *ptr;
|
|
if (sum & 0x10000) {
|
|
sum &= 0xffff;
|
|
sum++;
|
|
}
|
|
}
|
|
return (~sum & 0xffff);
|
|
}
|
|
|
|
die()
|
|
{
|
|
kill (pid, SIGKILL);
|
|
exit(1);
|
|
}
|
|
|
|
done()
|
|
{
|
|
int psec, bsec;
|
|
|
|
if (tv2.tv_usec == 0 && tv2.tv_usec == 0) { /* estimate */
|
|
gettimeofday(&tv2, 0);
|
|
}
|
|
if (rcvd != cnt)
|
|
printf("\n\t%d packets (%.3f%%) dropped\n",
|
|
cnt - rcvd, 100.0 * (cnt - rcvd)/cnt);
|
|
else
|
|
printf("\n\tno packets dropped\n");
|
|
if (tv2.tv_usec < tv1.tv_usec) {
|
|
tv2.tv_usec += 1000000;
|
|
tv2.tv_sec -= 1;
|
|
}
|
|
tv2.tv_sec -= tv1.tv_sec;
|
|
tv2.tv_usec -= tv1.tv_usec;
|
|
psec = (1000000.0 * cnt) / (1000000.0 * tv2.tv_sec + tv2.tv_usec);
|
|
bsec = ((lnth + IPHEADER) * 1000000.0 * cnt)/
|
|
(1000000.0 * tv2.tv_sec + tv2.tv_usec);
|
|
printf("\t%d packets/sec, %d bytes/sec\n", psec, bsec);
|
|
exit(0);
|
|
}
|
|
|
|
usage()
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: spray [-i] [-c cnt] [-l lnth] [-d usecs] host\n%s",
|
|
" cnt > 0 AND lnth > 0 AND usecs >= 0\n");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* A better way to check for an inet address : scan the entire string for
|
|
* nothing but . and digits. If a letter is found return FALSE. Yes, you can
|
|
* get some degenerate cases by it, but who names a host with *all* numbers?
|
|
*/
|
|
|
|
int
|
|
isinetaddr(str)
|
|
char *str;
|
|
{
|
|
while (*str) {
|
|
if (((*str >= '0') && (*str <= '9')) || (*str == '.'))
|
|
str++;
|
|
else
|
|
return (FALSE);
|
|
}
|
|
return (TRUE);
|
|
}
|