xref: /iperf/src/iperf_util.c (revision 0741ccff)
1 /*
2  * iperf, Copyright (c) 2014, 2016, 2017, The Regents of the University of
3  * California, through Lawrence Berkeley National Laboratory (subject
4  * to receipt of any required approvals from the U.S. Dept. of
5  * Energy).  All rights reserved.
6  *
7  * If you have questions about your rights to use or distribute this
8  * software, please contact Berkeley Lab's Technology Transfer
9  * Department at [email protected].
10  *
11  * NOTICE.  This software is owned by the U.S. Department of Energy.
12  * As such, the U.S. Government has been granted for itself and others
13  * acting on its behalf a paid-up, nonexclusive, irrevocable,
14  * worldwide license in the Software to reproduce, prepare derivative
15  * works, and perform publicly and display publicly.  Beginning five
16  * (5) years after the date permission to assert copyright is obtained
17  * from the U.S. Department of Energy, and subject to any subsequent
18  * five (5) year renewals, the U.S. Government is granted for itself
19  * and others acting on its behalf a paid-up, nonexclusive,
20  * irrevocable, worldwide license in the Software to reproduce,
21  * prepare derivative works, distribute copies to the public, perform
22  * publicly and display publicly, and to permit others to do so.
23  *
24  * This code is distributed under a BSD style license, see the LICENSE
25  * file for complete information.
26  */
27 /* iperf_util.c
28  *
29  * Iperf utility functions
30  *
31  */
32 #include "iperf_config.h"
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <stdarg.h>
39 #include <sys/select.h>
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <sys/resource.h>
43 #include <sys/utsname.h>
44 #include <time.h>
45 #include <errno.h>
46 
47 #include "cjson.h"
48 #include "iperf.h"
49 #include "iperf_api.h"
50 
51 /*
52  * Read entropy from /dev/urandom
53  * Errors are fatal.
54  * Returns 0 on success.
55  */
56 int readentropy(void *out, size_t outsize)
57 {
58     static FILE *frandom;
59     static const char rndfile[] = "/dev/urandom";
60 
61     if (!outsize) return 0;
62 
63     if (frandom == NULL) {
64         frandom = fopen(rndfile, "rb");
65         if (frandom == NULL) {
66             iperf_errexit(NULL, "error - failed to open %s: %s\n",
67                           rndfile, strerror(errno));
68         }
69         setbuf(frandom, NULL);
70     }
71     if (fread(out, 1, outsize, frandom) != outsize) {
72         iperf_errexit(NULL, "error - failed to read %s: %s\n",
73                       rndfile,
74                       feof(frandom) ? "EOF" : strerror(errno));
75     }
76     return 0;
77 }
78 
79 
80 /* make_cookie
81  *
82  * Generate and return a cookie string
83  *
84  * Iperf uses this function to create test "cookies" which
85  * server as unique test identifiers. These cookies are also
86  * used for the authentication of stream connections.
87  * Assumes cookie has size (COOKIE_SIZE + 1) char's.
88  */
89 
90 void
91 make_cookie(char *cookie)
92 {
93     unsigned char *out = (unsigned char*)cookie;
94     size_t pos;
95     static const unsigned char rndchars[] = "abcdefghijklmnopqrstuvwxyz234567";
96 
97     readentropy(out, COOKIE_SIZE);
98     for (pos = 0; pos < (COOKIE_SIZE - 1); pos++) {
99         out[pos] = rndchars[out[pos] % (sizeof(rndchars) - 1)];
100     }
101     out[pos] = '\0';
102 }
103 
104 
105 /* is_closed
106  *
107  * Test if the file descriptor fd is closed.
108  *
109  * Iperf uses this function to test whether a TCP stream socket
110  * is closed, because accepting and denying an invalid connection
111  * in iperf_tcp_accept is not considered an error.
112  */
113 
114 int
115 is_closed(int fd)
116 {
117     struct timeval tv;
118     fd_set readset;
119 
120     FD_ZERO(&readset);
121     FD_SET(fd, &readset);
122     tv.tv_sec = 0;
123     tv.tv_usec = 0;
124 
125     if (select(fd+1, &readset, NULL, NULL, &tv) < 0) {
126         if (errno == EBADF)
127             return 1;
128     }
129     return 0;
130 }
131 
132 
133 double
134 timeval_to_double(struct timeval * tv)
135 {
136     double d;
137 
138     d = tv->tv_sec + tv->tv_usec / 1000000;
139 
140     return d;
141 }
142 
143 int
144 timeval_equals(struct timeval * tv0, struct timeval * tv1)
145 {
146     if ( tv0->tv_sec == tv1->tv_sec && tv0->tv_usec == tv1->tv_usec )
147 	return 1;
148     else
149 	return 0;
150 }
151 
152 double
153 timeval_diff(struct timeval * tv0, struct timeval * tv1)
154 {
155     double time1, time2;
156 
157     time1 = tv0->tv_sec + (tv0->tv_usec / 1000000.0);
158     time2 = tv1->tv_sec + (tv1->tv_usec / 1000000.0);
159 
160     time1 = time1 - time2;
161     if (time1 < 0)
162         time1 = -time1;
163     return time1;
164 }
165 
166 
167 int
168 delay(int64_t ns)
169 {
170     struct timespec req, rem;
171 
172     req.tv_sec = 0;
173 
174     while (ns >= 1000000000L) {
175         ns -= 1000000000L;
176         req.tv_sec += 1;
177     }
178 
179     req.tv_nsec = ns;
180 
181     while (nanosleep(&req, &rem) == -1)
182         if (EINTR == errno)
183             memcpy(&req, &rem, sizeof(rem));
184         else
185             return -1;
186     return 0;
187 }
188 
189 # ifdef DELAY_SELECT_METHOD
190 int
191 delay(int us)
192 {
193     struct timeval tv;
194 
195     tv.tv_sec = 0;
196     tv.tv_usec = us;
197     (void) select(1, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &tv);
198     return 1;
199 }
200 #endif
201 
202 
203 void
204 cpu_util(double pcpu[3])
205 {
206     static struct timeval last;
207     static clock_t clast;
208     static struct rusage rlast;
209     struct timeval temp;
210     clock_t ctemp;
211     struct rusage rtemp;
212     double timediff;
213     double userdiff;
214     double systemdiff;
215 
216     if (pcpu == NULL) {
217         gettimeofday(&last, NULL);
218         clast = clock();
219 	getrusage(RUSAGE_SELF, &rlast);
220         return;
221     }
222 
223     gettimeofday(&temp, NULL);
224     ctemp = clock();
225     getrusage(RUSAGE_SELF, &rtemp);
226 
227     timediff = ((temp.tv_sec * 1000000.0 + temp.tv_usec) -
228                 (last.tv_sec * 1000000.0 + last.tv_usec));
229     userdiff = ((rtemp.ru_utime.tv_sec * 1000000.0 + rtemp.ru_utime.tv_usec) -
230                 (rlast.ru_utime.tv_sec * 1000000.0 + rlast.ru_utime.tv_usec));
231     systemdiff = ((rtemp.ru_stime.tv_sec * 1000000.0 + rtemp.ru_stime.tv_usec) -
232                   (rlast.ru_stime.tv_sec * 1000000.0 + rlast.ru_stime.tv_usec));
233 
234     pcpu[0] = (((ctemp - clast) * 1000000.0 / CLOCKS_PER_SEC) / timediff) * 100;
235     pcpu[1] = (userdiff / timediff) * 100;
236     pcpu[2] = (systemdiff / timediff) * 100;
237 }
238 
239 const char *
240 get_system_info(void)
241 {
242     static char buf[1024];
243     struct utsname  uts;
244 
245     memset(buf, 0, 1024);
246     uname(&uts);
247 
248     snprintf(buf, sizeof(buf), "%s %s %s %s %s", uts.sysname, uts.nodename,
249 	     uts.release, uts.version, uts.machine);
250 
251     return buf;
252 }
253 
254 
255 const char *
256 get_optional_features(void)
257 {
258     static char features[1024];
259     unsigned int numfeatures = 0;
260 
261     snprintf(features, sizeof(features), "Optional features available: ");
262 
263 #if defined(HAVE_CPU_AFFINITY)
264     if (numfeatures > 0) {
265 	strncat(features, ", ",
266 		sizeof(features) - strlen(features) - 1);
267     }
268     strncat(features, "CPU affinity setting",
269 	sizeof(features) - strlen(features) - 1);
270     numfeatures++;
271 #endif /* HAVE_CPU_AFFINITY */
272 
273 #if defined(HAVE_FLOWLABEL)
274     if (numfeatures > 0) {
275 	strncat(features, ", ",
276 		sizeof(features) - strlen(features) - 1);
277     }
278     strncat(features, "IPv6 flow label",
279 	sizeof(features) - strlen(features) - 1);
280     numfeatures++;
281 #endif /* HAVE_FLOWLABEL */
282 
283 #if defined(HAVE_SCTP)
284     if (numfeatures > 0) {
285 	strncat(features, ", ",
286 		sizeof(features) - strlen(features) - 1);
287     }
288     strncat(features, "SCTP",
289 	sizeof(features) - strlen(features) - 1);
290     numfeatures++;
291 #endif /* HAVE_SCTP */
292 
293 #if defined(HAVE_TCP_CONGESTION)
294     if (numfeatures > 0) {
295 	strncat(features, ", ",
296 		sizeof(features) - strlen(features) - 1);
297     }
298     strncat(features, "TCP congestion algorithm setting",
299 	sizeof(features) - strlen(features) - 1);
300     numfeatures++;
301 #endif /* HAVE_TCP_CONGESTION */
302 
303 #if defined(HAVE_SENDFILE)
304     if (numfeatures > 0) {
305 	strncat(features, ", ",
306 		sizeof(features) - strlen(features) - 1);
307     }
308     strncat(features, "sendfile / zerocopy",
309 	sizeof(features) - strlen(features) - 1);
310     numfeatures++;
311 #endif /* HAVE_SENDFILE */
312 
313 #if defined(HAVE_SO_MAX_PACING_RATE)
314     if (numfeatures > 0) {
315 	strncat(features, ", ",
316 		sizeof(features) - strlen(features) - 1);
317     }
318     strncat(features, "socket pacing",
319 	sizeof(features) - strlen(features) - 1);
320     numfeatures++;
321 #endif /* HAVE_SO_MAX_PACING_RATE */
322 
323 #if defined(HAVE_SSL)
324     if (numfeatures > 0) {
325 	strncat(features, ", ",
326 		sizeof(features) - strlen(features) - 1);
327     }
328     strncat(features, "authentication",
329 	sizeof(features) - strlen(features) - 1);
330     numfeatures++;
331 #endif /* HAVE_SSL */
332 
333     if (numfeatures == 0) {
334 	strncat(features, "None",
335 		sizeof(features) - strlen(features) - 1);
336     }
337 
338     return features;
339 }
340 
341 /* Helper routine for building cJSON objects in a printf-like manner.
342 **
343 ** Sample call:
344 **   j = iperf_json_printf("foo: %b  bar: %d  bletch: %f  eep: %s", b, i, f, s);
345 **
346 ** The four formatting characters and the types they expect are:
347 **   %b  boolean           int
348 **   %d  integer           int64_t
349 **   %f  floating point    double
350 **   %s  string            char *
351 ** If the values you're passing in are not these exact types, you must
352 ** cast them, there is no automatic type coercion/widening here.
353 **
354 ** The colons mark the end of field names, and blanks are ignored.
355 **
356 ** This routine is not particularly robust, but it's not part of the API,
357 ** it's just for internal iperf3 use.
358 */
359 cJSON*
360 iperf_json_printf(const char *format, ...)
361 {
362     cJSON* o;
363     va_list argp;
364     const char *cp;
365     char name[100];
366     char* np;
367     cJSON* j;
368 
369     o = cJSON_CreateObject();
370     if (o == NULL)
371         return NULL;
372     va_start(argp, format);
373     np = name;
374     for (cp = format; *cp != '\0'; ++cp) {
375 	switch (*cp) {
376 	    case ' ':
377 	    break;
378 	    case ':':
379 	    *np = '\0';
380 	    break;
381 	    case '%':
382 	    ++cp;
383 	    switch (*cp) {
384 		case 'b':
385 		j = cJSON_CreateBool(va_arg(argp, int));
386 		break;
387 		case 'd':
388 		j = cJSON_CreateNumber(va_arg(argp, int64_t));
389 		break;
390 		case 'f':
391 		j = cJSON_CreateNumber(va_arg(argp, double));
392 		break;
393 		case 's':
394 		j = cJSON_CreateString(va_arg(argp, char *));
395 		break;
396 		default:
397 		va_end(argp);
398 		return NULL;
399 	    }
400 	    if (j == NULL) {
401 	    	va_end(argp);
402 	    	return NULL;
403 	    }
404 	    cJSON_AddItemToObject(o, name, j);
405 	    np = name;
406 	    break;
407 	    default:
408 	    *np++ = *cp;
409 	    break;
410 	}
411     }
412     va_end(argp);
413     return o;
414 }
415 
416 /* Debugging routine to dump out an fd_set. */
417 void
418 iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds)
419 {
420     int fd;
421     int comma;
422 
423     fprintf(fp, "%s: [", str);
424     comma = 0;
425     for (fd = 0; fd < nfds; ++fd) {
426         if (FD_ISSET(fd, fds)) {
427 	    if (comma)
428 		fprintf(fp, ", ");
429 	    fprintf(fp, "%d", fd);
430 	    comma = 1;
431 	}
432     }
433     fprintf(fp, "]\n");
434 }
435