451 lines
9.4 KiB
C
451 lines
9.4 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)spray.c 1.1 88/03/07 4.0NFSSRC 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>
|
|
#include <errno.h>
|
|
#include <sys/capability.h>
|
|
|
|
#define DEFBYTES 100000 /* default numbers of bytes to send */
|
|
#define MAXPACKETLEN 1514
|
|
|
|
char *adrtostr(int);
|
|
void slp(int);
|
|
void usage(void);
|
|
void doicmp(int, int);
|
|
char *host;
|
|
int adr;
|
|
int lnth, cnt;
|
|
int icmp;
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
int err, i, rcved;
|
|
int delay = 0;
|
|
int psec, bsec;
|
|
int buf[SPRAYMAX/4];
|
|
struct hostent *hp;
|
|
struct sprayarr arr;
|
|
struct spraycumul cumul;
|
|
|
|
if (argc < 2)
|
|
usage();
|
|
|
|
cnt = -1;
|
|
lnth = SPRAYOVERHEAD;
|
|
while (argc > 1) {
|
|
if (argv[1][0] == '-') {
|
|
switch(argv[1][1]) {
|
|
case 'd':
|
|
delay = atoi(argv[2]);
|
|
argc--;
|
|
argv++;
|
|
break;
|
|
case 'i':
|
|
icmp++;
|
|
break;
|
|
case 'c':
|
|
cnt = atoi(argv[2]);
|
|
argc--;
|
|
argv++;
|
|
break;
|
|
case 'l':
|
|
lnth = atoi(argv[2]);
|
|
argc--;
|
|
argv++;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
else {
|
|
if (host)
|
|
usage();
|
|
else
|
|
host = argv[1];
|
|
}
|
|
argc--;
|
|
argv++;
|
|
}
|
|
if (host == NULL)
|
|
usage();
|
|
if (isinetaddr(host)) {
|
|
adr = inet_addr(host);
|
|
host = adrtostr(adr);
|
|
}
|
|
else {
|
|
if ((hp = gethostbyname(host)) == NULL) {
|
|
fprintf(stderr, "%s is unknown host name\n", host);
|
|
exit(1);
|
|
}
|
|
adr = *((int *)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);
|
|
fprintf(stderr, "\n");
|
|
return(err);
|
|
}
|
|
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);
|
|
fprintf(stderr, "\n");
|
|
return(err);
|
|
}
|
|
if (cumul.counter < cnt)
|
|
printf("\n\t%d packets (%.3f%%) dropped by %s\n",
|
|
cnt - cumul.counter,
|
|
100.0*(cnt - cumul.counter)/cnt, host);
|
|
else
|
|
printf("\n\tno packets dropped by %s\n", host);
|
|
psec = (1000000.0 * cumul.counter)
|
|
/ (1000000.0 * cumul.clock.tv_sec + cumul.clock.tv_usec);
|
|
bsec = (lnth * 1000000.0 * cumul.counter)/
|
|
(1000000.0 * cumul.clock.tv_sec + cumul.clock.tv_usec);
|
|
printf("\t%d packets/sec, %d bytes/sec\n", psec, bsec);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* like callrpc, but with addr instead of host name
|
|
*/
|
|
mycallrpc(addr, prognum, versnum, procnum, inproc, in, outproc, out)
|
|
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 int oldadr;
|
|
|
|
if (valid && oldprognum == prognum && oldversnum == versnum
|
|
&& adr == oldadr) {
|
|
/* reuse old client */
|
|
}
|
|
else {
|
|
close(socket);
|
|
socket = RPC_ANYSOCK;
|
|
if (client) {
|
|
clnt_destroy(client);
|
|
client = NULL;
|
|
}
|
|
timeout.tv_usec = 0;
|
|
timeout.tv_sec = 10;
|
|
bcopy(&adr, &server_addr.sin_addr, sizeof(adr));
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_port = 0;
|
|
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)
|
|
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 int oldadr;
|
|
|
|
if (valid && oldprognum == prognum && oldversnum == versnum
|
|
&& oldadr == adr) {
|
|
/* reuse old client */
|
|
}
|
|
else {
|
|
close(socket);
|
|
socket = RPC_ANYSOCK;
|
|
if (client) {
|
|
clnt_destroy(client);
|
|
client = NULL;
|
|
}
|
|
timeout.tv_usec = 0;
|
|
timeout.tv_sec = 0;
|
|
bcopy(&adr, &server_addr.sin_addr, sizeof(adr));
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_port = 0;
|
|
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)
|
|
valid = 0;
|
|
return ((int) clnt_stat);
|
|
}
|
|
|
|
char *
|
|
adrtostr(adr)
|
|
int adr;
|
|
{
|
|
struct hostent *hp;
|
|
static char buf[sizeof "0xnnnnnnnn"];
|
|
|
|
hp = gethostbyaddr((char *)&adr, sizeof(adr), AF_INET);
|
|
if (hp == NULL) {
|
|
sprintf(buf, "0x%x", adr);
|
|
return buf;
|
|
}
|
|
return hp->h_name;
|
|
}
|
|
|
|
void
|
|
slp(usecs)
|
|
int 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;
|
|
void die(int), done(int);
|
|
|
|
void
|
|
doicmp(adr, delay)
|
|
int adr, delay;
|
|
{
|
|
char buf[MAXICMP];
|
|
struct icmp *icp = (struct icmp *)buf;
|
|
int i, s;
|
|
int fromlen, size;
|
|
struct sockaddr_in to, from;
|
|
cap_t ocap;
|
|
cap_value_t cap_priv_port = CAP_PRIV_PORT;
|
|
|
|
ocap = cap_acquire(1, &cap_priv_port);
|
|
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
|
|
cap_surrender(ocap);
|
|
perror("ping: socket");
|
|
exit(1);
|
|
}
|
|
cap_surrender(ocap);
|
|
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.s_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) {
|
|
perror("ping: sendto");
|
|
if (errno != ENOBUFS)
|
|
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 ((size = 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);
|
|
}
|
|
|
|
void
|
|
die(int sig)
|
|
{
|
|
kill (pid, SIGKILL);
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
done(int sig)
|
|
{
|
|
int psec, bsec;
|
|
|
|
if (tv2.tv_usec == 0 && tv2.tv_usec == 0) {/* estimate */
|
|
gettimeofday(&tv2, 0);
|
|
tv2.tv_sec -= 1; /* allow for sleep(1) */
|
|
}
|
|
if (rcvd != cnt)
|
|
printf("\n\t%d packets (%.3f%%) dropped by %s\n",
|
|
cnt - rcvd, 100.0*(cnt - rcvd)/cnt, host);
|
|
else
|
|
printf("\n\tno packets dropped by %s\n", host);
|
|
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);
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: spray host [-i] [-c cnt] [-l lnth] [-d usecs]\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;
|
|
|
|
{
|
|
int i;
|
|
while (*str)
|
|
if (((*str >= '0') && (*str <= '9')) || (*str == '.')) str++;
|
|
else return(FALSE);
|
|
return(TRUE);
|
|
}
|