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