1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/types.h> 30*0Sstevel@tonic-gate #include <stdlib.h> 31*0Sstevel@tonic-gate #include <assert.h> 32*0Sstevel@tonic-gate #include <errno.h> 33*0Sstevel@tonic-gate #include <locale.h> 34*0Sstevel@tonic-gate #include <string.h> 35*0Sstevel@tonic-gate #include <unistd.h> 36*0Sstevel@tonic-gate #include <signal.h> 37*0Sstevel@tonic-gate #include <stdio.h> 38*0Sstevel@tonic-gate #include <dhcp_hostconf.h> 39*0Sstevel@tonic-gate #include <dhcp_symbol.h> 40*0Sstevel@tonic-gate #include <dhcpagent_ipc.h> 41*0Sstevel@tonic-gate #include <dhcpmsg.h> 42*0Sstevel@tonic-gate #include <netinet/dhcp.h> 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate #include "async.h" 45*0Sstevel@tonic-gate #include "agent.h" 46*0Sstevel@tonic-gate #include "script_handler.h" 47*0Sstevel@tonic-gate #include "util.h" 48*0Sstevel@tonic-gate #include "class_id.h" 49*0Sstevel@tonic-gate #include "states.h" 50*0Sstevel@tonic-gate #include "packet.h" 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gate #ifndef TEXT_DOMAIN 53*0Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" 54*0Sstevel@tonic-gate #endif 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate iu_timer_id_t inactivity_id; 57*0Sstevel@tonic-gate int class_id_len = 0; 58*0Sstevel@tonic-gate char *class_id; 59*0Sstevel@tonic-gate iu_eh_t *eh; 60*0Sstevel@tonic-gate iu_tq_t *tq; 61*0Sstevel@tonic-gate pid_t grandparent; 62*0Sstevel@tonic-gate 63*0Sstevel@tonic-gate static boolean_t shutdown_started = B_FALSE; 64*0Sstevel@tonic-gate static boolean_t do_adopt = B_FALSE; 65*0Sstevel@tonic-gate static unsigned int debug_level = 0; 66*0Sstevel@tonic-gate static iu_eh_callback_t accept_event, ipc_event; 67*0Sstevel@tonic-gate 68*0Sstevel@tonic-gate /* 69*0Sstevel@tonic-gate * The ipc_cmd_allowed[] table indicates which IPC commands are allowed in 70*0Sstevel@tonic-gate * which states; a non-zero value indicates the command is permitted. 71*0Sstevel@tonic-gate * 72*0Sstevel@tonic-gate * START is permitted if the interface is fresh, or if we are in the process 73*0Sstevel@tonic-gate * of trying to obtain a lease (as a convenience to save the administrator 74*0Sstevel@tonic-gate * from having to do an explicit DROP). EXTEND, RELEASE, and GET_TAG require 75*0Sstevel@tonic-gate * a lease to be obtained in order to make sense. INFORM is permitted if the 76*0Sstevel@tonic-gate * interface is fresh or has an INFORM in progress or previously done on it -- 77*0Sstevel@tonic-gate * otherwise a DROP or RELEASE is first required. PING and STATUS always make 78*0Sstevel@tonic-gate * sense and thus are always permitted, as is DROP in order to permit the 79*0Sstevel@tonic-gate * administrator to always bail out. 80*0Sstevel@tonic-gate */ 81*0Sstevel@tonic-gate static int ipc_cmd_allowed[DHCP_NSTATES][DHCP_NIPC] = { 82*0Sstevel@tonic-gate /* D E P R S S I G */ 83*0Sstevel@tonic-gate /* R X I E T T N E */ 84*0Sstevel@tonic-gate /* O T N L A A F T */ 85*0Sstevel@tonic-gate /* P E G E R T O _ */ 86*0Sstevel@tonic-gate /* . N . A T U R T */ 87*0Sstevel@tonic-gate /* . D . S . S M A */ 88*0Sstevel@tonic-gate /* . . . E . . . G */ 89*0Sstevel@tonic-gate /* INIT */ { 1, 0, 1, 0, 1, 1, 1, 0 }, 90*0Sstevel@tonic-gate /* SELECTING */ { 1, 0, 1, 0, 1, 1, 0, 0 }, 91*0Sstevel@tonic-gate /* REQUESTING */ { 1, 0, 1, 0, 1, 1, 0, 0 }, 92*0Sstevel@tonic-gate /* BOUND */ { 1, 1, 1, 1, 0, 1, 0, 1 }, 93*0Sstevel@tonic-gate /* RENEWING */ { 1, 1, 1, 1, 0, 1, 0, 1 }, 94*0Sstevel@tonic-gate /* REBINDING */ { 1, 1, 1, 1, 0, 1, 0, 1 }, 95*0Sstevel@tonic-gate /* INFORMATION */ { 1, 0, 1, 0, 0, 1, 1, 1 }, 96*0Sstevel@tonic-gate /* INIT_REBOOT */ { 1, 0, 1, 0, 1, 1, 0, 0 }, 97*0Sstevel@tonic-gate /* ADOPTING */ { 1, 0, 1, 0, 0, 1, 0, 0 }, 98*0Sstevel@tonic-gate /* INFORM_SENT */ { 1, 0, 1, 0, 0, 1, 1, 0 } 99*0Sstevel@tonic-gate }; 100*0Sstevel@tonic-gate 101*0Sstevel@tonic-gate int 102*0Sstevel@tonic-gate main(int argc, char **argv) 103*0Sstevel@tonic-gate { 104*0Sstevel@tonic-gate boolean_t is_daemon = B_TRUE; 105*0Sstevel@tonic-gate boolean_t is_verbose = B_FALSE; 106*0Sstevel@tonic-gate int ipc_fd; 107*0Sstevel@tonic-gate int c; 108*0Sstevel@tonic-gate struct rlimit rl; 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate /* 111*0Sstevel@tonic-gate * -l is ignored for compatibility with old agent. 112*0Sstevel@tonic-gate */ 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "vd:l:fa")) != EOF) { 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate switch (c) { 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate case 'a': 119*0Sstevel@tonic-gate do_adopt = B_TRUE; 120*0Sstevel@tonic-gate grandparent = getpid(); 121*0Sstevel@tonic-gate break; 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate case 'd': 124*0Sstevel@tonic-gate debug_level = strtoul(optarg, NULL, 0); 125*0Sstevel@tonic-gate break; 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate case 'f': 128*0Sstevel@tonic-gate is_daemon = B_FALSE; 129*0Sstevel@tonic-gate break; 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gate case 'v': 132*0Sstevel@tonic-gate is_verbose = B_TRUE; 133*0Sstevel@tonic-gate break; 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gate case '?': 136*0Sstevel@tonic-gate (void) fprintf(stderr, "usage: %s [-a] [-d n] [-f] [-v]" 137*0Sstevel@tonic-gate "\n", argv[0]); 138*0Sstevel@tonic-gate return (EXIT_FAILURE); 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gate default: 141*0Sstevel@tonic-gate break; 142*0Sstevel@tonic-gate } 143*0Sstevel@tonic-gate } 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 146*0Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate if (geteuid() != 0) { 149*0Sstevel@tonic-gate dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level); 150*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "must be super-user"); 151*0Sstevel@tonic-gate dhcpmsg_fini(); 152*0Sstevel@tonic-gate return (EXIT_FAILURE); 153*0Sstevel@tonic-gate } 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate if (is_daemon && daemonize() == 0) { 156*0Sstevel@tonic-gate dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level); 157*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot become daemon, exiting"); 158*0Sstevel@tonic-gate dhcpmsg_fini(); 159*0Sstevel@tonic-gate return (EXIT_FAILURE); 160*0Sstevel@tonic-gate } 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate dhcpmsg_init(argv[0], is_daemon, is_verbose, debug_level); 163*0Sstevel@tonic-gate (void) atexit(dhcpmsg_fini); 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate tq = iu_tq_create(); 166*0Sstevel@tonic-gate eh = iu_eh_create(); 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate if (eh == NULL || tq == NULL) { 169*0Sstevel@tonic-gate errno = ENOMEM; 170*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot create timer queue or event handler"); 171*0Sstevel@tonic-gate return (EXIT_FAILURE); 172*0Sstevel@tonic-gate } 173*0Sstevel@tonic-gate 174*0Sstevel@tonic-gate /* 175*0Sstevel@tonic-gate * ignore most signals that could be reasonably generated. 176*0Sstevel@tonic-gate */ 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate (void) signal(SIGTERM, graceful_shutdown); 179*0Sstevel@tonic-gate (void) signal(SIGQUIT, graceful_shutdown); 180*0Sstevel@tonic-gate (void) signal(SIGPIPE, SIG_IGN); 181*0Sstevel@tonic-gate (void) signal(SIGUSR1, SIG_IGN); 182*0Sstevel@tonic-gate (void) signal(SIGUSR2, SIG_IGN); 183*0Sstevel@tonic-gate (void) signal(SIGINT, SIG_IGN); 184*0Sstevel@tonic-gate (void) signal(SIGHUP, SIG_IGN); 185*0Sstevel@tonic-gate (void) signal(SIGCHLD, SIG_IGN); 186*0Sstevel@tonic-gate 187*0Sstevel@tonic-gate /* 188*0Sstevel@tonic-gate * upon SIGTHAW we need to refresh any non-infinite leases. 189*0Sstevel@tonic-gate */ 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate (void) iu_eh_register_signal(eh, SIGTHAW, refresh_ifslist, NULL); 192*0Sstevel@tonic-gate 193*0Sstevel@tonic-gate class_id = get_class_id(); 194*0Sstevel@tonic-gate if (class_id != NULL) 195*0Sstevel@tonic-gate class_id_len = strlen(class_id); 196*0Sstevel@tonic-gate else 197*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "get_class_id failed, continuing " 198*0Sstevel@tonic-gate "with no vendor class id"); 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate /* 201*0Sstevel@tonic-gate * the inactivity timer is enabled any time there are no 202*0Sstevel@tonic-gate * interfaces under DHCP control. if DHCP_INACTIVITY_WAIT 203*0Sstevel@tonic-gate * seconds transpire without an interface under DHCP control, 204*0Sstevel@tonic-gate * the agent shuts down. 205*0Sstevel@tonic-gate */ 206*0Sstevel@tonic-gate 207*0Sstevel@tonic-gate inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT, 208*0Sstevel@tonic-gate inactivity_shutdown, NULL); 209*0Sstevel@tonic-gate 210*0Sstevel@tonic-gate /* 211*0Sstevel@tonic-gate * max out the number available descriptors, just in case.. 212*0Sstevel@tonic-gate */ 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate rl.rlim_cur = RLIM_INFINITY; 215*0Sstevel@tonic-gate rl.rlim_max = RLIM_INFINITY; 216*0Sstevel@tonic-gate if (setrlimit(RLIMIT_NOFILE, &rl) == -1) 217*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "setrlimit failed"); 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gate /* 220*0Sstevel@tonic-gate * create the ipc channel that the agent will listen for 221*0Sstevel@tonic-gate * requests on, and register it with the event handler so that 222*0Sstevel@tonic-gate * `accept_event' will be called back. 223*0Sstevel@tonic-gate */ 224*0Sstevel@tonic-gate 225*0Sstevel@tonic-gate switch (dhcp_ipc_init(&ipc_fd)) { 226*0Sstevel@tonic-gate 227*0Sstevel@tonic-gate case 0: 228*0Sstevel@tonic-gate break; 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate case DHCP_IPC_E_BIND: 231*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "dhcp_ipc_init: cannot bind to port " 232*0Sstevel@tonic-gate "%i (agent already running?)", IPPORT_DHCPAGENT); 233*0Sstevel@tonic-gate return (EXIT_FAILURE); 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gate default: 236*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "dhcp_ipc_init failed"); 237*0Sstevel@tonic-gate return (EXIT_FAILURE); 238*0Sstevel@tonic-gate } 239*0Sstevel@tonic-gate 240*0Sstevel@tonic-gate if (iu_register_event(eh, ipc_fd, POLLIN, accept_event, 0) == -1) { 241*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot register ipc fd for messages"); 242*0Sstevel@tonic-gate return (EXIT_FAILURE); 243*0Sstevel@tonic-gate } 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate /* 246*0Sstevel@tonic-gate * if the -a (adopt) option was specified, try to adopt the 247*0Sstevel@tonic-gate * kernel-managed interface before we start. Our grandparent 248*0Sstevel@tonic-gate * will be waiting for us to finish this, so signal him when 249*0Sstevel@tonic-gate * we're done. 250*0Sstevel@tonic-gate */ 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate if (do_adopt) { 253*0Sstevel@tonic-gate int result; 254*0Sstevel@tonic-gate 255*0Sstevel@tonic-gate result = dhcp_adopt(); 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate if (grandparent != (pid_t)0) { 258*0Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "adoption complete, signalling " 259*0Sstevel@tonic-gate "parent (%i) to exit.", grandparent); 260*0Sstevel@tonic-gate (void) kill(grandparent, SIGALRM); 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate if (result == 0) 264*0Sstevel@tonic-gate return (EXIT_FAILURE); 265*0Sstevel@tonic-gate } 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate /* 268*0Sstevel@tonic-gate * enter the main event loop; this is where all the real work 269*0Sstevel@tonic-gate * takes place (through registering events and scheduling timers). 270*0Sstevel@tonic-gate * this function only returns when the agent is shutting down. 271*0Sstevel@tonic-gate */ 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate switch (iu_handle_events(eh, tq)) { 274*0Sstevel@tonic-gate 275*0Sstevel@tonic-gate case -1: 276*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "iu_handle_events exited abnormally"); 277*0Sstevel@tonic-gate break; 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate case DHCP_REASON_INACTIVITY: 280*0Sstevel@tonic-gate dhcpmsg(MSG_INFO, "no interfaces to manage, shutting down..."); 281*0Sstevel@tonic-gate break; 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate case DHCP_REASON_TERMINATE: 284*0Sstevel@tonic-gate dhcpmsg(MSG_INFO, "received SIGTERM, shutting down..."); 285*0Sstevel@tonic-gate break; 286*0Sstevel@tonic-gate 287*0Sstevel@tonic-gate case DHCP_REASON_SIGNAL: 288*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "received unexpected signal, shutting " 289*0Sstevel@tonic-gate "down..."); 290*0Sstevel@tonic-gate break; 291*0Sstevel@tonic-gate } 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate (void) iu_eh_unregister_signal(eh, SIGTHAW, NULL); 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate iu_eh_destroy(eh); 296*0Sstevel@tonic-gate iu_tq_destroy(tq); 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate return (EXIT_SUCCESS); 299*0Sstevel@tonic-gate } 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate /* 302*0Sstevel@tonic-gate * drain_script(): event loop callback during shutdown 303*0Sstevel@tonic-gate * 304*0Sstevel@tonic-gate * input: eh_t *: unused 305*0Sstevel@tonic-gate * void *: unused 306*0Sstevel@tonic-gate * output: boolean_t: B_TRUE if event loop should exit; B_FALSE otherwise 307*0Sstevel@tonic-gate */ 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate /* ARGSUSED */ 310*0Sstevel@tonic-gate boolean_t 311*0Sstevel@tonic-gate drain_script(iu_eh_t *ehp, void *arg) 312*0Sstevel@tonic-gate { 313*0Sstevel@tonic-gate if (shutdown_started == B_FALSE) { 314*0Sstevel@tonic-gate shutdown_started = B_TRUE; 315*0Sstevel@tonic-gate if (do_adopt == B_FALSE) /* see 4291141 */ 316*0Sstevel@tonic-gate nuke_ifslist(B_TRUE); 317*0Sstevel@tonic-gate } 318*0Sstevel@tonic-gate return (script_count == 0); 319*0Sstevel@tonic-gate } 320*0Sstevel@tonic-gate 321*0Sstevel@tonic-gate /* 322*0Sstevel@tonic-gate * accept_event(): accepts a new connection on the ipc socket and registers 323*0Sstevel@tonic-gate * to receive its messages with the event handler 324*0Sstevel@tonic-gate * 325*0Sstevel@tonic-gate * input: iu_eh_t *: unused 326*0Sstevel@tonic-gate * int: the file descriptor in the iu_eh_t * the connection came in on 327*0Sstevel@tonic-gate * (other arguments unused) 328*0Sstevel@tonic-gate * output: void 329*0Sstevel@tonic-gate */ 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate /* ARGSUSED */ 332*0Sstevel@tonic-gate static void 333*0Sstevel@tonic-gate accept_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 334*0Sstevel@tonic-gate { 335*0Sstevel@tonic-gate int client_fd; 336*0Sstevel@tonic-gate int is_priv; 337*0Sstevel@tonic-gate 338*0Sstevel@tonic-gate if (dhcp_ipc_accept(fd, &client_fd, &is_priv) != 0) { 339*0Sstevel@tonic-gate dhcpmsg(MSG_ERR, "accept_event: accept on ipc socket"); 340*0Sstevel@tonic-gate return; 341*0Sstevel@tonic-gate } 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate if (iu_register_event(eh, client_fd, POLLIN, ipc_event, 344*0Sstevel@tonic-gate (void *)is_priv) == -1) { 345*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "accept_event: cannot register ipc socket " 346*0Sstevel@tonic-gate "for callback"); 347*0Sstevel@tonic-gate } 348*0Sstevel@tonic-gate } 349*0Sstevel@tonic-gate 350*0Sstevel@tonic-gate /* 351*0Sstevel@tonic-gate * ipc_event(): processes incoming ipc requests 352*0Sstevel@tonic-gate * 353*0Sstevel@tonic-gate * input: iu_eh_t *: unused 354*0Sstevel@tonic-gate * int: the file descriptor in the iu_eh_t * the request came in on 355*0Sstevel@tonic-gate * short: unused 356*0Sstevel@tonic-gate * iu_event_id_t: unused 357*0Sstevel@tonic-gate * void *: indicates whether the request is from a privileged client 358*0Sstevel@tonic-gate * output: void 359*0Sstevel@tonic-gate */ 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate /* ARGSUSED */ 362*0Sstevel@tonic-gate static void 363*0Sstevel@tonic-gate ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 364*0Sstevel@tonic-gate { 365*0Sstevel@tonic-gate dhcp_ipc_request_t *request; 366*0Sstevel@tonic-gate struct ifslist *ifsp, *primary_ifsp; 367*0Sstevel@tonic-gate int error, is_priv = (int)arg; 368*0Sstevel@tonic-gate PKT_LIST *plp[2]; 369*0Sstevel@tonic-gate dhcp_ipc_type_t cmd; 370*0Sstevel@tonic-gate 371*0Sstevel@tonic-gate (void) iu_unregister_event(eh, id, NULL); 372*0Sstevel@tonic-gate 373*0Sstevel@tonic-gate if (dhcp_ipc_recv_request(fd, &request, DHCP_IPC_REQUEST_WAIT) != 0) { 374*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "ipc_event: dhcp_ipc_recv_request failed"); 375*0Sstevel@tonic-gate (void) dhcp_ipc_close(fd); 376*0Sstevel@tonic-gate return; 377*0Sstevel@tonic-gate } 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate cmd = DHCP_IPC_CMD(request->message_type); 380*0Sstevel@tonic-gate if (cmd >= DHCP_NIPC) { 381*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_CMD_UNKNOWN, &fd); 382*0Sstevel@tonic-gate return; 383*0Sstevel@tonic-gate } 384*0Sstevel@tonic-gate 385*0Sstevel@tonic-gate /* return EPERM for any of the privileged actions */ 386*0Sstevel@tonic-gate 387*0Sstevel@tonic-gate if (!is_priv) { 388*0Sstevel@tonic-gate switch (cmd) { 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate case DHCP_STATUS: 391*0Sstevel@tonic-gate case DHCP_PING: 392*0Sstevel@tonic-gate case DHCP_GET_TAG: 393*0Sstevel@tonic-gate break; 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate default: 396*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "ipc_event: privileged ipc " 397*0Sstevel@tonic-gate "command (%i) attempted on %s", cmd, 398*0Sstevel@tonic-gate request->ifname); 399*0Sstevel@tonic-gate 400*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_PERM, &fd); 401*0Sstevel@tonic-gate return; 402*0Sstevel@tonic-gate } 403*0Sstevel@tonic-gate } 404*0Sstevel@tonic-gate 405*0Sstevel@tonic-gate /* 406*0Sstevel@tonic-gate * try to locate the ifs associated with this command. if the 407*0Sstevel@tonic-gate * command is DHCP_START or DHCP_INFORM, then if there isn't 408*0Sstevel@tonic-gate * an ifs already, make one (there may already be one from a 409*0Sstevel@tonic-gate * previous failed attempt to START or INFORM). otherwise, 410*0Sstevel@tonic-gate * verify the interface is still valid. 411*0Sstevel@tonic-gate */ 412*0Sstevel@tonic-gate 413*0Sstevel@tonic-gate ifsp = lookup_ifs(request->ifname); 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate switch (cmd) { 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate case DHCP_START: /* FALLTHRU */ 418*0Sstevel@tonic-gate case DHCP_INFORM: 419*0Sstevel@tonic-gate /* 420*0Sstevel@tonic-gate * it's possible that the interface already exists, but 421*0Sstevel@tonic-gate * has been abandoned. usually in those cases we should 422*0Sstevel@tonic-gate * return DHCP_IPC_E_UNKIF, but that makes little sense 423*0Sstevel@tonic-gate * in the case of "start" or "inform", so just ignore 424*0Sstevel@tonic-gate * the abandoned interface and start over anew. 425*0Sstevel@tonic-gate */ 426*0Sstevel@tonic-gate 427*0Sstevel@tonic-gate if (ifsp != NULL && verify_ifs(ifsp) == 0) 428*0Sstevel@tonic-gate ifsp = NULL; 429*0Sstevel@tonic-gate 430*0Sstevel@tonic-gate /* 431*0Sstevel@tonic-gate * as part of initializing the ifs, insert_ifs() 432*0Sstevel@tonic-gate * creates a DLPI stream at ifsp->if_dlpi_fd. 433*0Sstevel@tonic-gate */ 434*0Sstevel@tonic-gate 435*0Sstevel@tonic-gate if (ifsp == NULL) { 436*0Sstevel@tonic-gate ifsp = insert_ifs(request->ifname, B_FALSE, &error); 437*0Sstevel@tonic-gate if (ifsp == NULL) { 438*0Sstevel@tonic-gate send_error_reply(request, error, &fd); 439*0Sstevel@tonic-gate return; 440*0Sstevel@tonic-gate } 441*0Sstevel@tonic-gate } 442*0Sstevel@tonic-gate break; 443*0Sstevel@tonic-gate 444*0Sstevel@tonic-gate default: 445*0Sstevel@tonic-gate if (ifsp == NULL) { 446*0Sstevel@tonic-gate if (request->ifname[0] == '\0') 447*0Sstevel@tonic-gate error = DHCP_IPC_E_NOPRIMARY; 448*0Sstevel@tonic-gate else 449*0Sstevel@tonic-gate error = DHCP_IPC_E_UNKIF; 450*0Sstevel@tonic-gate 451*0Sstevel@tonic-gate send_error_reply(request, error, &fd); 452*0Sstevel@tonic-gate return; 453*0Sstevel@tonic-gate } 454*0Sstevel@tonic-gate break; 455*0Sstevel@tonic-gate } 456*0Sstevel@tonic-gate 457*0Sstevel@tonic-gate if (verify_ifs(ifsp) == 0) { 458*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_UNKIF, &fd); 459*0Sstevel@tonic-gate return; 460*0Sstevel@tonic-gate } 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate if (ifsp->if_dflags & DHCP_IF_BOOTP) { 463*0Sstevel@tonic-gate switch (cmd) { 464*0Sstevel@tonic-gate 465*0Sstevel@tonic-gate case DHCP_EXTEND: 466*0Sstevel@tonic-gate case DHCP_RELEASE: 467*0Sstevel@tonic-gate case DHCP_INFORM: 468*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_BOOTP, &fd); 469*0Sstevel@tonic-gate return; 470*0Sstevel@tonic-gate 471*0Sstevel@tonic-gate default: 472*0Sstevel@tonic-gate break; 473*0Sstevel@tonic-gate } 474*0Sstevel@tonic-gate } 475*0Sstevel@tonic-gate 476*0Sstevel@tonic-gate /* 477*0Sstevel@tonic-gate * verify that the interface is in a state which will allow the 478*0Sstevel@tonic-gate * command. we do this up front so that we can return an error 479*0Sstevel@tonic-gate * *before* needlessly cancelling an in-progress transaction. 480*0Sstevel@tonic-gate */ 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate if (!ipc_cmd_allowed[ifsp->if_state][cmd]) { 483*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd); 484*0Sstevel@tonic-gate return; 485*0Sstevel@tonic-gate } 486*0Sstevel@tonic-gate 487*0Sstevel@tonic-gate if ((request->message_type & DHCP_PRIMARY) && is_priv) { 488*0Sstevel@tonic-gate if ((primary_ifsp = lookup_ifs("")) != NULL) 489*0Sstevel@tonic-gate primary_ifsp->if_dflags &= ~DHCP_IF_PRIMARY; 490*0Sstevel@tonic-gate ifsp->if_dflags |= DHCP_IF_PRIMARY; 491*0Sstevel@tonic-gate } 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate /* 494*0Sstevel@tonic-gate * current design dictates that there can be only one 495*0Sstevel@tonic-gate * outstanding transaction per interface -- this simplifies 496*0Sstevel@tonic-gate * the code considerably and also fits well with RFC2131. 497*0Sstevel@tonic-gate * it is worth classifying the different DHCP commands into 498*0Sstevel@tonic-gate * synchronous (those which we will handle now and be done 499*0Sstevel@tonic-gate * with) and asynchronous (those which require transactions 500*0Sstevel@tonic-gate * and will be completed at an indeterminate time in the 501*0Sstevel@tonic-gate * future): 502*0Sstevel@tonic-gate * 503*0Sstevel@tonic-gate * DROP: removes the agent's management of an interface. 504*0Sstevel@tonic-gate * asynchronous as the script program may be invoked. 505*0Sstevel@tonic-gate * 506*0Sstevel@tonic-gate * PING: checks to see if the agent controls an interface. 507*0Sstevel@tonic-gate * synchronous, since no packets need to be sent 508*0Sstevel@tonic-gate * to the DHCP server. 509*0Sstevel@tonic-gate * 510*0Sstevel@tonic-gate * STATUS: returns information about the an interface. 511*0Sstevel@tonic-gate * synchronous, since no packets need to be sent 512*0Sstevel@tonic-gate * to the DHCP server. 513*0Sstevel@tonic-gate * 514*0Sstevel@tonic-gate * RELEASE: releases the agent's management of an interface 515*0Sstevel@tonic-gate * and brings the interface down. asynchronous as 516*0Sstevel@tonic-gate * the script program may be invoked. 517*0Sstevel@tonic-gate * 518*0Sstevel@tonic-gate * EXTEND: renews a lease. asynchronous, since the agent 519*0Sstevel@tonic-gate * needs to wait for an ACK, etc. 520*0Sstevel@tonic-gate * 521*0Sstevel@tonic-gate * START: starts DHCP on an interface. asynchronous since 522*0Sstevel@tonic-gate * the agent needs to wait for OFFERs, ACKs, etc. 523*0Sstevel@tonic-gate * 524*0Sstevel@tonic-gate * INFORM: obtains configuration parameters for an externally 525*0Sstevel@tonic-gate * configured interface. asynchronous, since the 526*0Sstevel@tonic-gate * agent needs to wait for an ACK. 527*0Sstevel@tonic-gate * 528*0Sstevel@tonic-gate * notice that EXTEND, INFORM, START, DROP and RELEASE are 529*0Sstevel@tonic-gate * asynchronous. notice also that asynchronous commands may 530*0Sstevel@tonic-gate * occur from within the agent -- for instance, the agent 531*0Sstevel@tonic-gate * will need to do implicit EXTENDs to extend the lease. in 532*0Sstevel@tonic-gate * order to make the code simpler, the following rules apply 533*0Sstevel@tonic-gate * for asynchronous commands: 534*0Sstevel@tonic-gate * 535*0Sstevel@tonic-gate * there can only be one asynchronous command at a time per 536*0Sstevel@tonic-gate * interface. the current asynchronous command is managed by 537*0Sstevel@tonic-gate * the async_* api: async_start(), async_finish(), 538*0Sstevel@tonic-gate * async_timeout(), async_cancel(), and async_pending(). 539*0Sstevel@tonic-gate * async_start() starts management of a new asynchronous 540*0Sstevel@tonic-gate * command on an interface, which should only be done after 541*0Sstevel@tonic-gate * async_pending() is called to check that there are no 542*0Sstevel@tonic-gate * pending asynchronous commands on that interface. when the 543*0Sstevel@tonic-gate * command is completed, async_finish() should be called. all 544*0Sstevel@tonic-gate * asynchronous commands have an associated timer, which calls 545*0Sstevel@tonic-gate * async_timeout() when it times out. if async_timeout() 546*0Sstevel@tonic-gate * decides that the asynchronous command should be cancelled 547*0Sstevel@tonic-gate * (see below), it calls async_cancel() to attempt 548*0Sstevel@tonic-gate * cancellation. 549*0Sstevel@tonic-gate * 550*0Sstevel@tonic-gate * asynchronous commands started by a user command have an 551*0Sstevel@tonic-gate * associated ipc_action which provides the agent with 552*0Sstevel@tonic-gate * information for how to get in touch with the user command 553*0Sstevel@tonic-gate * when the action completes. these ipc_action records also 554*0Sstevel@tonic-gate * have an associated timeout which may be infinite. 555*0Sstevel@tonic-gate * ipc_action_start() should be called when starting an 556*0Sstevel@tonic-gate * asynchronous command requested by a user, which sets up the 557*0Sstevel@tonic-gate * timer and keeps track of the ipc information (file 558*0Sstevel@tonic-gate * descriptor, request type). when the asynchronous command 559*0Sstevel@tonic-gate * completes, ipc_action_finish() should be called to return a 560*0Sstevel@tonic-gate * command status code to the user and close the ipc 561*0Sstevel@tonic-gate * connection). if the command does not complete before the 562*0Sstevel@tonic-gate * timer fires, ipc_action_timeout() is called which closes 563*0Sstevel@tonic-gate * the ipc connection and returns DHCP_IPC_E_TIMEOUT to the 564*0Sstevel@tonic-gate * user. note that independent of ipc_action_timeout(), 565*0Sstevel@tonic-gate * ipc_action_finish() should be called. 566*0Sstevel@tonic-gate * 567*0Sstevel@tonic-gate * on a case-by-case basis, here is what happens (per interface): 568*0Sstevel@tonic-gate * 569*0Sstevel@tonic-gate * o when an asynchronous command is requested, then 570*0Sstevel@tonic-gate * async_pending() is called to see if there is already 571*0Sstevel@tonic-gate * an asynchronous event. if so, the command does not 572*0Sstevel@tonic-gate * proceed, and if there is an associated ipc_action, 573*0Sstevel@tonic-gate * the user command is sent DHCP_IPC_E_PEND. 574*0Sstevel@tonic-gate * 575*0Sstevel@tonic-gate * o otherwise, the the transaction is started with 576*0Sstevel@tonic-gate * async_start(). if the transaction is on behalf 577*0Sstevel@tonic-gate * of a user, ipc_action_start() is called to keep 578*0Sstevel@tonic-gate * track of the ipc information and set up the 579*0Sstevel@tonic-gate * ipc_action timer. 580*0Sstevel@tonic-gate * 581*0Sstevel@tonic-gate * o if the command completes normally and before a 582*0Sstevel@tonic-gate * timeout fires, then async_finish() is called. 583*0Sstevel@tonic-gate * if there was an associated ipc_action, 584*0Sstevel@tonic-gate * ipc_action_finish() is called to complete it. 585*0Sstevel@tonic-gate * 586*0Sstevel@tonic-gate * o if the command fails before a timeout fires, then 587*0Sstevel@tonic-gate * async_finish() is called, and the interface is 588*0Sstevel@tonic-gate * is returned to a known state based on the command. 589*0Sstevel@tonic-gate * if there was an associated ipc_action, 590*0Sstevel@tonic-gate * ipc_action_finish() is called to complete it. 591*0Sstevel@tonic-gate * 592*0Sstevel@tonic-gate * o if the ipc_action timer fires before command 593*0Sstevel@tonic-gate * completion, then DHCP_IPC_E_TIMEOUT is returned to 594*0Sstevel@tonic-gate * the user. however, the transaction continues to 595*0Sstevel@tonic-gate * be carried out asynchronously. 596*0Sstevel@tonic-gate * 597*0Sstevel@tonic-gate * o if async_timeout() fires before command completion, 598*0Sstevel@tonic-gate * then if the command was internal to the agent, it 599*0Sstevel@tonic-gate * is cancelled. otherwise, if it was a user command, 600*0Sstevel@tonic-gate * then if the user is still waiting for the command 601*0Sstevel@tonic-gate * to complete, the command continues and async_timeout() 602*0Sstevel@tonic-gate * is rescheduled. 603*0Sstevel@tonic-gate */ 604*0Sstevel@tonic-gate 605*0Sstevel@tonic-gate switch (cmd) { 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate case DHCP_DROP: /* FALLTHRU */ 608*0Sstevel@tonic-gate case DHCP_RELEASE: /* FALLTHRU */ 609*0Sstevel@tonic-gate case DHCP_EXTEND: /* FALLTHRU */ 610*0Sstevel@tonic-gate case DHCP_INFORM: /* FALLTHRU */ 611*0Sstevel@tonic-gate case DHCP_START: 612*0Sstevel@tonic-gate /* 613*0Sstevel@tonic-gate * if shutdown request has been received, send back an error. 614*0Sstevel@tonic-gate */ 615*0Sstevel@tonic-gate if (shutdown_started) { 616*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd); 617*0Sstevel@tonic-gate return; 618*0Sstevel@tonic-gate } 619*0Sstevel@tonic-gate 620*0Sstevel@tonic-gate if (async_pending(ifsp)) { 621*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_PEND, &fd); 622*0Sstevel@tonic-gate return; 623*0Sstevel@tonic-gate } 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate if (ipc_action_start(ifsp, request, fd) == 0) { 626*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "ipc_event: ipc_action_start " 627*0Sstevel@tonic-gate "failed for %s", ifsp->if_name); 628*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_MEMORY, &fd); 629*0Sstevel@tonic-gate return; 630*0Sstevel@tonic-gate } 631*0Sstevel@tonic-gate 632*0Sstevel@tonic-gate if (async_start(ifsp, cmd, B_TRUE) == 0) { 633*0Sstevel@tonic-gate ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 634*0Sstevel@tonic-gate return; 635*0Sstevel@tonic-gate } 636*0Sstevel@tonic-gate break; 637*0Sstevel@tonic-gate 638*0Sstevel@tonic-gate default: 639*0Sstevel@tonic-gate break; 640*0Sstevel@tonic-gate } 641*0Sstevel@tonic-gate 642*0Sstevel@tonic-gate switch (cmd) { 643*0Sstevel@tonic-gate 644*0Sstevel@tonic-gate case DHCP_DROP: 645*0Sstevel@tonic-gate (void) script_start(ifsp, EVENT_DROP, dhcp_drop, NULL, NULL); 646*0Sstevel@tonic-gate return; 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate case DHCP_EXTEND: 649*0Sstevel@tonic-gate (void) dhcp_extending(ifsp); 650*0Sstevel@tonic-gate break; 651*0Sstevel@tonic-gate 652*0Sstevel@tonic-gate case DHCP_GET_TAG: { 653*0Sstevel@tonic-gate dhcp_optnum_t optnum; 654*0Sstevel@tonic-gate DHCP_OPT *opt = NULL; 655*0Sstevel@tonic-gate boolean_t did_alloc = B_FALSE; 656*0Sstevel@tonic-gate PKT_LIST *ack = ifsp->if_ack; 657*0Sstevel@tonic-gate 658*0Sstevel@tonic-gate /* 659*0Sstevel@tonic-gate * verify the request makes sense. 660*0Sstevel@tonic-gate */ 661*0Sstevel@tonic-gate 662*0Sstevel@tonic-gate if (request->data_type != DHCP_TYPE_OPTNUM || 663*0Sstevel@tonic-gate request->data_length != sizeof (dhcp_optnum_t)) { 664*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_PROTO, &fd); 665*0Sstevel@tonic-gate return; 666*0Sstevel@tonic-gate } 667*0Sstevel@tonic-gate 668*0Sstevel@tonic-gate (void) memcpy(&optnum, request->buffer, sizeof (dhcp_optnum_t)); 669*0Sstevel@tonic-gate load_option: 670*0Sstevel@tonic-gate switch (optnum.category) { 671*0Sstevel@tonic-gate 672*0Sstevel@tonic-gate case DSYM_SITE: /* FALLTHRU */ 673*0Sstevel@tonic-gate case DSYM_STANDARD: 674*0Sstevel@tonic-gate if (optnum.code <= DHCP_LAST_OPT) 675*0Sstevel@tonic-gate opt = ack->opts[optnum.code]; 676*0Sstevel@tonic-gate break; 677*0Sstevel@tonic-gate 678*0Sstevel@tonic-gate case DSYM_VENDOR: 679*0Sstevel@tonic-gate /* 680*0Sstevel@tonic-gate * the test against VS_OPTION_START is broken up into 681*0Sstevel@tonic-gate * two tests to avoid compiler warnings under intel. 682*0Sstevel@tonic-gate */ 683*0Sstevel@tonic-gate 684*0Sstevel@tonic-gate if ((optnum.code > VS_OPTION_START || 685*0Sstevel@tonic-gate optnum.code == VS_OPTION_START) && 686*0Sstevel@tonic-gate optnum.code <= VS_OPTION_END) 687*0Sstevel@tonic-gate opt = ack->vs[optnum.code]; 688*0Sstevel@tonic-gate break; 689*0Sstevel@tonic-gate 690*0Sstevel@tonic-gate case DSYM_FIELD: 691*0Sstevel@tonic-gate if (optnum.code + optnum.size > sizeof (PKT)) 692*0Sstevel@tonic-gate break; 693*0Sstevel@tonic-gate 694*0Sstevel@tonic-gate /* + 2 to account for option code and length byte */ 695*0Sstevel@tonic-gate opt = malloc(optnum.size + 2); 696*0Sstevel@tonic-gate if (opt == NULL) { 697*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_MEMORY, 698*0Sstevel@tonic-gate &fd); 699*0Sstevel@tonic-gate return; 700*0Sstevel@tonic-gate } 701*0Sstevel@tonic-gate 702*0Sstevel@tonic-gate did_alloc = B_TRUE; 703*0Sstevel@tonic-gate opt->len = optnum.size; 704*0Sstevel@tonic-gate opt->code = optnum.code; 705*0Sstevel@tonic-gate (void) memcpy(&opt->value, (caddr_t)ack->pkt + 706*0Sstevel@tonic-gate opt->code, opt->len); 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate break; 709*0Sstevel@tonic-gate 710*0Sstevel@tonic-gate default: 711*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_PROTO, &fd); 712*0Sstevel@tonic-gate return; 713*0Sstevel@tonic-gate } 714*0Sstevel@tonic-gate 715*0Sstevel@tonic-gate /* 716*0Sstevel@tonic-gate * return the option payload, if there was one. the "+ 2" 717*0Sstevel@tonic-gate * accounts for the option code number and length byte. 718*0Sstevel@tonic-gate */ 719*0Sstevel@tonic-gate 720*0Sstevel@tonic-gate if (opt != NULL) { 721*0Sstevel@tonic-gate send_data_reply(request, &fd, 0, DHCP_TYPE_OPTION, opt, 722*0Sstevel@tonic-gate opt->len + 2); 723*0Sstevel@tonic-gate 724*0Sstevel@tonic-gate if (did_alloc) 725*0Sstevel@tonic-gate free(opt); 726*0Sstevel@tonic-gate return; 727*0Sstevel@tonic-gate } else if (ack != ifsp->if_orig_ack) { 728*0Sstevel@tonic-gate /* 729*0Sstevel@tonic-gate * There wasn't any definition for the option in the 730*0Sstevel@tonic-gate * current ack, so now retry with the original ack if 731*0Sstevel@tonic-gate * the original ack is not the current ack. 732*0Sstevel@tonic-gate */ 733*0Sstevel@tonic-gate ack = ifsp->if_orig_ack; 734*0Sstevel@tonic-gate goto load_option; 735*0Sstevel@tonic-gate } 736*0Sstevel@tonic-gate 737*0Sstevel@tonic-gate /* 738*0Sstevel@tonic-gate * note that an "okay" response is returned either in 739*0Sstevel@tonic-gate * the case of an unknown option or a known option 740*0Sstevel@tonic-gate * with no payload. this is okay (for now) since 741*0Sstevel@tonic-gate * dhcpinfo checks whether an option is valid before 742*0Sstevel@tonic-gate * ever performing ipc with the agent. 743*0Sstevel@tonic-gate */ 744*0Sstevel@tonic-gate 745*0Sstevel@tonic-gate send_ok_reply(request, &fd); 746*0Sstevel@tonic-gate return; 747*0Sstevel@tonic-gate } 748*0Sstevel@tonic-gate 749*0Sstevel@tonic-gate case DHCP_INFORM: 750*0Sstevel@tonic-gate dhcp_inform(ifsp); 751*0Sstevel@tonic-gate /* next destination: dhcp_acknak() */ 752*0Sstevel@tonic-gate return; 753*0Sstevel@tonic-gate 754*0Sstevel@tonic-gate case DHCP_PING: 755*0Sstevel@tonic-gate if (ifsp->if_dflags & DHCP_IF_FAILED) 756*0Sstevel@tonic-gate send_error_reply(request, DHCP_IPC_E_FAILEDIF, &fd); 757*0Sstevel@tonic-gate else 758*0Sstevel@tonic-gate send_ok_reply(request, &fd); 759*0Sstevel@tonic-gate return; 760*0Sstevel@tonic-gate 761*0Sstevel@tonic-gate case DHCP_RELEASE: 762*0Sstevel@tonic-gate (void) script_start(ifsp, EVENT_RELEASE, dhcp_release, 763*0Sstevel@tonic-gate "Finished with lease.", NULL); 764*0Sstevel@tonic-gate return; 765*0Sstevel@tonic-gate 766*0Sstevel@tonic-gate case DHCP_START: 767*0Sstevel@tonic-gate assert(ifsp->if_state == INIT); 768*0Sstevel@tonic-gate (void) canonize_ifs(ifsp); 769*0Sstevel@tonic-gate 770*0Sstevel@tonic-gate /* 771*0Sstevel@tonic-gate * if we have a valid hostconf lying around, then jump 772*0Sstevel@tonic-gate * into INIT_REBOOT. if it fails, we'll end up going 773*0Sstevel@tonic-gate * through the whole selecting() procedure again. 774*0Sstevel@tonic-gate */ 775*0Sstevel@tonic-gate 776*0Sstevel@tonic-gate error = read_hostconf(ifsp->if_name, plp, 2); 777*0Sstevel@tonic-gate if (error != -1) { 778*0Sstevel@tonic-gate ifsp->if_orig_ack = ifsp->if_ack = plp[0]; 779*0Sstevel@tonic-gate if (error > 1) { 780*0Sstevel@tonic-gate /* 781*0Sstevel@tonic-gate * Return indicated we had more than one packet 782*0Sstevel@tonic-gate * second one is the original ack. Older 783*0Sstevel@tonic-gate * versions of the agent wrote only one ack 784*0Sstevel@tonic-gate * to the file, we now keep both the first 785*0Sstevel@tonic-gate * ack as well as the last one. 786*0Sstevel@tonic-gate */ 787*0Sstevel@tonic-gate ifsp->if_orig_ack = plp[1]; 788*0Sstevel@tonic-gate } 789*0Sstevel@tonic-gate dhcp_init_reboot(ifsp); 790*0Sstevel@tonic-gate /* next destination: dhcp_acknak() */ 791*0Sstevel@tonic-gate return; 792*0Sstevel@tonic-gate } 793*0Sstevel@tonic-gate 794*0Sstevel@tonic-gate /* 795*0Sstevel@tonic-gate * if not debugging, wait for a few seconds before 796*0Sstevel@tonic-gate * going into SELECTING. 797*0Sstevel@tonic-gate */ 798*0Sstevel@tonic-gate 799*0Sstevel@tonic-gate if (debug_level == 0) { 800*0Sstevel@tonic-gate if (iu_schedule_timer_ms(tq, 801*0Sstevel@tonic-gate lrand48() % DHCP_SELECT_WAIT, dhcp_start, ifsp) 802*0Sstevel@tonic-gate != -1) { 803*0Sstevel@tonic-gate hold_ifs(ifsp); 804*0Sstevel@tonic-gate /* next destination: dhcp_start() */ 805*0Sstevel@tonic-gate return; 806*0Sstevel@tonic-gate } 807*0Sstevel@tonic-gate } 808*0Sstevel@tonic-gate 809*0Sstevel@tonic-gate dhcp_selecting(ifsp); 810*0Sstevel@tonic-gate /* next destination: dhcp_requesting() */ 811*0Sstevel@tonic-gate return; 812*0Sstevel@tonic-gate 813*0Sstevel@tonic-gate case DHCP_STATUS: { 814*0Sstevel@tonic-gate dhcp_status_t status; 815*0Sstevel@tonic-gate 816*0Sstevel@tonic-gate status.if_began = monosec_to_time(ifsp->if_curstart_monosec); 817*0Sstevel@tonic-gate 818*0Sstevel@tonic-gate if (ifsp->if_lease == DHCP_PERM) { 819*0Sstevel@tonic-gate status.if_t1 = DHCP_PERM; 820*0Sstevel@tonic-gate status.if_t2 = DHCP_PERM; 821*0Sstevel@tonic-gate status.if_lease = DHCP_PERM; 822*0Sstevel@tonic-gate } else { 823*0Sstevel@tonic-gate status.if_t1 = status.if_began + ifsp->if_t1; 824*0Sstevel@tonic-gate status.if_t2 = status.if_began + ifsp->if_t2; 825*0Sstevel@tonic-gate status.if_lease = status.if_began + ifsp->if_lease; 826*0Sstevel@tonic-gate } 827*0Sstevel@tonic-gate 828*0Sstevel@tonic-gate status.version = DHCP_STATUS_VER; 829*0Sstevel@tonic-gate status.if_state = ifsp->if_state; 830*0Sstevel@tonic-gate status.if_dflags = ifsp->if_dflags; 831*0Sstevel@tonic-gate status.if_sent = ifsp->if_sent; 832*0Sstevel@tonic-gate status.if_recv = ifsp->if_received; 833*0Sstevel@tonic-gate status.if_bad_offers = ifsp->if_bad_offers; 834*0Sstevel@tonic-gate 835*0Sstevel@tonic-gate (void) strlcpy(status.if_name, ifsp->if_name, IFNAMSIZ); 836*0Sstevel@tonic-gate 837*0Sstevel@tonic-gate send_data_reply(request, &fd, 0, DHCP_TYPE_STATUS, &status, 838*0Sstevel@tonic-gate sizeof (dhcp_status_t)); 839*0Sstevel@tonic-gate return; 840*0Sstevel@tonic-gate } 841*0Sstevel@tonic-gate 842*0Sstevel@tonic-gate default: 843*0Sstevel@tonic-gate return; 844*0Sstevel@tonic-gate } 845*0Sstevel@tonic-gate } 846