diff --git a/tools/tools/mctest/Makefile b/tools/tools/mctest/Makefile new file mode 100644 index 000000000000..03bd022f29bf --- /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 diff --git a/tools/tools/mctest/mctest.1 b/tools/tools/mctest/mctest.1 new file mode 100644 index 000000000000..c840810436e6 --- /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 8f7abf954ec6..d59ddff323a1 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++; } @@ -193,6 +205,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 // @@ -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 e4b30f0cb86a..955464c8795a 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;