xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_client.c (revision 1914:8a8c5f225b1b)
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