summaryrefslogtreecommitdiffstats
path: root/src/iperf_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/iperf_util.c')
-rw-r--r--src/iperf_util.c597
1 files changed, 597 insertions, 0 deletions
diff --git a/src/iperf_util.c b/src/iperf_util.c
new file mode 100644
index 0000000..81e8da1
--- /dev/null
+++ b/src/iperf_util.c
@@ -0,0 +1,597 @@
+/*
+ * iperf, Copyright (c) 2014, 2016, 2017, The Regents of the University of
+ * California, through Lawrence Berkeley National Laboratory (subject
+ * to receipt of any required approvals from the U.S. Dept. of
+ * Energy). All rights reserved.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+/* iperf_util.c
+ *
+ * Iperf utility functions
+ *
+ */
+#include "iperf_config.h"
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "cjson.h"
+#include "iperf.h"
+#include "iperf_api.h"
+
+/*
+ * Read entropy from /dev/urandom
+ * Errors are fatal.
+ * Returns 0 on success.
+ */
+int readentropy(void *out, size_t outsize)
+{
+ static FILE *frandom;
+ static const char rndfile[] = "/dev/urandom";
+
+ if (!outsize) return 0;
+
+ if (frandom == NULL) {
+ frandom = fopen(rndfile, "rb");
+ if (frandom == NULL) {
+ iperf_errexit(NULL, "error - failed to open %s: %s\n",
+ rndfile, strerror(errno));
+ }
+ setbuf(frandom, NULL);
+ }
+ if (fread(out, 1, outsize, frandom) != outsize) {
+ iperf_errexit(NULL, "error - failed to read %s: %s\n",
+ rndfile,
+ feof(frandom) ? "EOF" : strerror(errno));
+ }
+ return 0;
+}
+
+
+/*
+ * Fills buffer with repeating pattern (similar to pattern that used in iperf2)
+ */
+void fill_with_repeating_pattern(void *out, size_t outsize)
+{
+ size_t i;
+ int counter = 0;
+ char *buf = (char *)out;
+
+ if (!outsize) return;
+
+ for (i = 0; i < outsize; i++) {
+ buf[i] = (char)('0' + counter);
+ if (counter >= 9)
+ counter = 0;
+ else
+ counter++;
+ }
+}
+
+
+/* make_cookie
+ *
+ * Generate and return a cookie string
+ *
+ * Iperf uses this function to create test "cookies" which
+ * server as unique test identifiers. These cookies are also
+ * used for the authentication of stream connections.
+ * Assumes cookie has size (COOKIE_SIZE + 1) char's.
+ */
+
+void
+make_cookie(const char *cookie)
+{
+ unsigned char *out = (unsigned char*)cookie;
+ size_t pos;
+ static const unsigned char rndchars[] = "abcdefghijklmnopqrstuvwxyz234567";
+
+ readentropy(out, COOKIE_SIZE);
+ for (pos = 0; pos < (COOKIE_SIZE - 1); pos++) {
+ out[pos] = rndchars[out[pos] % (sizeof(rndchars) - 1)];
+ }
+ out[pos] = '\0';
+}
+
+
+/* is_closed
+ *
+ * Test if the file descriptor fd is closed.
+ *
+ * Iperf uses this function to test whether a TCP stream socket
+ * is closed, because accepting and denying an invalid connection
+ * in iperf_tcp_accept is not considered an error.
+ */
+
+int
+is_closed(int fd)
+{
+ struct timeval tv;
+ fd_set readset;
+
+ FD_ZERO(&readset);
+ FD_SET(fd, &readset);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ if (select(fd+1, &readset, NULL, NULL, &tv) < 0) {
+ if (errno == EBADF)
+ return 1;
+ }
+ return 0;
+}
+
+
+double
+timeval_to_double(struct timeval * tv)
+{
+ double d;
+
+ d = tv->tv_sec + tv->tv_usec / 1000000;
+
+ return d;
+}
+
+int
+timeval_equals(struct timeval * tv0, struct timeval * tv1)
+{
+ if ( tv0->tv_sec == tv1->tv_sec && tv0->tv_usec == tv1->tv_usec )
+ return 1;
+ else
+ return 0;
+}
+
+double
+timeval_diff(struct timeval * tv0, struct timeval * tv1)
+{
+ double time1, time2;
+
+ time1 = tv0->tv_sec + (tv0->tv_usec / 1000000.0);
+ time2 = tv1->tv_sec + (tv1->tv_usec / 1000000.0);
+
+ time1 = time1 - time2;
+ if (time1 < 0)
+ time1 = -time1;
+ return time1;
+}
+
+void
+cpu_util(double pcpu[3])
+{
+ static struct iperf_time last;
+ static clock_t clast;
+ static struct rusage rlast;
+ struct iperf_time now, temp_time;
+ clock_t ctemp;
+ struct rusage rtemp;
+ double timediff;
+ double userdiff;
+ double systemdiff;
+
+ if (pcpu == NULL) {
+ iperf_time_now(&last);
+ clast = clock();
+ getrusage(RUSAGE_SELF, &rlast);
+ return;
+ }
+
+ iperf_time_now(&now);
+ ctemp = clock();
+ getrusage(RUSAGE_SELF, &rtemp);
+
+ iperf_time_diff(&now, &last, &temp_time);
+ timediff = iperf_time_in_usecs(&temp_time);
+
+ userdiff = ((rtemp.ru_utime.tv_sec * 1000000.0 + rtemp.ru_utime.tv_usec) -
+ (rlast.ru_utime.tv_sec * 1000000.0 + rlast.ru_utime.tv_usec));
+ systemdiff = ((rtemp.ru_stime.tv_sec * 1000000.0 + rtemp.ru_stime.tv_usec) -
+ (rlast.ru_stime.tv_sec * 1000000.0 + rlast.ru_stime.tv_usec));
+
+ pcpu[0] = (((ctemp - clast) * 1000000.0 / CLOCKS_PER_SEC) / timediff) * 100;
+ pcpu[1] = (userdiff / timediff) * 100;
+ pcpu[2] = (systemdiff / timediff) * 100;
+}
+
+const char *
+get_system_info(void)
+{
+ static char buf[1024];
+ struct utsname uts;
+
+ memset(buf, 0, 1024);
+ uname(&uts);
+
+ snprintf(buf, sizeof(buf), "%s %s %s %s %s", uts.sysname, uts.nodename,
+ uts.release, uts.version, uts.machine);
+
+ return buf;
+}
+
+
+const char *
+get_optional_features(void)
+{
+ static char features[1024];
+ unsigned int numfeatures = 0;
+
+ snprintf(features, sizeof(features), "Optional features available: ");
+
+#if defined(HAVE_CPU_AFFINITY)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "CPU affinity setting",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_CPU_AFFINITY */
+
+#if defined(HAVE_FLOWLABEL)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "IPv6 flow label",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_FLOWLABEL */
+
+#if defined(HAVE_SCTP_H)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "SCTP",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SCTP_H */
+
+#if defined(HAVE_TCP_CONGESTION)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "TCP congestion algorithm setting",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_TCP_CONGESTION */
+
+#if defined(HAVE_SENDFILE)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "sendfile / zerocopy",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SENDFILE */
+
+#if defined(HAVE_SO_MAX_PACING_RATE)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "socket pacing",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SO_MAX_PACING_RATE */
+
+#if defined(HAVE_SSL)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "authentication",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SSL */
+
+#if defined(HAVE_SO_BINDTODEVICE)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "bind to device",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SO_BINDTODEVICE */
+
+#if defined(HAVE_DONT_FRAGMENT)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "support IPv4 don't fragment",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_DONT_FRAGMENT */
+
+#if defined(HAVE_PTHREAD)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "POSIX threads",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_PTHREAD */
+
+ if (numfeatures == 0) {
+ strncat(features, "None",
+ sizeof(features) - strlen(features) - 1);
+ }
+
+ return features;
+}
+
+/* Helper routine for building cJSON objects in a printf-like manner.
+**
+** Sample call:
+** j = iperf_json_printf("foo: %b bar: %d bletch: %f eep: %s", b, i, f, s);
+**
+** The four formatting characters and the types they expect are:
+** %b boolean int
+** %d integer int64_t
+** %f floating point double
+** %s string char *
+** If the values you're passing in are not these exact types, you must
+** cast them, there is no automatic type coercion/widening here.
+**
+** The colons mark the end of field names, and blanks are ignored.
+**
+** This routine is not particularly robust, but it's not part of the API,
+** it's just for internal iperf3 use.
+*/
+cJSON*
+iperf_json_printf(const char *format, ...)
+{
+ cJSON* o;
+ va_list argp;
+ const char *cp;
+ char name[100];
+ char* np;
+ cJSON* j;
+
+ o = cJSON_CreateObject();
+ if (o == NULL)
+ return NULL;
+ va_start(argp, format);
+ np = name;
+ for (cp = format; *cp != '\0'; ++cp) {
+ switch (*cp) {
+ case ' ':
+ break;
+ case ':':
+ *np = '\0';
+ break;
+ case '%':
+ ++cp;
+ switch (*cp) {
+ case 'b':
+ j = cJSON_CreateBool(va_arg(argp, int));
+ break;
+ case 'd':
+ j = cJSON_CreateNumber(va_arg(argp, int64_t));
+ break;
+ case 'f':
+ j = cJSON_CreateNumber(va_arg(argp, double));
+ break;
+ case 's':
+ j = cJSON_CreateString(va_arg(argp, char *));
+ break;
+ default:
+ va_end(argp);
+ return NULL;
+ }
+ if (j == NULL) {
+ va_end(argp);
+ return NULL;
+ }
+ cJSON_AddItemToObject(o, name, j);
+ np = name;
+ break;
+ default:
+ *np++ = *cp;
+ break;
+ }
+ }
+ va_end(argp);
+ return o;
+}
+
+/* Debugging routine to dump out an fd_set. */
+void
+iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds)
+{
+ int fd;
+ int comma;
+
+ fprintf(fp, "%s: [", str);
+ comma = 0;
+ for (fd = 0; fd < nfds; ++fd) {
+ if (FD_ISSET(fd, fds)) {
+ if (comma)
+ fprintf(fp, ", ");
+ fprintf(fp, "%d", fd);
+ comma = 1;
+ }
+ }
+ fprintf(fp, "]\n");
+}
+
+/*
+ * daemon(3) implementation for systems lacking one.
+ * Cobbled together from various daemon(3) implementations,
+ * not intended to be general-purpose. */
+#ifndef HAVE_DAEMON
+int daemon(int nochdir, int noclose)
+{
+ pid_t pid = 0;
+ pid_t sid = 0;
+ int fd;
+
+ /*
+ * Ignore any possible SIGHUP when the parent process exits.
+ * Note that the iperf3 server process will eventually install
+ * its own signal handler for SIGHUP, so we can be a little
+ * sloppy about not restoring the prior value. This does not
+ * generalize.
+ */
+ signal(SIGHUP, SIG_IGN);
+
+ pid = fork();
+ if (pid < 0) {
+ return -1;
+ }
+ if (pid > 0) {
+ /* Use _exit() to avoid doing atexit() stuff. */
+ _exit(0);
+ }
+
+ sid = setsid();
+ if (sid < 0) {
+ return -1;
+ }
+
+ /*
+ * Fork again to avoid becoming a session leader.
+ * This might only matter on old SVr4-derived OSs.
+ * Note in particular that glibc and FreeBSD libc
+ * only fork once.
+ */
+ pid = fork();
+ if (pid == -1) {
+ return -1;
+ } else if (pid != 0) {
+ _exit(0);
+ }
+
+ if (!nochdir) {
+ chdir("/");
+ }
+
+ if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > 2) {
+ close(fd);
+ }
+ }
+ return (0);
+}
+#endif /* HAVE_DAEMON */
+
+/* Compatibility version of getline(3) for systems that don't have it.. */
+#ifndef HAVE_GETLINE
+/* The following code adopted from NetBSD's getline.c, which is: */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+ssize_t
+getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
+{
+ char *ptr, *eptr;
+
+
+ if (*buf == NULL || *bufsiz == 0) {
+ *bufsiz = BUFSIZ;
+ if ((*buf = malloc(*bufsiz)) == NULL)
+ return -1;
+ }
+
+ for (ptr = *buf, eptr = *buf + *bufsiz;;) {
+ int c = fgetc(fp);
+ if (c == -1) {
+ if (feof(fp)) {
+ ssize_t diff = (ssize_t)(ptr - *buf);
+ if (diff != 0) {
+ *ptr = '\0';
+ return diff;
+ }
+ }
+ return -1;
+ }
+ *ptr++ = c;
+ if (c == delimiter) {
+ *ptr = '\0';
+ return ptr - *buf;
+ }
+ if (ptr + 2 >= eptr) {
+ char *nbuf;
+ size_t nbufsiz = *bufsiz * 2;
+ ssize_t d = ptr - *buf;
+ if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
+ return -1;
+ *buf = nbuf;
+ *bufsiz = nbufsiz;
+ eptr = nbuf + nbufsiz;
+ ptr = nbuf + d;
+ }
+ }
+}
+
+ssize_t
+getline(char **buf, size_t *bufsiz, FILE *fp)
+{
+ return getdelim(buf, bufsiz, '\n', fp);
+}
+
+#endif