Add a manual page and a Makefile.
Add code to reflect packets back from the sink so that we can measure round trip at the source.
This commit is contained in:
parent
caad30a422
commit
6d5a24c513
9
tools/tools/mctest/Makefile
Normal file
9
tools/tools/mctest/Makefile
Normal file
@ -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>
|
100
tools/tools/mctest/mctest.1
Normal file
100
tools/tools/mctest/mctest.1
Normal file
@ -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.
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user