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 }