summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/tools/mctest/Makefile9
-rw-r--r--tools/tools/mctest/mctest.1100
-rw-r--r--tools/tools/mctest/mctest.cc115
-rw-r--r--tools/tools/mctest/mctest.h1
4 files changed, 220 insertions, 5 deletions
diff --git a/tools/tools/mctest/Makefile b/tools/tools/mctest/Makefile
new file mode 100644
index 0000000..03bd022
--- /dev/null
+++ b/tools/tools/mctest/Makefile
@@ -0,0 +1,9 @@
+#
+# $FreeBSD$
+#
+# A Makefile that builds both the mctest program and its manual page.
+
+PROG_CXX= mctest
+LDADD+= -lpthread
+
+.include <bsd.prog.mk>
diff --git a/tools/tools/mctest/mctest.1 b/tools/tools/mctest/mctest.1
new file mode 100644
index 0000000..c840810
--- /dev/null
+++ b/tools/tools/mctest/mctest.1
@@ -0,0 +1,100 @@
+.\" Copyright (c) 2008 George V. Neville-Neil
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 3, 2008
+.Dt mctest 1
+.Os
+.Sh NAME
+.Nm mctest
+.Nd "multicast test"
+.Sh SYNOPSIS
+.Nm
+.Op Fl i Ar interface
+.Op Fl n Ar number
+.Op Fl s Ar size
+.Op Fl t Ar inter-packet gap
+.Op Fl r
+.Sh DESCRIPTION
+The
+.Nm
+command implements a multicast test which involved a source
+and a sink. The source sends packets to a pre-configured
+multicast address over the interface given as a command line
+argument. The sink listens for multicast packets, records
+the time at which they're received and then reflects them back
+over unicast to the source. When the source has captured all
+the reflected packets it prints out the round trip time of each.
+.Pp
+The sink prints out the time between the packets received.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl d Ar argument"
+.It Fl i Ar interface
+Network interface, which can be found with ifconfig(1).
+.It Fl s Ar size
+Packet size in bytes.
+.It Fl n Ar number
+Number of packets.
+.It Fl t Ar gap
+Inter-packet gap in nanoseconds.
+.It Fl r
+This version of
+.Nm
+is the receiver aka the sink. This option MUST
+only be used with one copy of the program at a time.
+.El
+.Sh EXAMPLES
+The following is an example of a typical usage
+of the
+.Nm
+command:
+.Pp
+Source
+.Dl "mctest -i em0 -s 1024 -n 100 -t 1"
+Sink
+.Dl "mctest -i em0 -s 1024 -n 100 -r"
+.Pp
+Send 100 packets of 1024 bytes, with an inter-packet gap of 1 nanosecond.
+.Pp
+Gaps are measured with
+.Xr nanosleep 2 ,
+and so are not accurate down to nanoseconds
+but depend on the setting of kern.hz.
+.Sh SEE ALSO
+.Xr ifconfig 8 ,
+.Xr netstat 1 ,
+.Xr nanosleep 2 .
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An George V. Neville-Neil Aq gnn@FreeBSD.org .
+.Sh BUGS
+Should be reported to the author or to net@freebsd.org.
diff --git a/tools/tools/mctest/mctest.cc b/tools/tools/mctest/mctest.cc
index 8f7abf9..d59ddff 100644
--- a/tools/tools/mctest/mctest.cc
+++ b/tools/tools/mctest/mctest.cc
@@ -92,7 +92,7 @@ void usage(string message)
int sink(char *interface, struct in_addr *group, int pkt_size, int number) {
- int sock;
+ int sock, backchan;
socklen_t recvd_len;
struct sockaddr_in local, recvd;
struct ip_mreq mreq;
@@ -111,6 +111,11 @@ int sink(char *interface, struct in_addr *group, int pkt_size, int number) {
return (-1);
}
+ if ((backchan = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("failed to open back channel socket");
+ return (-1);
+ }
+
strncpy(ifreq.ifr_name, interface, IFNAMSIZ);
if (ioctl(sock, SIOCGIFADDR, &ifreq) < 0) {
perror("no such interface");
@@ -159,6 +164,7 @@ int sink(char *interface, struct in_addr *group, int pkt_size, int number) {
perror("setsockopt failed");
while (n < number) {
+ recvd_len = sizeof(recvd);
if (recvfrom(sock, packet, pkt_size, 0, (struct sockaddr *)&recvd,
&recvd_len) < 0) {
if (errno == EWOULDBLOCK)
@@ -166,6 +172,12 @@ int sink(char *interface, struct in_addr *group, int pkt_size, int number) {
perror("recvfrom failed");
return -1;
}
+ recvd.sin_port = htons(SERVER_PORT);
+ if (sendto(backchan, packet, pkt_size, 0,
+ (struct sockaddr *)&recvd, sizeof(recvd)) < 0) {
+ perror("sendto failed");
+ return -1;
+ }
gettimeofday(&packets[ntohl(*(int *)packet)], 0);
n++;
}
@@ -194,6 +206,72 @@ int sink(char *interface, struct in_addr *group, int pkt_size, int number) {
}
//
+// Structure to hold thread arguments
+//
+typedef struct server_args {
+ struct timeval *packets; ///< The timestamps of returning packets
+ int number; ///< Number of packets to expect.
+ int pkt_size; ///< Size of the packets
+};
+
+//
+// server receives packets sent back from the sink
+//
+// @param passed ///< Arguments passed from the caller
+//
+// 0return always NULL
+void* server(void *passed) {
+
+ int sock, n =0;
+ struct timeval timeout;
+ struct sockaddr_in addr;
+ server_args *args = (server_args *)passed;
+
+ timerclear(&timeout);
+ timeout.tv_sec = TIMEOUT;
+
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("could not open server socket");
+ return NULL;
+ }
+
+ bzero(&addr, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons(SERVER_PORT);
+ addr.sin_len = sizeof(addr);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("could not bind server socket");
+ return NULL;
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
+ sizeof(timeout)) < 0)
+ perror("setsockopt failed");
+
+ char packet[args->pkt_size];
+ while (n < args->number) {
+ if (recvfrom(sock, &packet, args->pkt_size, 0, NULL, 0) < 0) {
+ if (errno == EWOULDBLOCK)
+ break;
+ perror("recvfrom failed");
+ return NULL;
+ }
+ gettimeofday(&args->packets[ntohl(*(int *)packet)], 0);
+ n++;
+ }
+
+ cout << "Packet Reflection Complete" << endl;
+
+ if (n < args->number)
+ cout << "Missed " << args->number - n << " packets." << endl;
+
+ return NULL;
+
+}
+
+//
// transmit packets for the multicast test
//
// @param interface ///< text name of the interface (em0 etc.)
@@ -268,6 +346,19 @@ int source(char *interface, struct in_addr *group, int pkt_size,
*(int *)packets[i] = htonl(i);
}
+ struct timeval received[number];
+ struct timeval sent[number];
+ server_args args;
+ pthread_t thread;
+ args.packets = received;
+ args.number = number;
+ args.pkt_size = pkt_size;
+
+ if (pthread_create(&thread, NULL, server, &args) < 0) {
+ perror("failed to create server thread");
+ return -1;
+ }
+
struct timespec sleeptime;
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = gap;
@@ -278,12 +369,26 @@ int source(char *interface, struct in_addr *group, int pkt_size,
perror("sendto failed");
return -1;
}
+ gettimeofday(&sent[i], 0);
if (gap > 0)
if (nanosleep(&sleeptime, NULL) < 0) {
perror("nanosleep failed");
return -1;
}
}
+
+ if (pthread_join(thread, NULL) < 0) {
+ perror("failed to join thread");
+ return -1;
+ }
+
+ timeval result;
+ for (int i = 0; i < number; i++) {
+ timersub(&args.packets[i], &sent[i], &result);
+ cout << "sec: " << result.tv_sec;
+ cout << " usecs: " << result.tv_usec << endl;
+ }
+
return 0;
}
@@ -305,11 +410,11 @@ int main(int argc, char**argv)
char ch; ///< character from getopt()
extern char* optarg; ///< option argument
- char* interface; ///< Name of the interface
+ char* interface = 0; ///< Name of the interface
struct in_addr *group = NULL; ///< the multicast group address
- int pkt_size; ///< packet size
- int gap; ///< inter packet gap (in nanoseconds)
- int number; ///< number of packets to transmit
+ int pkt_size = 0; ///< packet size
+ int gap = 0; ///< inter packet gap (in nanoseconds)
+ int number = 0; ///< number of packets to transmit
bool server = false;
if (argc < 2 || argc > 11)
diff --git a/tools/tools/mctest/mctest.h b/tools/tools/mctest/mctest.h
index e4b30f0..955464c 100644
--- a/tools/tools/mctest/mctest.h
+++ b/tools/tools/mctest/mctest.h
@@ -28,5 +28,6 @@
//
const char* DEFAULT_GROUP = "239.255.255.1";
+const int SERVER_PORT = 9999;
const int DEFAULT_PORT = 6666;
const int TIMEOUT = 10;
OpenPOWER on IntegriCloud