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
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 *
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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 *
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 }