10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*1914Scasper * Common Development and Distribution License (the "License"). 6*1914Scasper * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*1914Scasper * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate #include <stdlib.h> 300Sstevel@tonic-gate #include <assert.h> 310Sstevel@tonic-gate #include <errno.h> 320Sstevel@tonic-gate #include <locale.h> 330Sstevel@tonic-gate #include <string.h> 340Sstevel@tonic-gate #include <unistd.h> 350Sstevel@tonic-gate #include <signal.h> 360Sstevel@tonic-gate #include <stdio.h> 37*1914Scasper #include <stdio_ext.h> 380Sstevel@tonic-gate #include <dhcp_hostconf.h> 390Sstevel@tonic-gate #include <dhcp_symbol.h> 400Sstevel@tonic-gate #include <dhcpagent_ipc.h> 410Sstevel@tonic-gate #include <dhcpmsg.h> 420Sstevel@tonic-gate #include <netinet/dhcp.h> 430Sstevel@tonic-gate 440Sstevel@tonic-gate #include "async.h" 450Sstevel@tonic-gate #include "agent.h" 460Sstevel@tonic-gate #include "script_handler.h" 470Sstevel@tonic-gate #include "util.h" 480Sstevel@tonic-gate #include "class_id.h" 490Sstevel@tonic-gate #include "states.h" 500Sstevel@tonic-gate #include "packet.h" 510Sstevel@tonic-gate 520Sstevel@tonic-gate #ifndef TEXT_DOMAIN 530Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" 540Sstevel@tonic-gate #endif 550Sstevel@tonic-gate 560Sstevel@tonic-gate iu_timer_id_t inactivity_id; 570Sstevel@tonic-gate int class_id_len = 0; 580Sstevel@tonic-gate char *class_id; 590Sstevel@tonic-gate iu_eh_t *eh; 600Sstevel@tonic-gate iu_tq_t *tq; 610Sstevel@tonic-gate pid_t grandparent; 620Sstevel@tonic-gate 630Sstevel@tonic-gate static boolean_t shutdown_started = B_FALSE; 640Sstevel@tonic-gate static boolean_t do_adopt = B_FALSE; 650Sstevel@tonic-gate static unsigned int debug_level = 0; 660Sstevel@tonic-gate static iu_eh_callback_t accept_event, ipc_event; 670Sstevel@tonic-gate 680Sstevel@tonic-gate /* 690Sstevel@tonic-gate * The ipc_cmd_allowed[] table indicates which IPC commands are allowed in 700Sstevel@tonic-gate * which states; a non-zero value indicates the command is permitted. 710Sstevel@tonic-gate * 720Sstevel@tonic-gate * START is permitted if the interface is fresh, or if we are in the process 730Sstevel@tonic-gate * of trying to obtain a lease (as a convenience to save the administrator 740Sstevel@tonic-gate * from having to do an explicit DROP). EXTEND, RELEASE, and GET_TAG require 750Sstevel@tonic-gate * a lease to be obtained in order to make sense. INFORM is permitted if the 760Sstevel@tonic-gate * interface is fresh or has an INFORM in progress or previously done on it -- 770Sstevel@tonic-gate * otherwise a DROP or RELEASE is first required. PING and STATUS always make 780Sstevel@tonic-gate * sense and thus are always permitted, as is DROP in order to permit the 790Sstevel@tonic-gate * administrator to always bail out. 800Sstevel@tonic-gate */ 810Sstevel@tonic-gate static int ipc_cmd_allowed[DHCP_NSTATES][DHCP_NIPC] = { 820Sstevel@tonic-gate /* D E P R S S I G */ 830Sstevel@tonic-gate /* R X I E T T N E */ 840Sstevel@tonic-gate /* O T N L A A F T */ 850Sstevel@tonic-gate /* P E G E R T O _ */ 860Sstevel@tonic-gate /* . N . A T U R T */ 870Sstevel@tonic-gate /* . D . S . S M A */ 880Sstevel@tonic-gate /* . . . E . . . G */ 890Sstevel@tonic-gate /* INIT */ { 1, 0, 1, 0, 1, 1, 1, 0 }, 900Sstevel@tonic-gate /* SELECTING */ { 1, 0, 1, 0, 1, 1, 0, 0 }, 910Sstevel@tonic-gate /* REQUESTING */ { 1, 0, 1, 0, 1, 1, 0, 0 }, 920Sstevel@tonic-gate /* BOUND */ { 1, 1, 1, 1, 0, 1, 0, 1 }, 930Sstevel@tonic-gate /* RENEWING */ { 1, 1, 1, 1, 0, 1, 0, 1 }, 940Sstevel@tonic-gate /* REBINDING */ { 1, 1, 1, 1, 0, 1, 0, 1 }, 950Sstevel@tonic-gate /* INFORMATION */ { 1, 0, 1, 0, 0, 1, 1, 1 }, 960Sstevel@tonic-gate /* INIT_REBOOT */ { 1, 0, 1, 0, 1, 1, 0, 0 }, 970Sstevel@tonic-gate /* ADOPTING */ { 1, 0, 1, 0, 0, 1, 0, 0 }, 980Sstevel@tonic-gate /* INFORM_SENT */ { 1, 0, 1, 0, 0, 1, 1, 0 } 990Sstevel@tonic-gate }; 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate int 1020Sstevel@tonic-gate main(int argc, char **argv) 1030Sstevel@tonic-gate { 1040Sstevel@tonic-gate boolean_t is_daemon = B_TRUE; 1050Sstevel@tonic-gate boolean_t is_verbose = B_FALSE; 1060Sstevel@tonic-gate int ipc_fd; 1070Sstevel@tonic-gate int c; 1080Sstevel@tonic-gate struct rlimit rl; 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate /* 1110Sstevel@tonic-gate * -l is ignored for compatibility with old agent. 1120Sstevel@tonic-gate */ 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate while ((c = getopt(argc, argv, "vd:l:fa")) != EOF) { 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate switch (c) { 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate case 'a': 1190Sstevel@tonic-gate do_adopt = B_TRUE; 1200Sstevel@tonic-gate grandparent = getpid(); 1210Sstevel@tonic-gate break; 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate case 'd': 1240Sstevel@tonic-gate debug_level = strtoul(optarg, NULL, 0); 1250Sstevel@tonic-gate break; 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate case 'f': 1280Sstevel@tonic-gate is_daemon = B_FALSE; 1290Sstevel@tonic-gate break; 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate case 'v': 1320Sstevel@tonic-gate is_verbose = B_TRUE; 1330Sstevel@tonic-gate break; 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate case '?': 1360Sstevel@tonic-gate (void) fprintf(stderr, "usage: %s [-a] [-d n] [-f] [-v]" 1370Sstevel@tonic-gate "\n", argv[0]); 1380Sstevel@tonic-gate return (EXIT_FAILURE); 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate default: 1410Sstevel@tonic-gate break; 1420Sstevel@tonic-gate } 1430Sstevel@tonic-gate } 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 1460Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate if (geteuid() != 0) { 1490Sstevel@tonic-gate dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level); 1500Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "must be super-user"); 1510Sstevel@tonic-gate dhcpmsg_fini(); 1520Sstevel@tonic-gate return (EXIT_FAILURE); 1530Sstevel@tonic-gate } 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate if (is_daemon && daemonize() == 0) { 1560Sstevel@tonic-gate dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level); 1570Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot become daemon, exiting"); 1580Sstevel@tonic-gate dhcpmsg_fini(); 1590Sstevel@tonic-gate return (EXIT_FAILURE); 1600Sstevel@tonic-gate } 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate dhcpmsg_init(argv[0], is_daemon, is_verbose, debug_level); 1630Sstevel@tonic-gate (void) atexit(dhcpmsg_fini); 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate tq = iu_tq_create(); 1660Sstevel@tonic-gate eh = iu_eh_create(); 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate if (eh == NULL || tq == NULL) { 1690Sstevel@tonic-gate errno = ENOMEM; 1700Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot create timer queue or event handler"); 1710Sstevel@tonic-gate return (EXIT_FAILURE); 1720Sstevel@tonic-gate } 1730Sstevel@tonic-gate 1740Sstevel@tonic-gate /* 1750Sstevel@tonic-gate * ignore most signals that could be reasonably generated. 1760Sstevel@tonic-gate */ 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate (void) signal(SIGTERM, graceful_shutdown); 1790Sstevel@tonic-gate (void) signal(SIGQUIT, graceful_shutdown); 1800Sstevel@tonic-gate (void) signal(SIGPIPE, SIG_IGN); 1810Sstevel@tonic-gate (void) signal(SIGUSR1, SIG_IGN); 1820Sstevel@tonic-gate (void) signal(SIGUSR2, SIG_IGN); 1830Sstevel@tonic-gate (void) signal(SIGINT, SIG_IGN); 1840Sstevel@tonic-gate (void) signal(SIGHUP, SIG_IGN); 1850Sstevel@tonic-gate (void) signal(SIGCHLD, SIG_IGN); 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate /* 1880Sstevel@tonic-gate * upon SIGTHAW we need to refresh any non-infinite leases. 1890Sstevel@tonic-gate */ 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate (void) iu_eh_register_signal(eh, SIGTHAW, refresh_ifslist, NULL); 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate class_id = get_class_id(); 1940Sstevel@tonic-gate if (class_id != NULL) 1950Sstevel@tonic-gate class_id_len = strlen(class_id); 1960Sstevel@tonic-gate else 1970Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "get_class_id failed, continuing " 1980Sstevel@tonic-gate "with no vendor class id"); 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate /* 2010Sstevel@tonic-gate * the inactivity timer is enabled any time there are no 2020Sstevel@tonic-gate * interfaces under DHCP control. if DHCP_INACTIVITY_WAIT 2030Sstevel@tonic-gate * seconds transpire without an interface under DHCP control, 2040Sstevel@tonic-gate * the agent shuts down. 2050Sstevel@tonic-gate */ 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT, 2080Sstevel@tonic-gate inactivity_shutdown, NULL); 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate /* 2110Sstevel@tonic-gate * max out the number available descriptors, just in case.. 2120Sstevel@tonic-gate */ 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate rl.rlim_cur = RLIM_INFINITY; 2150Sstevel@tonic-gate rl.rlim_max = RLIM_INFINITY; 2160Sstevel@tonic-gate if (setrlimit(RLIMIT_NOFILE, &rl) == -1) 2170Sstevel@tonic-gate dhcpmsg(MSG_ERR, "setrlimit failed"); 2180Sstevel@tonic-gate 219*1914Scasper (void) enable_extended_FILE_stdio(-1, -1); 220*1914Scasper 2210Sstevel@tonic-gate /* 2220Sstevel@tonic-gate * create the ipc channel that the agent will listen for 2230Sstevel@tonic-gate * requests on, and register it with the event handler so that 2240Sstevel@tonic-gate * `accept_event' will be called back. 2250Sstevel@tonic-gate */ 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate switch (dhcp_ipc_init(&ipc_fd)) { 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate case 0: 2300Sstevel@tonic-gate break; 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate case DHCP_IPC_E_BIND: 2330Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "dhcp_ipc_init: cannot bind to port " 2340Sstevel@tonic-gate "%i (agent already running?)", IPPORT_DHCPAGENT); 2350Sstevel@tonic-gate return (EXIT_FAILURE); 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate default: 2380Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "dhcp_ipc_init failed"); 2390Sstevel@tonic-gate return (EXIT_FAILURE); 2400Sstevel@tonic-gate } 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate if (iu_register_event(eh, ipc_fd, POLLIN, accept_event, 0) == -1) { 2430Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot register ipc fd for messages"); 2440Sstevel@tonic-gate return (EXIT_FAILURE); 2450Sstevel@tonic-gate } 2460Sstevel@tonic-gate 2470Sstevel@tonic-gate /* 2480Sstevel@tonic-gate * if the -a (adopt) option was specified, try to adopt the 2490Sstevel@tonic-gate * kernel-managed interface before we start. Our grandparent 2500Sstevel@tonic-gate * will be waiting for us to finish this, so signal him when 2510Sstevel@tonic-gate * we're done. 2520Sstevel@tonic-gate */ 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate if (do_adopt) { 2550Sstevel@tonic-gate int result; 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate result = dhcp_adopt(); 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate if (grandparent != (pid_t)0) { 2600Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "adoption complete, signalling " 2610Sstevel@tonic-gate "parent (%i) to exit.", grandparent); 2620Sstevel@tonic-gate (void) kill(grandparent, SIGALRM); 2630Sstevel@tonic-gate } 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate if (result == 0) 2660Sstevel@tonic-gate return (EXIT_FAILURE); 2670Sstevel@tonic-gate } 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate /* 2700Sstevel@tonic-gate * enter the main event loop; this is where all the real work 2710Sstevel@tonic-gate * takes place (through registering events and scheduling timers). 2720Sstevel@tonic-gate * this function only returns when the agent is shutting down. 2730Sstevel@tonic-gate */ 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate switch (iu_handle_events(eh, tq)) { 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate case -1: 2780Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "iu_handle_events exited abnormally"); 2790Sstevel@tonic-gate break; 2800Sstevel@tonic-gate 2810Sstevel@tonic-gate case DHCP_REASON_INACTIVITY: 2820Sstevel@tonic-gate dhcpmsg(MSG_INFO, "no interfaces to manage, shutting down..."); 2830Sstevel@tonic-gate break; 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate case DHCP_REASON_TERMINATE: 2860Sstevel@tonic-gate dhcpmsg(MSG_INFO, "received SIGTERM, shutting down..."); 2870Sstevel@tonic-gate break; 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate case DHCP_REASON_SIGNAL: 2900Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "received unexpected signal, shutting " 2910Sstevel@tonic-gate "down..."); 2920Sstevel@tonic-gate break; 2930Sstevel@tonic-gate } 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate (void) iu_eh_unregister_signal(eh, SIGTHAW, NULL); 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate iu_eh_destroy(eh); 2980Sstevel@tonic-gate iu_tq_destroy(tq); 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate return (EXIT_SUCCESS); 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate /* 3040Sstevel@tonic-gate * drain_script(): event loop callback during shutdown 3050Sstevel@tonic-gate * 3060Sstevel@tonic-gate * input: eh_t *: unused 3070Sstevel@tonic-gate * void *: unused 3080Sstevel@tonic-gate * output: boolean_t: B_TRUE if event loop should exit; B_FALSE otherwise 3090Sstevel@tonic-gate */ 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate /* ARGSUSED */ 3120Sstevel@tonic-gate boolean_t 3130Sstevel@tonic-gate drain_script(iu_eh_t *ehp, void *arg) 3140Sstevel@tonic-gate { 3150Sstevel@tonic-gate if (shutdown_started == B_FALSE) { 3160Sstevel@tonic-gate shutdown_started = B_TRUE; 3170Sstevel@tonic-gate if (do_adopt == B_FALSE) /* see 4291141 */ 3180Sstevel@tonic-gate nuke_ifslist(B_TRUE); 3190Sstevel@tonic-gate } 3200Sstevel@tonic-gate return (script_count == 0); 3210Sstevel@tonic-gate } 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate /* 3240Sstevel@tonic-gate * accept_event(): accepts a new connection on the ipc socket and registers 3250Sstevel@tonic-gate * to receive its messages with the event handler 3260Sstevel@tonic-gate * 3270Sstevel@tonic-gate * input: iu_eh_t *: unused 3280Sstevel@tonic-gate * int: the file descriptor in the iu_eh_t * the connection came in on 3290Sstevel@tonic-gate * (other arguments unused) 3300Sstevel@tonic-gate * output: void 3310Sstevel@tonic-gate */ 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate /* ARGSUSED */ 3340Sstevel@tonic-gate static void 3350Sstevel@tonic-gate accept_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 3360Sstevel@tonic-gate { 3370Sstevel@tonic-gate int client_fd; 3380Sstevel@tonic-gate int is_priv; 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate if (dhcp_ipc_accept(fd, &client_fd, &is_priv) != 0) { 3410Sstevel@tonic-gate dhcpmsg(MSG_ERR, "accept_event: accept on ipc socket"); 3420Sstevel@tonic-gate return; 3430Sstevel@tonic-gate } 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate if (iu_register_event(eh, client_fd, POLLIN, ipc_event, 3460Sstevel@tonic-gate (void *)is_priv) == -1) { 3470Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "accept_event: cannot register ipc socket " 3480Sstevel@tonic-gate "for callback"); 3490Sstevel@tonic-gate } 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate 3520Sstevel@tonic-gate /* 3530Sstevel@tonic-gate * ipc_event(): processes incoming ipc requests 3540Sstevel@tonic-gate * 3550Sstevel@tonic-gate * input: iu_eh_t *: unused 3560Sstevel@tonic-gate * int: the file descriptor in the iu_eh_t * the request came in on 3570Sstevel@tonic-gate * short: unused 3580Sstevel@tonic-gate * iu_event_id_t: unused 3590Sstevel@tonic-gate * void *: indicates whether the request is from a privileged client 3600Sstevel@tonic-gate * output: void 3610Sstevel@tonic-gate */ 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate /* ARGSUSED */ 3640Sstevel@tonic-gate static void 3650Sstevel@tonic-gate ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 3660Sstevel@tonic-gate { 3670Sstevel@tonic-gate dhcp_ipc_request_t *request; 3680Sstevel@tonic-gate struct ifslist *ifsp, *primary_ifsp; 3690Sstevel@tonic-gate int error, is_priv = (int)arg; 3700Sstevel@tonic-gate PKT_LIST *plp[2]; 3710Sstevel@tonic-gate dhcp_ipc_type_t cmd; 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate (void) iu_unregister_event(eh, id, NULL); 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate if (dhcp_ipc_recv_request(fd, &request, DHCP_IPC_REQUEST_WAIT) != 0) { 3760Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "ipc_event: dhcp_ipc_recv_request failed"); 3770Sstevel@tonic-gate (void) dhcp_ipc_close(fd); 3780Sstevel@tonic-gate return; 3790Sstevel@tonic-gate } 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate cmd = DHCP_IPC_CMD(request->message_type); 3820Sstevel@tonic-gate if (cmd >= DHCP_NIPC) { 3830Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_CMD_UNKNOWN, &fd); 3840Sstevel@tonic-gate return; 3850Sstevel@tonic-gate } 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate /* return EPERM for any of the privileged actions */ 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate if (!is_priv) { 3900Sstevel@tonic-gate switch (cmd) { 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate case DHCP_STATUS: 3930Sstevel@tonic-gate case DHCP_PING: 3940Sstevel@tonic-gate case DHCP_GET_TAG: 3950Sstevel@tonic-gate break; 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate default: 3980Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "ipc_event: privileged ipc " 3990Sstevel@tonic-gate "command (%i) attempted on %s", cmd, 4000Sstevel@tonic-gate request->ifname); 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_PERM, &fd); 4030Sstevel@tonic-gate return; 4040Sstevel@tonic-gate } 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate /* 4080Sstevel@tonic-gate * try to locate the ifs associated with this command. if the 4090Sstevel@tonic-gate * command is DHCP_START or DHCP_INFORM, then if there isn't 4100Sstevel@tonic-gate * an ifs already, make one (there may already be one from a 4110Sstevel@tonic-gate * previous failed attempt to START or INFORM). otherwise, 4120Sstevel@tonic-gate * verify the interface is still valid. 4130Sstevel@tonic-gate */ 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate ifsp = lookup_ifs(request->ifname); 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate switch (cmd) { 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate case DHCP_START: /* FALLTHRU */ 4200Sstevel@tonic-gate case DHCP_INFORM: 4210Sstevel@tonic-gate /* 4220Sstevel@tonic-gate * it's possible that the interface already exists, but 4230Sstevel@tonic-gate * has been abandoned. usually in those cases we should 4240Sstevel@tonic-gate * return DHCP_IPC_E_UNKIF, but that makes little sense 4250Sstevel@tonic-gate * in the case of "start" or "inform", so just ignore 4260Sstevel@tonic-gate * the abandoned interface and start over anew. 4270Sstevel@tonic-gate */ 4280Sstevel@tonic-gate 4290Sstevel@tonic-gate if (ifsp != NULL && verify_ifs(ifsp) == 0) 4300Sstevel@tonic-gate ifsp = NULL; 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate /* 4330Sstevel@tonic-gate * as part of initializing the ifs, insert_ifs() 4340Sstevel@tonic-gate * creates a DLPI stream at ifsp->if_dlpi_fd. 4350Sstevel@tonic-gate */ 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate if (ifsp == NULL) { 4380Sstevel@tonic-gate ifsp = insert_ifs(request->ifname, B_FALSE, &error); 4390Sstevel@tonic-gate if (ifsp == NULL) { 4400Sstevel@tonic-gate send_error_reply(request, error, &fd); 4410Sstevel@tonic-gate return; 4420Sstevel@tonic-gate } 4430Sstevel@tonic-gate } 4440Sstevel@tonic-gate break; 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate default: 4470Sstevel@tonic-gate if (ifsp == NULL) { 4480Sstevel@tonic-gate if (request->ifname[0] == '\0') 4490Sstevel@tonic-gate error = DHCP_IPC_E_NOPRIMARY; 4500Sstevel@tonic-gate else 4510Sstevel@tonic-gate error = DHCP_IPC_E_UNKIF; 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate send_error_reply(request, error, &fd); 4540Sstevel@tonic-gate return; 4550Sstevel@tonic-gate } 4560Sstevel@tonic-gate break; 4570Sstevel@tonic-gate } 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate if (verify_ifs(ifsp) == 0) { 4600Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_UNKIF, &fd); 4610Sstevel@tonic-gate return; 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate if (ifsp->if_dflags & DHCP_IF_BOOTP) { 4650Sstevel@tonic-gate switch (cmd) { 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate case DHCP_EXTEND: 4680Sstevel@tonic-gate case DHCP_RELEASE: 4690Sstevel@tonic-gate case DHCP_INFORM: 4700Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_BOOTP, &fd); 4710Sstevel@tonic-gate return; 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate default: 4740Sstevel@tonic-gate break; 4750Sstevel@tonic-gate } 4760Sstevel@tonic-gate } 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate /* 4790Sstevel@tonic-gate * verify that the interface is in a state which will allow the 4800Sstevel@tonic-gate * command. we do this up front so that we can return an error 4810Sstevel@tonic-gate * *before* needlessly cancelling an in-progress transaction. 4820Sstevel@tonic-gate */ 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate if (!ipc_cmd_allowed[ifsp->if_state][cmd]) { 4850Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd); 4860Sstevel@tonic-gate return; 4870Sstevel@tonic-gate } 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate if ((request->message_type & DHCP_PRIMARY) && is_priv) { 4900Sstevel@tonic-gate if ((primary_ifsp = lookup_ifs("")) != NULL) 4910Sstevel@tonic-gate primary_ifsp->if_dflags &= ~DHCP_IF_PRIMARY; 4920Sstevel@tonic-gate ifsp->if_dflags |= DHCP_IF_PRIMARY; 4930Sstevel@tonic-gate } 4940Sstevel@tonic-gate 4950Sstevel@tonic-gate /* 4960Sstevel@tonic-gate * current design dictates that there can be only one 4970Sstevel@tonic-gate * outstanding transaction per interface -- this simplifies 4980Sstevel@tonic-gate * the code considerably and also fits well with RFC2131. 4990Sstevel@tonic-gate * it is worth classifying the different DHCP commands into 5000Sstevel@tonic-gate * synchronous (those which we will handle now and be done 5010Sstevel@tonic-gate * with) and asynchronous (those which require transactions 5020Sstevel@tonic-gate * and will be completed at an indeterminate time in the 5030Sstevel@tonic-gate * future): 5040Sstevel@tonic-gate * 5050Sstevel@tonic-gate * DROP: removes the agent's management of an interface. 5060Sstevel@tonic-gate * asynchronous as the script program may be invoked. 5070Sstevel@tonic-gate * 5080Sstevel@tonic-gate * PING: checks to see if the agent controls an interface. 5090Sstevel@tonic-gate * synchronous, since no packets need to be sent 5100Sstevel@tonic-gate * to the DHCP server. 5110Sstevel@tonic-gate * 5120Sstevel@tonic-gate * STATUS: returns information about the an interface. 5130Sstevel@tonic-gate * synchronous, since no packets need to be sent 5140Sstevel@tonic-gate * to the DHCP server. 5150Sstevel@tonic-gate * 5160Sstevel@tonic-gate * RELEASE: releases the agent's management of an interface 5170Sstevel@tonic-gate * and brings the interface down. asynchronous as 5180Sstevel@tonic-gate * the script program may be invoked. 5190Sstevel@tonic-gate * 5200Sstevel@tonic-gate * EXTEND: renews a lease. asynchronous, since the agent 5210Sstevel@tonic-gate * needs to wait for an ACK, etc. 5220Sstevel@tonic-gate * 5230Sstevel@tonic-gate * START: starts DHCP on an interface. asynchronous since 5240Sstevel@tonic-gate * the agent needs to wait for OFFERs, ACKs, etc. 5250Sstevel@tonic-gate * 5260Sstevel@tonic-gate * INFORM: obtains configuration parameters for an externally 5270Sstevel@tonic-gate * configured interface. asynchronous, since the 5280Sstevel@tonic-gate * agent needs to wait for an ACK. 5290Sstevel@tonic-gate * 5300Sstevel@tonic-gate * notice that EXTEND, INFORM, START, DROP and RELEASE are 5310Sstevel@tonic-gate * asynchronous. notice also that asynchronous commands may 5320Sstevel@tonic-gate * occur from within the agent -- for instance, the agent 5330Sstevel@tonic-gate * will need to do implicit EXTENDs to extend the lease. in 5340Sstevel@tonic-gate * order to make the code simpler, the following rules apply 5350Sstevel@tonic-gate * for asynchronous commands: 5360Sstevel@tonic-gate * 5370Sstevel@tonic-gate * there can only be one asynchronous command at a time per 5380Sstevel@tonic-gate * interface. the current asynchronous command is managed by 5390Sstevel@tonic-gate * the async_* api: async_start(), async_finish(), 5400Sstevel@tonic-gate * async_timeout(), async_cancel(), and async_pending(). 5410Sstevel@tonic-gate * async_start() starts management of a new asynchronous 5420Sstevel@tonic-gate * command on an interface, which should only be done after 5430Sstevel@tonic-gate * async_pending() is called to check that there are no 5440Sstevel@tonic-gate * pending asynchronous commands on that interface. when the 5450Sstevel@tonic-gate * command is completed, async_finish() should be called. all 5460Sstevel@tonic-gate * asynchronous commands have an associated timer, which calls 5470Sstevel@tonic-gate * async_timeout() when it times out. if async_timeout() 5480Sstevel@tonic-gate * decides that the asynchronous command should be cancelled 5490Sstevel@tonic-gate * (see below), it calls async_cancel() to attempt 5500Sstevel@tonic-gate * cancellation. 5510Sstevel@tonic-gate * 5520Sstevel@tonic-gate * asynchronous commands started by a user command have an 5530Sstevel@tonic-gate * associated ipc_action which provides the agent with 5540Sstevel@tonic-gate * information for how to get in touch with the user command 5550Sstevel@tonic-gate * when the action completes. these ipc_action records also 5560Sstevel@tonic-gate * have an associated timeout which may be infinite. 5570Sstevel@tonic-gate * ipc_action_start() should be called when starting an 5580Sstevel@tonic-gate * asynchronous command requested by a user, which sets up the 5590Sstevel@tonic-gate * timer and keeps track of the ipc information (file 5600Sstevel@tonic-gate * descriptor, request type). when the asynchronous command 5610Sstevel@tonic-gate * completes, ipc_action_finish() should be called to return a 5620Sstevel@tonic-gate * command status code to the user and close the ipc 5630Sstevel@tonic-gate * connection). if the command does not complete before the 5640Sstevel@tonic-gate * timer fires, ipc_action_timeout() is called which closes 5650Sstevel@tonic-gate * the ipc connection and returns DHCP_IPC_E_TIMEOUT to the 5660Sstevel@tonic-gate * user. note that independent of ipc_action_timeout(), 5670Sstevel@tonic-gate * ipc_action_finish() should be called. 5680Sstevel@tonic-gate * 5690Sstevel@tonic-gate * on a case-by-case basis, here is what happens (per interface): 5700Sstevel@tonic-gate * 5710Sstevel@tonic-gate * o when an asynchronous command is requested, then 5720Sstevel@tonic-gate * async_pending() is called to see if there is already 5730Sstevel@tonic-gate * an asynchronous event. if so, the command does not 5740Sstevel@tonic-gate * proceed, and if there is an associated ipc_action, 5750Sstevel@tonic-gate * the user command is sent DHCP_IPC_E_PEND. 5760Sstevel@tonic-gate * 5770Sstevel@tonic-gate * o otherwise, the the transaction is started with 5780Sstevel@tonic-gate * async_start(). if the transaction is on behalf 5790Sstevel@tonic-gate * of a user, ipc_action_start() is called to keep 5800Sstevel@tonic-gate * track of the ipc information and set up the 5810Sstevel@tonic-gate * ipc_action timer. 5820Sstevel@tonic-gate * 5830Sstevel@tonic-gate * o if the command completes normally and before a 5840Sstevel@tonic-gate * timeout fires, then async_finish() is called. 5850Sstevel@tonic-gate * if there was an associated ipc_action, 5860Sstevel@tonic-gate * ipc_action_finish() is called to complete it. 5870Sstevel@tonic-gate * 5880Sstevel@tonic-gate * o if the command fails before a timeout fires, then 5890Sstevel@tonic-gate * async_finish() is called, and the interface is 5900Sstevel@tonic-gate * is returned to a known state based on the command. 5910Sstevel@tonic-gate * if there was an associated ipc_action, 5920Sstevel@tonic-gate * ipc_action_finish() is called to complete it. 5930Sstevel@tonic-gate * 5940Sstevel@tonic-gate * o if the ipc_action timer fires before command 5950Sstevel@tonic-gate * completion, then DHCP_IPC_E_TIMEOUT is returned to 5960Sstevel@tonic-gate * the user. however, the transaction continues to 5970Sstevel@tonic-gate * be carried out asynchronously. 5980Sstevel@tonic-gate * 5990Sstevel@tonic-gate * o if async_timeout() fires before command completion, 6000Sstevel@tonic-gate * then if the command was internal to the agent, it 6010Sstevel@tonic-gate * is cancelled. otherwise, if it was a user command, 6020Sstevel@tonic-gate * then if the user is still waiting for the command 6030Sstevel@tonic-gate * to complete, the command continues and async_timeout() 6040Sstevel@tonic-gate * is rescheduled. 6050Sstevel@tonic-gate */ 6060Sstevel@tonic-gate 6070Sstevel@tonic-gate switch (cmd) { 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate case DHCP_DROP: /* FALLTHRU */ 6100Sstevel@tonic-gate case DHCP_RELEASE: /* FALLTHRU */ 6110Sstevel@tonic-gate case DHCP_EXTEND: /* FALLTHRU */ 6120Sstevel@tonic-gate case DHCP_INFORM: /* FALLTHRU */ 6130Sstevel@tonic-gate case DHCP_START: 6140Sstevel@tonic-gate /* 6150Sstevel@tonic-gate * if shutdown request has been received, send back an error. 6160Sstevel@tonic-gate */ 6170Sstevel@tonic-gate if (shutdown_started) { 6180Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd); 6190Sstevel@tonic-gate return; 6200Sstevel@tonic-gate } 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate if (async_pending(ifsp)) { 6230Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_PEND, &fd); 6240Sstevel@tonic-gate return; 6250Sstevel@tonic-gate } 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate if (ipc_action_start(ifsp, request, fd) == 0) { 6280Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "ipc_event: ipc_action_start " 6290Sstevel@tonic-gate "failed for %s", ifsp->if_name); 6300Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_MEMORY, &fd); 6310Sstevel@tonic-gate return; 6320Sstevel@tonic-gate } 6330Sstevel@tonic-gate 6340Sstevel@tonic-gate if (async_start(ifsp, cmd, B_TRUE) == 0) { 6350Sstevel@tonic-gate ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 6360Sstevel@tonic-gate return; 6370Sstevel@tonic-gate } 6380Sstevel@tonic-gate break; 6390Sstevel@tonic-gate 6400Sstevel@tonic-gate default: 6410Sstevel@tonic-gate break; 6420Sstevel@tonic-gate } 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate switch (cmd) { 6450Sstevel@tonic-gate 6460Sstevel@tonic-gate case DHCP_DROP: 6470Sstevel@tonic-gate (void) script_start(ifsp, EVENT_DROP, dhcp_drop, NULL, NULL); 6480Sstevel@tonic-gate return; 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate case DHCP_EXTEND: 6510Sstevel@tonic-gate (void) dhcp_extending(ifsp); 6520Sstevel@tonic-gate break; 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate case DHCP_GET_TAG: { 6550Sstevel@tonic-gate dhcp_optnum_t optnum; 6560Sstevel@tonic-gate DHCP_OPT *opt = NULL; 6570Sstevel@tonic-gate boolean_t did_alloc = B_FALSE; 6580Sstevel@tonic-gate PKT_LIST *ack = ifsp->if_ack; 6590Sstevel@tonic-gate 6600Sstevel@tonic-gate /* 6610Sstevel@tonic-gate * verify the request makes sense. 6620Sstevel@tonic-gate */ 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate if (request->data_type != DHCP_TYPE_OPTNUM || 6650Sstevel@tonic-gate request->data_length != sizeof (dhcp_optnum_t)) { 6660Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_PROTO, &fd); 6670Sstevel@tonic-gate return; 6680Sstevel@tonic-gate } 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate (void) memcpy(&optnum, request->buffer, sizeof (dhcp_optnum_t)); 6710Sstevel@tonic-gate load_option: 6720Sstevel@tonic-gate switch (optnum.category) { 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate case DSYM_SITE: /* FALLTHRU */ 6750Sstevel@tonic-gate case DSYM_STANDARD: 6760Sstevel@tonic-gate if (optnum.code <= DHCP_LAST_OPT) 6770Sstevel@tonic-gate opt = ack->opts[optnum.code]; 6780Sstevel@tonic-gate break; 6790Sstevel@tonic-gate 6800Sstevel@tonic-gate case DSYM_VENDOR: 6810Sstevel@tonic-gate /* 6820Sstevel@tonic-gate * the test against VS_OPTION_START is broken up into 6830Sstevel@tonic-gate * two tests to avoid compiler warnings under intel. 6840Sstevel@tonic-gate */ 6850Sstevel@tonic-gate 6860Sstevel@tonic-gate if ((optnum.code > VS_OPTION_START || 6870Sstevel@tonic-gate optnum.code == VS_OPTION_START) && 6880Sstevel@tonic-gate optnum.code <= VS_OPTION_END) 6890Sstevel@tonic-gate opt = ack->vs[optnum.code]; 6900Sstevel@tonic-gate break; 6910Sstevel@tonic-gate 6920Sstevel@tonic-gate case DSYM_FIELD: 6930Sstevel@tonic-gate if (optnum.code + optnum.size > sizeof (PKT)) 6940Sstevel@tonic-gate break; 6950Sstevel@tonic-gate 6960Sstevel@tonic-gate /* + 2 to account for option code and length byte */ 6970Sstevel@tonic-gate opt = malloc(optnum.size + 2); 6980Sstevel@tonic-gate if (opt == NULL) { 6990Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_MEMORY, 7000Sstevel@tonic-gate &fd); 7010Sstevel@tonic-gate return; 7020Sstevel@tonic-gate } 7030Sstevel@tonic-gate 7040Sstevel@tonic-gate did_alloc = B_TRUE; 7050Sstevel@tonic-gate opt->len = optnum.size; 7060Sstevel@tonic-gate opt->code = optnum.code; 7070Sstevel@tonic-gate (void) memcpy(&opt->value, (caddr_t)ack->pkt + 7080Sstevel@tonic-gate opt->code, opt->len); 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate break; 7110Sstevel@tonic-gate 7120Sstevel@tonic-gate default: 7130Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_PROTO, &fd); 7140Sstevel@tonic-gate return; 7150Sstevel@tonic-gate } 7160Sstevel@tonic-gate 7170Sstevel@tonic-gate /* 7180Sstevel@tonic-gate * return the option payload, if there was one. the "+ 2" 7190Sstevel@tonic-gate * accounts for the option code number and length byte. 7200Sstevel@tonic-gate */ 7210Sstevel@tonic-gate 7220Sstevel@tonic-gate if (opt != NULL) { 7230Sstevel@tonic-gate send_data_reply(request, &fd, 0, DHCP_TYPE_OPTION, opt, 7240Sstevel@tonic-gate opt->len + 2); 7250Sstevel@tonic-gate 7260Sstevel@tonic-gate if (did_alloc) 7270Sstevel@tonic-gate free(opt); 7280Sstevel@tonic-gate return; 7290Sstevel@tonic-gate } else if (ack != ifsp->if_orig_ack) { 7300Sstevel@tonic-gate /* 7310Sstevel@tonic-gate * There wasn't any definition for the option in the 7320Sstevel@tonic-gate * current ack, so now retry with the original ack if 7330Sstevel@tonic-gate * the original ack is not the current ack. 7340Sstevel@tonic-gate */ 7350Sstevel@tonic-gate ack = ifsp->if_orig_ack; 7360Sstevel@tonic-gate goto load_option; 7370Sstevel@tonic-gate } 7380Sstevel@tonic-gate 7390Sstevel@tonic-gate /* 7400Sstevel@tonic-gate * note that an "okay" response is returned either in 7410Sstevel@tonic-gate * the case of an unknown option or a known option 7420Sstevel@tonic-gate * with no payload. this is okay (for now) since 7430Sstevel@tonic-gate * dhcpinfo checks whether an option is valid before 7440Sstevel@tonic-gate * ever performing ipc with the agent. 7450Sstevel@tonic-gate */ 7460Sstevel@tonic-gate 7470Sstevel@tonic-gate send_ok_reply(request, &fd); 7480Sstevel@tonic-gate return; 7490Sstevel@tonic-gate } 7500Sstevel@tonic-gate 7510Sstevel@tonic-gate case DHCP_INFORM: 7520Sstevel@tonic-gate dhcp_inform(ifsp); 7530Sstevel@tonic-gate /* next destination: dhcp_acknak() */ 7540Sstevel@tonic-gate return; 7550Sstevel@tonic-gate 7560Sstevel@tonic-gate case DHCP_PING: 7570Sstevel@tonic-gate if (ifsp->if_dflags & DHCP_IF_FAILED) 7580Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_FAILEDIF, &fd); 7590Sstevel@tonic-gate else 7600Sstevel@tonic-gate send_ok_reply(request, &fd); 7610Sstevel@tonic-gate return; 7620Sstevel@tonic-gate 7630Sstevel@tonic-gate case DHCP_RELEASE: 7640Sstevel@tonic-gate (void) script_start(ifsp, EVENT_RELEASE, dhcp_release, 7650Sstevel@tonic-gate "Finished with lease.", NULL); 7660Sstevel@tonic-gate return; 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate case DHCP_START: 7690Sstevel@tonic-gate assert(ifsp->if_state == INIT); 7700Sstevel@tonic-gate (void) canonize_ifs(ifsp); 7710Sstevel@tonic-gate 7720Sstevel@tonic-gate /* 7730Sstevel@tonic-gate * if we have a valid hostconf lying around, then jump 7740Sstevel@tonic-gate * into INIT_REBOOT. if it fails, we'll end up going 7750Sstevel@tonic-gate * through the whole selecting() procedure again. 7760Sstevel@tonic-gate */ 7770Sstevel@tonic-gate 7780Sstevel@tonic-gate error = read_hostconf(ifsp->if_name, plp, 2); 7790Sstevel@tonic-gate if (error != -1) { 7800Sstevel@tonic-gate ifsp->if_orig_ack = ifsp->if_ack = plp[0]; 7810Sstevel@tonic-gate if (error > 1) { 7820Sstevel@tonic-gate /* 7830Sstevel@tonic-gate * Return indicated we had more than one packet 7840Sstevel@tonic-gate * second one is the original ack. Older 7850Sstevel@tonic-gate * versions of the agent wrote only one ack 7860Sstevel@tonic-gate * to the file, we now keep both the first 7870Sstevel@tonic-gate * ack as well as the last one. 7880Sstevel@tonic-gate */ 7890Sstevel@tonic-gate ifsp->if_orig_ack = plp[1]; 7900Sstevel@tonic-gate } 7910Sstevel@tonic-gate dhcp_init_reboot(ifsp); 7920Sstevel@tonic-gate /* next destination: dhcp_acknak() */ 7930Sstevel@tonic-gate return; 7940Sstevel@tonic-gate } 7950Sstevel@tonic-gate 7960Sstevel@tonic-gate /* 7970Sstevel@tonic-gate * if not debugging, wait for a few seconds before 7980Sstevel@tonic-gate * going into SELECTING. 7990Sstevel@tonic-gate */ 8000Sstevel@tonic-gate 8010Sstevel@tonic-gate if (debug_level == 0) { 8020Sstevel@tonic-gate if (iu_schedule_timer_ms(tq, 8030Sstevel@tonic-gate lrand48() % DHCP_SELECT_WAIT, dhcp_start, ifsp) 8040Sstevel@tonic-gate != -1) { 8050Sstevel@tonic-gate hold_ifs(ifsp); 8060Sstevel@tonic-gate /* next destination: dhcp_start() */ 8070Sstevel@tonic-gate return; 8080Sstevel@tonic-gate } 8090Sstevel@tonic-gate } 8100Sstevel@tonic-gate 8110Sstevel@tonic-gate dhcp_selecting(ifsp); 8120Sstevel@tonic-gate /* next destination: dhcp_requesting() */ 8130Sstevel@tonic-gate return; 8140Sstevel@tonic-gate 8150Sstevel@tonic-gate case DHCP_STATUS: { 8160Sstevel@tonic-gate dhcp_status_t status; 8170Sstevel@tonic-gate 8180Sstevel@tonic-gate status.if_began = monosec_to_time(ifsp->if_curstart_monosec); 8190Sstevel@tonic-gate 8200Sstevel@tonic-gate if (ifsp->if_lease == DHCP_PERM) { 8210Sstevel@tonic-gate status.if_t1 = DHCP_PERM; 8220Sstevel@tonic-gate status.if_t2 = DHCP_PERM; 8230Sstevel@tonic-gate status.if_lease = DHCP_PERM; 8240Sstevel@tonic-gate } else { 8250Sstevel@tonic-gate status.if_t1 = status.if_began + ifsp->if_t1; 8260Sstevel@tonic-gate status.if_t2 = status.if_began + ifsp->if_t2; 8270Sstevel@tonic-gate status.if_lease = status.if_began + ifsp->if_lease; 8280Sstevel@tonic-gate } 8290Sstevel@tonic-gate 8300Sstevel@tonic-gate status.version = DHCP_STATUS_VER; 8310Sstevel@tonic-gate status.if_state = ifsp->if_state; 8320Sstevel@tonic-gate status.if_dflags = ifsp->if_dflags; 8330Sstevel@tonic-gate status.if_sent = ifsp->if_sent; 8340Sstevel@tonic-gate status.if_recv = ifsp->if_received; 8350Sstevel@tonic-gate status.if_bad_offers = ifsp->if_bad_offers; 8360Sstevel@tonic-gate 8370Sstevel@tonic-gate (void) strlcpy(status.if_name, ifsp->if_name, IFNAMSIZ); 8380Sstevel@tonic-gate 8390Sstevel@tonic-gate send_data_reply(request, &fd, 0, DHCP_TYPE_STATUS, &status, 8400Sstevel@tonic-gate sizeof (dhcp_status_t)); 8410Sstevel@tonic-gate return; 8420Sstevel@tonic-gate } 8430Sstevel@tonic-gate 8440Sstevel@tonic-gate default: 8450Sstevel@tonic-gate return; 8460Sstevel@tonic-gate } 8470Sstevel@tonic-gate } 848