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