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:
George V. Neville-Neil 2008-04-03 05:26:54 +00:00
parent caad30a422
commit 6d5a24c513
4 changed files with 220 additions and 5 deletions

View 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
View 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.

View File

@ -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)

View File

@ -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;