xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c (revision 4106:dd61d0159830)
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
52088Smeem  * Common Development and Distribution License (the "License").
62088Smeem  * 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 /*
223431Scarlsonj  * Copyright 2007 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 <unistd.h>
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <sys/stat.h>
310Sstevel@tonic-gate #include <stdlib.h>
320Sstevel@tonic-gate #include <netinet/in.h>		/* struct in_addr */
330Sstevel@tonic-gate #include <netinet/dhcp.h>
340Sstevel@tonic-gate #include <signal.h>
350Sstevel@tonic-gate #include <sys/socket.h>
360Sstevel@tonic-gate #include <net/route.h>
370Sstevel@tonic-gate #include <net/if_arp.h>
380Sstevel@tonic-gate #include <string.h>
390Sstevel@tonic-gate #include <dhcpmsg.h>
400Sstevel@tonic-gate #include <ctype.h>
410Sstevel@tonic-gate #include <netdb.h>
420Sstevel@tonic-gate #include <fcntl.h>
430Sstevel@tonic-gate #include <stdio.h>
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #include "states.h"
460Sstevel@tonic-gate #include "agent.h"
470Sstevel@tonic-gate #include "interface.h"
480Sstevel@tonic-gate #include "util.h"
490Sstevel@tonic-gate #include "packet.h"
500Sstevel@tonic-gate 
510Sstevel@tonic-gate /*
520Sstevel@tonic-gate  * this file contains utility functions that have no real better home
530Sstevel@tonic-gate  * of their own.  they can largely be broken into six categories:
540Sstevel@tonic-gate  *
550Sstevel@tonic-gate  *  o  conversion functions -- functions to turn integers into strings,
560Sstevel@tonic-gate  *     or to convert between units of a similar measure.
570Sstevel@tonic-gate  *
583431Scarlsonj  *  o  time and timer functions -- functions to handle time measurement
593431Scarlsonj  *     and events.
603431Scarlsonj  *
610Sstevel@tonic-gate  *  o  ipc-related functions -- functions to simplify the generation of
620Sstevel@tonic-gate  *     ipc messages to the agent's clients.
630Sstevel@tonic-gate  *
640Sstevel@tonic-gate  *  o  signal-related functions -- functions to clean up the agent when
650Sstevel@tonic-gate  *     it receives a signal.
660Sstevel@tonic-gate  *
670Sstevel@tonic-gate  *  o  routing table manipulation functions
680Sstevel@tonic-gate  *
690Sstevel@tonic-gate  *  o  true miscellany -- anything else
700Sstevel@tonic-gate  */
710Sstevel@tonic-gate 
720Sstevel@tonic-gate /*
730Sstevel@tonic-gate  * pkt_type_to_string(): stringifies a packet type
740Sstevel@tonic-gate  *
753431Scarlsonj  *   input: uchar_t: a DHCP packet type value, RFC 2131 or 3315
763431Scarlsonj  *	    boolean_t: B_TRUE if IPv6
770Sstevel@tonic-gate  *  output: const char *: the stringified packet type
780Sstevel@tonic-gate  */
790Sstevel@tonic-gate 
800Sstevel@tonic-gate const char *
813431Scarlsonj pkt_type_to_string(uchar_t type, boolean_t isv6)
820Sstevel@tonic-gate {
830Sstevel@tonic-gate 	/*
843431Scarlsonj 	 * note: the ordering in these arrays allows direct indexing of the
853431Scarlsonj 	 *	 table based on the RFC packet type value passed in.
860Sstevel@tonic-gate 	 */
870Sstevel@tonic-gate 
883431Scarlsonj 	static const char *v4types[] = {
890Sstevel@tonic-gate 		"BOOTP",  "DISCOVER", "OFFER",   "REQUEST", "DECLINE",
900Sstevel@tonic-gate 		"ACK",    "NAK",      "RELEASE", "INFORM"
910Sstevel@tonic-gate 	};
923431Scarlsonj 	static const char *v6types[] = {
933431Scarlsonj 		NULL, "SOLICIT", "ADVERTISE", "REQUEST",
943431Scarlsonj 		"CONFIRM", "RENEW", "REBIND", "REPLY",
953431Scarlsonj 		"RELEASE", "DECLINE", "RECONFIGURE", "INFORMATION-REQUEST",
963431Scarlsonj 		"RELAY-FORW", "RELAY-REPL"
973431Scarlsonj 	};
980Sstevel@tonic-gate 
993431Scarlsonj 	if (isv6) {
1003431Scarlsonj 		if (type >= sizeof (v6types) / sizeof (*v6types) ||
1013431Scarlsonj 		    v6types[type] == NULL)
1023431Scarlsonj 			return ("<unknown>");
1033431Scarlsonj 		else
1043431Scarlsonj 			return (v6types[type]);
1053431Scarlsonj 	} else {
1063431Scarlsonj 		if (type >= sizeof (v4types) / sizeof (*v4types) ||
1073431Scarlsonj 		    v4types[type] == NULL)
1083431Scarlsonj 			return ("<unknown>");
1093431Scarlsonj 		else
1103431Scarlsonj 			return (v4types[type]);
1110Sstevel@tonic-gate 	}
1120Sstevel@tonic-gate }
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate /*
1150Sstevel@tonic-gate  * monosec_to_string(): converts a monosec_t into a date string
1160Sstevel@tonic-gate  *
1170Sstevel@tonic-gate  *   input: monosec_t: the monosec_t to convert
1180Sstevel@tonic-gate  *  output: const char *: the corresponding date string
1190Sstevel@tonic-gate  */
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate const char *
1220Sstevel@tonic-gate monosec_to_string(monosec_t monosec)
1230Sstevel@tonic-gate {
1240Sstevel@tonic-gate 	time_t	time = monosec_to_time(monosec);
1250Sstevel@tonic-gate 	char	*time_string = ctime(&time);
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	/* strip off the newline -- ugh, why, why, why.. */
1280Sstevel@tonic-gate 	time_string[strlen(time_string) - 1] = '\0';
1290Sstevel@tonic-gate 	return (time_string);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate /*
1330Sstevel@tonic-gate  * monosec(): returns a monotonically increasing time in seconds that
1340Sstevel@tonic-gate  *            is not affected by stime(2) or adjtime(2).
1350Sstevel@tonic-gate  *
1360Sstevel@tonic-gate  *   input: void
1370Sstevel@tonic-gate  *  output: monosec_t: the number of seconds since some time in the past
1380Sstevel@tonic-gate  */
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate monosec_t
1410Sstevel@tonic-gate monosec(void)
1420Sstevel@tonic-gate {
1430Sstevel@tonic-gate 	return (gethrtime() / NANOSEC);
1440Sstevel@tonic-gate }
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate /*
1470Sstevel@tonic-gate  * monosec_to_time(): converts a monosec_t into real wall time
1480Sstevel@tonic-gate  *
1490Sstevel@tonic-gate  *    input: monosec_t: the absolute monosec_t to convert
1500Sstevel@tonic-gate  *   output: time_t: the absolute time that monosec_t represents in wall time
1510Sstevel@tonic-gate  */
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate time_t
1540Sstevel@tonic-gate monosec_to_time(monosec_t abs_monosec)
1550Sstevel@tonic-gate {
1560Sstevel@tonic-gate 	return (abs_monosec - monosec()) + time(NULL);
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate /*
1603431Scarlsonj  * hrtime_to_monosec(): converts a hrtime_t to monosec_t
1610Sstevel@tonic-gate  *
1623431Scarlsonj  *    input: hrtime_t: the time to convert
1633431Scarlsonj  *   output: monosec_t: the time in monosec_t
1640Sstevel@tonic-gate  */
1650Sstevel@tonic-gate 
1663431Scarlsonj monosec_t
1673431Scarlsonj hrtime_to_monosec(hrtime_t hrtime)
1680Sstevel@tonic-gate {
1693431Scarlsonj 	return (hrtime / NANOSEC);
1700Sstevel@tonic-gate }
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate /*
1730Sstevel@tonic-gate  * print_server_msg(): prints a message from a DHCP server
1740Sstevel@tonic-gate  *
1753431Scarlsonj  *   input: dhcp_smach_t *: the state machine the message is associated with
1763431Scarlsonj  *	    const char *: the string to display
1773431Scarlsonj  *	    uint_t: length of string
1780Sstevel@tonic-gate  *  output: void
1790Sstevel@tonic-gate  */
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate void
1823431Scarlsonj print_server_msg(dhcp_smach_t *dsmp, const char *msg, uint_t msglen)
1830Sstevel@tonic-gate {
1843431Scarlsonj 	if (msglen > 0) {
1853431Scarlsonj 		dhcpmsg(MSG_INFO, "%s: message from server: %.*s",
1863431Scarlsonj 		    dsmp->dsm_name, msglen, msg);
1873431Scarlsonj 	}
1880Sstevel@tonic-gate }
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate /*
1910Sstevel@tonic-gate  * alrm_exit(): Signal handler for SIGARLM. terminates grandparent.
1920Sstevel@tonic-gate  *
1930Sstevel@tonic-gate  *    input: int: signal the handler was called with.
1940Sstevel@tonic-gate  *
1950Sstevel@tonic-gate  *   output: void
1960Sstevel@tonic-gate  */
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate static void
1990Sstevel@tonic-gate alrm_exit(int sig)
2000Sstevel@tonic-gate {
2010Sstevel@tonic-gate 	int exitval;
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	if (sig == SIGALRM && grandparent != 0)
2040Sstevel@tonic-gate 		exitval = EXIT_SUCCESS;
2050Sstevel@tonic-gate 	else
2060Sstevel@tonic-gate 		exitval = EXIT_FAILURE;
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	_exit(exitval);
2090Sstevel@tonic-gate }
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate /*
2120Sstevel@tonic-gate  * daemonize(): daemonizes the process
2130Sstevel@tonic-gate  *
2140Sstevel@tonic-gate  *   input: void
2150Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
2160Sstevel@tonic-gate  */
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate int
2190Sstevel@tonic-gate daemonize(void)
2200Sstevel@tonic-gate {
2210Sstevel@tonic-gate 	/*
2220Sstevel@tonic-gate 	 * We've found that adoption takes sufficiently long that
2230Sstevel@tonic-gate 	 * a dhcpinfo run after dhcpagent -a is started may occur
2240Sstevel@tonic-gate 	 * before the agent is ready to process the request.
2250Sstevel@tonic-gate 	 * The result is an error message and an unhappy user.
2260Sstevel@tonic-gate 	 *
2270Sstevel@tonic-gate 	 * The initial process now sleeps for DHCP_ADOPT_SLEEP,
2280Sstevel@tonic-gate 	 * unless interrupted by a SIGALRM, in which case it
2290Sstevel@tonic-gate 	 * exits immediately. This has the effect that the
2300Sstevel@tonic-gate 	 * grandparent doesn't exit until the dhcpagent is ready
2310Sstevel@tonic-gate 	 * to process requests. This defers the the balance of
2320Sstevel@tonic-gate 	 * the system start-up script processing until the
2330Sstevel@tonic-gate 	 * dhcpagent is ready to field requests.
2340Sstevel@tonic-gate 	 *
2350Sstevel@tonic-gate 	 * grandparent is only set for the adopt case; other
2360Sstevel@tonic-gate 	 * cases do not require the wait.
2370Sstevel@tonic-gate 	 */
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	if (grandparent != 0)
2400Sstevel@tonic-gate 		(void) signal(SIGALRM, alrm_exit);
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	switch (fork()) {
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 	case -1:
2450Sstevel@tonic-gate 		return (0);
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	case  0:
2480Sstevel@tonic-gate 		if (grandparent != 0)
2490Sstevel@tonic-gate 			(void) signal(SIGALRM, SIG_DFL);
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 		/*
2520Sstevel@tonic-gate 		 * setsid() makes us lose our controlling terminal,
2530Sstevel@tonic-gate 		 * and become both a session leader and a process
2540Sstevel@tonic-gate 		 * group leader.
2550Sstevel@tonic-gate 		 */
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 		(void) setsid();
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 		/*
2600Sstevel@tonic-gate 		 * under POSIX, a session leader can accidentally
2610Sstevel@tonic-gate 		 * (through open(2)) acquire a controlling terminal if
2620Sstevel@tonic-gate 		 * it does not have one.  just to be safe, fork again
2630Sstevel@tonic-gate 		 * so we are not a session leader.
2640Sstevel@tonic-gate 		 */
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 		switch (fork()) {
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 		case -1:
2690Sstevel@tonic-gate 			return (0);
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 		case 0:
2720Sstevel@tonic-gate 			(void) signal(SIGHUP, SIG_IGN);
2730Sstevel@tonic-gate 			(void) chdir("/");
2740Sstevel@tonic-gate 			(void) umask(022);
2750Sstevel@tonic-gate 			closefrom(0);
2760Sstevel@tonic-gate 			break;
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 		default:
2790Sstevel@tonic-gate 			_exit(EXIT_SUCCESS);
2800Sstevel@tonic-gate 		}
2810Sstevel@tonic-gate 		break;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	default:
2840Sstevel@tonic-gate 		if (grandparent != 0) {
2850Sstevel@tonic-gate 			(void) signal(SIGCHLD, SIG_IGN);
2863431Scarlsonj 			/*
2873431Scarlsonj 			 * Note that we're not the agent here, so the DHCP
2883431Scarlsonj 			 * logging subsystem hasn't been configured yet.
2893431Scarlsonj 			 */
2903431Scarlsonj 			syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
2910Sstevel@tonic-gate 			    "waiting for adoption to complete.");
2920Sstevel@tonic-gate 			if (sleep(DHCP_ADOPT_SLEEP) == 0) {
2933431Scarlsonj 				syslog(LOG_WARNING | LOG_DAEMON,
2943431Scarlsonj 				    "dhcpagent: daemonize: timed out awaiting "
2953431Scarlsonj 				    "adoption.");
2960Sstevel@tonic-gate 			}
2973431Scarlsonj 			syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
2983431Scarlsonj 			    "wait finished");
2990Sstevel@tonic-gate 		}
3000Sstevel@tonic-gate 		_exit(EXIT_SUCCESS);
3010Sstevel@tonic-gate 	}
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	return (1);
3040Sstevel@tonic-gate }
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate /*
3070Sstevel@tonic-gate  * update_default_route(): update the interface's default route
3080Sstevel@tonic-gate  *
3090Sstevel@tonic-gate  *   input: int: the type of message; either RTM_ADD or RTM_DELETE
3100Sstevel@tonic-gate  *	    struct in_addr: the default gateway to use
3110Sstevel@tonic-gate  *	    const char *: the interface associated with the route
3120Sstevel@tonic-gate  *	    int: any additional flags (besides RTF_STATIC and RTF_GATEWAY)
3133431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
3140Sstevel@tonic-gate  */
3150Sstevel@tonic-gate 
3163431Scarlsonj static boolean_t
317*4106Scarlsonj update_default_route(uint32_t ifindex, int type, struct in_addr *gateway_nbo,
3180Sstevel@tonic-gate     int flags)
3190Sstevel@tonic-gate {
3200Sstevel@tonic-gate 	struct {
3210Sstevel@tonic-gate 		struct rt_msghdr	rm_mh;
3220Sstevel@tonic-gate 		struct sockaddr_in	rm_dst;
3230Sstevel@tonic-gate 		struct sockaddr_in	rm_gw;
3240Sstevel@tonic-gate 		struct sockaddr_in	rm_mask;
3250Sstevel@tonic-gate 		struct sockaddr_dl	rm_ifp;
3260Sstevel@tonic-gate 	} rtmsg;
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	(void) memset(&rtmsg, 0, sizeof (rtmsg));
3290Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_version = RTM_VERSION;
3300Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_msglen	= sizeof (rtmsg);
3310Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_type	= type;
3320Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_pid	= getpid();
3330Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_flags	= RTF_GATEWAY | RTF_STATIC | flags;
3340Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_addrs	= RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP;
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	rtmsg.rm_gw.sin_family	= AF_INET;
3370Sstevel@tonic-gate 	rtmsg.rm_gw.sin_addr	= *gateway_nbo;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	rtmsg.rm_dst.sin_family = AF_INET;
3400Sstevel@tonic-gate 	rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	rtmsg.rm_mask.sin_family = AF_INET;
3430Sstevel@tonic-gate 	rtmsg.rm_mask.sin_addr.s_addr = htonl(0);
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	rtmsg.rm_ifp.sdl_family	= AF_LINK;
346*4106Scarlsonj 	rtmsg.rm_ifp.sdl_index	= ifindex;
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg));
3490Sstevel@tonic-gate }
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate /*
3520Sstevel@tonic-gate  * add_default_route(): add the default route to the given gateway
3530Sstevel@tonic-gate  *
3540Sstevel@tonic-gate  *   input: const char *: the name of the interface associated with the route
3550Sstevel@tonic-gate  *	    struct in_addr: the default gateway to add
3563431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE otherwise
3570Sstevel@tonic-gate  */
3580Sstevel@tonic-gate 
3593431Scarlsonj boolean_t
360*4106Scarlsonj add_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
3610Sstevel@tonic-gate {
362*4106Scarlsonj 	return (update_default_route(ifindex, RTM_ADD, gateway_nbo, RTF_UP));
3630Sstevel@tonic-gate }
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate /*
3660Sstevel@tonic-gate  * del_default_route(): deletes the default route to the given gateway
3670Sstevel@tonic-gate  *
3680Sstevel@tonic-gate  *   input: const char *: the name of the interface associated with the route
3690Sstevel@tonic-gate  *	    struct in_addr: if not INADDR_ANY, the default gateway to remove
3703431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
3710Sstevel@tonic-gate  */
3720Sstevel@tonic-gate 
3733431Scarlsonj boolean_t
374*4106Scarlsonj del_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
3750Sstevel@tonic-gate {
3760Sstevel@tonic-gate 	if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */
3773431Scarlsonj 		return (B_TRUE);
3780Sstevel@tonic-gate 
379*4106Scarlsonj 	return (update_default_route(ifindex, RTM_DELETE, gateway_nbo, 0));
3800Sstevel@tonic-gate }
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate /*
3833431Scarlsonj  * inactivity_shutdown(): shuts down agent if there are no state machines left
3843431Scarlsonj  *			  to manage
3850Sstevel@tonic-gate  *
3860Sstevel@tonic-gate  *   input: iu_tq_t *: unused
3870Sstevel@tonic-gate  *	    void *: unused
3880Sstevel@tonic-gate  *  output: void
3890Sstevel@tonic-gate  */
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate /* ARGSUSED */
3920Sstevel@tonic-gate void
3930Sstevel@tonic-gate inactivity_shutdown(iu_tq_t *tqp, void *arg)
3940Sstevel@tonic-gate {
3953431Scarlsonj 	if (smach_count() > 0)	/* shouldn't happen, but... */
3960Sstevel@tonic-gate 		return;
3970Sstevel@tonic-gate 
3983431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "inactivity_shutdown: timed out");
3993431Scarlsonj 
4000Sstevel@tonic-gate 	iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
4010Sstevel@tonic-gate }
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate /*
4040Sstevel@tonic-gate  * graceful_shutdown(): shuts down the agent gracefully
4050Sstevel@tonic-gate  *
4060Sstevel@tonic-gate  *   input: int: the signal that caused graceful_shutdown to be called
4070Sstevel@tonic-gate  *  output: void
4080Sstevel@tonic-gate  */
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate void
4110Sstevel@tonic-gate graceful_shutdown(int sig)
4120Sstevel@tonic-gate {
4132088Smeem 	iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE :
4142088Smeem 	    DHCP_REASON_SIGNAL), drain_script, NULL);
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate /*
4180Sstevel@tonic-gate  * bind_sock(): binds a socket to a given IP address and port number
4190Sstevel@tonic-gate  *
4200Sstevel@tonic-gate  *   input: int: the socket to bind
4210Sstevel@tonic-gate  *	    in_port_t: the port number to bind to, host byte order
4220Sstevel@tonic-gate  *	    in_addr_t: the address to bind to, host byte order
4233431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
4240Sstevel@tonic-gate  */
4250Sstevel@tonic-gate 
4263431Scarlsonj boolean_t
4270Sstevel@tonic-gate bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo)
4280Sstevel@tonic-gate {
4290Sstevel@tonic-gate 	struct sockaddr_in	sin;
4300Sstevel@tonic-gate 	int			on = 1;
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
4330Sstevel@tonic-gate 	sin.sin_family = AF_INET;
4340Sstevel@tonic-gate 	sin.sin_port   = htons(port_hbo);
4350Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(addr_hbo);
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 	(void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 	return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0);
4400Sstevel@tonic-gate }
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate /*
4433431Scarlsonj  * bind_sock_v6(): binds a socket to a given IP address and port number
4443431Scarlsonj  *
4453431Scarlsonj  *   input: int: the socket to bind
4463431Scarlsonj  *	    in_port_t: the port number to bind to, host byte order
4473431Scarlsonj  *	    in6_addr_t: the address to bind to, network byte order
4483431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
4493431Scarlsonj  */
4503431Scarlsonj 
4513431Scarlsonj boolean_t
4523431Scarlsonj bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo)
4533431Scarlsonj {
4543431Scarlsonj 	struct sockaddr_in6	sin6;
4553431Scarlsonj 	int			on = 1;
4563431Scarlsonj 
4573431Scarlsonj 	(void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
4583431Scarlsonj 	sin6.sin6_family = AF_INET6;
4593431Scarlsonj 	sin6.sin6_port   = htons(port_hbo);
4603431Scarlsonj 	if (addr_nbo != NULL) {
4613431Scarlsonj 		(void) memcpy(&sin6.sin6_addr, addr_nbo,
4623431Scarlsonj 		    sizeof (sin6.sin6_addr));
4633431Scarlsonj 	}
4643431Scarlsonj 
4653431Scarlsonj 	(void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
4663431Scarlsonj 
4673431Scarlsonj 	return (bind(fd, (struct sockaddr *)&sin6, sizeof (sin6)) == 0);
4683431Scarlsonj }
4693431Scarlsonj 
4703431Scarlsonj /*
4710Sstevel@tonic-gate  * valid_hostname(): check whether a string is a valid hostname
4720Sstevel@tonic-gate  *
4730Sstevel@tonic-gate  *   input: const char *: the string to verify as a hostname
4740Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if the string is a valid hostname
4750Sstevel@tonic-gate  *
4760Sstevel@tonic-gate  * Note that we accept both host names beginning with a digit and
4770Sstevel@tonic-gate  * those containing hyphens.  Neither is strictly legal according
4780Sstevel@tonic-gate  * to the RFCs, but both are in common practice, so we endeavour
4790Sstevel@tonic-gate  * to not break what customers are using.
4800Sstevel@tonic-gate  */
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate static boolean_t
4830Sstevel@tonic-gate valid_hostname(const char *hostname)
4840Sstevel@tonic-gate {
4850Sstevel@tonic-gate 	unsigned int i;
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 	for (i = 0; hostname[i] != '\0'; i++) {
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 		if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
4900Sstevel@tonic-gate 		    (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
4910Sstevel@tonic-gate 			continue;
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 		return (B_FALSE);
4940Sstevel@tonic-gate 	}
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 	return (i > 0);
4970Sstevel@tonic-gate }
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate /*
5000Sstevel@tonic-gate  * iffile_to_hostname(): return the hostname contained on a line of the form
5010Sstevel@tonic-gate  *
5020Sstevel@tonic-gate  * [ ^I]*inet[ ^I]+hostname[\n]*\0
5030Sstevel@tonic-gate  *
5040Sstevel@tonic-gate  * in the file located at the specified path
5050Sstevel@tonic-gate  *
5060Sstevel@tonic-gate  *   input: const char *: the path of the file to look in for the hostname
5070Sstevel@tonic-gate  *  output: const char *: the hostname at that path, or NULL on failure
5080Sstevel@tonic-gate  */
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate #define	IFLINE_MAX	1024	/* maximum length of a hostname.<if> line */
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate const char *
5130Sstevel@tonic-gate iffile_to_hostname(const char *path)
5140Sstevel@tonic-gate {
5150Sstevel@tonic-gate 	FILE		*fp;
5160Sstevel@tonic-gate 	static char	ifline[IFLINE_MAX];
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	fp = fopen(path, "r");
5190Sstevel@tonic-gate 	if (fp == NULL)
5200Sstevel@tonic-gate 		return (NULL);
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	/*
5230Sstevel@tonic-gate 	 * /etc/hostname.<if> may contain multiple ifconfig commands, but each
5240Sstevel@tonic-gate 	 * such command is on a separate line (see the "while read ifcmds" code
5250Sstevel@tonic-gate 	 * in /etc/init.d/inetinit).  Thus we will read the file a line at a
5260Sstevel@tonic-gate 	 * time, searching for a line of the form
5270Sstevel@tonic-gate 	 *
5280Sstevel@tonic-gate 	 * [ ^I]*inet[ ^I]+hostname[\n]*\0
5290Sstevel@tonic-gate 	 *
5300Sstevel@tonic-gate 	 * extract the host name from it, and check it for validity.
5310Sstevel@tonic-gate 	 */
5320Sstevel@tonic-gate 	while (fgets(ifline, sizeof (ifline), fp) != NULL) {
5330Sstevel@tonic-gate 		char *p;
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 		if ((p = strstr(ifline, "inet")) != NULL) {
5360Sstevel@tonic-gate 			if ((p != ifline) && !isspace(p[-1])) {
5370Sstevel@tonic-gate 				(void) fclose(fp);
5380Sstevel@tonic-gate 				return (NULL);
5390Sstevel@tonic-gate 			}
5400Sstevel@tonic-gate 			p += 4;	/* skip over "inet" and expect spaces or tabs */
5410Sstevel@tonic-gate 			if ((*p == '\n') || (*p == '\0')) {
5420Sstevel@tonic-gate 				(void) fclose(fp);
5430Sstevel@tonic-gate 				return (NULL);
5440Sstevel@tonic-gate 			}
5450Sstevel@tonic-gate 			if (isspace(*p)) {
5460Sstevel@tonic-gate 				char *nlptr;
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 				/* no need to read more of the file */
5490Sstevel@tonic-gate 				(void) fclose(fp);
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 				while (isspace(*p))
5520Sstevel@tonic-gate 					p++;
5530Sstevel@tonic-gate 				if ((nlptr = strrchr(p, '\n')) != NULL)
5540Sstevel@tonic-gate 					*nlptr = '\0';
5550Sstevel@tonic-gate 				if (strlen(p) > MAXHOSTNAMELEN) {
5560Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
5570Sstevel@tonic-gate 					    "iffile_to_hostname:"
5580Sstevel@tonic-gate 					    " host name too long");
5590Sstevel@tonic-gate 					return (NULL);
5600Sstevel@tonic-gate 				}
5610Sstevel@tonic-gate 				if (valid_hostname(p)) {
5620Sstevel@tonic-gate 					return (p);
5630Sstevel@tonic-gate 				} else {
5640Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
5650Sstevel@tonic-gate 					    "iffile_to_hostname:"
5660Sstevel@tonic-gate 					    " host name not valid");
5670Sstevel@tonic-gate 					return (NULL);
5680Sstevel@tonic-gate 				}
5690Sstevel@tonic-gate 			} else {
5700Sstevel@tonic-gate 				(void) fclose(fp);
5710Sstevel@tonic-gate 				return (NULL);
5720Sstevel@tonic-gate 			}
5730Sstevel@tonic-gate 		}
5740Sstevel@tonic-gate 	}
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 	(void) fclose(fp);
5770Sstevel@tonic-gate 	return (NULL);
5780Sstevel@tonic-gate }
5793431Scarlsonj 
5803431Scarlsonj /*
5813431Scarlsonj  * init_timer(): set up a DHCP timer
5823431Scarlsonj  *
5833431Scarlsonj  *   input: dhcp_timer_t *: the timer to set up
5843431Scarlsonj  *  output: void
5853431Scarlsonj  */
5863431Scarlsonj 
5873431Scarlsonj void
5883431Scarlsonj init_timer(dhcp_timer_t *dt, lease_t startval)
5893431Scarlsonj {
5903431Scarlsonj 	dt->dt_id = -1;
5913431Scarlsonj 	dt->dt_start = startval;
5923431Scarlsonj }
5933431Scarlsonj 
5943431Scarlsonj /*
5953431Scarlsonj  * cancel_timer(): cancel a DHCP timer
5963431Scarlsonj  *
5973431Scarlsonj  *   input: dhcp_timer_t *: the timer to cancel
5983431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE otherwise
5993431Scarlsonj  */
6003431Scarlsonj 
6013431Scarlsonj boolean_t
6023431Scarlsonj cancel_timer(dhcp_timer_t *dt)
6033431Scarlsonj {
6043431Scarlsonj 	if (dt->dt_id == -1)
6053431Scarlsonj 		return (B_TRUE);
6063431Scarlsonj 
6073431Scarlsonj 	if (iu_cancel_timer(tq, dt->dt_id, NULL) == 1) {
6083431Scarlsonj 		dt->dt_id = -1;
6093431Scarlsonj 		return (B_TRUE);
6103431Scarlsonj 	}
6113431Scarlsonj 
6123431Scarlsonj 	return (B_FALSE);
6133431Scarlsonj }
6143431Scarlsonj 
6153431Scarlsonj /*
6163431Scarlsonj  * schedule_timer(): schedule a DHCP timer.  Note that it must not be already
6173431Scarlsonj  *		     running, and that we can't cancel here.  If it were, and
6183431Scarlsonj  *		     we did, we'd leak a reference to the callback argument.
6193431Scarlsonj  *
6203431Scarlsonj  *   input: dhcp_timer_t *: the timer to schedule
6213431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE otherwise
6223431Scarlsonj  */
6233431Scarlsonj 
6243431Scarlsonj boolean_t
6253431Scarlsonj schedule_timer(dhcp_timer_t *dt, iu_tq_callback_t *cbfunc, void *arg)
6263431Scarlsonj {
6273431Scarlsonj 	if (dt->dt_id != -1)
6283431Scarlsonj 		return (B_FALSE);
6293431Scarlsonj 	dt->dt_id = iu_schedule_timer(tq, dt->dt_start, cbfunc, arg);
6303431Scarlsonj 	return (dt->dt_id != -1);
6313431Scarlsonj }
6323431Scarlsonj 
6333431Scarlsonj /*
6343431Scarlsonj  * dhcpv6_status_code(): report on a DHCPv6 status code found in an option
6353431Scarlsonj  *			 buffer.
6363431Scarlsonj  *
6373431Scarlsonj  *   input: const dhcpv6_option_t *: pointer to option
6383431Scarlsonj  *	    uint_t: option length
6393431Scarlsonj  *	    const char **: error string (nul-terminated)
6403431Scarlsonj  *	    const char **: message from server (unterminated)
6413431Scarlsonj  *	    uint_t *: length of server message
6423431Scarlsonj  *  output: int: -1 on error, or >= 0 for a DHCPv6 status code
6433431Scarlsonj  */
6443431Scarlsonj 
6453431Scarlsonj int
6463431Scarlsonj dhcpv6_status_code(const dhcpv6_option_t *d6o, uint_t olen, const char **estr,
6473431Scarlsonj     const char **msg, uint_t *msglenp)
6483431Scarlsonj {
6493431Scarlsonj 	uint16_t status;
6503431Scarlsonj 	static const char *v6_status[] = {
6513431Scarlsonj 		NULL,
6523431Scarlsonj 		"Unknown reason",
6533431Scarlsonj 		"Server has no addresses available",
6543431Scarlsonj 		"Client record unavailable",
6553431Scarlsonj 		"Prefix inappropriate for link",
6563431Scarlsonj 		"Client must use multicast",
6573431Scarlsonj 		"No prefix available"
6583431Scarlsonj 	};
6593431Scarlsonj 	static char sbuf[32];
6603431Scarlsonj 
6613431Scarlsonj 	*estr = "";
6623431Scarlsonj 	*msg = "";
6633431Scarlsonj 	*msglenp = 0;
6643431Scarlsonj 	if (d6o == NULL)
6653431Scarlsonj 		return (0);
6663431Scarlsonj 	olen -= sizeof (*d6o);
6673431Scarlsonj 	if (olen < 2) {
6683431Scarlsonj 		*estr = "garbled status code";
6693431Scarlsonj 		return (-1);
6703431Scarlsonj 	}
6713431Scarlsonj 
6723431Scarlsonj 	*msg = (const char *)(d6o + 1) + 2;
6733431Scarlsonj 	*msglenp = olen - 2;
6743431Scarlsonj 
6753431Scarlsonj 	(void) memcpy(&status, d6o + 1, sizeof (status));
6763431Scarlsonj 	status = ntohs(status);
6773431Scarlsonj 	if (status > 0) {
6783431Scarlsonj 		if (status > DHCPV6_STAT_NOPREFIX) {
6793431Scarlsonj 			(void) snprintf(sbuf, sizeof (sbuf), "status %u",
6803431Scarlsonj 			    status);
6813431Scarlsonj 			*estr = sbuf;
6823431Scarlsonj 		} else {
6833431Scarlsonj 			*estr = v6_status[status];
6843431Scarlsonj 		}
6853431Scarlsonj 	}
6863431Scarlsonj 	return (status);
6873431Scarlsonj }
688