xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/main.c (revision 12864:78ceb0079fba)
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 (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * This file contains the argument parsing routines of the dhcpd daemon.
27  * It corresponds to the START state as spec'ed.
28  */
29 
30 /*
31  * Multithreading Notes:
32  * =====================
33  *
34  * For Enterprise DHCP scalability, libdhcpsvc has been made reentrant,
35  * and the server has been organized with a worker thread per client.
36  *
37  * There is a thread per configured interface which reads requests,
38  * determines if they are for this server, and appends them to the
39  * interface's PKT list. This thread spawns worker threads as needed
40  * to service incoming clients.
41  *
42  * The main thread creates a thread to handle signals. All subsequent threads
43  * (and the main thread) mask out all signals.
44  *
45  * The signal thread will deal with the -t option. This is done by
46  * waiting in sigtimedwait() for the timeout period, then spawning
47  * a reinitialization thread.
48  *
49  * dhcp: each client worker thread moves through the multi-packet
50  * state machine inline, performing icmp_echo_check() as needed.
51  * We prevent multiple threads from registering the same address for ICMP
52  * validation due to multiple DISCOVERS by reserving addresses in
53  * select_offer() to ensure we don't offer IP addresses currently
54  * undergoing ICMP validation.
55  *
56  * bootp: If automatic allocation is in effect,
57  * bootp behaves in the same fashion as dhcp_offer.
58  *
59  * Summary:
60  *
61  *	Threads:
62  *		1) Main thread: Handles startup and shutdown chores.
63  *
64  *		2) Signal thread: The main thread creates this thread, and
65  *		   then masks out all signals. The signal thread waits on
66  *		   sigwait(), and processes all signals. It notifies the
67  *		   main thread of EINTR or ETERM via a global variable, which
68  *		   the main thread checks upon the exit to cond_wait.
69  *		   This thread is on it's own LWP, and is DETACHED | DAEMON.
70  *		   The thread function is sig_handle().
71  *
72  *		3) Interface threads: Each interface structure has a thread
73  *		   associated with it (created in open_interfaces) which is
74  *		   responsible for polling the interface, validating bootp
75  *		   packets received, and placing them on the client's
76  *		   PKT_LIST. The thread function is monitor_interface().
77  *		   When notified by the main thread via the thr_exit flag,
78  *		   the thread prints interface statistics for the interface,
79  *		   and then exits.
80  *
81  *		4) Client threads: Created as needed when the interface
82  *		   thread processes each incoming packet. These threads are
83  *		   created DETACHED and SUSPENDED by the interface thread,
84  *		   which then  places each plp structure on the client's
85  *		   PKT_LIST, then continues the thread. A client thread exits
86  *		   when it has processed all incoming packets, and no
87  *		   deferred client work is queued. See per_dnet.h for
88  *		   more information on client locks.
89  *
90  *	Locks:
91  *		1) if_head_mtx	-	Locks the global interface list.
92  *
93  *		2) ifp_mtx	-	Locks contents of the enclosed
94  *					interface (IF) structure, including
95  *					such things as thr_exit flag and
96  *					statistics counters.
97  *
98  *		3) pkt_mtx	-	Locks PKT_LIST head list within the
99  *					enclosed client (dsvc_clnt_t) struct.
100  */
101 
102 #include <stdio.h>
103 #include <stdio_ext.h>
104 #include <stdlib.h>
105 #include <unistd.h>
106 #include <ctype.h>
107 #include <string.h>
108 #include <syslog.h>
109 #include <signal.h>
110 #include <time.h>
111 #include <limits.h>
112 #include <sys/resource.h>
113 #include <sys/fcntl.h>
114 #include <stdarg.h>
115 #include <sys/types.h>
116 #include <assert.h>
117 #include <fcntl.h>
118 #include <sys/resource.h>
119 #include <sys/stat.h>
120 #include <sys/systeminfo.h>
121 #include <sys/socket.h>
122 #include <sys/sockio.h>
123 #include <net/if.h>
124 #include <netinet/in.h>
125 #include <arpa/inet.h>
126 #include <errno.h>
127 #include <netinet/dhcp.h>
128 #include <synch.h>
129 #include <sys/param.h>
130 #include <sys/sysmacros.h>
131 #include <netdb.h>
132 #include <dhcp_svc_confkey.h>
133 #include "dhcpd.h"
134 #include "per_dnet.h"
135 #include "interfaces.h"
136 #include <locale.h>
137 #include <mtmalloc.h>
138 #include <resolv.h>
139 
140 extern int optind, opterr;
141 extern char *optarg;
142 
143 typedef struct dhcp_cops {
144 	char		*cop_name;			/* opt name */
145 	boolean_t	cop_present;			/* opt present? */
146 	boolean_t	(*cop_vinit)(struct dhcp_cops *, const char *);
147 	union {
148 		char 		*ucop_str;
149 		boolean_t	ucop_bool;
150 		int		ucop_num;
151 	} dhcp_cops_un;
152 #define	cop_bool	dhcp_cops_un.ucop_bool	/* opt val: boolean_t */
153 #define	cop_num		dhcp_cops_un.ucop_num	/* opt val: int */
154 #define	cop_str		dhcp_cops_un.ucop_str	/* opt val: string */
155 } DHCP_COP;
156 
157 static boolean_t bool_v(DHCP_COP *, const char *);
158 static boolean_t uchar_v(DHCP_COP *, const char *);
159 static boolean_t int_v(DHCP_COP *, const char *);
160 static boolean_t uint_v(DHCP_COP *, const char *);
161 static boolean_t str_v(DHCP_COP *, const char *);
162 static boolean_t bootp_v(DHCP_COP *, const char *);
163 static boolean_t logging_v(DHCP_COP *, const char *);
164 static boolean_t runmode_v(DHCP_COP *, const char *);
165 static int collect_options(int, char **);
166 static void usage(void);
167 static void local_closelog(void);
168 static void *sig_handle(void *);
169 
170 #define	C_RUNMODE	0
171 #define	C_DEBUG		1
172 #define	C_VERBOSE	2
173 #define	C_HOPS		3
174 #define	C_LOGGING	4
175 #define	C_IF		5
176 #define	C_OFFER		6
177 #define	C_ICMP		7
178 #define	C_RESCAN	8
179 #define	C_BOOTP		9
180 #define	C_CLIENT	10
181 #define	C_THREADS	11
182 #define	C_MINLRU	12
183 #define	C_RELAY		13
184 #define	C_NSUPDATE	14
185 #define	C_CACHE		15
186 
187 #define	C_DBGPORT	16
188 #define	C_RENOG		17
189 #define	C_OWNER		18
190 #ifdef	DEBUG
191 #define	C_DBGNET	19
192 #define	C_LAST		C_DBGNET
193 #else	/* DEBUG */
194 #define	C_LAST		C_OWNER
195 #endif	/* DEBUG */
196 
197 
198 static DHCP_COP options[C_LAST + 1] = {
199 /* name				Present?  Verify func   Value */
200 /* ====				========  ===========   ===== */
201 	/* Run mode / BOOTP relay agent selection option */
202 { DSVC_CK_RUN_MODE,		B_FALSE,  runmode_v,	DSVC_CV_SERVER },
203 	/* Generic daemon options */
204 { "DEBUG",			B_FALSE,  bool_v,	B_FALSE },
205 { DSVC_CK_VERBOSE,		B_FALSE,  bool_v,	B_FALSE },
206 { DSVC_CK_RELAY_HOPS,		B_FALSE,  uchar_v,	(char *)DSVC_CV_HOPS },
207 { DSVC_CK_LOGGING_FACILITY,	B_FALSE,  logging_v,	0 },
208 { DSVC_CK_INTERFACES,		B_FALSE,  str_v,	NULL },
209 	/* DHCP server run mode options */
210 { DSVC_CK_OFFER_CACHE_TIMEOUT,	B_FALSE,  uint_v,   (char *)DSVC_CV_OFFER_TTL },
211 { DSVC_CK_ICMP_VERIFY,		B_FALSE,  bool_v,	(char *)B_TRUE },
212 { DSVC_CK_RESCAN_INTERVAL,	B_FALSE,  int_v,	0 },
213 { DSVC_CK_BOOTP_COMPAT,		B_FALSE,  bootp_v,	NULL },
214 { DSVC_CK_MAX_CLIENTS,		B_FALSE,  int_v,	(char *)0 },
215 { DSVC_CK_MAX_THREADS,		B_FALSE,  int_v,	(char *)0 },
216 { DSVC_CK_LEASE_MIN_LRU,	B_FALSE,  int_v,    (char *)DSVC_CV_MIN_LRU },
217 	/* BOOTP relay agent options */
218 { DSVC_CK_RELAY_DESTINATIONS,	B_FALSE,  str_v,	NULL },
219 	/* Name service update timeout */
220 { DSVC_CK_NSU_TIMEOUT,		B_FALSE,  uint_v,   (char *)DSVC_CV_NSU_TO },
221 { DSVC_CK_CACHE_TIMEOUT,	B_FALSE,  int_v,   (char *)DSVC_CV_CACHE_TTL },
222 { DSVC_CK_DBG_PORT_OFFSET,	B_FALSE,  int_v,	0 },
223 { DSVC_CK_RENOG_INTERVAL,	B_FALSE,  uint_v,  (char *)DSVC_CV_RENOG_INT },
224 { DSVC_CK_OWNER_IP,		B_FALSE,  str_v,	NULL },
225 #ifdef	DEBUG
226 { DSVC_CK_DBG_MEMORY_NET,	B_FALSE,  str_v,	NULL }
227 #endif	/* DEBUG */
228 };
229 
230 #define	DHCPCOP_NAME(x)		(options[x].cop_name)
231 #define	DHCPCOP_PRES(x)		(options[x].cop_present)
232 #define	DHCPCOP_VINIT(x, y)	(options[x].cop_vinit(&options[x], y))
233 #define	DHCPCOP_BOOL(x)		(options[x].cop_bool)
234 #define	DHCPCOP_NUM(x)		(options[x].cop_num)
235 #define	DHCPCOP_STR(x)		(options[x].cop_str)
236 
237 int debug;
238 boolean_t verbose;
239 boolean_t noping;		/* Always ping before offer by default */
240 boolean_t no_dhcptab;		/* set if no dhcptab exists */
241 boolean_t server_mode;		/* set if running in server mode */
242 static boolean_t bootp_compat;	/* bootp compatibility */
243 boolean_t be_automatic;		/* set if bootp server should allocate IPs */
244 uchar_t max_hops;		/* max relay hops before discard */
245 int log_local;			/* syslog local facility number */
246 int icmp_tries = DHCP_ICMP_ATTEMPTS; /* Number of attempts @ icmp_timeout */
247 time_t off_secs;		/* def ttl of an offer */
248 time_t cache_secs;		/* def ttl of netmask and table caches */
249 time_t renog_secs;		/* def wait time for secondary server timeout */
250 time_t min_lru;			/* def minimum lru of a reclaimed lease */
251 time_t icmp_timeout = DHCP_ICMP_TIMEOUT; /* milliseconds to wait for response */
252 time_t nsutimeout_secs;		/* seconds to wait for a name service up date */
253 struct in_addr	server_ip;	/* IP address of server's primary interface */
254 struct in_addr	*owner_ip;	/* owner IP address list */
255 static dhcp_confopt_t *dsp;	/* Confopt for datastore access */
256 dsvc_datastore_t datastore;	/* Datastore for container access */
257 int max_threads;		/* maximum number of worker threads per net */
258 int max_clients;		/* maximum number of active clients per net */
259 ushort_t port_offset = 0;	/* offset to port for multiple server */
260 int net_thresh = DHCP_NET_THRESHOLD;	/* secs to keep pernet reference */
261 int clnt_thresh = DHCP_CLIENT_THRESHOLD; /* secs to keep client reference */
262 struct __res_state resolv_conf;	/* DNS resolver data, includes domain-name */
263 static int rescan_scale = DHCP_RESCAN_SCALE;	/* secs to scale */
264 #ifdef	DEBUG
265 char *dbg_net;			/* Simulated debug net (see misc.c) */
266 #endif	/* DEBUG */
267 
268 static time_t rescan_interval;	/* dhcptab rescan interval */
269 
270 
271 /*
272  * This global is set by the signal handler when the main thread (and thus
273  * the daemon) should exit. We only use the mutex in this file, since we make
274  * the main thread wait on it becoming true using a condition variable.
275  */
276 boolean_t time_to_go = B_FALSE;
277 static mutex_t	ttg_mtx;
278 static cond_t ttg_cv;
279 
280 /* local syslog facilities */
281 static int log_facilities[] = {
282 	LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4,
283 	LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7
284 };
285 
286 time_t	reinit_time;			/* reinitialization time */
287 static thread_t init_thread;		/* reinitialization thread */
288 
289 int
main(int argc,char * argv[])290 main(int argc, char *argv[])
291 {
292 	sigset_t	set;
293 	int		i, ns, err = 0;
294 	struct rlimit	rl;
295 	struct hostent	*hp;
296 	thread_t	sigthread;
297 	int		nss_lwp = 0;
298 	int32_t		ncpus;
299 	char		scratch[MAXHOSTNAMELEN + 1];
300 	char		ntoab[INET_ADDRSTRLEN];
301 	char		*ownerip_args, *sip, *lasts;
302 	int		np = 1;
303 	struct in_addr	*oip;
304 
305 #ifdef	DEBUG
306 	mallocctl(MTDEBUGPATTERN, 1);
307 	mallocctl(MTINITBUFFER, 1);
308 #endif	/* DEBUG */
309 
310 	(void) setlocale(LC_ALL, "");
311 
312 #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
313 #define	TEXT_DOMAIN	"SYS_TEXT"
314 #endif	/* ! TEXT_DOMAIN */
315 
316 	(void) textdomain(TEXT_DOMAIN);
317 
318 	if (geteuid() != (uid_t)0) {
319 		(void) fprintf(stderr, gettext("Must be 'root' to run %s.\n"),
320 		    DHCPD);
321 		return (EPERM);
322 	}
323 
324 	if ((err = collect_options(argc, argv)) != 0) {
325 		if (errno == EAGAIN) {
326 			(void) fprintf(stderr, gettext("DHCP daemon config "
327 			    "file locked.\n"));
328 			err = EAGAIN;
329 		} else {
330 			usage();
331 			err = EINVAL;
332 		}
333 		return (err);
334 	}
335 
336 	/* Deal with run mode generic options first */
337 	debug = DHCPCOP_BOOL(C_DEBUG);
338 	verbose = DHCPCOP_BOOL(C_VERBOSE);
339 	max_hops = DHCPCOP_NUM(C_HOPS);
340 	interfaces = DHCPCOP_STR(C_IF);
341 	bootp_compat = DHCPCOP_PRES(C_BOOTP); /* present then yes */
342 	max_clients = DHCPCOP_NUM(C_CLIENT);
343 	max_threads = DHCPCOP_NUM(C_THREADS);
344 	log_local = DHCPCOP_PRES(C_LOGGING) ?
345 	    log_facilities[DHCPCOP_NUM(C_LOGGING)] : -1;
346 
347 	server_mode = (strcasecmp(DHCPCOP_STR(C_RUNMODE), DSVC_CV_SERVER) == 0);
348 	if (server_mode) {
349 
350 		if (bootp_compat) {
351 			be_automatic = (strcasecmp(DHCPCOP_STR(C_BOOTP),
352 			    DSVC_CV_AUTOMATIC) == 0);
353 		}
354 
355 		if (DHCPCOP_BOOL(C_ICMP) == B_FALSE) {
356 			(void) fprintf(stderr, gettext("\nWARNING: Disabling \
357 duplicate IP address detection!\n\n"));
358 			noping = B_TRUE;
359 		} else {
360 			noping = B_FALSE;
361 		}
362 
363 		off_secs = DHCPCOP_NUM(C_OFFER);
364 		cache_secs = DHCPCOP_NUM(C_CACHE);
365 		renog_secs = DHCPCOP_NUM(C_RENOG);
366 		min_lru = DHCPCOP_NUM(C_MINLRU);
367 		port_offset = DHCPCOP_NUM(C_DBGPORT);	/* Private debug flag */
368 #ifdef	DEBUG
369 		dbg_net = DHCPCOP_STR(C_DBGNET);
370 #endif	/* DEBUG */
371 		nsutimeout_secs = DHCPCOP_PRES(C_NSUPDATE) ?
372 		    DHCPCOP_NUM(C_NSUPDATE) : DHCP_NO_NSU;
373 
374 		if ((rescan_interval = DHCPCOP_NUM(C_RESCAN)) != 0) {
375 			rescan_interval *= rescan_scale;
376 		}
377 
378 		/* Load current datastore, if any. */
379 		if (dsp == NULL)
380 			return (1);
381 		if ((i = confopt_to_datastore(dsp, &datastore)) !=
382 		    DSVC_SUCCESS) {
383 			(void) fprintf(stderr, gettext(
384 			    "WARNING: Invalid datastore: %s\n"),
385 			    dhcpsvc_errmsg(i));
386 			return (1);
387 		}
388 		free_dsvc_conf(dsp);
389 
390 		ns = status_dd(&datastore);
391 		if (ns != DSVC_SUCCESS) {
392 			(void) fprintf(stderr, gettext(
393 			    "Datastore status error: %s\n"),
394 			    dhcpsvc_errmsg(ns));
395 			return (1);
396 		}
397 	} else {
398 		if (!DHCPCOP_PRES(C_RELAY)) {
399 			(void) fprintf(stderr, gettext("Missing BOOTP "
400 			    "relay destinations (%s)\n"),
401 			    DSVC_CK_RELAY_DESTINATIONS);
402 			return (1);
403 		}
404 		if ((err = relay_agent_init(DHCPCOP_STR(C_RELAY))) != 0)
405 			return (err);
406 	}
407 
408 	if (!debug) {
409 		/* Daemon (background, detach from controlling tty). */
410 		switch (fork()) {
411 		case -1:
412 			(void) fprintf(stderr,
413 			    gettext("Daemon cannot fork(): %s\n"),
414 			    strerror(errno));
415 			return (errno);
416 		case 0:
417 			/* child */
418 			break;
419 		default:
420 			/* parent */
421 			return (0);
422 		}
423 
424 		closefrom(0);	/* close all open files */
425 		errno = 0;	/* clean up benign bad file no error */
426 		(void) open("/dev/null", O_RDONLY, 0);
427 		(void) dup2(0, 1);
428 		(void) dup2(0, 2);
429 
430 		/* Detach console */
431 		(void) setsid();
432 
433 		(void) openlog(DHCPD, LOG_PID, LOG_DAEMON);
434 	}
435 
436 	/* set NOFILE to unlimited */
437 	rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
438 	if ((err = setrlimit(RLIMIT_NOFILE, &rl)) < 0) {
439 		dhcpmsg(LOG_ERR, "Cannot set open file limit: %s\n",
440 		    strerror(errno));
441 		return (err);
442 	}
443 	(void) enable_extended_FILE_stdio(-1, -1);
444 
445 	if (verbose)
446 		dhcpmsg(LOG_INFO, "Daemon started.\n");
447 
448 	/*
449 	 * Block all signals in main thread - threads created will also
450 	 * ignore signals.
451 	 */
452 	(void) sigfillset(&set);
453 
454 	(void) sigdelset(&set, SIGABRT);	/* allow for user abort */
455 
456 	(void) thr_sigsetmask(SIG_SETMASK, &set, NULL);
457 
458 	/*
459 	 * Create signal handling thread.
460 	 * Due to threads library limitations, the main program
461 	 * thread currently cannot function as the signal thread, and
462 	 * must be a bound thread.
463 	 */
464 	if ((err = thr_create(NULL, 0, sig_handle, NULL, THR_NEW_LWP |
465 	    THR_DAEMON | THR_BOUND | THR_DETACHED, &sigthread)) != 0) {
466 		(void) fprintf(stderr,
467 		    gettext("Cannot start signal handling thread, error: %d\n"),
468 		    err);
469 		return (err);
470 	}
471 #ifdef	DEBUG
472 	(void) fprintf(stderr,
473 	    gettext("Started signal handling thread: %d\n"), sigthread);
474 #endif	/* DEBUG */
475 
476 	/* Save away the IP address associated with our HOSTNAME. */
477 
478 #ifdef	DEBUG
479 	/* Debugging: allow shared use of difficult to create databases. */
480 	if (getenv("DHCP_HOSTNAME") != NULL)
481 		(void) strcpy(scratch, getenv("DHCP_HOSTNAME"));
482 	else
483 #endif	/* DEBUG */
484 	(void) sysinfo(SI_HOSTNAME, scratch, MAXHOSTNAMELEN + 1);
485 
486 	if ((hp = gethostbyname(scratch)) != NULL &&
487 	    hp->h_addrtype == AF_INET &&
488 	    hp->h_length == sizeof (struct in_addr)) {
489 		(void) memcpy((char *)&server_ip, hp->h_addr_list[0],
490 		    sizeof (server_ip));
491 		/*
492 		 * server_ip is supplemented by owner_ip list
493 		 * the first in the list of owner_ips always = server_ip
494 		 */
495 		owner_ip = smalloc((sizeof (struct in_addr)) * (np + 1));
496 		(void) memcpy(owner_ip, &server_ip, sizeof (server_ip));
497 
498 		if (DHCPCOP_PRES(C_OWNER)) {
499 			ownerip_args = DHCPCOP_STR(C_OWNER);
500 			sip = strtok_r(ownerip_args, ",", &lasts);
501 			while (sip != NULL) {
502 				owner_ip = srealloc(owner_ip,
503 				    (sizeof (struct in_addr)) * (np + 2));
504 				oip = owner_ip + np;
505 				if (inet_pton(AF_INET, sip, oip) == 0 ||
506 				    oip->s_addr == INADDR_ANY) {
507 					dhcpmsg(LOG_ERR,
508 					    "Invalid OWNER IP address %s\n",
509 					    sip);
510 					sip = strtok_r(NULL, ",", &lasts);
511 					continue;
512 				}
513 				np++;
514 				sip = strtok_r(NULL, ",", &lasts);
515 			}
516 		}
517 		oip = owner_ip + np;
518 		oip->s_addr = INADDR_ANY;
519 	} else {
520 		dhcpmsg(LOG_ERR,
521 		    "Cannot determine server hostname/IP address.\n");
522 		local_closelog();
523 		return (1);
524 	}
525 	(void) memset(&resolv_conf, 0, sizeof (resolv_conf));
526 	if (res_ninit(&resolv_conf) == -1) {
527 		dhcpmsg(LOG_ERR, "Cannot acquire resolver configuration.\n");
528 	}
529 	i = 0;
530 	if (server_mode) {
531 		/*
532 		 * Calculate limits to maximum concurrency. Special values:
533 		 * If max_{threads,clients} == 0, calculate limits
534 		 * based on cpu and memory.
535 		 * Else if max_{threads,clients} is set to -1, run without
536 		 * concurrency limits.
537 		 * Else use supplied limits.
538 		 */
539 		if ((ncpus = sysconf(_SC_NPROCESSORS_CONF)) < 0)
540 			ncpus = 1;
541 
542 		if (max_clients == 0)
543 			max_clients = DHCP_DEFAULT_CLIENTS * ncpus;
544 
545 		/* Require a minimum number of client structs. */
546 		if (max_clients != -1 && max_clients < DHCP_MIN_CLIENTS) {
547 			max_clients = DHCP_MIN_CLIENTS;
548 			dhcpmsg(LOG_ERR, "Warning: adjusting MAX_CLIENTS"
549 			    " to minimum value %d\n", max_clients);
550 		}
551 
552 		if (max_threads == 0)
553 			max_threads = max_clients/4;
554 
555 		/*
556 		 * 4321342: Alloc additional lwps for unbound library threads.
557 		 * Remove this performance workaround when bug fixed.
558 		 */
559 		if (max_clients != 0)
560 			nss_lwp = max_clients/8;
561 		if (nss_lwp <= 0 || nss_lwp > DHCP_NSS_LWP)
562 			nss_lwp = DHCP_NSS_LWP;
563 		i = thr_setconcurrency(nss_lwp);
564 	}
565 
566 	if (verbose) {
567 		if (i != 0)
568 			dhcpmsg(LOG_ERR, "Error setting concurrency %d: %s\n",
569 			    max_threads, strerror(i));
570 		dhcpmsg(LOG_INFO, "Daemon Version: %s\n", DAEMON_VERS);
571 		dhcpmsg(LOG_INFO, "Maximum relay hops: %d\n", max_hops);
572 		if (log_local > -1) {
573 			dhcpmsg(LOG_INFO,
574 			    "Transaction logging to %s enabled.\n",
575 			    debug ? "console" : "syslog");
576 		}
577 		if (server_mode) {
578 			dhcpmsg(LOG_INFO, "Run mode is: DHCP Server Mode.\n");
579 			dhcpmsg(LOG_INFO, "Datastore resource: %s\n",
580 			    datastore.d_resource ?
581 			    datastore.d_resource : "");
582 			dhcpmsg(LOG_INFO, "Location: %s\n",
583 			    datastore.d_location ?
584 			    datastore.d_location : "");
585 			dhcpmsg(LOG_INFO, "DHCP offer TTL: %ld\n", off_secs);
586 			if (bootp_compat)
587 				dhcpmsg(LOG_INFO,
588 				    "BOOTP compatibility enabled.\n");
589 			if (rescan_interval != 0) {
590 				dhcpmsg(LOG_INFO,
591 				    "Dhcptab rescan interval: %ld minutes.\n",
592 				    rescan_interval / rescan_scale);
593 			}
594 			dhcpmsg(LOG_INFO, "ICMP validation timeout: %ld "
595 			    "milliseconds, Attempts: %d.\n", icmp_timeout,
596 			    icmp_tries);
597 			if (nsutimeout_secs != DHCP_NO_NSU) {
598 				dhcpmsg(LOG_INFO, "Name service update "
599 				    "enabled, timeout: %ld seconds\n",
600 				    nsutimeout_secs);
601 			}
602 			for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++)
603 				dhcpmsg(LOG_INFO, "Owner IP address: %s\n",
604 				    inet_ntop(AF_INET, oip, ntoab,
605 				    sizeof (ntoab)));
606 			dhcpmsg(LOG_INFO, "Maximum concurrent clients: %d\n",
607 			    max_clients);
608 			dhcpmsg(LOG_INFO, "Maximum threads: %d\n", max_threads);
609 		} else
610 			dhcpmsg(LOG_INFO, "Run mode is: Relay Agent Mode.\n");
611 	}
612 
613 	(void) mutex_init(&ttg_mtx, USYNC_THREAD, 0);
614 	(void) cond_init(&ttg_cv, USYNC_THREAD, 0);
615 
616 	if (server_mode) {
617 
618 		if (initntab() != 0) {
619 			dhcpmsg(LOG_ERR, "Cannot allocate per network hash "
620 			    "table.\n");
621 			local_closelog();
622 			(void) mutex_destroy(&ttg_mtx);
623 			(void) cond_destroy(&ttg_cv);
624 			res_ndestroy(&resolv_conf);
625 			return (1);
626 		}
627 
628 		if (initmtab() != 0) {
629 			dhcpmsg(LOG_ERR, "Cannot allocate macro hash table.\n");
630 			local_closelog();
631 			(void) mutex_destroy(&ttg_mtx);
632 			(void) cond_destroy(&ttg_cv);
633 			res_ndestroy(&resolv_conf);
634 			return (1);
635 		}
636 
637 		if ((err = checktab()) != 0 ||
638 		    (err = readtab(NEW_DHCPTAB)) != 0) {
639 			if (err == ENOENT) {
640 				no_dhcptab = B_TRUE;
641 			} else {
642 				dhcpmsg(LOG_ERR,
643 				    "Error reading macro table.\n");
644 				local_closelog();
645 				(void) mutex_destroy(&ttg_mtx);
646 				(void) cond_destroy(&ttg_cv);
647 				res_ndestroy(&resolv_conf);
648 				return (err);
649 			}
650 		} else
651 			no_dhcptab = B_FALSE;
652 	}
653 
654 	if ((err = open_interfaces()) != 0) {
655 		local_closelog();
656 		(void) mutex_destroy(&ttg_mtx);
657 		(void) cond_destroy(&ttg_cv);
658 		res_ndestroy(&resolv_conf);
659 		return (err);
660 	}
661 
662 	/*
663 	 * While forever, handle signals and dispatch them.
664 	 */
665 	while (!time_to_go) {
666 		(void) mutex_lock(&ttg_mtx);
667 		while (!time_to_go)
668 			(void) cond_wait(&ttg_cv, &ttg_mtx);
669 		(void) mutex_unlock(&ttg_mtx);
670 	}
671 
672 	/* Daemon terminated. */
673 	if (server_mode) {
674 		resettab(B_TRUE);
675 		close_clnts();	/* reaps client threads */
676 	}
677 
678 	close_interfaces();		/* reaps monitor threads */
679 	local_closelog();
680 	(void) fflush(NULL);
681 	(void) mutex_destroy(&ttg_mtx);
682 	(void) cond_destroy(&ttg_cv);
683 	res_ndestroy(&resolv_conf);
684 	return (err);
685 }
686 
687 /*
688  * Signal handler routine. All signals handled by calling thread.
689  */
690 /* ARGSUSED */
691 static void *
sig_handle(void * arg)692 sig_handle(void *arg)
693 {
694 	int		err;
695 	int		sig;
696 	sigset_t	set;
697 	char buf[SIG2STR_MAX];
698 	timespec_t	ts;
699 	siginfo_t	si;
700 
701 	(void) sigfillset(&set);		/* catch all signals */
702 
703 	ts.tv_sec = rescan_interval == 0 ? DEFAULT_LEASE : rescan_interval;
704 	ts.tv_nsec = 0L;
705 
706 	/* wait for a signal */
707 	while (!time_to_go) {
708 		switch (sig = sigtimedwait(&set, &si, &ts)) {
709 		case -1:
710 			if (rescan_interval == 0 || errno != EAGAIN)
711 				break;
712 			/*FALLTHRU*/
713 		case SIGHUP:
714 			/*
715 			 * Create reinitialization thread.
716 			 */
717 			if (init_thread != NULL)
718 					break;
719 
720 			if ((err = thr_create(NULL, 0, reinitialize,
721 			    &init_thread, THR_BOUND | THR_DETACHED,
722 			    &init_thread)) != 0) {
723 				(void) fprintf(stderr, gettext(
724 				    "Cannot start reinit thread, error: %d\n"),
725 				    err);
726 			}
727 			break;
728 		case SIGTERM:
729 			/* FALLTHRU */
730 		case SIGINT:
731 			(void) sig2str(sig, buf);
732 			dhcpmsg(LOG_NOTICE, "Signal: %s received...Exiting\n",
733 			    buf);
734 			time_to_go = B_TRUE;
735 			break;
736 		default:
737 			if (verbose) {
738 				(void) sig2str(sig, buf);
739 				dhcpmsg(LOG_INFO,
740 				    "Signal: %s received...Ignored\n",
741 				    buf);
742 			}
743 			break;
744 		}
745 		if (time_to_go) {
746 			(void) mutex_lock(&ttg_mtx);
747 			(void) cond_signal(&ttg_cv);
748 			(void) mutex_unlock(&ttg_mtx);
749 			break;
750 		}
751 	}
752 	return ((void *)sig);	/* NOTREACHED */
753 }
754 
755 static void
usage(void)756 usage(void)
757 {
758 	(void) fprintf(stderr, gettext(
759 	    "%s:\n\n\tCommon: [-d] [-v] [-i interface, ...] "
760 	    "[-h hops] [-l local_facility]\n\n\t"
761 	    "Server: [-n] [-t rescan_interval] [-o DHCP_offer_TTL]\n\t\t"
762 	    "[ -b automatic | manual]\n\n\t"
763 	    "Relay Agent: -r IP | hostname, ...\n"), DHCPD);
764 }
765 
766 static void
local_closelog(void)767 local_closelog(void)
768 {
769 	dhcpmsg(LOG_INFO, "Daemon terminated.\n");
770 	if (!debug)
771 		closelog();
772 }
773 
774 /*
775  * Given a received BOOTP packet, generate an appropriately sized,
776  * and generically initialized BOOTP packet.
777  */
778 PKT *
gen_bootp_pkt(int size,PKT * srcpktp)779 gen_bootp_pkt(int size, PKT *srcpktp)
780 {
781 	PKT *pkt = (PKT *)smalloc(size);
782 
783 	pkt->htype = srcpktp->htype;
784 	pkt->hlen = srcpktp->hlen;
785 	pkt->xid = srcpktp->xid;
786 	pkt->secs = srcpktp->secs;
787 	pkt->flags = srcpktp->flags;
788 	pkt->giaddr.s_addr = srcpktp->giaddr.s_addr;
789 	(void) memcpy(pkt->cookie, srcpktp->cookie, 4);
790 	(void) memcpy(pkt->chaddr, srcpktp->chaddr, srcpktp->hlen);
791 
792 	return (pkt);
793 }
794 
795 /*
796  * Points field serves to identify those packets whose allocated size
797  * and address is not represented by the address in pkt.
798  */
799 void
free_plp(PKT_LIST * plp)800 free_plp(PKT_LIST *plp)
801 {
802 	char *tmpp;
803 
804 #ifdef	DEBUG
805 	dhcpmsg(LOG_DEBUG,
806 "%04d: free_plp(0x%x)pkt(0x%x)len(%d)next(0x%x)prev(0x%x)\n",
807 	    thr_self(), plp, plp->pkt, plp->len,
808 	    plp->next, plp->prev);
809 #endif	/* DEBUG */
810 	if (plp->pkt) {
811 		if (plp->offset != 0)
812 			tmpp = (char *)((uint_t)plp->pkt - plp->offset);
813 		else
814 			tmpp = (char *)plp->pkt;
815 		free(tmpp);
816 	}
817 	free(plp);
818 	plp = NULL;
819 }
820 
821 /*
822  * Validate boolean is "B_TRUE" or "B_FALSE".
823  * Returns B_TRUE if successful, B_FALSE otherwise.
824  */
825 static boolean_t
bool_v(DHCP_COP * dp,const char * option)826 bool_v(DHCP_COP *dp, const char *option)
827 {
828 	boolean_t	i;
829 
830 	assert(dp != NULL && option != NULL);
831 
832 	if (strcasecmp(option, DSVC_CV_TRUE) == 0) {
833 		i = B_TRUE;
834 	} else if (strcasecmp(option, DSVC_CV_FALSE) == 0) {
835 		i = B_FALSE;
836 	} else {
837 		return (B_FALSE); /* huh? */
838 	}
839 	dp->cop_bool = i;
840 	return (B_TRUE);
841 }
842 
843 /*
844  * Validate uchar data.
845  * Returns B_TRUE if successful, B_FALSE otherwise.
846  */
847 static boolean_t
uchar_v(DHCP_COP * dp,const char * option)848 uchar_v(DHCP_COP *dp, const char *option)
849 {
850 	if (dp == NULL || option == NULL || !isdigit(*option))
851 		return (B_FALSE);
852 	dp->cop_num = strtoul(option, 0L, 0L);
853 	if (dp->cop_num < 0 || dp->cop_num > 0xFF)
854 		return (B_FALSE);
855 	return (B_TRUE);
856 }
857 
858 /*
859  * Validate integer data.
860  * Returns B_TRUE if successful, B_FALSE otherwise.
861  */
862 static boolean_t
int_v(DHCP_COP * dp,const char * option)863 int_v(DHCP_COP *dp, const char *option)
864 {
865 	if (dp != NULL && option != NULL) {
866 		errno = 0;
867 		dp->cop_num = strtol(option, NULL, 0L);
868 		if (errno == 0)
869 			return (B_TRUE);
870 	}
871 	return (B_FALSE);
872 }
873 
874 /*
875  * Validate unsigned integer data.
876  * Returns B_TRUE if successful, B_FALSE otherwise.
877  */
878 static boolean_t
uint_v(DHCP_COP * dp,const char * option)879 uint_v(DHCP_COP *dp, const char *option)
880 {
881 	if (dp != NULL && option != NULL) {
882 		errno = 0;
883 		dp->cop_num = strtoul(option, NULL, 0L);
884 		if (errno == 0)
885 			return (B_TRUE);
886 	}
887 	return (B_FALSE);
888 }
889 
890 /*
891  * Check if value is a string.
892  * Returns B_TRUE if successful, B_FALSE otherwise
893  */
894 static boolean_t
str_v(DHCP_COP * dp,const char * option)895 str_v(DHCP_COP *dp, const char *option)
896 {
897 	if (dp == NULL || option == NULL ||
898 	    (dp->cop_str = strdup(option)) == NULL) {
899 		return (B_FALSE);
900 	}
901 	return (B_TRUE);
902 }
903 
904 /*
905  * Validate bootp compatibility options. Must be "automatic" or
906  * "manual".
907  * Returns B_TRUE if successful, B_FALSE otherwise.
908  */
909 static boolean_t
bootp_v(DHCP_COP * dp,const char * option)910 bootp_v(DHCP_COP *dp, const char *option)
911 {
912 	if (dp == NULL || option == NULL)
913 		return (B_FALSE);
914 
915 	if ((strcasecmp(option, DSVC_CV_AUTOMATIC) == 0 ||
916 	    strcasecmp(option, DSVC_CV_MANUAL) == 0) &&
917 	    (dp->cop_str = strdup(option)) != NULL) {
918 		return (B_TRUE);
919 	}
920 	return (B_FALSE);
921 }
922 
923 /*
924  * Validate logging facility. Must be a number between 0 and 7 inclusive.
925  * Returns B_TRUE if successful, B_FALSE otherwise.
926  */
927 static boolean_t
logging_v(DHCP_COP * dp,const char * option)928 logging_v(DHCP_COP *dp, const char *option)
929 {
930 	if (uint_v(dp, option) && dp->cop_num <= 7)
931 		return (B_TRUE);
932 
933 	(void) fprintf(stderr, gettext("Syslog local facility must be in the "
934 	    "range of 0 through 7.\n"));
935 	return (B_FALSE);
936 }
937 
938 /*
939  * Validate run mode. Must be "server" or "relay".
940  * Returns B_TRUE if successful, B_FALSE otherwise
941  */
942 static boolean_t
runmode_v(DHCP_COP * dp,const char * option)943 runmode_v(DHCP_COP *dp, const char *option)
944 {
945 	if (dp == NULL || option == NULL)
946 		return (B_FALSE);
947 	if ((strcasecmp(option, DSVC_CV_SERVER) == 0 ||
948 	    strcasecmp(option, DSVC_CV_RELAY) == 0) &&
949 	    (dp->cop_str = strdup(option)) != NULL) {
950 		return (B_TRUE);
951 	}
952 	return (B_FALSE);
953 }
954 
955 /*
956  * Initialize options table based upon config file settings or command
957  * line flags. Handle all option inter-dependency checking here. No value
958  * checking is done here.
959  *
960  * Returns 0 if successful, nonzero otherwise.
961  */
962 static int
collect_options(int count,char ** args)963 collect_options(int count, char **args)
964 {
965 	int			c, i, j;
966 	char			*mode;
967 
968 	/* First, load the configuration options from the file, if present. */
969 	for (errno = 0, i = 0; i < DHCP_RDCOP_RETRIES &&
970 	    read_dsvc_conf(&dsp) < 0; i++) {
971 		(void) fprintf(stderr, gettext(
972 		    "WARNING: DHCP daemon config file: %s\n"),
973 		    strerror(errno));
974 		if (errno == EAGAIN) {
975 			/* file's busy, wait one second and try again */
976 			(void) sleep(1);
977 		} else
978 			break;
979 	}
980 	if (errno == EAGAIN)
981 		return (EAGAIN);
982 
983 	/* set default RUN_MODE to server if it wasn't found in the file */
984 	if (query_dsvc_conf(dsp, DSVC_CK_RUN_MODE, &mode) < 0) {
985 		if (errno == ENOENT) {
986 			if (add_dsvc_conf(&dsp, DSVC_CK_RUN_MODE,
987 			    DSVC_CV_SERVER) != 0)
988 				return (errno);
989 		}
990 	} else
991 		free(mode);
992 
993 	/*
994 	 * Second, pick up the user's preferences from the command line,
995 	 * which modify the config file settings.
996 	 */
997 	while ((c = getopt(count, args, "dnvh:o:r:b:i:t:l:")) != -1) {
998 
999 		boolean_t	relay_mode = B_FALSE;
1000 		char		*key = NULL, *value = NULL;
1001 
1002 		switch (c) {
1003 		case 'd':
1004 			key = "DEBUG";
1005 			value = DSVC_CV_TRUE;
1006 			break;
1007 		case 'n':
1008 			key = DSVC_CK_ICMP_VERIFY;
1009 			value = DSVC_CV_FALSE;
1010 			break;
1011 		case 'v':
1012 			key = DSVC_CK_VERBOSE;
1013 			value = DSVC_CV_TRUE;
1014 			break;
1015 		case 'r':
1016 			key = DSVC_CK_RELAY_DESTINATIONS;
1017 			value = optarg;
1018 			relay_mode = B_TRUE;
1019 			break;
1020 		case 'b':
1021 			key = DSVC_CK_BOOTP_COMPAT;
1022 			value = optarg;
1023 			break;
1024 		case 'h':
1025 			key = DSVC_CK_RELAY_HOPS;
1026 			value = optarg;
1027 			break;
1028 		case 'i':
1029 			key = DSVC_CK_INTERFACES;
1030 			value = optarg;
1031 			break;
1032 		case 'o':
1033 			key = DSVC_CK_OFFER_CACHE_TIMEOUT;
1034 			value = optarg;
1035 			break;
1036 		case 't':
1037 			key = DSVC_CK_RESCAN_INTERVAL;
1038 			value = optarg;
1039 			break;
1040 		case 'l':
1041 			key = DSVC_CK_LOGGING_FACILITY;
1042 			value = optarg;
1043 			break;
1044 		default:
1045 			(void) fprintf(stderr, gettext("Unknown option: %c\n"),
1046 			    c);
1047 			return (EINVAL);
1048 		}
1049 
1050 		/*
1051 		 * Create parameters if they don't exist, or replace
1052 		 * their value if they exist.
1053 		 */
1054 		if (replace_dsvc_conf(&dsp, key, value) < 0)
1055 			return (errno);
1056 
1057 		if (relay_mode) {
1058 			if (replace_dsvc_conf(&dsp, DSVC_CK_RUN_MODE,
1059 			    DSVC_CV_RELAY) < 0)
1060 				return (errno);
1061 		}
1062 	}
1063 
1064 	if (optind < count) {
1065 
1066 		/* get all unused arguments */
1067 		(void) fprintf(stderr, "%s: unexpected argument(s) \"",
1068 		    args[0]);
1069 		for (; optind < count; optind++) {
1070 			if (args[optind][0] != '-')
1071 				(void) fprintf(stderr, " %s", args[optind]);
1072 			else
1073 				break;
1074 		}
1075 		(void) fprintf(stderr, "\"; Aborting\n");
1076 		return (EINVAL);
1077 	}
1078 
1079 	/* load options table, validating value portions of present as we go */
1080 	for (i = 0; dsp != NULL && dsp[i].co_key != NULL; i++) {
1081 		if (dsp[i].co_type != DHCP_KEY)
1082 			continue;	/* comment */
1083 		for (j = 0; j <= C_LAST; j++) {
1084 			if (strcasecmp(DHCPCOP_NAME(j),
1085 			    dsp[i].co_key) == 0) {
1086 				DHCPCOP_PRES(j) = B_TRUE;
1087 				if (DHCPCOP_VINIT(j, dsp[i].co_value))
1088 					break;
1089 				else {
1090 					(void) fprintf(stderr, gettext(
1091 					    "Invalid value for option: %s\n"),
1092 					    DHCPCOP_NAME(j));
1093 					return (EINVAL);
1094 				}
1095 			}
1096 		}
1097 	}
1098 
1099 	return (0);
1100 }
1101 
1102 /*
1103  * monitor_client: worker thread from pool created for each network.
1104  * We loop through and process one packet. Relay agent tasks are handled by
1105  * the per-interface threads, thus we should only be dealing with bootp/dhcp
1106  * server bound packets here.
1107  *
1108  * The worker thread treats the client packet lists as
1109  * "stacks", or FIFO objects. We do this so that we get
1110  * the latest, equivalent request from the client before
1111  * responding, thus keeping the chance of responding to
1112  * moldy requests to an absolute minimum.
1113  *
1114  * Performance: a pool of threads are used, to avoid thread startup/teardown.
1115  * Per-interface threads keep track of clients who cannot be serviced
1116  * due to a lack of threads. After completing the current request, threads
1117  * look for other work to do, before suspending and waiting to be
1118  * continued when work is available.
1119  */
1120 void *
monitor_client(void * arg)1121 monitor_client(void *arg)
1122 {
1123 	dsvc_thr_t	*thrp = (dsvc_thr_t *)arg;
1124 	dsvc_clnt_t	*pcd;
1125 	dsvc_dnet_t	*pnd;
1126 	dsvc_pendclnt_t	*workp;
1127 	IF		*ifp;
1128 	PKT_LIST	*plp = NULL;
1129 	int		nclients;
1130 	boolean_t	delete;
1131 	uint_t		flags = 0;
1132 
1133 	/*
1134 	 * Initialize variables.
1135 	 *
1136 	 * Due to a possible race between suspend and continue, we must
1137 	 * provide a positive indication that the thread has continued to
1138 	 * the per-interface thread.
1139 	 */
1140 	(void) mutex_lock(&thrp->thr_mtx);
1141 	pcd = thrp->thr_pcd;
1142 	thrp->thr_pcd = NULL;
1143 	(void) mutex_unlock(&thrp->thr_mtx);
1144 
1145 	ifp = pcd->ifp;
1146 	pnd = pcd->pnd;
1147 
1148 	/*
1149 	 * The per-interface thread leaves the client struct open,
1150 	 * so it cannot be garbage-collected in the interim.
1151 	 * Keep track of when we must release client structs.
1152 	 */
1153 	(void) mutex_lock(&pnd->thr_mtx);
1154 	nclients = pnd->nclients;
1155 	(void) mutex_unlock(&pnd->thr_mtx);
1156 
1157 	for (; (flags & DHCP_THR_EXITING) == 0; ) {
1158 		if (pcd == NULL) {
1159 			/*
1160 			 * No work. Place thread struct on free list
1161 			 * if it isn't already, and suspend
1162 			 * until new work is available.
1163 			 */
1164 			(void) mutex_lock(&thrp->thr_mtx);
1165 			if ((thrp->thr_flags & DHCP_THR_LIST) == 0) {
1166 				thrp->thr_flags |= DHCP_THR_LIST;
1167 				thrp->thr_pcd = NULL;
1168 				thrp->thr_next = NULL;
1169 
1170 				(void) mutex_lock(&pnd->thr_mtx);
1171 				if (pnd->thrhead != NULL) {
1172 					pnd->thrtail->thr_next = thrp;
1173 				} else {
1174 					pnd->thrhead = thrp;
1175 				}
1176 				pnd->thrtail = thrp;
1177 				(void) mutex_unlock(&pnd->thr_mtx);
1178 			}
1179 
1180 			/* Wait for new work. */
1181 			(void) cond_wait(&thrp->thr_cv,  &thrp->thr_mtx);
1182 
1183 			/*
1184 			 * Resume with new client if any.
1185 			 */
1186 			pcd = thrp->thr_pcd;
1187 			thrp->thr_pcd = NULL;
1188 			flags = thrp->thr_flags;
1189 			(void) mutex_unlock(&thrp->thr_mtx);
1190 			continue;
1191 		}
1192 
1193 		(void) mutex_lock(&pcd->pkt_mtx);
1194 		/*
1195 		 * Remove the first packet from the list
1196 		 */
1197 		plp = pcd->pkthead;
1198 		if (plp != NULL) {
1199 
1200 			detach_plp(pcd, plp);
1201 			pcd->pending--;
1202 
1203 			/*
1204 			 * See if there's a later one
1205 			 * exchanging this plp for that one.
1206 			 */
1207 			plp = refresh_pktlist(pcd, plp);
1208 		}
1209 		(void) mutex_unlock(&pcd->pkt_mtx);
1210 
1211 		(void) mutex_lock(&pcd->pcd_mtx);
1212 		if (plp == NULL || (pcd->flags & DHCP_PCD_CLOSING) != 0) {
1213 
1214 			if (plp) {
1215 				free_plp(plp); /* Free the packet. */
1216 				plp = NULL;
1217 			}
1218 
1219 			/*
1220 			 * No work remaining for this client. Release,
1221 			 * and check for other deferred clients on the
1222 			 * per net work list.
1223 			 */
1224 			pcd->flags &= ~DHCP_PCD_WORK;
1225 
1226 			/*
1227 			 * Housekeeping: delete pcd immediately if above
1228 			 * threshold and no offer has been made, or offer
1229 			 * has been completed. Only perform deletion if no
1230 			 * other thread has.
1231 			 */
1232 			delete = B_FALSE;
1233 			if (max_clients != -1 &&
1234 			    (pcd->flags & DHCP_PCD_CLOSING) == 0) {
1235 				if (nclients >=
1236 				    max_clients - DHCP_MINFREE_CLIENTS &&
1237 				    pcd->off_ip.s_addr == htonl(INADDR_ANY)) {
1238 
1239 					/* Remove clients without offers. */
1240 					pcd->flags |= DHCP_PCD_CLOSING;
1241 					delete = B_TRUE;
1242 
1243 				} else if (nclients > max_clients/2 &&
1244 				    (pcd->state == ACK ||
1245 				    (pcd->state == REQUEST &&
1246 				    pcd->off_ip.s_addr == htonl(INADDR_ANY)))) {
1247 
1248 					/* Remove completed clients. */
1249 					pcd->flags |= DHCP_PCD_CLOSING;
1250 					delete = B_TRUE;
1251 
1252 				} else if (pcd->state == RELEASE ||
1253 				    pcd->state == DECLINE) {
1254 
1255 					/* Remove freed clients. */
1256 					pcd->flags |= DHCP_PCD_CLOSING;
1257 					delete = B_TRUE;
1258 				}
1259 			}
1260 			pcd->clnt_thread = NULL;
1261 			(void) mutex_unlock(&pcd->pcd_mtx);
1262 
1263 			/* Close the client. */
1264 			close_clnt(pcd, delete);
1265 			pcd = NULL;
1266 
1267 			/*
1268 			 * Remove next deferred work from list.
1269 			 */
1270 			workp = NULL;
1271 			(void) mutex_lock(&pnd->thr_mtx);
1272 			nclients = pnd->nclients;
1273 			workp = pnd->workhead;
1274 			if (workp &&
1275 			    (pnd->workhead = pnd->workhead->pnd_next) == NULL)
1276 				pnd->worktail = NULL;
1277 			(void) mutex_unlock(&pnd->thr_mtx);
1278 
1279 			if (workp != NULL) {
1280 				/* See if the deferred client still exists. */
1281 				if (open_clnt(pnd, &pcd, workp->pnd_cid,
1282 				    workp->pnd_cid_len, B_TRUE) != DSVC_SUCCESS)
1283 					pcd = NULL;
1284 				if (pcd == NULL) {
1285 					free(workp);
1286 					continue;
1287 				}
1288 
1289 				(void) mutex_lock(&pcd->pcd_mtx);
1290 				/* Check if it needs a worker thread. */
1291 				if (pcd->clnt_thread == NULL &&
1292 				    (pcd->flags & DHCP_PCD_WORK) != 0 &&
1293 				    (pcd->flags & DHCP_PCD_CLOSING) == 0) {
1294 					/* Found a valid client. Restart. */
1295 					pcd->clnt_thread = thrp;
1296 					(void) mutex_unlock(&pcd->pcd_mtx);
1297 					ifp = pcd->ifp;
1298 					free(workp);
1299 					continue;
1300 				}
1301 				(void) mutex_unlock(&pcd->pcd_mtx);
1302 				close_clnt(pcd, B_FALSE);
1303 				pcd = NULL;
1304 				free(workp);
1305 			}
1306 			continue;
1307 		}
1308 		(void) mutex_unlock(&pcd->pcd_mtx);
1309 
1310 		/*
1311 		 * Based on the packet type, process accordingly.
1312 		 */
1313 		if (plp->pkt->op == BOOTREQUEST) {
1314 			if (plp->opts[CD_DHCP_TYPE]) {
1315 				/* DHCP packet */
1316 				dhcp(pcd, plp);
1317 			} else {
1318 				/* BOOTP packet */
1319 				if (!bootp_compat) {
1320 					dhcpmsg(LOG_INFO, "BOOTP request "
1321 					    "received on interface: %s "
1322 					    "ignored.\n", ifp->nm);
1323 				} else {
1324 					bootp(pcd, plp);
1325 				}
1326 			}
1327 		}
1328 		if (plp != NULL) {
1329 			free_plp(plp); /* Free the packet. */
1330 			plp = NULL;
1331 		}
1332 
1333 		(void) mutex_lock(&ifp->ifp_mtx);
1334 		ifp->processed++;
1335 		(void) mutex_unlock(&ifp->ifp_mtx);
1336 	}
1337 
1338 	/* Free the packet. */
1339 	if (plp != NULL)
1340 		free_plp(plp);
1341 
1342 	/* Release the client structure. */
1343 	if (pcd != NULL) {
1344 		(void) mutex_lock(&pcd->pcd_mtx);
1345 		pcd->flags &= ~DHCP_PCD_WORK;
1346 		pcd->clnt_thread = NULL;
1347 		(void) mutex_unlock(&pcd->pcd_mtx);
1348 
1349 		close_clnt(pcd, B_FALSE);
1350 	}
1351 
1352 	/* Release the thread reference in pernet structure. */
1353 	if (pnd != NULL) {
1354 		(void) mutex_lock(&pnd->thr_mtx);
1355 		pnd->nthreads--;
1356 		(void) cond_signal(&pnd->thr_cv);
1357 		(void) mutex_unlock(&pnd->thr_mtx);
1358 	}
1359 
1360 	return (NULL);
1361 }
1362