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