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