1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <stdio_ext.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <ctype.h> 33 #include <string.h> 34 #include <syslog.h> 35 #include <signal.h> 36 #include <time.h> 37 #include <limits.h> 38 #include <sys/resource.h> 39 #include <sys/fcntl.h> 40 #include <sys/types.h> 41 #include <fcntl.h> 42 #include <sys/resource.h> 43 #include <sys/stat.h> 44 #include <sys/systeminfo.h> 45 #include <sys/socket.h> 46 #include <sys/sockio.h> 47 #include <net/if.h> 48 #include <netinet/in.h> 49 #include <arpa/inet.h> 50 #include <errno.h> 51 #include <sys/stropts.h> 52 #include <netinet/dhcp.h> 53 #include <dhcp_impl.h> 54 #include <synch.h> 55 #include <netdb.h> 56 #include <locale.h> 57 #include <mtmalloc.h> 58 #include <tnf/probe.h> 59 #include <libinetutil.h> 60 61 struct client { 62 thread_t id; 63 PKT_LIST *pktlistp; 64 cond_t cv; 65 cond_t acv; 66 mutex_t mtx; 67 int proto; 68 uchar_t chaddr[20]; 69 char chost[40]; 70 int hlen; 71 uint_t xid; 72 int flags; 73 time_t ltime; 74 int state; 75 }; 76 77 #define CLIENT_BUSY 0x1 78 #define CLIENT_FIRSTTIME 0x2 79 80 ushort_t port_offset = 0; /* offset to port for multiple server */ 81 int fast = 0; /* higher load */ 82 int bound = 0; /* only broadcast on given interface */ 83 int lrecv = 1; /* only receive on given interface */ 84 static struct in_addr relay; /* spoof being a relay agent */ 85 static struct sockaddr_in from, relfrom; 86 static int clients, s, srelay = -1; 87 static struct client *clientsp; 88 89 static PKT request; 90 static char ifname[IFNAMSIZ]; 91 static int startindex; 92 static mutex_t go_mtx; 93 static cond_t go_cv; 94 static boolean_t time_to_go; 95 static int release_time = 0; 96 static int desynch = 1; 97 static double avg = 0; 98 static timespec_t avgslp; 99 static volatile ulong_t tops, otops; 100 static volatile ulong_t minops[6]; 101 static volatile time_t mintim[6]; 102 static volatile int minind; 103 long sample_time = 10L; 104 long nsamples = 2; 105 106 static volatile ulong_t ops_outstanding; 107 static time_t start, ostart; 108 int verbose = 0; 109 int dohost = 0; 110 int randcl = 0; 111 int randhlen = 0; 112 int randerr = 0; 113 int dos = 0; 114 int dofork = 0; 115 int printid = 0; 116 117 static time_t ltime; 118 static struct lifreq lifr; 119 120 static void corrupt(char *, int); 121 122 static void 123 dhcpmsgtype(uchar_t pkt, char *buf) 124 { 125 char *p; 126 127 switch (pkt) { 128 case DISCOVER: 129 p = "DISCOVER"; 130 break; 131 case OFFER: 132 p = "OFFER"; 133 break; 134 case REQUEST: 135 p = "REQUEST"; 136 break; 137 case DECLINE: 138 p = "DECLINE"; 139 break; 140 case ACK: 141 p = "ACK"; 142 break; 143 case NAK: 144 p = "NAK"; 145 break; 146 case RELEASE: 147 p = "RELEASE"; 148 break; 149 case INFORM: 150 p = "INFORM"; 151 break; 152 default: 153 p = "UNKNOWN"; 154 break; 155 } 156 157 (void) strcpy(buf, p); 158 } 159 160 static int 161 closeif(int ms, char *cifname, struct sockaddr_in *myip, thread_t myself) 162 { 163 struct ifreq ifr; 164 int error = 0; 165 166 (void) strcpy(ifr.ifr_name, cifname); 167 if (ioctl(ms, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { 168 (void) fprintf(stderr, 169 "Client %04d - can't get interface flags on %s\n", myself, 170 cifname); 171 error = 7; 172 } 173 ifr.ifr_flags &= ~IFF_UP; 174 if (ioctl(ms, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { 175 (void) fprintf(stderr, 176 "Client %04d - can't set interface flags on %s\n", myself, 177 cifname); 178 error = 7; 179 } 180 myip->sin_addr.s_addr = htonl(INADDR_ANY); 181 ifr.ifr_addr = *(struct sockaddr *)myip; 182 if (ioctl(ms, SIOCSIFADDR, (caddr_t)&ifr)) { 183 (void) fprintf(stderr, 184 "Client %04d - Can't unset address on %s\n", myself, 185 cifname); 186 error = 8; 187 } 188 (void) close(ms); 189 return (error); 190 } 191 192 static void * 193 client(void *args) 194 { 195 PKT crequest, *irequestp; 196 PKT_LIST *bp = NULL, *wbp, *twbp; 197 struct client *mep = (struct client *)args; 198 time_t retry_time = 2, lease, sleep_time = 0; 199 uchar_t *endp; 200 boolean_t done, config, timeout; 201 int nstate, ms = -1; 202 DHCP_OPT *optp, *unused_optp; 203 timespec_t ts, tr; 204 int error = 0; 205 thread_t myself = thr_self(); 206 struct sockaddr_in to, myip, maskip; 207 struct in_addr serverip; 208 time_t start_time, expired = 0; 209 char cid[BUFSIZ]; 210 char cifname[IFNAMSIZ]; 211 char host[40]; 212 char domain[40]; 213 char p[30], np[30]; 214 struct ifreq ifr; 215 int moldy; 216 int i; 217 uint_t cidlen; 218 char *domainp; 219 220 forever: 221 if (bp) { 222 (void) free(bp->pkt); 223 (void) free(bp); 224 bp = NULL; 225 } 226 if (!time_to_go) { 227 (void) mutex_lock(&mep->mtx); 228 mep->flags &= ~CLIENT_BUSY; 229 if (mep->flags & CLIENT_FIRSTTIME) { 230 mep->flags &= ~CLIENT_FIRSTTIME; 231 } else { 232 tops++; 233 ops_outstanding--; 234 } 235 if (avg) 236 (void) cond_wait(&mep->acv, &mep->mtx); 237 mep->flags |= CLIENT_BUSY; 238 mep->ltime = time(NULL); 239 ops_outstanding++; 240 (void) mutex_unlock(&mep->mtx); 241 } 242 if (desynch) 243 (void) sleep((desynch & myself) + 3); /* desynch clients */ 244 if (verbose == 1) 245 (void) fprintf(stdout, "Client %04d - started.\n", myself); 246 start_time = time(NULL); 247 (void) sprintf(cifname, "%s:%d", ifname, startindex + myself); 248 249 /* reset client addr each time */ 250 if (relay.s_addr != INADDR_ANY) { 251 to.sin_addr.s_addr = relay.s_addr; 252 } else { 253 to.sin_addr.s_addr = INADDR_BROADCAST; 254 } 255 to.sin_port = htons(IPPORT_BOOTPS + port_offset); 256 to.sin_family = AF_INET; 257 258 domain[0] = host[0] = NULL; 259 if (randcl) { 260 /* Further randomize. */ 261 if (randhlen > 0) { 262 mep->hlen = randhlen; 263 } 264 265 for (i = 3; i < mep->hlen; i++) { 266 mep->chaddr[i] = random() & 0xff; 267 } 268 } 269 270 (void) memcpy(&crequest, &request, sizeof (request)); 271 (void) memcpy(crequest.chaddr, mep->chaddr, mep->hlen); 272 crequest.hlen = mep->hlen; 273 274 275 if (mep->proto) { 276 mep->state = DISCOVER; 277 optp = (DHCP_OPT *) & crequest.options[3]; /* skip TYPE */ 278 optp->code = CD_CLIENT_ID; 279 optp->len = mep->hlen + 1; 280 optp->value[0] = 0x01; 281 (void) memcpy(&optp->value[1], mep->chaddr, mep->hlen); 282 cidlen = sizeof (cid); 283 (void) octet_to_hexascii(optp->value, mep->hlen + 1, cid, 284 &cidlen); 285 unused_optp = (DHCP_OPT *) & optp->value[mep->hlen + 1]; 286 } else { 287 mep->state = 0; 288 cidlen = sizeof (cid); 289 (void) octet_to_hexascii(mep->chaddr, mep->hlen, cid, &cidlen); 290 unused_optp = (DHCP_OPT *)&crequest.options[3]; /* skip TYPE */ 291 } 292 293 /* Use global descriptor at first */ 294 ms = s; 295 296 myip.sin_addr.s_addr = htonl(INADDR_ANY); 297 done = B_FALSE; 298 config = B_FALSE; 299 do { 300 timeout = B_FALSE; 301 302 TNF_PROBE_2(client, 303 "client", 304 "client%debug 'in func client'", 305 tnf_ulong, state, mep->state, 306 tnf_string, cid, (char *)cid); 307 308 if (time_to_go) { 309 if (mep->state == ACK) { 310 mep->state = RELEASE; 311 if (verbose == 1) 312 (void) fprintf(stderr, 313 "Client %04d - RELEASEing %s\n", 314 myself, inet_ntoa(myip.sin_addr)); 315 else if (verbose == 2) 316 fprintf(stderr, "[%d %s]", 317 clientsp[i].id, 318 inet_ntoa(myip.sin_addr)); 319 optp = (DHCP_OPT *) crequest.options; 320 (void) memset((char *)unused_optp, 0, 321 (int)((char *)&crequest.options[ 322 sizeof (crequest.options)] - 323 (char *)unused_optp)); 324 optp->value[0] = RELEASE; 325 } else { 326 done = B_TRUE; 327 if (verbose == 1) 328 (void) fprintf(stderr, 329 "Client %04d - terminated.\n", 330 myself); 331 break; 332 } 333 } else if (release_time || avg) { 334 if (mep->state == ACK) { 335 336 /* lru testing: don't release lease */ 337 if (randcl & 0x2) { 338 done = B_FALSE; 339 mep->state = nstate = 0; 340 sleep_time = 0; 341 if (bp) { 342 (void) free(bp->pkt); 343 (void) free(bp); 344 bp = NULL; 345 } 346 goto forever; 347 } 348 mep->state = RELEASE; 349 if (verbose == 1) 350 (void) fprintf(stderr, 351 "Client %04d - RELEASEing %s\n", 352 myself, inet_ntoa(myip.sin_addr)); 353 else if (verbose == 2) 354 fprintf(stderr, "[%d %s]", 355 clientsp[i].id, 356 inet_ntoa(myip.sin_addr)); 357 optp = (DHCP_OPT *) crequest.options; 358 (void) memset((char *)unused_optp, 0, 359 (int)((char *)&crequest.options[ 360 sizeof (crequest.options)] - 361 (char *)unused_optp)); 362 optp->value[0] = RELEASE; 363 } 364 } 365 if (mep->state == REQUEST && expired < time(NULL)) { 366 /* drop back to INIT state. */ 367 if (verbose == 1) 368 (void) fprintf(stderr, 369 "Client %04d - Dropping back to INIT.\n", 370 myself); 371 done = B_FALSE; 372 mep->state = nstate = 0; 373 sleep_time = 0; 374 if (bp) { 375 (void) free(bp->pkt); 376 (void) free(bp); 377 bp = NULL; 378 } 379 goto forever; 380 } 381 if (mep->state == RELEASE && !time_to_go) { 382 (void) mutex_lock(&mep->mtx); 383 tops++; 384 ops_outstanding--; 385 mep->flags &= ~CLIENT_BUSY; 386 if (avg) 387 (void) cond_wait(&mep->acv, &mep->mtx); 388 mep->ltime = time(NULL); 389 ops_outstanding++; 390 mep->flags |= CLIENT_BUSY; 391 (void) mutex_unlock(&mep->mtx); 392 } 393 /* Send request... */ 394 crequest.secs = htons((ushort_t)(time(NULL) - start_time)); 395 crequest.xid = htonl((myself << 2) + mep->xid++); 396 397 /* Randomly corrupt packets of a certain type. */ 398 if ((randerr & 0xF) == mep->state || (randerr & 0xF) == 0xF) { 399 if (randerr & 0x10) { 400 /* Randomly corrupt entire request. */ 401 corrupt((char *)&crequest, sizeof (crequest)); 402 } else { 403 /* Randomly corrupt options. */ 404 corrupt((char *)&crequest.options[3], 405 sizeof (crequest.options) - 3); 406 } 407 } 408 409 if (sendto(ms, (char *)&crequest, sizeof (PKT), 0, 410 (struct sockaddr *)&to, sizeof (struct sockaddr)) < 0) { 411 perror("Sendto"); 412 error = 4; 413 thr_exit(&error); 414 } 415 if (mep->state == RELEASE) { 416 done = B_TRUE; 417 if (!avg) { 418 (void) strcpy(ifr.ifr_name, cifname); 419 if (ioctl(ms, SIOCGIFFLAGS, 420 (caddr_t)&ifr) < 0) { 421 (void) fprintf(stderr, "Client %04d - " 422 "can't get interface flags on %s\n", 423 myself, cifname); 424 error = 7; 425 } 426 ifr.ifr_flags &= ~IFF_UP; 427 if (ioctl(ms, SIOCSIFFLAGS, 428 (caddr_t)&ifr) < 0) { 429 (void) fprintf(stderr, "Client %04d - " 430 "can't set interface flags on %s\n", 431 myself, cifname); 432 error = 7; 433 } 434 myip.sin_addr.s_addr = htonl(INADDR_ANY); 435 ifr.ifr_addr = *(struct sockaddr *)&myip; 436 if (ioctl(ms, SIOCSIFADDR, (caddr_t)&ifr)) { 437 (void) fprintf(stderr, "Client %04d - " 438 "Can't unset address on %s\n", 439 myself, cifname); 440 error = 8; 441 } 442 (void) close(ms); 443 } 444 if (release_time || avg) { 445 done = B_FALSE; 446 mep->state = nstate = 0; 447 sleep_time = 0; 448 if (bp) { 449 (void) free(bp->pkt); 450 (void) free(bp); 451 bp = NULL; 452 } 453 goto forever; 454 } 455 break; 456 } 457 /* await reply */ 458 moldy: 459 (void) mutex_lock(&mep->mtx); 460 ts.tv_sec = time(NULL) + retry_time; 461 ts.tv_nsec = 0; 462 463 while (mep->pktlistp == NULL) 464 if (cond_timedwait(&mep->cv, &mep->mtx, &ts) == ETIME) { 465 timeout = B_TRUE; 466 if (retry_time > 64) 467 retry_time = 2; 468 else if (fast) 469 retry_time += 2; 470 else 471 retry_time *= 2; 472 break; 473 } else { 474 if (time_to_go) 475 break; 476 } 477 (void) mutex_unlock(&mep->mtx); 478 479 if (time_to_go || timeout) 480 continue; 481 482 (void) mutex_lock(&mep->mtx); 483 moldy = 0; 484 if (bp) { 485 (void) free(bp->pkt); 486 (void) free(bp); 487 } 488 bp = NULL; 489 wbp = mep->pktlistp; 490 while (wbp != NULL) { 491 irequestp = wbp->pkt; 492 if (bp == NULL && irequestp->op == BOOTREPLY && 493 memcmp(&crequest.xid, &irequestp->xid, 494 sizeof (crequest.xid)) == 0) { 495 bp = wbp; 496 wbp = wbp->next; 497 continue; 498 } 499 (void) free(wbp->pkt); 500 twbp = wbp; 501 wbp = wbp->next; 502 (void) free(twbp); 503 if (verbose == 1) 504 (void) fprintf(stderr, 505 "Client %04d - Moldy xid\n", myself); 506 moldy++; 507 } 508 509 mep->pktlistp = NULL; 510 (void) mutex_unlock(&mep->mtx); 511 512 if (bp == NULL) { 513 if (moldy > 0) 514 goto moldy; 515 516 continue; 517 } 518 irequestp = bp->pkt; 519 520 if (mep->proto) { 521 /* 522 * Scan for CD_DHCP_TYPE, CD_SERVER_ID, and 523 * CD_LEASE_TIME if proto. 524 */ 525 nstate = 0; 526 maskip.sin_addr.s_addr = serverip.s_addr = INADDR_ANY; 527 maskip.sin_family = AF_INET; 528 lease = (time_t)0; 529 optp = (DHCP_OPT *) irequestp->options; 530 endp = (uchar_t *)irequestp + bp->len; 531 host[0] = NULL; 532 while ((uchar_t *)optp < (uchar_t *)endp) { 533 switch (optp->code) { 534 case CD_HOSTNAME: 535 (void) strncpy(host, 536 (const char *)optp->value, 537 optp->len); 538 host[optp->len] = '\0'; 539 break; 540 case CD_DNSDOMAIN: 541 (void) strncpy(domain, 542 (const char *)optp->value, 543 optp->len); 544 domain[optp->len] = '\0'; 545 break; 546 case CD_DHCP_TYPE: 547 nstate = optp->value[0]; 548 break; 549 case CD_SUBNETMASK: 550 (void) memcpy(&maskip.sin_addr, 551 optp->value, 552 sizeof (struct in_addr)); 553 break; 554 case CD_SERVER_ID: 555 (void) memcpy(&serverip, optp->value, 556 sizeof (struct in_addr)); 557 break; 558 case CD_LEASE_TIME: 559 (void) memcpy(&lease, optp->value, 560 sizeof (time_t)); 561 lease = htonl(lease); 562 break; 563 } 564 optp = (DHCP_OPT *) & optp->value[optp->len]; 565 } 566 if (mep->state == DISCOVER && nstate == OFFER) { 567 mep->state = REQUEST; 568 expired = time(NULL) + 60; 569 /* 570 * Add in the requested IP address option and 571 * server ID. 572 */ 573 optp = (DHCP_OPT *) crequest.options; 574 optp->value[0] = REQUEST; 575 optp = unused_optp; /* step over CD_DHCP_TYPE */ 576 optp->code = CD_REQUESTED_IP_ADDR; 577 optp->len = sizeof (struct in_addr); 578 (void) memcpy(optp->value, &irequestp->yiaddr, 579 sizeof (struct in_addr)); 580 optp = (DHCP_OPT *) & optp->value[ 581 sizeof (struct in_addr)]; 582 optp->code = CD_SERVER_ID; 583 optp->len = sizeof (struct in_addr); 584 (void) memcpy(optp->value, &serverip, 585 sizeof (struct in_addr)); 586 optp = (DHCP_OPT *) & optp->value[ 587 sizeof (struct in_addr)]; 588 if (dohost == 0) { 589 if (bp) { 590 (void) free(bp->pkt); 591 (void) free(bp); 592 bp = NULL; 593 } 594 continue; 595 } 596 597 if (domain[0] == '\0' && host[0] != '\0' && 598 (domainp = strchr(host, '.')) != NULL) { 599 (void) snprintf(domain, sizeof (domain), 600 "%s", domainp); 601 } 602 603 if (dohost & 0x2) { 604 cidlen = sizeof (cid); 605 (void) octet_to_hexascii(mep->chaddr, 606 mep->hlen, host, &cidlen); 607 608 if (domain[0]) 609 (void) snprintf(host, 610 sizeof (host), "%s.%s", 611 cid, domain); 612 else 613 (void) snprintf(host, 614 sizeof (host), "%s", cid); 615 } 616 617 optp->code = CD_HOSTNAME; 618 optp->len = strlen(host); 619 (void) memcpy(optp->value, host, strlen(host)); 620 optp->value[strlen(host)] = '\0'; 621 if (randcl && (random() & 0x1)) { 622 /* create a random name */ 623 for (i = 0; i < optp->len && 624 optp->value[i] != '.'; i++) 625 if (i & 1) 626 optp->value[i] = '0' + 627 (mep->chaddr[i] & 0x7); 628 else 629 optp->value[i] = 'a' + 630 (mep->chaddr[i] & 0x7); 631 strcpy((char *)mep->chost, 632 (const char *)optp->value); 633 } else if (randcl && mep->chost[0]) { 634 /* use the previous one */ 635 optp->len = strlen(mep->chost); 636 (void) memcpy(optp->value, mep->chost, 637 strlen(mep->chost)); 638 optp->value[strlen(mep->chost)] = '\0'; 639 } 640 if (bp) { 641 (void) free(bp->pkt); 642 (void) free(bp); 643 bp = NULL; 644 } 645 continue; 646 } else if ((mep->state == REQUEST || 647 mep->state == ACK) && nstate == ACK) { 648 /* 649 * we're bound. defend the lease. Add the 650 * address to our interface. Due to the 651 * service architecture of this program, we 652 * can't unset the broadcast bit.. 653 */ 654 mep->state = ACK; 655 nstate = 0; 656 retry_time = 2; 657 myip.sin_family = AF_INET; 658 myip.sin_addr.s_addr = irequestp->yiaddr.s_addr; 659 crequest.ciaddr.s_addr = myip.sin_addr.s_addr; 660 optp = unused_optp; 661 optp->code = CD_LEASE_TIME; 662 optp->len = sizeof (time_t); 663 (void) memcpy(optp->value, &lease, 664 sizeof (time_t)); 665 optp = (DHCP_OPT *) 666 & optp->value[sizeof (time_t)]; 667 (void) memset((char *)optp, 0, (int)((char *) 668 &crequest.options[ 669 sizeof (crequest.options)] - 670 (char *)optp)); 671 to.sin_addr.s_addr = serverip.s_addr; 672 673 if (lease == -1) { 674 done = B_TRUE; /* permanent lease */ 675 sleep_time = 0; 676 } else { 677 sleep_time = lease / 2; 678 lease = time(NULL) + lease; 679 } 680 681 if (release_time || avg) { 682 sleep_time = release_time; 683 done = B_FALSE; 684 } 685 if (verbose == 1) 686 (void) fprintf(stdout, 687 "Client %04d(%s) - DHCP: %s == %s", 688 myself, cid, 689 inet_ntoa(myip.sin_addr), 690 (lease == -1) ? "Forever\n" : 691 ctime(&lease)); 692 else if (verbose == 2) 693 fprintf(stderr, "(%d %s)", mep->id, 694 cid); 695 if (!config && !avg) { 696 /* Add mask and address */ 697 if ((ms = socket(AF_INET, SOCK_DGRAM, 698 0)) < 0) { 699 (void) fprintf(stderr, 700 "Client %04d - can't open " 701 "DGRAM socket.\n", myself); 702 error = 7; 703 break; 704 } 705 (void) strcpy(ifr.ifr_name, cifname); 706 ifr.ifr_addr = 707 *(struct sockaddr *)&myip; 708 /* 709 * XXXX: needed in on81 710 * for initial 711 * interface creation 712 */ 713 (void) (ioctl(ms, SIOCLIFADDIF, 714 (caddr_t)&ifr)); 715 (void) strcpy(ifr.ifr_name, cifname); 716 ifr.ifr_addr = 717 *(struct sockaddr *)&maskip; 718 if (ioctl(ms, SIOCSIFNETMASK, 719 (caddr_t)&ifr)) { 720 (void) fprintf(stderr, 721 "Client %04d - Can't set " 722 "netmask: %s on %s\n", 723 myself, 724 inet_ntoa(maskip.sin_addr), 725 cifname); 726 error = 7; 727 (void) close(ms); 728 break; 729 } 730 if (ioctl(ms, SIOCGIFFLAGS, 731 (caddr_t)&ifr) < 0) { 732 (void) fprintf(stderr, 733 "Client %04d - can't get " 734 "interface flags on %s\n", 735 myself, cifname); 736 error = 7; 737 (void) close(ms); 738 break; 739 } 740 ifr.ifr_flags |= IFF_UP; 741 if (ioctl(ms, SIOCSIFFLAGS, 742 (caddr_t)&ifr) < 0) { 743 (void) fprintf(stderr, 744 "Client %04d - can't set " 745 "interface flags on %s\n", 746 myself, cifname); 747 error = 7; 748 (void) close(ms); 749 break; 750 } 751 ifr.ifr_addr = 752 *(struct sockaddr *)&myip; 753 if (ioctl(ms, SIOCSIFADDR, 754 (caddr_t)&ifr)) { 755 (void) fprintf(stderr, 756 "Client %04d - Can't set " 757 "address on %s\n", 758 myself, cifname); 759 error = 8; 760 (void) close(ms); 761 break; 762 } 763 config = B_TRUE; 764 } 765 if (sleep_time != 0) { 766 /* Go to sleep for 50% of lease time. */ 767 tr.tv_sec = time(NULL) + sleep_time; 768 if (verbose == 1) 769 (void) fprintf(stderr, 770 "Client %04d - sleeping " 771 "until %s", myself, 772 ctime(&tr.tv_sec)); 773 tr.tv_nsec = 0; 774 (void) mutex_lock(&go_mtx); 775 while (!time_to_go) { 776 if (cond_timedwait(&go_cv, 777 &go_mtx, &tr) == ETIME) 778 break; 779 } 780 (void) mutex_unlock(&go_mtx); 781 if (verbose == 1) 782 (void) fprintf(stderr, 783 "Client %04d - awake\n", 784 myself); 785 } 786 } else if (mep->state == ACK && nstate == NAK) { 787 /* drop back to INIT state. */ 788 if (verbose == 1) { 789 (void) fprintf(stdout, "Client %04d - " 790 "DHCP: we got NAKed.\n", myself); 791 (void) fprintf(stderr, "Client %04d - " 792 "Dropping back to INIT.\n", myself); 793 } 794 if (!avg) 795 (void) closeif(ms, cifname, 796 &myip, myself); 797 done = B_FALSE; 798 mep->state = nstate = 0; 799 sleep_time = 0; 800 if (bp) { 801 (void) free(bp->pkt); 802 (void) free(bp); 803 bp = NULL; 804 } 805 goto forever; 806 } else { 807 dhcpmsgtype(nstate, np); 808 dhcpmsgtype(mep->state, p); 809 (void) fprintf(stderr, "Client %04d - " 810 "unexpected mesg: %s, when I'm in state: " 811 "%s.\n", myself, np, p); 812 error = 9; 813 break; 814 } 815 } else { 816 done = B_TRUE; /* BOOTP is done */ 817 if (verbose == 1) 818 (void) fprintf(stdout, 819 "Client %04d(%s) - BOOTP: %s\n", myself, 820 cid, inet_ntoa(irequestp->yiaddr)); 821 if (release_time || avg) { 822 done = B_FALSE; 823 mep->state = nstate = 0; 824 sleep_time = 0; 825 if (bp) { 826 (void) free(bp->pkt); 827 (void) free(bp); 828 bp = NULL; 829 } 830 goto forever; 831 } 832 } 833 if (bp) { 834 (void) free(bp->pkt); 835 (void) free(bp); 836 bp = NULL; 837 } 838 } while (!done); 839 840 if (!done) { 841 (void) fprintf(stderr, 842 "Client %04d - %s: configuration failed.\n", 843 myself, (mep->proto) ? "DHCP" : "BOOTP"); 844 } 845 wbp = mep->pktlistp; 846 while (wbp != NULL) { 847 twbp = wbp->next; 848 if (wbp->pkt != NULL) 849 (void) free(wbp->pkt); 850 (void) free(wbp); 851 wbp = twbp; 852 } 853 854 thr_exit(&error); 855 return (NULL); /* NOTREACHED */ 856 } 857 858 /* 859 * Never returns. Just loads client lists. 860 */ 861 static void * 862 service(void *args) 863 { 864 struct client *clientp = (struct client *)args; 865 PKT_LIST *bp, *wbp; 866 PKT *irequestp; 867 int error = 0; 868 struct pollfd pfd[2]; 869 ulong_t *bufp; /* ulong_t to force alignment */ 870 int len, i; 871 872 pfd[0].fd = s; 873 pfd[0].events = POLLIN | POLLPRI; 874 if (relay.s_addr != INADDR_ANY) { 875 pfd[1].fd = srelay; 876 pfd[1].events = POLLIN | POLLPRI; 877 } else { 878 pfd[1].fd = -1; 879 pfd[1].events = 0; 880 } 881 882 for (;;) { 883 pfd[0].revents = 0; 884 pfd[1].revents = 0; 885 if (poll(pfd, (nfds_t)2, INFTIM) < 0) { 886 (void) fprintf(stderr, "Service - can't poll...\n"); 887 error = 5; 888 break; 889 } 890 (void) mutex_lock(&go_mtx); 891 if (time_to_go) { 892 (void) fprintf(stderr, "Service - exiting...\n"); 893 error = 0; 894 break; 895 } 896 (void) mutex_unlock(&go_mtx); 897 len = BUFSIZ * 2; 898 bufp = malloc(len); 899 if (pfd[0].revents) 900 len = recv(s, (char *)bufp, len, 0); 901 else { 902 len = recv(srelay, (char *)bufp, len, 0); 903 } 904 905 if (len < 0) { 906 (void) fprintf(stderr, 907 "Service - can't receive - %s\n", strerror(errno)); 908 error = 6; 909 break; 910 } else { 911 irequestp = (PKT *) bufp; 912 for (i = 0; i < clients; i++) { 913 if (memcmp(clientp[i].chaddr, irequestp->chaddr, 914 clientp[i].hlen) == 0) { 915 (void) mutex_lock(&clientp[i].mtx); 916 bp = malloc(sizeof (PKT_LIST)); 917 bp->pkt = irequestp; 918 bp->len = len; 919 if (verbose == 1) 920 (void) fprintf(stderr, 921 "Service - received packet " 922 "for thread %04d...\n", 923 clientp[i].id); 924 if (clientp[i].pktlistp == NULL) { 925 clientp[i].pktlistp = bp; 926 bp->prev = NULL; 927 } else { 928 for (wbp = clientp[i].pktlistp; 929 wbp->next != NULL; 930 wbp = wbp->next) 931 /* null */; 932 wbp->next = bp; 933 bp->prev = wbp; 934 } 935 bp->next = NULL; 936 (void) cond_signal(&clientp[i].cv); 937 (void) mutex_unlock(&clientp[i].mtx); 938 break; 939 } 940 } 941 if (i >= clients) 942 free(bufp); 943 } 944 } 945 thr_exit(&error); 946 return (NULL); /* NOTREACHED */ 947 } 948 949 /* ARGSUSED */ 950 static void * 951 sig_handle(void *arg) 952 { 953 boolean_t leave = B_FALSE; 954 int sig; 955 sigset_t set; 956 char buf[SIG2STR_MAX]; 957 int old, new, unstarted; 958 int i; 959 int oldi; 960 uint_t cidlen; 961 char cid[BUFSIZ]; 962 int discover, offer, req, decline, ack, nak; 963 int release, inform, unknown; 964 int kicked; 965 ulong_t minavg; 966 time_t minstime; 967 968 (void) sigfillset(&set); 969 970 if (avg == 0) { 971 avgslp.tv_sec = sample_time; 972 avgslp.tv_nsec = 0L; 973 } 974 while (!leave) { 975 discover = offer = req = decline = ack = nak = 0; 976 release = inform = unknown = 0; 977 switch (sig = sigtimedwait(&set, NULL, &avgslp)) { 978 case SIGHUP: 979 case -1: 980 old = time(NULL); 981 new = unstarted = 0; 982 kicked = 0; 983 for (i = 0; i < clients; i++) { 984 /* Start next client at avgslp offset */ 985 if (avg && kicked == 0 && 986 (clientsp[i].flags & 987 (CLIENT_FIRSTTIME | CLIENT_BUSY)) == 0) { 988 (void) mutex_lock(&clientsp[i].mtx); 989 (void) cond_signal(&clientsp[i].acv); 990 (void) mutex_unlock(&clientsp[i].mtx); 991 kicked++; 992 } 993 switch (clientsp[i].state) { 994 case DISCOVER: 995 discover++; 996 break; 997 case OFFER: 998 offer++; 999 break; 1000 case REQUEST: 1001 req++; 1002 break; 1003 case DECLINE: 1004 decline++; 1005 break; 1006 case ACK: 1007 ack++; 1008 break; 1009 case NAK: 1010 nak++; 1011 break; 1012 case RELEASE: 1013 release++; 1014 break; 1015 case INFORM: 1016 inform++; 1017 break; 1018 default: 1019 unknown++; 1020 break; 1021 } 1022 if (clientsp[i].ltime == NULL || 1023 (clientsp[i].flags & CLIENT_BUSY) == 0) 1024 unstarted++; 1025 if (clientsp[i].ltime && 1026 clientsp[i].ltime < old) { 1027 old = clientsp[i].ltime; 1028 oldi = i; 1029 } 1030 if (clientsp[i].ltime && 1031 clientsp[i].ltime > new) { 1032 new = clientsp[i].ltime; 1033 } 1034 } 1035 1036 if (time(NULL) < ltime + sample_time) 1037 continue; 1038 ltime = time(NULL); 1039 1040 if (start == 0) { 1041 /* toss initial sample */ 1042 ostart = start = time(NULL); 1043 otops = tops = 0; 1044 minind = 0; 1045 } else { 1046 minops[minind] = tops - otops; 1047 mintim[minind] = ostart; 1048 otops = tops; 1049 ostart = time(NULL); 1050 minind = minind + 1 > nsamples - 1 ? 0 : 1051 minind + 1; 1052 minstime = 0; 1053 minavg = 0; 1054 for (i = 0; i < nsamples; i++) { 1055 if (mintim[i]) 1056 minavg += minops[i]; 1057 if (minstime == 0) 1058 minstime = mintim[i]; 1059 else if (mintim[i] && 1060 mintim[i] < minstime) 1061 minstime = mintim[i]; 1062 } 1063 1064 cidlen = sizeof (cid); 1065 (void) octet_to_hexascii(clientsp[oldi].chaddr, 1066 clientsp[oldi].hlen, cid, &cidlen); 1067 fprintf(stderr, "%9.9d: Totops %d Curr %d " 1068 "Persec %4.2f (%4.2f) Oldest %d (%d) " 1069 "Gap %d Free %d\n", time(NULL), tops, 1070 ops_outstanding, 1071 (double)tops / (double)(time(NULL) - start), 1072 (double)minavg / (double)(time(NULL) 1073 - minstime), 1074 time(NULL) - old, clientsp[oldi].id, cid, 1075 new - old, unstarted); 1076 fprintf(stderr, "\tdiscov %d off %d req %d " 1077 "decl %d ack %d nak %d rel %d inf %d " 1078 "free/unknown %d\n", discover, offer, req, 1079 decline, ack, nak, release, inform, 1080 unknown); 1081 } 1082 break; 1083 case SIGINT: 1084 /* FALLTHRU */ 1085 case SIGTERM: 1086 (void) sig2str(sig, buf); 1087 (void) fprintf(stderr, 1088 "Signal: %s received...Exiting\n", buf); 1089 (void) mutex_lock(&go_mtx); 1090 time_to_go = B_TRUE; 1091 (void) cond_broadcast(&go_cv); 1092 (void) mutex_unlock(&go_mtx); 1093 for (i = 0; i < clients; i++) { 1094 (void) mutex_lock(&clientsp[i].mtx); 1095 (void) cond_signal(&clientsp[i].acv); 1096 (void) mutex_unlock(&clientsp[i].mtx); 1097 } 1098 leave = B_TRUE; 1099 break; 1100 default: 1101 (void) sig2str(sig, buf); 1102 (void) fprintf(stderr, 1103 "Signal: %s received...Ignoring\n", buf); 1104 leave = B_FALSE; 1105 break; 1106 } 1107 } 1108 thr_exit((void *) NULL); 1109 return (NULL); /* NOTREACHED */ 1110 } 1111 1112 int 1113 main(int argc, char *argv[]) 1114 { 1115 boolean_t proto; 1116 int i, j, threrror = 0, *threrrorp; 1117 int sockoptbuf = 1; 1118 register char *endp, *octet; 1119 thread_t service_id, sig_id; 1120 sigset_t set; 1121 uint_t buf; 1122 int slen; 1123 socklen_t sslen = sizeof (slen); 1124 unsigned int ifceno; 1125 char cifname[IFNAMSIZ]; 1126 struct rlimit rl; 1127 1128 if (randcl) 1129 srandom(time(NULL)); 1130 1131 if (dofork) { 1132 if (fork() != 0) 1133 exit(0); 1134 } 1135 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { 1136 (void) fprintf(stderr, "Cannot get open file limit: %s\n", 1137 strerror(errno)); 1138 } 1139 /* handle cases where limit is infinity */ 1140 if (rl.rlim_cur == RLIM_INFINITY) { 1141 rl.rlim_cur = (rl.rlim_max == RLIM_INFINITY) ? 1142 OPEN_MAX : rl.rlim_max; 1143 } 1144 /* set NOFILE to unlimited */ 1145 rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; 1146 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { 1147 (void) fprintf(stderr, "Cannot set open file limit: %s\n", 1148 strerror(errno)); 1149 } 1150 (void) enable_extended_FILE_stdio(-1, -1); 1151 1152 if (argc < 5) { 1153 (void) fprintf(stderr, "%s <interface> <ether_addr> <protocol> " 1154 "<clients> [time] [desynch] [avg] [relayaddr]\n", argv[0]); 1155 return (1); 1156 } 1157 (void) strcpy(ifname, argv[1]); 1158 (void) strcpy(cifname, argv[1]); 1159 if ((endp = strchr(ifname, ':')) != NULL) { 1160 *endp = '\0'; 1161 startindex = strtol(endp + 1, 0L, 0L); 1162 } 1163 if (strcasecmp(argv[3], "dhcp") == 0) 1164 proto = B_TRUE; 1165 else 1166 proto = B_FALSE; 1167 1168 clients = atoi(argv[4]); 1169 1170 if (argc >= 6) { 1171 release_time = atoi(argv[5]); 1172 } 1173 if (argc >= 7) 1174 desynch = atoi(argv[6]); 1175 1176 if (argc >= 8) { 1177 avg = atof(argv[7]); 1178 if (avg > 0.0) { 1179 avgslp.tv_sec = avg; 1180 avgslp.tv_nsec = (avg - 1181 (double)((int)avg)) * 1000000000.0; 1182 } else if (avg < 0.0) { 1183 avgslp.tv_sec = abs((int)avg); 1184 avgslp.tv_nsec = (avg + abs((double)((int)avg))) * 1185 1000000000.0; 1186 } 1187 } 1188 if (argc >= 9) 1189 relay.s_addr = inet_addr(argv[8]); 1190 1191 if (argc >= 10) 1192 slen = strtol(argv[0], 0L, 0L); 1193 else 1194 slen = 1024 * 64; 1195 1196 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 1197 perror("Socket"); 1198 return (1); 1199 } 1200 (void) setsockopt(s, SOL_SOCKET, SO_SNDBUF, &slen, sslen); 1201 (void) setsockopt(s, SOL_SOCKET, SO_RCVBUF, &slen, sslen); 1202 1203 if (relay.s_addr == INADDR_ANY) 1204 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&sockoptbuf, 1205 (int)sizeof (sockoptbuf)) < 0) { 1206 perror("Setsockopt"); 1207 return (2); 1208 } 1209 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 1210 (char *)&sockoptbuf, (int)sizeof (sockoptbuf)) < 0) { 1211 perror("Setsockopt: REUSEADDR"); 1212 return (2); 1213 } 1214 if (relay.s_addr != INADDR_ANY) { 1215 relfrom.sin_port = htons(IPPORT_BOOTPS + port_offset); 1216 relfrom.sin_family = AF_INET; 1217 relfrom.sin_addr.s_addr = INADDR_ANY; 1218 relfrom.sin_port = htons(IPPORT_BOOTPS + port_offset); 1219 1220 (void) strncpy(lifr.lifr_name, cifname, 1221 sizeof (lifr.lifr_name)); 1222 if (lrecv) { 1223 if (ioctl(s, SIOCGLIFADDR, (char *)&lifr) < 0) { 1224 (void) fprintf(stderr, "Warning: SIOCGLIFADDR: %s", 1225 strerror(errno)); 1226 } else { 1227 relfrom.sin_addr.s_addr = 1228 ((struct sockaddr_in *) 1229 &lifr.lifr_addr)->sin_addr.s_addr; 1230 } 1231 } 1232 1233 if ((srelay = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 1234 perror("Socket"); 1235 return (1); 1236 } 1237 (void) setsockopt(srelay, SOL_SOCKET, SO_SNDBUF, &slen, sslen); 1238 (void) setsockopt(srelay, SOL_SOCKET, SO_RCVBUF, &slen, sslen); 1239 1240 if (setsockopt(srelay, SOL_SOCKET, SO_REUSEADDR, 1241 (char *)&sockoptbuf, (int)sizeof (sockoptbuf)) < 0) { 1242 perror("Setsockopt: REUSEADDR"); 1243 return (2); 1244 } 1245 if (bind(srelay, (struct sockaddr *)&relfrom, 1246 sizeof (relfrom)) < 0) { 1247 perror("Bind"); 1248 return (3); 1249 } 1250 ifceno = if_nametoindex(cifname); 1251 if (bound) { 1252 if (setsockopt(s, IPPROTO_IP, IP_BOUND_IF, 1253 (char *)&ifceno, (int)sizeof (char *)) < 0) { 1254 perror("Setsockopt bind"); 1255 return (3); 1256 } 1257 } 1258 } 1259 from.sin_family = AF_INET; 1260 if (relay.s_addr != INADDR_ANY) { 1261 from.sin_addr.s_addr = 1262 ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr; 1263 } else { 1264 from.sin_addr.s_addr = INADDR_ANY; 1265 } 1266 from.sin_port = htons(IPPORT_BOOTPC + port_offset); 1267 1268 if (bind(s, (struct sockaddr *)&from, sizeof (from)) < 0) { 1269 perror("Bind"); 1270 return (3); 1271 } 1272 ifceno = if_nametoindex(cifname); 1273 if (bound) { 1274 if (setsockopt(s, IPPROTO_IP, IP_BOUND_IF, 1275 (char *)&ifceno, (int)sizeof (char *)) < 0) { 1276 perror("Setsockopt bind"); 1277 return (3); 1278 } 1279 } 1280 request.op = 1; /* BOOTP request */ 1281 request.htype = 1; /* Ethernet */ 1282 request.hlen = 6; /* Ethernet addr len */ 1283 1284 endp = octet = argv[2]; 1285 for (i = 0; i < (int)request.hlen && octet != NULL; i++) { 1286 if ((endp = (char *)strchr(endp, ':')) != NULL) 1287 *endp++ = '\0'; 1288 (void) sscanf(octet, "%x", &buf); 1289 request.chaddr[i] = (uchar_t)buf; 1290 octet = endp; 1291 } 1292 1293 /* broadcast bit */ 1294 if (relay.s_addr == INADDR_ANY) 1295 request.flags = htons(0x8000); 1296 1297 /* magic cookie */ 1298 request.cookie[0] = 99; 1299 request.cookie[1] = 130; 1300 request.cookie[2] = 83; 1301 request.cookie[3] = 99; 1302 1303 if (proto) { 1304 /* Pretend to be a discover packet */ 1305 request.options[0] = CD_DHCP_TYPE; 1306 request.options[1] = 1; 1307 request.options[2] = DISCOVER; 1308 request.options[3] = 0xff; 1309 1310 (void) cond_init(&go_cv, USYNC_THREAD, NULL); 1311 (void) mutex_init(&go_mtx, USYNC_THREAD, NULL); 1312 } 1313 if (relay.s_addr != INADDR_ANY) 1314 request.giaddr.s_addr = from.sin_addr.s_addr; 1315 1316 (void) sigfillset(&set); 1317 1318 (void) sigdelset(&set, SIGABRT); /* allow for user abort */ 1319 1320 (void) thr_sigsetmask(SIG_SETMASK, &set, NULL); 1321 1322 /* 1323 * Create the client threads 1324 */ 1325 clientsp = malloc(sizeof (struct client) * clients); 1326 (void) memset(clientsp, 0, sizeof (struct client) * clients); 1327 if (clientsp == NULL) 1328 return (1); 1329 1330 for (i = 0; i < clients; i++) { 1331 (void) memcpy(clientsp[i].chaddr, request.chaddr, request.hlen); 1332 clientsp[i].hlen = request.hlen; 1333 if (i > 100) 1334 j = 3; 1335 else if (i > 50) 1336 j = 2; 1337 else 1338 j = 1; 1339 1340 if (i) { 1341 clientsp[i].chaddr[j] = (unsigned char) i; 1342 clientsp[i].chaddr[3] += (unsigned char) j; 1343 clientsp[i].chaddr[4] = (unsigned char) (i * j); 1344 } 1345 if (printid) 1346 fprintf(stderr, "ID %x:%x:%x:%x:%x:%x\n", 1347 clientsp[i].chaddr[0], 1348 clientsp[i].chaddr[1], 1349 clientsp[i].chaddr[2], 1350 clientsp[i].chaddr[3], 1351 clientsp[i].chaddr[4], 1352 clientsp[i].chaddr[5]); 1353 1354 (void) cond_init(&clientsp[i].cv, USYNC_THREAD, 0); 1355 (void) mutex_init(&clientsp[i].mtx, USYNC_THREAD, 0); 1356 clientsp[i].proto = proto; 1357 clientsp[i].flags = CLIENT_FIRSTTIME; 1358 if (thr_create(NULL, NULL, client, (void *) &clientsp[i], 1359 THR_BOUND | THR_SUSPENDED, &clientsp[i].id) != 0) { 1360 (void) fprintf(stderr, "Error starting Client %04d\n", 1361 clientsp[i].id); 1362 } 1363 } 1364 1365 /* 1366 * Create signal handling thread. 1367 */ 1368 if (thr_create(NULL, 0, sig_handle, NULL, 1369 THR_BOUND | THR_DAEMON | THR_DETACHED, &sig_id) != 0) { 1370 (void) fprintf(stderr, "Error starting signal handler.\n"); 1371 return (1); 1372 } else 1373 (void) fprintf(stderr, "Started Signal handler: %04d...\n", 1374 sig_id); 1375 1376 /* 1377 * Create/start the service thread. 1378 */ 1379 if (thr_create(NULL, NULL, service, (void *) clientsp, THR_BOUND, 1380 &service_id) != 0) { 1381 (void) fprintf(stderr, "Error starting Service %d\n", 1382 service_id); 1383 exit(1); 1384 } else 1385 (void) fprintf(stderr, "Started Service %04d...\n", 1386 service_id); 1387 1388 /* 1389 * Continue the client threads. 1390 */ 1391 for (i = 0; i < clients; i++) { 1392 (void) thr_continue(clientsp[i].id); 1393 } 1394 1395 /* 1396 * join them 1397 */ 1398 threrrorp = &threrror; 1399 for (i = 0; i < clients; i++) { 1400 if (thr_join(clientsp[i].id, NULL, (void **) &threrrorp) == 0) { 1401 if (threrror != 0) { 1402 (void) fprintf(stdout, 1403 "Client %04d - exited with %d\n", 1404 clientsp[i].id, threrror); 1405 } 1406 (void) cond_destroy(&clientsp[i].cv); 1407 (void) mutex_destroy(&clientsp[i].mtx); 1408 } 1409 } 1410 1411 (void) close(s); /* force service out of poll */ 1412 1413 if (thr_join(service_id, NULL, (void **) &threrrorp) == 0) { 1414 if (threrror != 0) { 1415 (void) fprintf(stdout, "Service - exited with %d\n", 1416 threrror); 1417 } 1418 } 1419 (void) free((char *)clientsp); 1420 (void) fprintf(stdout, "Exiting...\n"); 1421 1422 return (0); 1423 } 1424 1425 /* 1426 * corrupt: simulate packet corruption for debugging server 1427 */ 1428 static void 1429 corrupt(char *pktp, int size) 1430 { 1431 int c; 1432 int i; 1433 int p; 1434 char *pp; 1435 char *pe = pktp + size; 1436 int li = rand() % (size - 1) + 1; 1437 1438 for (pp = pktp; pp < pe; pp += li) { 1439 c = ((pe - pp) < li ? pe - pp : li); 1440 i = (rand() % c)>>1; 1441 while (--i > 0) { 1442 p = (rand() % c); 1443 pp[p] = (unsigned char)(rand() & 0xFF); 1444 } 1445 } 1446 }