xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c (revision 3431:9f2d277dcffa)
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 /*
22*3431Scarlsonj  * 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  *
58*3431Scarlsonj  *  o  time and timer functions -- functions to handle time measurement
59*3431Scarlsonj  *     and events.
60*3431Scarlsonj  *
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  *
75*3431Scarlsonj  *   input: uchar_t: a DHCP packet type value, RFC 2131 or 3315
76*3431Scarlsonj  *	    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 *
81*3431Scarlsonj pkt_type_to_string(uchar_t type, boolean_t isv6)
820Sstevel@tonic-gate {
830Sstevel@tonic-gate 	/*
84*3431Scarlsonj 	 * note: the ordering in these arrays allows direct indexing of the
85*3431Scarlsonj 	 *	 table based on the RFC packet type value passed in.
860Sstevel@tonic-gate 	 */
870Sstevel@tonic-gate 
88*3431Scarlsonj 	static const char *v4types[] = {
890Sstevel@tonic-gate 		"BOOTP",  "DISCOVER", "OFFER",   "REQUEST", "DECLINE",
900Sstevel@tonic-gate 		"ACK",    "NAK",      "RELEASE", "INFORM"
910Sstevel@tonic-gate 	};
92*3431Scarlsonj 	static const char *v6types[] = {
93*3431Scarlsonj 		NULL, "SOLICIT", "ADVERTISE", "REQUEST",
94*3431Scarlsonj 		"CONFIRM", "RENEW", "REBIND", "REPLY",
95*3431Scarlsonj 		"RELEASE", "DECLINE", "RECONFIGURE", "INFORMATION-REQUEST",
96*3431Scarlsonj 		"RELAY-FORW", "RELAY-REPL"
97*3431Scarlsonj 	};
980Sstevel@tonic-gate 
99*3431Scarlsonj 	if (isv6) {
100*3431Scarlsonj 		if (type >= sizeof (v6types) / sizeof (*v6types) ||
101*3431Scarlsonj 		    v6types[type] == NULL)
102*3431Scarlsonj 			return ("<unknown>");
103*3431Scarlsonj 		else
104*3431Scarlsonj 			return (v6types[type]);
105*3431Scarlsonj 	} else {
106*3431Scarlsonj 		if (type >= sizeof (v4types) / sizeof (*v4types) ||
107*3431Scarlsonj 		    v4types[type] == NULL)
108*3431Scarlsonj 			return ("<unknown>");
109*3431Scarlsonj 		else
110*3431Scarlsonj 			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 /*
160*3431Scarlsonj  * hrtime_to_monosec(): converts a hrtime_t to monosec_t
1610Sstevel@tonic-gate  *
162*3431Scarlsonj  *    input: hrtime_t: the time to convert
163*3431Scarlsonj  *   output: monosec_t: the time in monosec_t
1640Sstevel@tonic-gate  */
1650Sstevel@tonic-gate 
166*3431Scarlsonj monosec_t
167*3431Scarlsonj hrtime_to_monosec(hrtime_t hrtime)
1680Sstevel@tonic-gate {
169*3431Scarlsonj 	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  *
175*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine the message is associated with
176*3431Scarlsonj  *	    const char *: the string to display
177*3431Scarlsonj  *	    uint_t: length of string
1780Sstevel@tonic-gate  *  output: void
1790Sstevel@tonic-gate  */
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate void
182*3431Scarlsonj print_server_msg(dhcp_smach_t *dsmp, const char *msg, uint_t msglen)
1830Sstevel@tonic-gate {
184*3431Scarlsonj 	if (msglen > 0) {
185*3431Scarlsonj 		dhcpmsg(MSG_INFO, "%s: message from server: %.*s",
186*3431Scarlsonj 		    dsmp->dsm_name, msglen, msg);
187*3431Scarlsonj 	}
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);
286*3431Scarlsonj 			/*
287*3431Scarlsonj 			 * Note that we're not the agent here, so the DHCP
288*3431Scarlsonj 			 * logging subsystem hasn't been configured yet.
289*3431Scarlsonj 			 */
290*3431Scarlsonj 			syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
2910Sstevel@tonic-gate 			    "waiting for adoption to complete.");
2920Sstevel@tonic-gate 			if (sleep(DHCP_ADOPT_SLEEP) == 0) {
293*3431Scarlsonj 				syslog(LOG_WARNING | LOG_DAEMON,
294*3431Scarlsonj 				    "dhcpagent: daemonize: timed out awaiting "
295*3431Scarlsonj 				    "adoption.");
2960Sstevel@tonic-gate 			}
297*3431Scarlsonj 			syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
298*3431Scarlsonj 			    "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)
313*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
3140Sstevel@tonic-gate  */
3150Sstevel@tonic-gate 
316*3431Scarlsonj static boolean_t
3170Sstevel@tonic-gate update_default_route(const char *ifname, 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;
3460Sstevel@tonic-gate 	rtmsg.rm_ifp.sdl_index	= if_nametoindex(ifname);
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
356*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE otherwise
3570Sstevel@tonic-gate  */
3580Sstevel@tonic-gate 
359*3431Scarlsonj boolean_t
3600Sstevel@tonic-gate add_default_route(const char *ifname, struct in_addr *gateway_nbo)
3610Sstevel@tonic-gate {
3620Sstevel@tonic-gate 	if (strchr(ifname, ':') != NULL)	/* see README */
363*3431Scarlsonj 		return (B_TRUE);
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	return (update_default_route(ifname, RTM_ADD, gateway_nbo, RTF_UP));
3660Sstevel@tonic-gate }
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate /*
3690Sstevel@tonic-gate  * del_default_route(): deletes the default route to the given gateway
3700Sstevel@tonic-gate  *
3710Sstevel@tonic-gate  *   input: const char *: the name of the interface associated with the route
3720Sstevel@tonic-gate  *	    struct in_addr: if not INADDR_ANY, the default gateway to remove
373*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
3740Sstevel@tonic-gate  */
3750Sstevel@tonic-gate 
376*3431Scarlsonj boolean_t
3770Sstevel@tonic-gate del_default_route(const char *ifname, struct in_addr *gateway_nbo)
3780Sstevel@tonic-gate {
3790Sstevel@tonic-gate 	if (strchr(ifname, ':') != NULL)
380*3431Scarlsonj 		return (B_TRUE);
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */
383*3431Scarlsonj 		return (B_TRUE);
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 	return (update_default_route(ifname, RTM_DELETE, gateway_nbo, 0));
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate /*
389*3431Scarlsonj  * inactivity_shutdown(): shuts down agent if there are no state machines left
390*3431Scarlsonj  *			  to manage
3910Sstevel@tonic-gate  *
3920Sstevel@tonic-gate  *   input: iu_tq_t *: unused
3930Sstevel@tonic-gate  *	    void *: unused
3940Sstevel@tonic-gate  *  output: void
3950Sstevel@tonic-gate  */
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate /* ARGSUSED */
3980Sstevel@tonic-gate void
3990Sstevel@tonic-gate inactivity_shutdown(iu_tq_t *tqp, void *arg)
4000Sstevel@tonic-gate {
401*3431Scarlsonj 	if (smach_count() > 0)	/* shouldn't happen, but... */
4020Sstevel@tonic-gate 		return;
4030Sstevel@tonic-gate 
404*3431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "inactivity_shutdown: timed out");
405*3431Scarlsonj 
4060Sstevel@tonic-gate 	iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
4070Sstevel@tonic-gate }
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate /*
4100Sstevel@tonic-gate  * graceful_shutdown(): shuts down the agent gracefully
4110Sstevel@tonic-gate  *
4120Sstevel@tonic-gate  *   input: int: the signal that caused graceful_shutdown to be called
4130Sstevel@tonic-gate  *  output: void
4140Sstevel@tonic-gate  */
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate void
4170Sstevel@tonic-gate graceful_shutdown(int sig)
4180Sstevel@tonic-gate {
4192088Smeem 	iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE :
4202088Smeem 	    DHCP_REASON_SIGNAL), drain_script, NULL);
4210Sstevel@tonic-gate }
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate /*
4240Sstevel@tonic-gate  * bind_sock(): binds a socket to a given IP address and port number
4250Sstevel@tonic-gate  *
4260Sstevel@tonic-gate  *   input: int: the socket to bind
4270Sstevel@tonic-gate  *	    in_port_t: the port number to bind to, host byte order
4280Sstevel@tonic-gate  *	    in_addr_t: the address to bind to, host byte order
429*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
4300Sstevel@tonic-gate  */
4310Sstevel@tonic-gate 
432*3431Scarlsonj boolean_t
4330Sstevel@tonic-gate bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo)
4340Sstevel@tonic-gate {
4350Sstevel@tonic-gate 	struct sockaddr_in	sin;
4360Sstevel@tonic-gate 	int			on = 1;
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
4390Sstevel@tonic-gate 	sin.sin_family = AF_INET;
4400Sstevel@tonic-gate 	sin.sin_port   = htons(port_hbo);
4410Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(addr_hbo);
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 	(void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0);
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate /*
449*3431Scarlsonj  * bind_sock_v6(): binds a socket to a given IP address and port number
450*3431Scarlsonj  *
451*3431Scarlsonj  *   input: int: the socket to bind
452*3431Scarlsonj  *	    in_port_t: the port number to bind to, host byte order
453*3431Scarlsonj  *	    in6_addr_t: the address to bind to, network byte order
454*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
455*3431Scarlsonj  */
456*3431Scarlsonj 
457*3431Scarlsonj boolean_t
458*3431Scarlsonj bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo)
459*3431Scarlsonj {
460*3431Scarlsonj 	struct sockaddr_in6	sin6;
461*3431Scarlsonj 	int			on = 1;
462*3431Scarlsonj 
463*3431Scarlsonj 	(void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
464*3431Scarlsonj 	sin6.sin6_family = AF_INET6;
465*3431Scarlsonj 	sin6.sin6_port   = htons(port_hbo);
466*3431Scarlsonj 	if (addr_nbo != NULL) {
467*3431Scarlsonj 		(void) memcpy(&sin6.sin6_addr, addr_nbo,
468*3431Scarlsonj 		    sizeof (sin6.sin6_addr));
469*3431Scarlsonj 	}
470*3431Scarlsonj 
471*3431Scarlsonj 	(void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
472*3431Scarlsonj 
473*3431Scarlsonj 	return (bind(fd, (struct sockaddr *)&sin6, sizeof (sin6)) == 0);
474*3431Scarlsonj }
475*3431Scarlsonj 
476*3431Scarlsonj /*
4770Sstevel@tonic-gate  * valid_hostname(): check whether a string is a valid hostname
4780Sstevel@tonic-gate  *
4790Sstevel@tonic-gate  *   input: const char *: the string to verify as a hostname
4800Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if the string is a valid hostname
4810Sstevel@tonic-gate  *
4820Sstevel@tonic-gate  * Note that we accept both host names beginning with a digit and
4830Sstevel@tonic-gate  * those containing hyphens.  Neither is strictly legal according
4840Sstevel@tonic-gate  * to the RFCs, but both are in common practice, so we endeavour
4850Sstevel@tonic-gate  * to not break what customers are using.
4860Sstevel@tonic-gate  */
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate static boolean_t
4890Sstevel@tonic-gate valid_hostname(const char *hostname)
4900Sstevel@tonic-gate {
4910Sstevel@tonic-gate 	unsigned int i;
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	for (i = 0; hostname[i] != '\0'; i++) {
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 		if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
4960Sstevel@tonic-gate 		    (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
4970Sstevel@tonic-gate 			continue;
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 		return (B_FALSE);
5000Sstevel@tonic-gate 	}
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	return (i > 0);
5030Sstevel@tonic-gate }
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate /*
5060Sstevel@tonic-gate  * iffile_to_hostname(): return the hostname contained on a line of the form
5070Sstevel@tonic-gate  *
5080Sstevel@tonic-gate  * [ ^I]*inet[ ^I]+hostname[\n]*\0
5090Sstevel@tonic-gate  *
5100Sstevel@tonic-gate  * in the file located at the specified path
5110Sstevel@tonic-gate  *
5120Sstevel@tonic-gate  *   input: const char *: the path of the file to look in for the hostname
5130Sstevel@tonic-gate  *  output: const char *: the hostname at that path, or NULL on failure
5140Sstevel@tonic-gate  */
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate #define	IFLINE_MAX	1024	/* maximum length of a hostname.<if> line */
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate const char *
5190Sstevel@tonic-gate iffile_to_hostname(const char *path)
5200Sstevel@tonic-gate {
5210Sstevel@tonic-gate 	FILE		*fp;
5220Sstevel@tonic-gate 	static char	ifline[IFLINE_MAX];
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 	fp = fopen(path, "r");
5250Sstevel@tonic-gate 	if (fp == NULL)
5260Sstevel@tonic-gate 		return (NULL);
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	/*
5290Sstevel@tonic-gate 	 * /etc/hostname.<if> may contain multiple ifconfig commands, but each
5300Sstevel@tonic-gate 	 * such command is on a separate line (see the "while read ifcmds" code
5310Sstevel@tonic-gate 	 * in /etc/init.d/inetinit).  Thus we will read the file a line at a
5320Sstevel@tonic-gate 	 * time, searching for a line of the form
5330Sstevel@tonic-gate 	 *
5340Sstevel@tonic-gate 	 * [ ^I]*inet[ ^I]+hostname[\n]*\0
5350Sstevel@tonic-gate 	 *
5360Sstevel@tonic-gate 	 * extract the host name from it, and check it for validity.
5370Sstevel@tonic-gate 	 */
5380Sstevel@tonic-gate 	while (fgets(ifline, sizeof (ifline), fp) != NULL) {
5390Sstevel@tonic-gate 		char *p;
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 		if ((p = strstr(ifline, "inet")) != NULL) {
5420Sstevel@tonic-gate 			if ((p != ifline) && !isspace(p[-1])) {
5430Sstevel@tonic-gate 				(void) fclose(fp);
5440Sstevel@tonic-gate 				return (NULL);
5450Sstevel@tonic-gate 			}
5460Sstevel@tonic-gate 			p += 4;	/* skip over "inet" and expect spaces or tabs */
5470Sstevel@tonic-gate 			if ((*p == '\n') || (*p == '\0')) {
5480Sstevel@tonic-gate 				(void) fclose(fp);
5490Sstevel@tonic-gate 				return (NULL);
5500Sstevel@tonic-gate 			}
5510Sstevel@tonic-gate 			if (isspace(*p)) {
5520Sstevel@tonic-gate 				char *nlptr;
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 				/* no need to read more of the file */
5550Sstevel@tonic-gate 				(void) fclose(fp);
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 				while (isspace(*p))
5580Sstevel@tonic-gate 					p++;
5590Sstevel@tonic-gate 				if ((nlptr = strrchr(p, '\n')) != NULL)
5600Sstevel@tonic-gate 					*nlptr = '\0';
5610Sstevel@tonic-gate 				if (strlen(p) > MAXHOSTNAMELEN) {
5620Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
5630Sstevel@tonic-gate 					    "iffile_to_hostname:"
5640Sstevel@tonic-gate 					    " host name too long");
5650Sstevel@tonic-gate 					return (NULL);
5660Sstevel@tonic-gate 				}
5670Sstevel@tonic-gate 				if (valid_hostname(p)) {
5680Sstevel@tonic-gate 					return (p);
5690Sstevel@tonic-gate 				} else {
5700Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
5710Sstevel@tonic-gate 					    "iffile_to_hostname:"
5720Sstevel@tonic-gate 					    " host name not valid");
5730Sstevel@tonic-gate 					return (NULL);
5740Sstevel@tonic-gate 				}
5750Sstevel@tonic-gate 			} else {
5760Sstevel@tonic-gate 				(void) fclose(fp);
5770Sstevel@tonic-gate 				return (NULL);
5780Sstevel@tonic-gate 			}
5790Sstevel@tonic-gate 		}
5800Sstevel@tonic-gate 	}
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	(void) fclose(fp);
5830Sstevel@tonic-gate 	return (NULL);
5840Sstevel@tonic-gate }
585*3431Scarlsonj 
586*3431Scarlsonj /*
587*3431Scarlsonj  * init_timer(): set up a DHCP timer
588*3431Scarlsonj  *
589*3431Scarlsonj  *   input: dhcp_timer_t *: the timer to set up
590*3431Scarlsonj  *  output: void
591*3431Scarlsonj  */
592*3431Scarlsonj 
593*3431Scarlsonj void
594*3431Scarlsonj init_timer(dhcp_timer_t *dt, lease_t startval)
595*3431Scarlsonj {
596*3431Scarlsonj 	dt->dt_id = -1;
597*3431Scarlsonj 	dt->dt_start = startval;
598*3431Scarlsonj }
599*3431Scarlsonj 
600*3431Scarlsonj /*
601*3431Scarlsonj  * cancel_timer(): cancel a DHCP timer
602*3431Scarlsonj  *
603*3431Scarlsonj  *   input: dhcp_timer_t *: the timer to cancel
604*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE otherwise
605*3431Scarlsonj  */
606*3431Scarlsonj 
607*3431Scarlsonj boolean_t
608*3431Scarlsonj cancel_timer(dhcp_timer_t *dt)
609*3431Scarlsonj {
610*3431Scarlsonj 	if (dt->dt_id == -1)
611*3431Scarlsonj 		return (B_TRUE);
612*3431Scarlsonj 
613*3431Scarlsonj 	if (iu_cancel_timer(tq, dt->dt_id, NULL) == 1) {
614*3431Scarlsonj 		dt->dt_id = -1;
615*3431Scarlsonj 		return (B_TRUE);
616*3431Scarlsonj 	}
617*3431Scarlsonj 
618*3431Scarlsonj 	return (B_FALSE);
619*3431Scarlsonj }
620*3431Scarlsonj 
621*3431Scarlsonj /*
622*3431Scarlsonj  * schedule_timer(): schedule a DHCP timer.  Note that it must not be already
623*3431Scarlsonj  *		     running, and that we can't cancel here.  If it were, and
624*3431Scarlsonj  *		     we did, we'd leak a reference to the callback argument.
625*3431Scarlsonj  *
626*3431Scarlsonj  *   input: dhcp_timer_t *: the timer to schedule
627*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE otherwise
628*3431Scarlsonj  */
629*3431Scarlsonj 
630*3431Scarlsonj boolean_t
631*3431Scarlsonj schedule_timer(dhcp_timer_t *dt, iu_tq_callback_t *cbfunc, void *arg)
632*3431Scarlsonj {
633*3431Scarlsonj 	if (dt->dt_id != -1)
634*3431Scarlsonj 		return (B_FALSE);
635*3431Scarlsonj 	dt->dt_id = iu_schedule_timer(tq, dt->dt_start, cbfunc, arg);
636*3431Scarlsonj 	return (dt->dt_id != -1);
637*3431Scarlsonj }
638*3431Scarlsonj 
639*3431Scarlsonj /*
640*3431Scarlsonj  * dhcpv6_status_code(): report on a DHCPv6 status code found in an option
641*3431Scarlsonj  *			 buffer.
642*3431Scarlsonj  *
643*3431Scarlsonj  *   input: const dhcpv6_option_t *: pointer to option
644*3431Scarlsonj  *	    uint_t: option length
645*3431Scarlsonj  *	    const char **: error string (nul-terminated)
646*3431Scarlsonj  *	    const char **: message from server (unterminated)
647*3431Scarlsonj  *	    uint_t *: length of server message
648*3431Scarlsonj  *  output: int: -1 on error, or >= 0 for a DHCPv6 status code
649*3431Scarlsonj  */
650*3431Scarlsonj 
651*3431Scarlsonj int
652*3431Scarlsonj dhcpv6_status_code(const dhcpv6_option_t *d6o, uint_t olen, const char **estr,
653*3431Scarlsonj     const char **msg, uint_t *msglenp)
654*3431Scarlsonj {
655*3431Scarlsonj 	uint16_t status;
656*3431Scarlsonj 	static const char *v6_status[] = {
657*3431Scarlsonj 		NULL,
658*3431Scarlsonj 		"Unknown reason",
659*3431Scarlsonj 		"Server has no addresses available",
660*3431Scarlsonj 		"Client record unavailable",
661*3431Scarlsonj 		"Prefix inappropriate for link",
662*3431Scarlsonj 		"Client must use multicast",
663*3431Scarlsonj 		"No prefix available"
664*3431Scarlsonj 	};
665*3431Scarlsonj 	static char sbuf[32];
666*3431Scarlsonj 
667*3431Scarlsonj 	*estr = "";
668*3431Scarlsonj 	*msg = "";
669*3431Scarlsonj 	*msglenp = 0;
670*3431Scarlsonj 	if (d6o == NULL)
671*3431Scarlsonj 		return (0);
672*3431Scarlsonj 	olen -= sizeof (*d6o);
673*3431Scarlsonj 	if (olen < 2) {
674*3431Scarlsonj 		*estr = "garbled status code";
675*3431Scarlsonj 		return (-1);
676*3431Scarlsonj 	}
677*3431Scarlsonj 
678*3431Scarlsonj 	*msg = (const char *)(d6o + 1) + 2;
679*3431Scarlsonj 	*msglenp = olen - 2;
680*3431Scarlsonj 
681*3431Scarlsonj 	(void) memcpy(&status, d6o + 1, sizeof (status));
682*3431Scarlsonj 	status = ntohs(status);
683*3431Scarlsonj 	if (status > 0) {
684*3431Scarlsonj 		if (status > DHCPV6_STAT_NOPREFIX) {
685*3431Scarlsonj 			(void) snprintf(sbuf, sizeof (sbuf), "status %u",
686*3431Scarlsonj 			    status);
687*3431Scarlsonj 			*estr = sbuf;
688*3431Scarlsonj 		} else {
689*3431Scarlsonj 			*estr = v6_status[status];
690*3431Scarlsonj 		}
691*3431Scarlsonj 	}
692*3431Scarlsonj 	return (status);
693*3431Scarlsonj }
694