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