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