113242Sgraichen/*
213242Sgraichen * Copyright (c) 1993 Winning Strategies, Inc.
313242Sgraichen * All rights reserved.
413242Sgraichen *
513242Sgraichen * Redistribution and use in source and binary forms, with or without
613242Sgraichen * modification, are permitted provided that the following conditions
713242Sgraichen * are met:
813242Sgraichen * 1. Redistributions of source code must retain the above copyright
913242Sgraichen *    notice, this list of conditions and the following disclaimer.
1013242Sgraichen * 2. Redistributions in binary form must reproduce the above copyright
1113242Sgraichen *    notice, this list of conditions and the following disclaimer in the
1213242Sgraichen *    documentation and/or other materials provided with the distribution.
1313242Sgraichen * 3. All advertising materials mentioning features or use of this software
1413242Sgraichen *    must display the following acknowledgement:
1513242Sgraichen *      This product includes software developed by Winning Strategies, Inc.
1613242Sgraichen * 4. The name of the author may not be used to endorse or promote products
1713242Sgraichen *    derived from this software without specific prior written permission
1813242Sgraichen *
1913242Sgraichen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2013242Sgraichen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2113242Sgraichen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2213242Sgraichen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2313242Sgraichen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2413242Sgraichen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2513242Sgraichen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2613242Sgraichen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2713242Sgraichen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2813242Sgraichen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2913242Sgraichen */
3013242Sgraichen
3130598Scharnier#ifndef lint
3230598Scharnierstatic const char rcsid[] =
3350479Speter  "$FreeBSD$";
3430598Scharnier#endif /* not lint */
3530598Scharnier
3678722Sdd#include <err.h>
3713242Sgraichen#include <stdio.h>
3813242Sgraichen#include <stdlib.h>
3913242Sgraichen#include <unistd.h>
4013242Sgraichen
4113242Sgraichen#include <rpc/rpc.h>
4213242Sgraichen#include <rpcsvc/spray.h>
4313242Sgraichen
4413242Sgraichen#ifndef SPRAYOVERHEAD
4513242Sgraichen#define SPRAYOVERHEAD	86
4613242Sgraichen#endif
4713242Sgraichen
4878780Sddstatic void usage(void);
4978780Sddstatic void print_xferstats(unsigned int, int, double);
5013242Sgraichen
5113242Sgraichen/* spray buffer */
52227261Sedstatic char spray_buffer[SPRAYMAX];
5313242Sgraichen
5413242Sgraichen/* RPC timeouts */
55227261Sedstatic struct timeval NO_DEFAULT = { -1, -1 };
56227261Sedstatic struct timeval ONE_WAY = { 0, 0 };
57227261Sedstatic struct timeval TIMEOUT = { 25, 0 };
5813242Sgraichen
5913242Sgraichenint
6078780Sddmain(int argc, char *argv[])
6113242Sgraichen{
6213242Sgraichen	spraycumul	host_stats;
6313242Sgraichen	sprayarr	host_array;
6413242Sgraichen	CLIENT *cl;
6513242Sgraichen	int c;
6678780Sdd	u_int i;
6778780Sdd	u_int count = 0;
6813242Sgraichen	int delay = 0;
6913242Sgraichen	int length = 0;
7013242Sgraichen	double xmit_time;			/* time to receive data */
7113242Sgraichen
7213242Sgraichen	while ((c = getopt(argc, argv, "c:d:l:")) != -1) {
7313242Sgraichen		switch (c) {
7413242Sgraichen		case 'c':
7513242Sgraichen			count = atoi(optarg);
7613242Sgraichen			break;
7713242Sgraichen		case 'd':
7813242Sgraichen			delay = atoi(optarg);
7913242Sgraichen			break;
8013242Sgraichen		case 'l':
8113242Sgraichen			length = atoi(optarg);
8213242Sgraichen			break;
8313242Sgraichen		default:
8413242Sgraichen			usage();
8513242Sgraichen			/* NOTREACHED */
8613242Sgraichen		}
8713242Sgraichen	}
8813242Sgraichen	argc -= optind;
8913242Sgraichen	argv += optind;
9013242Sgraichen
9113242Sgraichen	if (argc != 1) {
9213242Sgraichen		usage();
9313242Sgraichen		/* NOTREACHED */
9413242Sgraichen	}
9513242Sgraichen
9613242Sgraichen
9713242Sgraichen	/* Correct packet length. */
9813242Sgraichen	if (length > SPRAYMAX) {
9913242Sgraichen		length = SPRAYMAX;
10013242Sgraichen	} else if (length < SPRAYOVERHEAD) {
10113242Sgraichen		length = SPRAYOVERHEAD;
10213242Sgraichen	} else {
10313242Sgraichen		/* The RPC portion of the packet is a multiple of 32 bits. */
10413242Sgraichen		length -= SPRAYOVERHEAD - 3;
10513242Sgraichen		length &= ~3;
10613242Sgraichen		length += SPRAYOVERHEAD;
10713242Sgraichen	}
10813242Sgraichen
10913242Sgraichen
11013242Sgraichen	/*
11113242Sgraichen	 * The default value of count is the number of packets required
11213242Sgraichen	 * to make the total stream size 100000 bytes.
11313242Sgraichen	 */
11413242Sgraichen	if (!count) {
11513242Sgraichen		count = 100000 / length;
11613242Sgraichen	}
11713242Sgraichen
11813242Sgraichen	/* Initialize spray argument */
11913242Sgraichen	host_array.sprayarr_len = length - SPRAYOVERHEAD;
12013242Sgraichen	host_array.sprayarr_val = spray_buffer;
12113242Sgraichen
12213242Sgraichen
12313242Sgraichen	/* create connection with server */
12413242Sgraichen	cl = clnt_create(*argv, SPRAYPROG, SPRAYVERS, "udp");
12561027Scharnier	if (cl == NULL)
126100121Salfred		errx(1, "%s", clnt_spcreateerror(""));
12713242Sgraichen
12813242Sgraichen
12913242Sgraichen	/*
13013242Sgraichen	 * For some strange reason, RPC 4.0 sets the default timeout,
13113242Sgraichen	 * thus timeouts specified in clnt_call() are always ignored.
13213242Sgraichen	 *
13313242Sgraichen	 * The following (undocumented) hack resets the internal state
13413242Sgraichen	 * of the client handle.
13513242Sgraichen	 */
13695658Sdes	clnt_control(cl, CLSET_TIMEOUT, &NO_DEFAULT);
13713242Sgraichen
13813242Sgraichen
13913242Sgraichen	/* Clear server statistics */
14095658Sdes	if (clnt_call(cl, SPRAYPROC_CLEAR, (xdrproc_t)xdr_void, NULL,
14195658Sdes		(xdrproc_t)xdr_void, NULL, TIMEOUT) != RPC_SUCCESS)
142100121Salfred		errx(1, "%s", clnt_sperror(cl, ""));
14313242Sgraichen
14413242Sgraichen
14513242Sgraichen	/* Spray server with packets */
14678780Sdd	printf ("sending %u packets of lnth %d to %s ...", count, length,
14778780Sdd	    *argv);
14813242Sgraichen	fflush (stdout);
14913242Sgraichen
15013242Sgraichen	for (i = 0; i < count; i++) {
15195658Sdes		clnt_call(cl, SPRAYPROC_SPRAY, (xdrproc_t)xdr_sprayarr,
15295658Sdes		    &host_array, (xdrproc_t)xdr_void, NULL, ONE_WAY);
15313242Sgraichen
15413242Sgraichen		if (delay) {
15513242Sgraichen			usleep(delay);
15613242Sgraichen		}
15713242Sgraichen	}
15813242Sgraichen
15913242Sgraichen
16013242Sgraichen	/* Collect statistics from server */
16195658Sdes	if (clnt_call(cl, SPRAYPROC_GET, (xdrproc_t)xdr_void, NULL,
16295658Sdes		(xdrproc_t)xdr_spraycumul, &host_stats, TIMEOUT) != RPC_SUCCESS)
163100121Salfred		errx(1, "%s", clnt_sperror(cl, ""));
16413242Sgraichen
16513242Sgraichen	xmit_time = host_stats.clock.sec +
16613242Sgraichen			(host_stats.clock.usec / 1000000.0);
16713242Sgraichen
16813242Sgraichen	printf ("\n\tin %.2f seconds elapsed time\n", xmit_time);
16913242Sgraichen
17013242Sgraichen
17113242Sgraichen	/* report dropped packets */
17213242Sgraichen	if (host_stats.counter != count) {
17313242Sgraichen		int packets_dropped = count - host_stats.counter;
17413242Sgraichen
17513242Sgraichen		printf("\t%d packets (%.2f%%) dropped\n",
17613242Sgraichen			packets_dropped,
17713242Sgraichen			100.0 * packets_dropped / count );
17813242Sgraichen	} else {
17913242Sgraichen		printf("\tno packets dropped\n");
18013242Sgraichen	}
18113242Sgraichen
18213242Sgraichen	printf("Sent:");
18313242Sgraichen	print_xferstats(count, length, xmit_time);
18413242Sgraichen
18513242Sgraichen	printf("Rcvd:");
18613242Sgraichen	print_xferstats(host_stats.counter, length, xmit_time);
18713242Sgraichen
18813242Sgraichen	exit (0);
18913242Sgraichen}
19013242Sgraichen
19113242Sgraichen
19278780Sddstatic void
19378780Sddprint_xferstats(u_int packets, int packetlen, double xfertime)
19413242Sgraichen{
19513242Sgraichen	int datalen;
19613242Sgraichen	double pps;		/* packets per second */
19713242Sgraichen	double bps;		/* bytes per second */
19813242Sgraichen
19913242Sgraichen	datalen = packets * packetlen;
20013242Sgraichen	pps = packets / xfertime;
20113242Sgraichen	bps = datalen / xfertime;
20213242Sgraichen
20313242Sgraichen	printf("\t%.0f packets/sec, ", pps);
20413242Sgraichen
20513242Sgraichen	if (bps >= 1024)
20613242Sgraichen		printf ("%.1fK ", bps / 1024);
20713242Sgraichen	else
20813242Sgraichen		printf ("%.0f ", bps);
20913242Sgraichen
21013242Sgraichen	printf("bytes/sec\n");
21113242Sgraichen}
21213242Sgraichen
21313242Sgraichen
21430598Scharnierstatic void
21578780Sddusage(void)
21613242Sgraichen{
21730598Scharnier	fprintf(stderr,
21830598Scharnier		"usage: spray [-c count] [-l length] [-d delay] host\n");
21913242Sgraichen	exit(1);
22013242Sgraichen}
221