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 }