1177387Sgnn//
2177388Sgnn//  Copyright 2008, George V. Neville-Neil
3177387Sgnn//  All rights reserved.
4177387Sgnn//
5177387Sgnn//
6177388Sgnn// Redistribution and use in source and binary forms, with or without
7177388Sgnn// modification, are permitted provided that the following conditions
8177388Sgnn// are met:
9177388Sgnn// 1. Redistributions of source code must retain the above copyright
10177388Sgnn//    notice, this list of conditions and the following disclaimer.
11177388Sgnn// 2. Redistributions in binary form must reproduce the above copyright
12177388Sgnn//    notice, this list of conditions and the following disclaimer in the
13177388Sgnn//    documentation and/or other materials provided with the distribution.
14177388Sgnn//
15177388Sgnn// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16177388Sgnn// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17177388Sgnn// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18177388Sgnn// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19177388Sgnn// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20177388Sgnn// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21177388Sgnn// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22177388Sgnn// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23177388Sgnn// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24177388Sgnn// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25177388Sgnn// SUCH DAMAGE.
26177388Sgnn//
27177387Sgnn//  This is a relatively simple multicast test which can act as a
28177387Sgnn//  source and sink.  The purpose of this test is to determine the
29177387Sgnn//  latency between two hosts, the source and the sink.  The programs
30177387Sgnn//  expect to be run somewhat unsynchronized hosts.  The source and
31177387Sgnn//  the sink both record the time on their own machine and then the
32177387Sgnn//  sink will correlate the data at the end of the run.
33177387Sgnn//
34177387Sgnn
35177387Sgnn#include <sys/cdefs.h>
36177387Sgnn__FBSDID("$FreeBSD$");
37177387Sgnn
38177387Sgnn// C++ STL and other related includes
39177387Sgnn#include <iostream>
40177387Sgnn#include <string>
41213327Sgnn#include <vector>
42213327Sgnn#include <algorithm>
43177387Sgnn
44177387Sgnn// Operating System and other C based includes
45177387Sgnn#include <unistd.h>
46177387Sgnn#include <errno.h>
47177387Sgnn#include <sys/types.h>
48177387Sgnn#include <sys/time.h>
49177387Sgnn#include <sys/socket.h>
50177387Sgnn#include <sys/ioctl.h>
51177387Sgnn#include <netinet/in.h>
52177387Sgnn#include <net/if.h>
53177387Sgnn#include <arpa/inet.h>
54177387Sgnn
55177387Sgnn// Private include files
56177387Sgnn#include "mctest.h"
57177387Sgnn
58177387Sgnnusing namespace std;
59177387Sgnn
60177387Sgnn//
61177387Sgnn// usage - just the program's usage line
62177387Sgnn//
63177387Sgnn//
64177387Sgnnvoid usage()
65177387Sgnn{
66178456Sgnn    cout << "mctest [-r] -M clients -m client number -i interface -g multicast group -s packet size -n number -t inter-packet gap\n";
67177387Sgnn    exit(-1);
68177387Sgnn}
69177387Sgnn
70177387Sgnn//
71177387Sgnn// usage - print out the usage with a possible message and exit
72177387Sgnn//
73177387Sgnn// \param message optional string
74177387Sgnn//
75177387Sgnn//
76177387Sgnnvoid usage(string message)
77177387Sgnn{
78177387Sgnn
79177387Sgnn    cerr << message << endl;
80177387Sgnn    usage();
81177387Sgnn}
82177387Sgnn
83177387Sgnn
84177387Sgnn//
85177387Sgnn// absorb and record packets
86177387Sgnn//
87177387Sgnn// @param interface             ///< text name of the interface (em0 etc.)
88177387Sgnn// @param group			///< multicast group
89177387Sgnn// @param pkt_size		///< packet Size
90177387Sgnn// @param number                ///< number of packets we're expecting
91178456Sgnn// @param clients               ///< total number of clients  (N)
92178456Sgnn// @param client		///< our client number (0..N)
93177387Sgnn//
94177387Sgnn// @return 0 for 0K, -1 for error, sets errno
95177387Sgnn//
96178456Sgnnint sink(char *interface, struct in_addr *group, int pkt_size, int number,
97179528Sgnn	 int clients, int client, short base_port) {
98177387Sgnn
99177387Sgnn
100177872Sgnn    int sock, backchan;
101177387Sgnn    socklen_t recvd_len;
102177387Sgnn    struct sockaddr_in local, recvd;
103177387Sgnn    struct ip_mreq mreq;
104177387Sgnn    struct ifreq ifreq;
105177387Sgnn    struct in_addr lgroup;
106177387Sgnn    struct timeval timeout;
107177387Sgnn
108177387Sgnn    if (group == NULL) {
109177387Sgnn	group = &lgroup;
110177387Sgnn	if (inet_pton(AF_INET, DEFAULT_GROUP, group) < 1)
111177387Sgnn	    return (-1);
112177387Sgnn    }
113177387Sgnn
114177387Sgnn    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
115177387Sgnn	perror("failed to open datagram socket");
116177387Sgnn	return (-1);
117177387Sgnn    }
118177387Sgnn
119177872Sgnn    if ((backchan = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
120177872Sgnn	perror("failed to open back channel socket");
121177872Sgnn	return (-1);
122177872Sgnn    }
123177872Sgnn
124177387Sgnn    strncpy(ifreq.ifr_name, interface, IFNAMSIZ);
125177387Sgnn    if (ioctl(sock, SIOCGIFADDR, &ifreq) < 0) {
126177387Sgnn	perror("no such interface");
127177387Sgnn	return (-1);
128177387Sgnn    }
129177387Sgnn
130177387Sgnn    memcpy(&mreq.imr_interface,
131177387Sgnn	   &((struct sockaddr_in*) &ifreq.ifr_addr)->sin_addr,
132177387Sgnn	   sizeof(struct in_addr));
133177387Sgnn
134177387Sgnn    mreq.imr_multiaddr.s_addr = group->s_addr;
135177387Sgnn    if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
136177387Sgnn		   sizeof(mreq)) < 0) {
137177387Sgnn	perror("failed to add membership");
138177387Sgnn	return (-1);
139177387Sgnn    }
140177387Sgnn
141177387Sgnn    if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
142177387Sgnn		   &((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr,
143177387Sgnn		   sizeof(struct in_addr)) < 0) {
144177387Sgnn	perror("failed to bind interface");
145177387Sgnn	return (-1);
146177387Sgnn    }
147177387Sgnn
148177387Sgnn    local.sin_family = AF_INET;
149177387Sgnn    local.sin_addr.s_addr = group->s_addr;
150177387Sgnn    local.sin_port = htons(DEFAULT_PORT);
151177387Sgnn    local.sin_len = sizeof(local);
152177387Sgnn
153177387Sgnn    if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0) {
154177387Sgnn	perror("could not bind socket");
155177387Sgnn	return (-1);
156177387Sgnn    }
157177387Sgnn
158177387Sgnn    timeval packets[number];
159177387Sgnn    timeval result;
160177387Sgnn    char *packet;
161177387Sgnn    packet = new char[pkt_size];
162177387Sgnn    int n = 0;
163177387Sgnn
164177387Sgnn    timerclear(&timeout);
165177387Sgnn    timeout.tv_sec = TIMEOUT;
166177387Sgnn
167177387Sgnn    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
168177387Sgnn		   sizeof(timeout)) < 0)
169177387Sgnn	perror("setsockopt failed");
170177387Sgnn
171177387Sgnn    while (n < number) {
172177872Sgnn	recvd_len = sizeof(recvd);
173177387Sgnn	if (recvfrom(sock, packet, pkt_size, 0, (struct sockaddr *)&recvd,
174177387Sgnn		     &recvd_len) < 0) {
175177387Sgnn	    if (errno == EWOULDBLOCK)
176177387Sgnn		break;
177177387Sgnn	    perror("recvfrom failed");
178177387Sgnn	    return -1;
179177387Sgnn	}
180178456Sgnn	/*
181178456Sgnn	 * Bandwidth limiting.  If there are N clients then we want
182178456Sgnn	 * 1/N packets from each, otherwise the clients will overwhelm
183178456Sgnn	 * the sender.
184178456Sgnn	 */
185178456Sgnn	if (n % clients == client) {
186179528Sgnn		recvd.sin_port = htons(base_port + client);
187178456Sgnn		if (sendto(backchan, packet, pkt_size, 0,
188178456Sgnn			   (struct sockaddr *)&recvd, sizeof(recvd)) < 0) {
189178456Sgnn		    perror("sendto failed");
190178456Sgnn		    return -1;
191178456Sgnn		}
192177872Sgnn	}
193177387Sgnn	gettimeofday(&packets[ntohl(*(int *)packet)], 0);
194177387Sgnn	n++;
195177387Sgnn    }
196177387Sgnn
197177387Sgnn    cout << "Packet run complete\n";
198177387Sgnn    if (n < number)
199177387Sgnn	cout << "Missed " << number - n << " packets." << endl;
200177387Sgnn    long maxgap = 0, mingap= INT_MAX;
201177387Sgnn    for (int i = 0; i < number; i++) {
202177387Sgnn	cout << "sec: " << packets[i].tv_sec << "  usec: " <<
203177387Sgnn	    packets[i].tv_usec << endl;
204177387Sgnn	if (i < number - 1) {
205177387Sgnn	    timersub(&packets[i+1], &packets[i], &result);
206177387Sgnn	    long gap = (result.tv_sec * 1000000) + result.tv_usec;
207177387Sgnn	    if (gap > maxgap)
208177387Sgnn		maxgap = gap;
209177387Sgnn	    if (gap < mingap)
210177387Sgnn		mingap = gap;
211177387Sgnn	}
212177387Sgnn    }
213177387Sgnn
214177387Sgnn    cout << "maximum gap (usecs): " << maxgap << endl;
215177387Sgnn    cout << "minimum gap (usecs): " << mingap << endl;
216177387Sgnn    return 0;
217177387Sgnn
218177387Sgnn}
219177387Sgnn
220177387Sgnn//
221177872Sgnn// Structure to hold thread arguments
222177872Sgnn//
223178456Sgnnstruct server_args {
224177872Sgnn    struct timeval *packets; 	///< The timestamps of returning packets
225177872Sgnn    int number;			///< Number of packets to expect.
226177872Sgnn    int pkt_size;		///< Size of the packets
227178456Sgnn    int client;			///< Which client we listen for
228177872Sgnn};
229177872Sgnn
230177872Sgnn//
231177872Sgnn// server receives packets sent back from the sink
232177872Sgnn//
233177872Sgnn// @param passed		///< Arguments passed from the caller
234177872Sgnn//
235177872Sgnn// 0return  always NULL
236177872Sgnnvoid* server(void *passed) {
237177872Sgnn
238177872Sgnn    int sock, n =0;
239177872Sgnn    struct timeval timeout;
240177872Sgnn    struct sockaddr_in addr;
241177872Sgnn    server_args *args = (server_args *)passed;
242177872Sgnn
243177872Sgnn    timerclear(&timeout);
244177872Sgnn    timeout.tv_sec = TIMEOUT;
245177872Sgnn
246177872Sgnn    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
247177872Sgnn	perror("could not open server socket");
248177872Sgnn	return NULL;
249177872Sgnn    }
250177872Sgnn
251177872Sgnn    bzero(&addr, sizeof(addr));
252177872Sgnn    addr.sin_family = AF_INET;
253177872Sgnn    addr.sin_addr.s_addr = INADDR_ANY;
254179528Sgnn    addr.sin_port = htons(args->client);
255177872Sgnn    addr.sin_len = sizeof(addr);
256177872Sgnn
257177872Sgnn    if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
258177872Sgnn	perror("could not bind server socket");
259177872Sgnn	return NULL;
260177872Sgnn    }
261177872Sgnn
262177872Sgnn    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
263177872Sgnn		   sizeof(timeout)) < 0)
264177872Sgnn	perror("setsockopt failed");
265177872Sgnn
266177872Sgnn    char packet[args->pkt_size];
267177872Sgnn    while (n < args->number) {
268177872Sgnn	if (recvfrom(sock, &packet, args->pkt_size, 0, NULL, 0) < 0) {
269177872Sgnn	    if (errno == EWOULDBLOCK)
270177872Sgnn		break;
271177872Sgnn	    perror("recvfrom failed");
272177872Sgnn	    return NULL;
273177872Sgnn	}
274177872Sgnn	gettimeofday(&args->packets[ntohl(*(int *)packet)], 0);
275177872Sgnn	n++;
276177872Sgnn    }
277177872Sgnn
278177872Sgnn    cout << "Packet Reflection Complete" << endl;
279177872Sgnn
280177872Sgnn    if (n < args->number)
281177872Sgnn	cout << "Missed " << args->number - n << " packets." << endl;
282177872Sgnn
283177872Sgnn    return NULL;
284177872Sgnn
285177872Sgnn}
286177872Sgnn
287177872Sgnn//
288177387Sgnn// transmit packets for the multicast test
289177387Sgnn//
290177387Sgnn// @param interface             ///< text name of the interface (em0 etc.)
291177387Sgnn// @param group			///< multicast group
292177387Sgnn// @param pkt_size		///< packet size
293177387Sgnn// @param number                ///< number of packets
294177387Sgnn// @param gap			///< inter packet gap in nano-seconds
295178456Sgnn// @param clients		///< number of clients we intend to run
296177387Sgnn//
297177387Sgnn// @return 0 for OK, -1 for error, sets errno
298177387Sgnn//
299177387Sgnnint source(char *interface, struct in_addr *group, int pkt_size,
300179528Sgnn	   int number, int gap, int clients, short base_port) {
301177387Sgnn
302177387Sgnn    int sock;
303177387Sgnn    struct sockaddr_in addr;
304177387Sgnn    struct ip_mreq mreq;
305177387Sgnn    struct ifreq ifreq;
306177387Sgnn    struct in_addr lgroup;
307177387Sgnn
308177387Sgnn    if (group == NULL) {
309177387Sgnn	group = &lgroup;
310177387Sgnn	if (inet_pton(AF_INET, DEFAULT_GROUP, group) < 1)
311177387Sgnn	    return (-1);
312177387Sgnn    }
313177387Sgnn
314177387Sgnn    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
315177387Sgnn	perror("could not open dgram socket");
316177387Sgnn	return (-1);
317177387Sgnn    }
318177387Sgnn
319177387Sgnn    bzero(&addr, sizeof(addr));
320177387Sgnn    addr.sin_family = AF_INET;
321177387Sgnn    addr.sin_port = htons(DEFAULT_PORT);
322177387Sgnn    addr.sin_addr.s_addr = group->s_addr;
323177387Sgnn    addr.sin_len = sizeof(addr);
324177387Sgnn
325177387Sgnn    strncpy(ifreq.ifr_name, interface, IFNAMSIZ);
326177387Sgnn    if (ioctl(sock, SIOCGIFADDR, &ifreq) < 0) {
327177387Sgnn	perror("no such interface");
328177387Sgnn	return (-1);
329177387Sgnn    }
330177387Sgnn
331177387Sgnn    memcpy(&mreq.imr_interface,
332177387Sgnn	   &((struct sockaddr_in*) &ifreq.ifr_addr)->sin_addr,
333177387Sgnn	   sizeof(struct in_addr));
334177387Sgnn
335177387Sgnn    mreq.imr_multiaddr.s_addr = group->s_addr;
336177387Sgnn    if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
337177387Sgnn		   sizeof(mreq)) < 0) {
338177387Sgnn	perror("failed to add membership");
339177387Sgnn	return (-1);
340177387Sgnn    }
341177387Sgnn
342177387Sgnn    if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
343177387Sgnn		   &((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr,
344177387Sgnn		   sizeof(struct in_addr)) < 0) {
345177387Sgnn	perror("failed to bind interface");
346177387Sgnn	return (-1);
347177387Sgnn    }
348177387Sgnn
349177387Sgnn    u_char ttl = 64;
350177387Sgnn
351177387Sgnn    if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
352177387Sgnn		   &ttl, sizeof(ttl)) < 0) {
353177387Sgnn	perror("failed to set TTL");
354177387Sgnn	return (-1);
355177387Sgnn    }
356177387Sgnn
357177387Sgnn    char *packets[number];
358177387Sgnn    for (int i = 0;i < number; i++) {
359177387Sgnn	packets[i] = new char[pkt_size];
360177387Sgnn	*(int *)packets[i] = htonl(i);
361177387Sgnn    }
362177387Sgnn
363177872Sgnn    struct timeval sent[number];
364178456Sgnn    struct timeval received[clients][number];
365178456Sgnn    server_args args[clients];
366178456Sgnn    pthread_t thread[clients];
367177872Sgnn
368178456Sgnn    for (int i = 0;i < clients; i++) {
369178456Sgnn        args[i].pkt_size = pkt_size;
370178456Sgnn        args[i].packets = received[i];
371178456Sgnn        args[i].number = number / clients;
372179528Sgnn	args[i].client = base_port + i;
373203800Sru	if (pthread_create(&thread[i], NULL, server, &args[i]) != 0) {
374178456Sgnn	    perror("failed to create server thread");
375178456Sgnn	    return -1;
376178456Sgnn        }
377178456Sgnn    }
378177872Sgnn
379177387Sgnn    struct timespec sleeptime;
380177387Sgnn    sleeptime.tv_sec = 0;
381177387Sgnn    sleeptime.tv_nsec = gap;
382177387Sgnn
383177387Sgnn    for (int i = 0;i < number; i++) {
384177387Sgnn	if (sendto(sock, (void *)packets[i], pkt_size, 0,
385177387Sgnn		   (struct sockaddr *)&addr, sizeof(addr)) < 0) {
386177387Sgnn	    perror("sendto failed");
387177387Sgnn	    return -1;
388177387Sgnn	}
389177872Sgnn	gettimeofday(&sent[i], 0);
390177387Sgnn	if (gap > 0)
391177387Sgnn	    if (nanosleep(&sleeptime, NULL) < 0) {
392177387Sgnn		perror("nanosleep failed");
393177387Sgnn		return -1;
394177387Sgnn	    }
395177387Sgnn    }
396177872Sgnn
397178456Sgnn    for (int i = 0; i < clients; i++) {
398203800Sru        if (pthread_join(thread[i], NULL) != 0) {
399178456Sgnn 	    perror("failed to join thread");
400178456Sgnn 	    return -1;
401178456Sgnn        }
402178456Sgnn    }
403178456Sgnn
404177872Sgnn    timeval result;
405213327Sgnn    vector<int> deltas;
406213327Sgnn    double idx[] = { .0001, .001, .01, .1, .5, .9, .99, .999, .9999, 0.0 };
407213327Sgnn
408178456Sgnn    for (int client = 0;client < clients; client++) {
409213327Sgnn	deltas.clear();
410178456Sgnn	cout << "Results from client #" << client << endl;
411213327Sgnn	cout << "in usecs" << endl;
412178456Sgnn        for (int i = 0; i < number; i++) {
413213327Sgnn// 	    if (i % clients != client)
414213327Sgnn// 		continue;
415213327Sgnn            if (&args[client].packets[i].tv_sec == 0)
416213327Sgnn			continue;
417178456Sgnn	    timersub(&args[client].packets[i], &sent[i], &result);
418213327Sgnn	    deltas.push_back(result.tv_usec);
419213327Sgnn// 	    cout << "sec: " << result.tv_sec;
420213327Sgnn// 	    cout << " usecs: " << result.tv_usec << endl;
421178456Sgnn            }
422215409Sgnn	cout << "comparing " << long(deltas.size()) << " deltas" << endl;
423213327Sgnn	cout << "number represents usecs of round-trip time" << endl;
424213327Sgnn	sort(deltas.begin(), deltas.end());
425213327Sgnn	for (int i = 0; idx[i] != 0; ++i) {
426213327Sgnn		printf("%s% 5d", (i == 0) ? "" : " ",
427213327Sgnn		       deltas[(int) (idx[i] * deltas.size())]);
428213327Sgnn	}
429213327Sgnn	printf("\n");
430177872Sgnn    }
431177872Sgnn
432177387Sgnn    return 0;
433177387Sgnn}
434177387Sgnn
435177387Sgnn
436177387Sgnn//
437177387Sgnn// main - the main program
438177387Sgnn//
439177387Sgnn// \param -g multicast group address to which to send/recv packets on
440177387Sgnn// \param -n the number of packets to send
441177387Sgnn// \param -s packet size in bytes
442177387Sgnn// \param -t inter-packet gap, in nanoseconds
443177387Sgnn//
444177387Sgnn//
445177387Sgnnint main(int argc, char**argv)
446177387Sgnn{
447177387Sgnn
448177387Sgnn	const int MAXNSECS = 999999999; ///< Must be < 1.0 x 10**9 nanoseconds
449177387Sgnn
450177387Sgnn	char ch;		///< character from getopt()
451177387Sgnn	extern char* optarg;	///< option argument
452177387Sgnn
453177872Sgnn	char* interface = 0;    ///< Name of the interface
454177387Sgnn	struct in_addr *group = NULL;	///< the multicast group address
455177872Sgnn	int pkt_size = 0;       ///< packet size
456177872Sgnn	int gap = 0;		///< inter packet gap (in nanoseconds)
457177872Sgnn	int number = 0;         ///< number of packets to transmit
458178456Sgnn	bool server = false;	///< are we on he receiving end of multicast
459178456Sgnn	int client = 0;		///< for receivers which client are we
460178456Sgnn	int clients = 1;	///< for senders how many clients are there
461179528Sgnn	short base_port = SERVER_PORT; ///< to have multiple copies running at once
462177387Sgnn
463179528Sgnn	if (argc < 2 || argc > 16)
464177387Sgnn		usage();
465177387Sgnn
466179528Sgnn	while ((ch = getopt(argc, argv, "M:m:g:i:n:s:t:b:rh")) != -1) {
467177387Sgnn		switch (ch) {
468177387Sgnn		case 'g':
469177387Sgnn			group = new (struct in_addr );
470177387Sgnn			if (inet_pton(AF_INET, optarg, group) < 1)
471177387Sgnn				usage(argv[0] + string(" Error: invalid multicast group") +
472177387Sgnn				      optarg);
473177387Sgnn			break;
474177387Sgnn		case 'i':
475177387Sgnn			interface = optarg;
476177387Sgnn			break;
477177387Sgnn		case 'n':
478177387Sgnn			number = atoi(optarg);
479178456Sgnn			if (number < 0 || number > INT_MAX)
480177387Sgnn				usage(argv[0] + string(" Error: ") + optarg +
481177387Sgnn				      string(" number of packets out of range"));
482177387Sgnn			break;
483177387Sgnn		case 's':
484177387Sgnn			pkt_size = atoi(optarg);
485177387Sgnn			if (pkt_size < 0 || pkt_size > 65535)
486177387Sgnn				usage(argv[0] + string(" Error: ") + optarg +
487177387Sgnn				      string(" packet size out of range"));
488177387Sgnn			break;
489177387Sgnn		case 't':
490177387Sgnn			gap = atoi(optarg);
491177387Sgnn			if (gap < 0 || gap > MAXNSECS)
492177387Sgnn				usage(argv[0] + string(" Error: ") + optarg +
493177387Sgnn				      string(" gap out of range"));
494177387Sgnn			break;
495177387Sgnn		case 'r':
496177387Sgnn			server = true;
497177387Sgnn			break;
498178456Sgnn		case 'm':
499178456Sgnn			client = atoi(optarg);
500178456Sgnn			break;
501178456Sgnn		case 'M':
502178456Sgnn			clients = atoi(optarg);
503178456Sgnn			break;
504179528Sgnn		case 'b':
505179528Sgnn			base_port = atoi(optarg);
506179528Sgnn			break;
507177387Sgnn		case 'h':
508177387Sgnn			usage(string("Help\n"));
509177387Sgnn			break;
510177387Sgnn		}
511177387Sgnn	}
512177387Sgnn
513177387Sgnn	if (server) {
514178456Sgnn	    if (clients <= 0 || client < 0)
515178456Sgnn		usage("must specify client (-m) and number of clients (-M)");
516179528Sgnn	    sink(interface, group, pkt_size, number, clients, client,
517179528Sgnn		 base_port);
518177387Sgnn	} else {
519178456Sgnn	    if (clients <= 0)
520178456Sgnn		usage("must specify number of clients (-M)");
521179528Sgnn	    source(interface, group, pkt_size, number, gap, clients,
522179528Sgnn		   base_port);
523177387Sgnn	}
524177387Sgnn
525177387Sgnn}
526