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