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
dhcpmsgtype(uchar_t pkt,char * buf)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
closeif(int ms,char * cifname,struct sockaddr_in * myip,thread_t myself)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 *
client(void * args)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 *
service(void * args)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 *
sig_handle(void * arg)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
main(int argc,char * argv[])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
corrupt(char * pktp,int size)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 }
1447