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