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