xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c (revision 2088:b3a819c50e1e)
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*2088Smeem  * Common Development and Distribution License (the "License").
6*2088Smeem  * 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*2088Smeem  * 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 <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/dlpi.h>
360Sstevel@tonic-gate #include <sys/sockio.h>
370Sstevel@tonic-gate #include <sys/socket.h>
380Sstevel@tonic-gate #include <errno.h>
390Sstevel@tonic-gate #include <net/route.h>
400Sstevel@tonic-gate #include <net/if_arp.h>
410Sstevel@tonic-gate #include <string.h>
420Sstevel@tonic-gate #include <dhcpmsg.h>
430Sstevel@tonic-gate #include <ctype.h>
440Sstevel@tonic-gate #include <netdb.h>
450Sstevel@tonic-gate #include <fcntl.h>
460Sstevel@tonic-gate #include <stdio.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate #include "states.h"
490Sstevel@tonic-gate #include "agent.h"
500Sstevel@tonic-gate #include "interface.h"
510Sstevel@tonic-gate #include "util.h"
520Sstevel@tonic-gate #include "packet.h"
530Sstevel@tonic-gate #include "defaults.h"
540Sstevel@tonic-gate 
550Sstevel@tonic-gate /*
560Sstevel@tonic-gate  * this file contains utility functions that have no real better home
570Sstevel@tonic-gate  * of their own.  they can largely be broken into six categories:
580Sstevel@tonic-gate  *
590Sstevel@tonic-gate  *  o  conversion functions -- functions to turn integers into strings,
600Sstevel@tonic-gate  *     or to convert between units of a similar measure.
610Sstevel@tonic-gate  *
620Sstevel@tonic-gate  *  o  ipc-related functions -- functions to simplify the generation of
630Sstevel@tonic-gate  *     ipc messages to the agent's clients.
640Sstevel@tonic-gate  *
650Sstevel@tonic-gate  *  o  signal-related functions -- functions to clean up the agent when
660Sstevel@tonic-gate  *     it receives a signal.
670Sstevel@tonic-gate  *
680Sstevel@tonic-gate  *  o  routing table manipulation functions
690Sstevel@tonic-gate  *
700Sstevel@tonic-gate  *  o  acknak handler functions
710Sstevel@tonic-gate  *
720Sstevel@tonic-gate  *  o  true miscellany -- anything else
730Sstevel@tonic-gate  */
740Sstevel@tonic-gate 
750Sstevel@tonic-gate /*
760Sstevel@tonic-gate  * pkt_type_to_string(): stringifies a packet type
770Sstevel@tonic-gate  *
780Sstevel@tonic-gate  *   input: uchar_t: a DHCP packet type value, as defined in RFC2131
790Sstevel@tonic-gate  *  output: const char *: the stringified packet type
800Sstevel@tonic-gate  */
810Sstevel@tonic-gate 
820Sstevel@tonic-gate const char *
830Sstevel@tonic-gate pkt_type_to_string(uchar_t type)
840Sstevel@tonic-gate {
850Sstevel@tonic-gate 	/*
860Sstevel@tonic-gate 	 * note: the ordering here allows direct indexing of the table
870Sstevel@tonic-gate 	 *	 based on the RFC2131 packet type value passed in.
880Sstevel@tonic-gate 	 */
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 	static const char *types[] = {
910Sstevel@tonic-gate 		"BOOTP",  "DISCOVER", "OFFER",   "REQUEST", "DECLINE",
920Sstevel@tonic-gate 		"ACK",    "NAK",      "RELEASE", "INFORM"
930Sstevel@tonic-gate 	};
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	if (type >= (sizeof (types) / sizeof (*types)) || types[type] == NULL)
960Sstevel@tonic-gate 		return ("<unknown>");
970Sstevel@tonic-gate 
980Sstevel@tonic-gate 	return (types[type]);
990Sstevel@tonic-gate }
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate /*
1020Sstevel@tonic-gate  * dlpi_to_arp(): converts DLPI datalink types into ARP datalink types
1030Sstevel@tonic-gate  *
1040Sstevel@tonic-gate  *   input: uchar_t: the DLPI datalink type
1050Sstevel@tonic-gate  *  output: uchar_t: the ARP datalink type (0 if no corresponding code)
1060Sstevel@tonic-gate  */
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate uchar_t
1090Sstevel@tonic-gate dlpi_to_arp(uchar_t dlpi_type)
1100Sstevel@tonic-gate {
1110Sstevel@tonic-gate 	switch (dlpi_type) {
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	case DL_ETHER:
1140Sstevel@tonic-gate 		return (1);
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 	case DL_FRAME:
1170Sstevel@tonic-gate 		return (15);
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate 	case DL_ATM:
1200Sstevel@tonic-gate 		return (16);
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 	case DL_HDLC:
1230Sstevel@tonic-gate 		return (17);
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate 	case DL_FC:
1260Sstevel@tonic-gate 		return (18);
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate 	case DL_CSMACD:				/* ieee 802 networks */
1290Sstevel@tonic-gate 	case DL_TPB:
1300Sstevel@tonic-gate 	case DL_TPR:
1310Sstevel@tonic-gate 	case DL_METRO:
1320Sstevel@tonic-gate 	case DL_FDDI:
1330Sstevel@tonic-gate 		return (6);
1340Sstevel@tonic-gate 	case DL_IB:
1350Sstevel@tonic-gate 		return (ARPHRD_IB);
1360Sstevel@tonic-gate 	}
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 	return (0);
1390Sstevel@tonic-gate }
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate /*
1420Sstevel@tonic-gate  * monosec_to_string(): converts a monosec_t into a date string
1430Sstevel@tonic-gate  *
1440Sstevel@tonic-gate  *   input: monosec_t: the monosec_t to convert
1450Sstevel@tonic-gate  *  output: const char *: the corresponding date string
1460Sstevel@tonic-gate  */
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate const char *
1490Sstevel@tonic-gate monosec_to_string(monosec_t monosec)
1500Sstevel@tonic-gate {
1510Sstevel@tonic-gate 	time_t	time = monosec_to_time(monosec);
1520Sstevel@tonic-gate 	char	*time_string = ctime(&time);
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 	/* strip off the newline -- ugh, why, why, why.. */
1550Sstevel@tonic-gate 	time_string[strlen(time_string) - 1] = '\0';
1560Sstevel@tonic-gate 	return (time_string);
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate /*
1600Sstevel@tonic-gate  * monosec(): returns a monotonically increasing time in seconds that
1610Sstevel@tonic-gate  *            is not affected by stime(2) or adjtime(2).
1620Sstevel@tonic-gate  *
1630Sstevel@tonic-gate  *   input: void
1640Sstevel@tonic-gate  *  output: monosec_t: the number of seconds since some time in the past
1650Sstevel@tonic-gate  */
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate monosec_t
1680Sstevel@tonic-gate monosec(void)
1690Sstevel@tonic-gate {
1700Sstevel@tonic-gate 	return (gethrtime() / NANOSEC);
1710Sstevel@tonic-gate }
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate /*
1740Sstevel@tonic-gate  * monosec_to_time(): converts a monosec_t into real wall time
1750Sstevel@tonic-gate  *
1760Sstevel@tonic-gate  *    input: monosec_t: the absolute monosec_t to convert
1770Sstevel@tonic-gate  *   output: time_t: the absolute time that monosec_t represents in wall time
1780Sstevel@tonic-gate  */
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate time_t
1810Sstevel@tonic-gate monosec_to_time(monosec_t abs_monosec)
1820Sstevel@tonic-gate {
1830Sstevel@tonic-gate 	return (abs_monosec - monosec()) + time(NULL);
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate /*
1870Sstevel@tonic-gate  * send_ok_reply(): sends an "ok" reply to a request and closes the ipc
1880Sstevel@tonic-gate  *		    connection
1890Sstevel@tonic-gate  *
1900Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request to reply to
1910Sstevel@tonic-gate  *	    int *: the ipc connection file descriptor (set to -1 on return)
1920Sstevel@tonic-gate  *  output: void
1930Sstevel@tonic-gate  *    note: the request is freed (thus the request must be on the heap).
1940Sstevel@tonic-gate  */
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate void
1970Sstevel@tonic-gate send_ok_reply(dhcp_ipc_request_t *request, int *control_fd)
1980Sstevel@tonic-gate {
1990Sstevel@tonic-gate 	send_error_reply(request, 0, control_fd);
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate  * send_error_reply(): sends an "error" reply to a request and closes the ipc
2040Sstevel@tonic-gate  *		       connection
2050Sstevel@tonic-gate  *
2060Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request to reply to
2070Sstevel@tonic-gate  *	    int: the error to send back on the ipc connection
2080Sstevel@tonic-gate  *	    int *: the ipc connection file descriptor (set to -1 on return)
2090Sstevel@tonic-gate  *  output: void
2100Sstevel@tonic-gate  *    note: the request is freed (thus the request must be on the heap).
2110Sstevel@tonic-gate  */
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate void
2140Sstevel@tonic-gate send_error_reply(dhcp_ipc_request_t *request, int error, int *control_fd)
2150Sstevel@tonic-gate {
2160Sstevel@tonic-gate 	send_data_reply(request, control_fd, error, DHCP_TYPE_NONE, NULL, NULL);
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate /*
2200Sstevel@tonic-gate  * send_data_reply(): sends a reply to a request and closes the ipc connection
2210Sstevel@tonic-gate  *
2220Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request to reply to
2230Sstevel@tonic-gate  *	    int *: the ipc connection file descriptor (set to -1 on return)
2240Sstevel@tonic-gate  *	    int: the status to send back on the ipc connection (zero for
2250Sstevel@tonic-gate  *		 success, DHCP_IPC_E_* otherwise).
2260Sstevel@tonic-gate  *	    dhcp_data_type_t: the type of the payload in the reply
2270Sstevel@tonic-gate  *	    void *: the payload for the reply, or NULL if there is no payload
2280Sstevel@tonic-gate  *	    size_t: the size of the payload
2290Sstevel@tonic-gate  *  output: void
2300Sstevel@tonic-gate  *    note: the request is freed (thus the request must be on the heap).
2310Sstevel@tonic-gate  */
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate void
2340Sstevel@tonic-gate send_data_reply(dhcp_ipc_request_t *request, int *control_fd,
2350Sstevel@tonic-gate     int error, dhcp_data_type_t type, void *buffer, size_t size)
2360Sstevel@tonic-gate {
2370Sstevel@tonic-gate 	dhcp_ipc_reply_t	*reply;
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	if (*control_fd == -1)
2400Sstevel@tonic-gate 		return;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	reply = dhcp_ipc_alloc_reply(request, error, buffer, size, type);
2430Sstevel@tonic-gate 	if (reply == NULL)
2440Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "send_data_reply: cannot allocate reply");
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	else if (dhcp_ipc_send_reply(*control_fd, reply) != 0)
2470Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "send_data_reply: dhcp_ipc_send_reply");
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	/*
2500Sstevel@tonic-gate 	 * free the request since we've now used it to send our reply.
2510Sstevel@tonic-gate 	 * we can also close the socket since the reply has been sent.
2520Sstevel@tonic-gate 	 */
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 	free(reply);
2550Sstevel@tonic-gate 	free(request);
2560Sstevel@tonic-gate 	(void) dhcp_ipc_close(*control_fd);
2570Sstevel@tonic-gate 	*control_fd = -1;
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate /*
2610Sstevel@tonic-gate  * print_server_msg(): prints a message from a DHCP server
2620Sstevel@tonic-gate  *
2630Sstevel@tonic-gate  *   input: struct ifslist *: the interface the message came in on
2640Sstevel@tonic-gate  *	    DHCP_OPT *: the option containing the string to display
2650Sstevel@tonic-gate  *  output: void
2660Sstevel@tonic-gate  */
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate void
2690Sstevel@tonic-gate print_server_msg(struct ifslist *ifsp, DHCP_OPT *p)
2700Sstevel@tonic-gate {
2710Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "%s: message from server: %.*s", ifsp->if_name,
2720Sstevel@tonic-gate 	    p->len, p->value);
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate /*
2760Sstevel@tonic-gate  * alrm_exit(): Signal handler for SIGARLM. terminates grandparent.
2770Sstevel@tonic-gate  *
2780Sstevel@tonic-gate  *    input: int: signal the handler was called with.
2790Sstevel@tonic-gate  *
2800Sstevel@tonic-gate  *   output: void
2810Sstevel@tonic-gate  */
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate static void
2840Sstevel@tonic-gate alrm_exit(int sig)
2850Sstevel@tonic-gate {
2860Sstevel@tonic-gate 	int exitval;
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	if (sig == SIGALRM && grandparent != 0)
2890Sstevel@tonic-gate 		exitval = EXIT_SUCCESS;
2900Sstevel@tonic-gate 	else
2910Sstevel@tonic-gate 		exitval = EXIT_FAILURE;
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	_exit(exitval);
2940Sstevel@tonic-gate }
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate /*
2970Sstevel@tonic-gate  * daemonize(): daemonizes the process
2980Sstevel@tonic-gate  *
2990Sstevel@tonic-gate  *   input: void
3000Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
3010Sstevel@tonic-gate  */
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate int
3040Sstevel@tonic-gate daemonize(void)
3050Sstevel@tonic-gate {
3060Sstevel@tonic-gate 	/*
3070Sstevel@tonic-gate 	 * We've found that adoption takes sufficiently long that
3080Sstevel@tonic-gate 	 * a dhcpinfo run after dhcpagent -a is started may occur
3090Sstevel@tonic-gate 	 * before the agent is ready to process the request.
3100Sstevel@tonic-gate 	 * The result is an error message and an unhappy user.
3110Sstevel@tonic-gate 	 *
3120Sstevel@tonic-gate 	 * The initial process now sleeps for DHCP_ADOPT_SLEEP,
3130Sstevel@tonic-gate 	 * unless interrupted by a SIGALRM, in which case it
3140Sstevel@tonic-gate 	 * exits immediately. This has the effect that the
3150Sstevel@tonic-gate 	 * grandparent doesn't exit until the dhcpagent is ready
3160Sstevel@tonic-gate 	 * to process requests. This defers the the balance of
3170Sstevel@tonic-gate 	 * the system start-up script processing until the
3180Sstevel@tonic-gate 	 * dhcpagent is ready to field requests.
3190Sstevel@tonic-gate 	 *
3200Sstevel@tonic-gate 	 * grandparent is only set for the adopt case; other
3210Sstevel@tonic-gate 	 * cases do not require the wait.
3220Sstevel@tonic-gate 	 */
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 	if (grandparent != 0)
3250Sstevel@tonic-gate 		(void) signal(SIGALRM, alrm_exit);
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	switch (fork()) {
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	case -1:
3300Sstevel@tonic-gate 		return (0);
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	case  0:
3330Sstevel@tonic-gate 		if (grandparent != 0)
3340Sstevel@tonic-gate 			(void) signal(SIGALRM, SIG_DFL);
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 		/*
3370Sstevel@tonic-gate 		 * setsid() makes us lose our controlling terminal,
3380Sstevel@tonic-gate 		 * and become both a session leader and a process
3390Sstevel@tonic-gate 		 * group leader.
3400Sstevel@tonic-gate 		 */
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 		(void) setsid();
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 		/*
3450Sstevel@tonic-gate 		 * under POSIX, a session leader can accidentally
3460Sstevel@tonic-gate 		 * (through open(2)) acquire a controlling terminal if
3470Sstevel@tonic-gate 		 * it does not have one.  just to be safe, fork again
3480Sstevel@tonic-gate 		 * so we are not a session leader.
3490Sstevel@tonic-gate 		 */
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 		switch (fork()) {
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 		case -1:
3540Sstevel@tonic-gate 			return (0);
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 		case 0:
3570Sstevel@tonic-gate 			(void) signal(SIGHUP, SIG_IGN);
3580Sstevel@tonic-gate 			(void) chdir("/");
3590Sstevel@tonic-gate 			(void) umask(022);
3600Sstevel@tonic-gate 			closefrom(0);
3610Sstevel@tonic-gate 			break;
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 		default:
3640Sstevel@tonic-gate 			_exit(EXIT_SUCCESS);
3650Sstevel@tonic-gate 		}
3660Sstevel@tonic-gate 		break;
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	default:
3690Sstevel@tonic-gate 		if (grandparent != 0) {
3700Sstevel@tonic-gate 			(void) signal(SIGCHLD, SIG_IGN);
3710Sstevel@tonic-gate 			dhcpmsg(MSG_DEBUG, "dhcpagent: daemonize: "
3720Sstevel@tonic-gate 			    "waiting for adoption to complete.");
3730Sstevel@tonic-gate 			if (sleep(DHCP_ADOPT_SLEEP) == 0) {
3740Sstevel@tonic-gate 				dhcpmsg(MSG_WARNING, "dhcpagent: daemonize: "
3750Sstevel@tonic-gate 				    "timed out awaiting adoption.");
3760Sstevel@tonic-gate 			}
3770Sstevel@tonic-gate 		}
3780Sstevel@tonic-gate 		_exit(EXIT_SUCCESS);
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 	return (1);
3820Sstevel@tonic-gate }
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate /*
3850Sstevel@tonic-gate  * update_default_route(): update the interface's default route
3860Sstevel@tonic-gate  *
3870Sstevel@tonic-gate  *   input: int: the type of message; either RTM_ADD or RTM_DELETE
3880Sstevel@tonic-gate  *	    struct in_addr: the default gateway to use
3890Sstevel@tonic-gate  *	    const char *: the interface associated with the route
3900Sstevel@tonic-gate  *	    int: any additional flags (besides RTF_STATIC and RTF_GATEWAY)
3910Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
3920Sstevel@tonic-gate  */
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate static int
3950Sstevel@tonic-gate update_default_route(const char *ifname, int type, struct in_addr *gateway_nbo,
3960Sstevel@tonic-gate     int flags)
3970Sstevel@tonic-gate {
3980Sstevel@tonic-gate 	static int rtsock_fd = -1;
3990Sstevel@tonic-gate 	struct {
4000Sstevel@tonic-gate 		struct rt_msghdr	rm_mh;
4010Sstevel@tonic-gate 		struct sockaddr_in	rm_dst;
4020Sstevel@tonic-gate 		struct sockaddr_in	rm_gw;
4030Sstevel@tonic-gate 		struct sockaddr_in	rm_mask;
4040Sstevel@tonic-gate 		struct sockaddr_dl	rm_ifp;
4050Sstevel@tonic-gate 	} rtmsg;
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	if (rtsock_fd == -1) {
4080Sstevel@tonic-gate 		rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0);
4090Sstevel@tonic-gate 		if (rtsock_fd == -1) {
4100Sstevel@tonic-gate 			dhcpmsg(MSG_ERR, "update_default_route: "
4110Sstevel@tonic-gate 			    "cannot create routing socket");
4120Sstevel@tonic-gate 			return (0);
4130Sstevel@tonic-gate 		}
4140Sstevel@tonic-gate 	}
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	(void) memset(&rtmsg, 0, sizeof (rtmsg));
4170Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_version = RTM_VERSION;
4180Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_msglen	= sizeof (rtmsg);
4190Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_type	= type;
4200Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_pid	= getpid();
4210Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_flags	= RTF_GATEWAY | RTF_STATIC | flags;
4220Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_addrs	= RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP;
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	rtmsg.rm_gw.sin_family	= AF_INET;
4250Sstevel@tonic-gate 	rtmsg.rm_gw.sin_addr	= *gateway_nbo;
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	rtmsg.rm_dst.sin_family = AF_INET;
4280Sstevel@tonic-gate 	rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY);
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	rtmsg.rm_mask.sin_family = AF_INET;
4310Sstevel@tonic-gate 	rtmsg.rm_mask.sin_addr.s_addr = htonl(0);
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	rtmsg.rm_ifp.sdl_family	= AF_LINK;
4340Sstevel@tonic-gate 	rtmsg.rm_ifp.sdl_index	= if_nametoindex(ifname);
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg));
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate /*
4400Sstevel@tonic-gate  * add_default_route(): add the default route to the given gateway
4410Sstevel@tonic-gate  *
4420Sstevel@tonic-gate  *   input: const char *: the name of the interface associated with the route
4430Sstevel@tonic-gate  *	    struct in_addr: the default gateway to add
4440Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
4450Sstevel@tonic-gate  */
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate int
4480Sstevel@tonic-gate add_default_route(const char *ifname, struct in_addr *gateway_nbo)
4490Sstevel@tonic-gate {
4500Sstevel@tonic-gate 	if (strchr(ifname, ':') != NULL)	/* see README */
4510Sstevel@tonic-gate 		return (1);
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	return (update_default_route(ifname, RTM_ADD, gateway_nbo, RTF_UP));
4540Sstevel@tonic-gate }
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate /*
4570Sstevel@tonic-gate  * del_default_route(): deletes the default route to the given gateway
4580Sstevel@tonic-gate  *
4590Sstevel@tonic-gate  *   input: const char *: the name of the interface associated with the route
4600Sstevel@tonic-gate  *	    struct in_addr: if not INADDR_ANY, the default gateway to remove
4610Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
4620Sstevel@tonic-gate  */
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate int
4650Sstevel@tonic-gate del_default_route(const char *ifname, struct in_addr *gateway_nbo)
4660Sstevel@tonic-gate {
4670Sstevel@tonic-gate 	if (strchr(ifname, ':') != NULL)
4680Sstevel@tonic-gate 		return (1);
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */
4710Sstevel@tonic-gate 		return (1);
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	return (update_default_route(ifname, RTM_DELETE, gateway_nbo, 0));
4740Sstevel@tonic-gate }
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate /*
4770Sstevel@tonic-gate  * inactivity_shutdown(): shuts down agent if there are no interfaces to manage
4780Sstevel@tonic-gate  *
4790Sstevel@tonic-gate  *   input: iu_tq_t *: unused
4800Sstevel@tonic-gate  *	    void *: unused
4810Sstevel@tonic-gate  *  output: void
4820Sstevel@tonic-gate  */
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate /* ARGSUSED */
4850Sstevel@tonic-gate void
4860Sstevel@tonic-gate inactivity_shutdown(iu_tq_t *tqp, void *arg)
4870Sstevel@tonic-gate {
4880Sstevel@tonic-gate 	if (ifs_count() > 0)	/* shouldn't happen, but... */
4890Sstevel@tonic-gate 		return;
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 	iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
4920Sstevel@tonic-gate }
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate /*
4950Sstevel@tonic-gate  * graceful_shutdown(): shuts down the agent gracefully
4960Sstevel@tonic-gate  *
4970Sstevel@tonic-gate  *   input: int: the signal that caused graceful_shutdown to be called
4980Sstevel@tonic-gate  *  output: void
4990Sstevel@tonic-gate  */
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate void
5020Sstevel@tonic-gate graceful_shutdown(int sig)
5030Sstevel@tonic-gate {
504*2088Smeem 	iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE :
505*2088Smeem 	    DHCP_REASON_SIGNAL), drain_script, NULL);
5060Sstevel@tonic-gate }
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate /*
5090Sstevel@tonic-gate  * register_acknak(): registers dhcp_acknak() to be called back when ACK or
5100Sstevel@tonic-gate  *		      NAK packets are received on a given interface
5110Sstevel@tonic-gate  *
5120Sstevel@tonic-gate  *   input: struct ifslist *: the interface to register for
5130Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
5140Sstevel@tonic-gate  */
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate int
5170Sstevel@tonic-gate register_acknak(struct ifslist *ifsp)
5180Sstevel@tonic-gate {
5190Sstevel@tonic-gate 	iu_event_id_t	ack_id, ack_bcast_id = -1;
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 	/*
5220Sstevel@tonic-gate 	 * having an acknak id already registered isn't impossible;
5230Sstevel@tonic-gate 	 * handle the situation as gracefully as possible.
5240Sstevel@tonic-gate 	 */
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	if (ifsp->if_acknak_id != -1) {
5270Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "register_acknak: acknak id pending, "
5280Sstevel@tonic-gate 		    "attempting to cancel");
5290Sstevel@tonic-gate 		if (unregister_acknak(ifsp) == 0)
5300Sstevel@tonic-gate 			return (0);
5310Sstevel@tonic-gate 	}
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	switch (ifsp->if_state) {
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 	case BOUND:
5360Sstevel@tonic-gate 	case REBINDING:
5370Sstevel@tonic-gate 	case RENEWING:
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 		ack_bcast_id = iu_register_event(eh, ifsp->if_sock_fd, POLLIN,
5400Sstevel@tonic-gate 		    dhcp_acknak, ifsp);
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 		if (ack_bcast_id == -1) {
5430Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "register_acknak: cannot "
5440Sstevel@tonic-gate 			    "register to receive socket broadcasts");
5450Sstevel@tonic-gate 			return (0);
5460Sstevel@tonic-gate 		}
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 		ack_id = iu_register_event(eh, ifsp->if_sock_ip_fd, POLLIN,
5490Sstevel@tonic-gate 		    dhcp_acknak, ifsp);
5500Sstevel@tonic-gate 		break;
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	default:
5530Sstevel@tonic-gate 		ack_id = iu_register_event(eh, ifsp->if_dlpi_fd, POLLIN,
5540Sstevel@tonic-gate 		    dhcp_acknak, ifsp);
5550Sstevel@tonic-gate 		break;
5560Sstevel@tonic-gate 	}
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	if (ack_id == -1) {
5590Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "register_acknak: cannot register event");
5600Sstevel@tonic-gate 		(void) iu_unregister_event(eh, ack_bcast_id, NULL);
5610Sstevel@tonic-gate 		return (0);
5620Sstevel@tonic-gate 	}
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	ifsp->if_acknak_id = ack_id;
5650Sstevel@tonic-gate 	hold_ifs(ifsp);
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 	ifsp->if_acknak_bcast_id = ack_bcast_id;
5680Sstevel@tonic-gate 	if (ifsp->if_acknak_bcast_id != -1) {
5690Sstevel@tonic-gate 		hold_ifs(ifsp);
5700Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "register_acknak: registered broadcast id "
5710Sstevel@tonic-gate 		    "%d", ack_bcast_id);
5720Sstevel@tonic-gate 	}
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "register_acknak: registered acknak id %d", ack_id);
5750Sstevel@tonic-gate 	return (1);
5760Sstevel@tonic-gate }
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate /*
5790Sstevel@tonic-gate  * unregister_acknak(): unregisters dhcp_acknak() to be called back
5800Sstevel@tonic-gate  *
5810Sstevel@tonic-gate  *   input: struct ifslist *: the interface to unregister for
5820Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
5830Sstevel@tonic-gate  */
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate int
5860Sstevel@tonic-gate unregister_acknak(struct ifslist *ifsp)
5870Sstevel@tonic-gate {
5880Sstevel@tonic-gate 	if (ifsp->if_acknak_id != -1) {
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 		if (iu_unregister_event(eh, ifsp->if_acknak_id, NULL) == 0) {
5910Sstevel@tonic-gate 			dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot "
5920Sstevel@tonic-gate 			    "unregister acknak id %d on %s",
5930Sstevel@tonic-gate 			    ifsp->if_acknak_id, ifsp->if_name);
5940Sstevel@tonic-gate 			return (0);
5950Sstevel@tonic-gate 		}
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered acknak id "
5980Sstevel@tonic-gate 		    "%d", ifsp->if_acknak_id);
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate 		ifsp->if_acknak_id = -1;
6010Sstevel@tonic-gate 		(void) release_ifs(ifsp);
6020Sstevel@tonic-gate 	}
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate 	if (ifsp->if_acknak_bcast_id != -1) {
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 		if (iu_unregister_event(eh, ifsp->if_acknak_bcast_id, NULL)
6070Sstevel@tonic-gate 		    == 0) {
6080Sstevel@tonic-gate 			dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot "
6090Sstevel@tonic-gate 			    "unregister broadcast id %d on %s",
6100Sstevel@tonic-gate 			    ifsp->if_acknak_id, ifsp->if_name);
6110Sstevel@tonic-gate 			return (0);
6120Sstevel@tonic-gate 		}
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered "
6150Sstevel@tonic-gate 		    "broadcast id %d", ifsp->if_acknak_bcast_id);
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 		ifsp->if_acknak_bcast_id = -1;
6180Sstevel@tonic-gate 		(void) release_ifs(ifsp);
6190Sstevel@tonic-gate 	}
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 	return (1);
6220Sstevel@tonic-gate }
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate /*
6250Sstevel@tonic-gate  * bind_sock(): binds a socket to a given IP address and port number
6260Sstevel@tonic-gate  *
6270Sstevel@tonic-gate  *   input: int: the socket to bind
6280Sstevel@tonic-gate  *	    in_port_t: the port number to bind to, host byte order
6290Sstevel@tonic-gate  *	    in_addr_t: the address to bind to, host byte order
6300Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
6310Sstevel@tonic-gate  */
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate int
6340Sstevel@tonic-gate bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo)
6350Sstevel@tonic-gate {
6360Sstevel@tonic-gate 	struct sockaddr_in	sin;
6370Sstevel@tonic-gate 	int			on = 1;
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
6400Sstevel@tonic-gate 	sin.sin_family = AF_INET;
6410Sstevel@tonic-gate 	sin.sin_port   = htons(port_hbo);
6420Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(addr_hbo);
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	(void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 	return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0);
6470Sstevel@tonic-gate }
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate /*
6500Sstevel@tonic-gate  * valid_hostname(): check whether a string is a valid hostname
6510Sstevel@tonic-gate  *
6520Sstevel@tonic-gate  *   input: const char *: the string to verify as a hostname
6530Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if the string is a valid hostname
6540Sstevel@tonic-gate  *
6550Sstevel@tonic-gate  * Note that we accept both host names beginning with a digit and
6560Sstevel@tonic-gate  * those containing hyphens.  Neither is strictly legal according
6570Sstevel@tonic-gate  * to the RFCs, but both are in common practice, so we endeavour
6580Sstevel@tonic-gate  * to not break what customers are using.
6590Sstevel@tonic-gate  */
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate static boolean_t
6620Sstevel@tonic-gate valid_hostname(const char *hostname)
6630Sstevel@tonic-gate {
6640Sstevel@tonic-gate 	unsigned int i;
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 	for (i = 0; hostname[i] != '\0'; i++) {
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 		if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
6690Sstevel@tonic-gate 		    (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
6700Sstevel@tonic-gate 			continue;
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate 		return (B_FALSE);
6730Sstevel@tonic-gate 	}
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	return (i > 0);
6760Sstevel@tonic-gate }
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate /*
6790Sstevel@tonic-gate  * iffile_to_hostname(): return the hostname contained on a line of the form
6800Sstevel@tonic-gate  *
6810Sstevel@tonic-gate  * [ ^I]*inet[ ^I]+hostname[\n]*\0
6820Sstevel@tonic-gate  *
6830Sstevel@tonic-gate  * in the file located at the specified path
6840Sstevel@tonic-gate  *
6850Sstevel@tonic-gate  *   input: const char *: the path of the file to look in for the hostname
6860Sstevel@tonic-gate  *  output: const char *: the hostname at that path, or NULL on failure
6870Sstevel@tonic-gate  */
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate #define	IFLINE_MAX	1024	/* maximum length of a hostname.<if> line */
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate const char *
6920Sstevel@tonic-gate iffile_to_hostname(const char *path)
6930Sstevel@tonic-gate {
6940Sstevel@tonic-gate 	FILE		*fp;
6950Sstevel@tonic-gate 	static char	ifline[IFLINE_MAX];
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 	fp = fopen(path, "r");
6980Sstevel@tonic-gate 	if (fp == NULL)
6990Sstevel@tonic-gate 		return (NULL);
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	/*
7020Sstevel@tonic-gate 	 * /etc/hostname.<if> may contain multiple ifconfig commands, but each
7030Sstevel@tonic-gate 	 * such command is on a separate line (see the "while read ifcmds" code
7040Sstevel@tonic-gate 	 * in /etc/init.d/inetinit).  Thus we will read the file a line at a
7050Sstevel@tonic-gate 	 * time, searching for a line of the form
7060Sstevel@tonic-gate 	 *
7070Sstevel@tonic-gate 	 * [ ^I]*inet[ ^I]+hostname[\n]*\0
7080Sstevel@tonic-gate 	 *
7090Sstevel@tonic-gate 	 * extract the host name from it, and check it for validity.
7100Sstevel@tonic-gate 	 */
7110Sstevel@tonic-gate 	while (fgets(ifline, sizeof (ifline), fp) != NULL) {
7120Sstevel@tonic-gate 		char *p;
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate 		if ((p = strstr(ifline, "inet")) != NULL) {
7150Sstevel@tonic-gate 			if ((p != ifline) && !isspace(p[-1])) {
7160Sstevel@tonic-gate 				(void) fclose(fp);
7170Sstevel@tonic-gate 				return (NULL);
7180Sstevel@tonic-gate 			}
7190Sstevel@tonic-gate 			p += 4;	/* skip over "inet" and expect spaces or tabs */
7200Sstevel@tonic-gate 			if ((*p == '\n') || (*p == '\0')) {
7210Sstevel@tonic-gate 				(void) fclose(fp);
7220Sstevel@tonic-gate 				return (NULL);
7230Sstevel@tonic-gate 			}
7240Sstevel@tonic-gate 			if (isspace(*p)) {
7250Sstevel@tonic-gate 				char *nlptr;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 				/* no need to read more of the file */
7280Sstevel@tonic-gate 				(void) fclose(fp);
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 				while (isspace(*p))
7310Sstevel@tonic-gate 					p++;
7320Sstevel@tonic-gate 				if ((nlptr = strrchr(p, '\n')) != NULL)
7330Sstevel@tonic-gate 					*nlptr = '\0';
7340Sstevel@tonic-gate 				if (strlen(p) > MAXHOSTNAMELEN) {
7350Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
7360Sstevel@tonic-gate 					    "iffile_to_hostname:"
7370Sstevel@tonic-gate 					    " host name too long");
7380Sstevel@tonic-gate 					return (NULL);
7390Sstevel@tonic-gate 				}
7400Sstevel@tonic-gate 				if (valid_hostname(p)) {
7410Sstevel@tonic-gate 					return (p);
7420Sstevel@tonic-gate 				} else {
7430Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
7440Sstevel@tonic-gate 					    "iffile_to_hostname:"
7450Sstevel@tonic-gate 					    " host name not valid");
7460Sstevel@tonic-gate 					return (NULL);
7470Sstevel@tonic-gate 				}
7480Sstevel@tonic-gate 			} else {
7490Sstevel@tonic-gate 				(void) fclose(fp);
7500Sstevel@tonic-gate 				return (NULL);
7510Sstevel@tonic-gate 			}
7520Sstevel@tonic-gate 		}
7530Sstevel@tonic-gate 	}
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	(void) fclose(fp);
7560Sstevel@tonic-gate 	return (NULL);
7570Sstevel@tonic-gate }
758