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
53431Scarlsonj  * Common Development and Distribution License (the "License").
63431Scarlsonj  * 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*8562SPeter.Memishian@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <string.h>
270Sstevel@tonic-gate #include <unistd.h>
280Sstevel@tonic-gate #include <stdlib.h>
290Sstevel@tonic-gate #include <sys/uio.h>
300Sstevel@tonic-gate #include <sys/socket.h>
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <fcntl.h>
330Sstevel@tonic-gate #include <errno.h>
340Sstevel@tonic-gate #include <netinet/in.h>
353431Scarlsonj #include <netinet/tcp.h>
360Sstevel@tonic-gate #include <net/if.h>
370Sstevel@tonic-gate #include <sys/sockio.h>
380Sstevel@tonic-gate #include <sys/fcntl.h>
393431Scarlsonj #include <sys/time.h>
400Sstevel@tonic-gate #include <stdio.h>		/* snprintf */
410Sstevel@tonic-gate #include <arpa/inet.h>		/* ntohl, ntohs, etc */
420Sstevel@tonic-gate 
430Sstevel@tonic-gate #include "dhcpagent_ipc.h"
440Sstevel@tonic-gate #include "dhcpagent_util.h"
450Sstevel@tonic-gate 
460Sstevel@tonic-gate /*
470Sstevel@tonic-gate  * the protocol used here is a simple request/reply scheme: a client
480Sstevel@tonic-gate  * sends a dhcp_ipc_request_t message to the agent, and the agent
490Sstevel@tonic-gate  * sends a dhcp_ipc_reply_t back to the client.  since the requests
500Sstevel@tonic-gate  * and replies can be variable-length, they are prefixed on "the wire"
510Sstevel@tonic-gate  * by a 32-bit number that tells the other end how many bytes to
520Sstevel@tonic-gate  * expect.
530Sstevel@tonic-gate  *
540Sstevel@tonic-gate  * the format of a request consists of a single dhcp_ipc_request_t;
550Sstevel@tonic-gate  * note that the length of this dhcp_ipc_request_t is variable (using
560Sstevel@tonic-gate  * the standard c array-of-size-1 trick).  the type of the payload is
570Sstevel@tonic-gate  * given by `data_type', which is guaranteed to be `data_length' bytes
580Sstevel@tonic-gate  * long starting at `buffer'.  note that `buffer' is guaranteed to be
590Sstevel@tonic-gate  * 32-bit aligned but it is poor taste to rely on this.
600Sstevel@tonic-gate  *
610Sstevel@tonic-gate  * the format of a reply is much the same: a single dhcp_ipc_reply_t;
620Sstevel@tonic-gate  * note again that the length of the dhcp_ipc_reply_t is variable.
630Sstevel@tonic-gate  * the type of the payload is given by `data_type', which is
640Sstevel@tonic-gate  * guaranteed to be `data_length' bytes long starting at `buffer'.
650Sstevel@tonic-gate  * once again, note that `buffer' is guaranteed to be 32-bit aligned
660Sstevel@tonic-gate  * but it is poor taste to rely on this.
670Sstevel@tonic-gate  *
680Sstevel@tonic-gate  * requests and replies can be paired up by comparing `ipc_id' fields.
690Sstevel@tonic-gate  */
700Sstevel@tonic-gate 
710Sstevel@tonic-gate #define	BUFMAX	256
720Sstevel@tonic-gate 
730Sstevel@tonic-gate static int	dhcp_ipc_timed_read(int, void *, unsigned int, int *);
740Sstevel@tonic-gate static int	getinfo_ifnames(const char *, dhcp_optnum_t *, DHCP_OPT **);
750Sstevel@tonic-gate static char	*get_ifnames(int, int);
760Sstevel@tonic-gate 
774106Scarlsonj /* must be kept in sync with enum in dhcpagent_ipc.h */
784106Scarlsonj static const char *ipc_typestr[] = {
794106Scarlsonj 	"drop", "extend", "ping", "release", "start", "status",
804106Scarlsonj 	"inform", "get_tag"
814106Scarlsonj };
824106Scarlsonj 
830Sstevel@tonic-gate /*
840Sstevel@tonic-gate  * dhcp_ipc_alloc_request(): allocates a dhcp_ipc_request_t of the given type
850Sstevel@tonic-gate  *			     and interface, with a timeout of 0.
860Sstevel@tonic-gate  *
870Sstevel@tonic-gate  *   input: dhcp_ipc_type_t: the type of ipc request to allocate
880Sstevel@tonic-gate  *	    const char *: the interface to associate the request with
893431Scarlsonj  *	    const void *: the payload to send with the message (NULL if none)
900Sstevel@tonic-gate  *	    uint32_t: the payload size (0 if none)
910Sstevel@tonic-gate  *	    dhcp_data_type_t: the description of the type of payload
920Sstevel@tonic-gate  *  output: dhcp_ipc_request_t *: the request on success, NULL on failure
930Sstevel@tonic-gate  */
940Sstevel@tonic-gate 
950Sstevel@tonic-gate dhcp_ipc_request_t *
963431Scarlsonj dhcp_ipc_alloc_request(dhcp_ipc_type_t type, const char *ifname,
973431Scarlsonj     const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
980Sstevel@tonic-gate {
990Sstevel@tonic-gate 	dhcp_ipc_request_t *request = calloc(1, DHCP_IPC_REQUEST_SIZE +
1000Sstevel@tonic-gate 	    buffer_size);
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 	if (request == NULL)
1030Sstevel@tonic-gate 		return (NULL);
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	request->message_type   = type;
1060Sstevel@tonic-gate 	request->data_length    = buffer_size;
1070Sstevel@tonic-gate 	request->data_type	= data_type;
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	if (ifname != NULL)
110*8562SPeter.Memishian@Sun.COM 		(void) strlcpy(request->ifname, ifname, LIFNAMSIZ);
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	if (buffer != NULL)
1130Sstevel@tonic-gate 		(void) memcpy(request->buffer, buffer, buffer_size);
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	return (request);
1160Sstevel@tonic-gate }
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate /*
1190Sstevel@tonic-gate  * dhcp_ipc_alloc_reply(): allocates a dhcp_ipc_reply_t
1200Sstevel@tonic-gate  *
1210Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request the reply is for
1220Sstevel@tonic-gate  *	    int: the return code (0 for success, DHCP_IPC_E_* otherwise)
1233431Scarlsonj  *	    const void *: the payload to send with the message (NULL if none)
1240Sstevel@tonic-gate  *	    uint32_t: the payload size (0 if none)
1250Sstevel@tonic-gate  *	    dhcp_data_type_t: the description of the type of payload
1260Sstevel@tonic-gate  *  output: dhcp_ipc_reply_t *: the reply on success, NULL on failure
1270Sstevel@tonic-gate  */
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate dhcp_ipc_reply_t *
1303431Scarlsonj dhcp_ipc_alloc_reply(dhcp_ipc_request_t *request, int return_code,
1313431Scarlsonj     const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
1320Sstevel@tonic-gate {
1330Sstevel@tonic-gate 	dhcp_ipc_reply_t *reply = calloc(1, DHCP_IPC_REPLY_SIZE + buffer_size);
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	if (reply == NULL)
1360Sstevel@tonic-gate 		return (NULL);
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 	reply->message_type	= request->message_type;
1390Sstevel@tonic-gate 	reply->ipc_id		= request->ipc_id;
1400Sstevel@tonic-gate 	reply->return_code	= return_code;
1410Sstevel@tonic-gate 	reply->data_length	= buffer_size;
1420Sstevel@tonic-gate 	reply->data_type	= data_type;
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 	if (buffer != NULL)
1450Sstevel@tonic-gate 		(void) memcpy(reply->buffer, buffer, buffer_size);
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate 	return (reply);
1480Sstevel@tonic-gate }
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate /*
1510Sstevel@tonic-gate  * dhcp_ipc_get_data(): gets the data and data type from a dhcp_ipc_reply_t
1520Sstevel@tonic-gate  *
1530Sstevel@tonic-gate  *   input: dhcp_ipc_reply_t *: the reply to get data from
1540Sstevel@tonic-gate  *	    size_t *: the size of the resulting data
1550Sstevel@tonic-gate  *	    dhcp_data_type_t *: the type of the message (returned)
1560Sstevel@tonic-gate  *  output: void *: a pointer to the data, if there is any.
1570Sstevel@tonic-gate  */
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate void *
1600Sstevel@tonic-gate dhcp_ipc_get_data(dhcp_ipc_reply_t *reply, size_t *size, dhcp_data_type_t *type)
1610Sstevel@tonic-gate {
1620Sstevel@tonic-gate 	if (reply == NULL || reply->data_length == 0) {
1630Sstevel@tonic-gate 		*size = 0;
1640Sstevel@tonic-gate 		return (NULL);
1650Sstevel@tonic-gate 	}
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	if (type != NULL)
1680Sstevel@tonic-gate 		*type = reply->data_type;
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	*size = reply->data_length;
1710Sstevel@tonic-gate 	return (reply->buffer);
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate /*
1750Sstevel@tonic-gate  * dhcp_ipc_recv_msg(): gets a message using the agent's ipc protocol
1760Sstevel@tonic-gate  *
1770Sstevel@tonic-gate  *   input: int: the file descriptor to get the message from
1780Sstevel@tonic-gate  *	    void **: the address of a pointer to store the message
1790Sstevel@tonic-gate  *		     (dynamically allocated)
1800Sstevel@tonic-gate  *	    uint32_t: the minimum length of the packet
1810Sstevel@tonic-gate  *	    int: the # of milliseconds to wait for the message (-1 is forever)
1823431Scarlsonj  *  output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
1830Sstevel@tonic-gate  */
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate static int
1860Sstevel@tonic-gate dhcp_ipc_recv_msg(int fd, void **msg, uint32_t base_length, int msec)
1870Sstevel@tonic-gate {
1883431Scarlsonj 	int			retval;
1890Sstevel@tonic-gate 	dhcp_ipc_reply_t	*ipc_msg;
1900Sstevel@tonic-gate 	uint32_t		length;
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	retval = dhcp_ipc_timed_read(fd, &length, sizeof (uint32_t), &msec);
1933431Scarlsonj 	if (retval != DHCP_IPC_SUCCESS)
1943431Scarlsonj 		return (retval);
1953431Scarlsonj 
1963431Scarlsonj 	if (length == 0)
1973431Scarlsonj 		return (DHCP_IPC_E_PROTO);
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	*msg = malloc(length);
2000Sstevel@tonic-gate 	if (*msg == NULL)
2010Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	retval = dhcp_ipc_timed_read(fd, *msg, length, &msec);
2043431Scarlsonj 	if (retval != DHCP_IPC_SUCCESS) {
2050Sstevel@tonic-gate 		free(*msg);
2063431Scarlsonj 		return (retval);
2070Sstevel@tonic-gate 	}
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	if (length < base_length) {
2100Sstevel@tonic-gate 		free(*msg);
2113431Scarlsonj 		return (DHCP_IPC_E_PROTO);
2120Sstevel@tonic-gate 	}
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	/*
2150Sstevel@tonic-gate 	 * the data_length field is in the same place in either ipc message.
2160Sstevel@tonic-gate 	 */
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	ipc_msg = (dhcp_ipc_reply_t *)(*msg);
2190Sstevel@tonic-gate 	if (ipc_msg->data_length + base_length != length) {
2200Sstevel@tonic-gate 		free(*msg);
2213431Scarlsonj 		return (DHCP_IPC_E_PROTO);
2220Sstevel@tonic-gate 	}
2230Sstevel@tonic-gate 
2243431Scarlsonj 	return (DHCP_IPC_SUCCESS);
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate /*
2280Sstevel@tonic-gate  * dhcp_ipc_recv_request(): gets a request using the agent's ipc protocol
2290Sstevel@tonic-gate  *
2300Sstevel@tonic-gate  *   input: int: the file descriptor to get the message from
2310Sstevel@tonic-gate  *	    dhcp_ipc_request_t **: address of a pointer to store the request
2320Sstevel@tonic-gate  *				 (dynamically allocated)
2330Sstevel@tonic-gate  *	    int: the # of milliseconds to wait for the message (-1 is forever)
2340Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
2350Sstevel@tonic-gate  */
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate int
2380Sstevel@tonic-gate dhcp_ipc_recv_request(int fd, dhcp_ipc_request_t **request, int msec)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate 	int	retval;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	retval = dhcp_ipc_recv_msg(fd, (void **)request, DHCP_IPC_REQUEST_SIZE,
2430Sstevel@tonic-gate 	    msec);
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	/* guarantee that ifname will be NUL-terminated */
2460Sstevel@tonic-gate 	if (retval == 0)
247*8562SPeter.Memishian@Sun.COM 		(*request)->ifname[LIFNAMSIZ - 1] = '\0';
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	return (retval);
2500Sstevel@tonic-gate }
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate /*
2530Sstevel@tonic-gate  * dhcp_ipc_recv_reply(): gets a reply using the agent's ipc protocol
2540Sstevel@tonic-gate  *
2550Sstevel@tonic-gate  *   input: int: the file descriptor to get the message from
2560Sstevel@tonic-gate  *	    dhcp_ipc_reply_t **: address of a pointer to store the reply
2570Sstevel@tonic-gate  *				 (dynamically allocated)
2580Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
2590Sstevel@tonic-gate  */
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate static int
2620Sstevel@tonic-gate dhcp_ipc_recv_reply(int fd, dhcp_ipc_reply_t **reply)
2630Sstevel@tonic-gate {
2640Sstevel@tonic-gate 	return (dhcp_ipc_recv_msg(fd, (void **)reply, DHCP_IPC_REPLY_SIZE, -1));
2650Sstevel@tonic-gate }
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate /*
2680Sstevel@tonic-gate  * dhcp_ipc_send_msg(): transmits a message using the agent's ipc protocol
2690Sstevel@tonic-gate  *
2700Sstevel@tonic-gate  *   input: int: the file descriptor to transmit on
2710Sstevel@tonic-gate  *	    void *: the message to send
2720Sstevel@tonic-gate  *	    uint32_t: the message length
2730Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
2740Sstevel@tonic-gate  */
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate static int
2770Sstevel@tonic-gate dhcp_ipc_send_msg(int fd, void *msg, uint32_t message_length)
2780Sstevel@tonic-gate {
2790Sstevel@tonic-gate 	struct iovec	iovec[2];
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	iovec[0].iov_base = (caddr_t)&message_length;
2820Sstevel@tonic-gate 	iovec[0].iov_len  = sizeof (uint32_t);
2830Sstevel@tonic-gate 	iovec[1].iov_base = msg;
2840Sstevel@tonic-gate 	iovec[1].iov_len  = message_length;
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	if (writev(fd, iovec, sizeof (iovec) / sizeof (*iovec)) == -1)
2870Sstevel@tonic-gate 		return (DHCP_IPC_E_WRITEV);
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	return (0);
2900Sstevel@tonic-gate }
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate /*
2930Sstevel@tonic-gate  * dhcp_ipc_send_reply(): transmits a reply using the agent's ipc protocol
2940Sstevel@tonic-gate  *
2950Sstevel@tonic-gate  *   input: int: the file descriptor to transmit on
2960Sstevel@tonic-gate  *	    dhcp_ipc_reply_t *: the reply to send
2970Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
2980Sstevel@tonic-gate  */
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate int
3010Sstevel@tonic-gate dhcp_ipc_send_reply(int fd, dhcp_ipc_reply_t *reply)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate 	return (dhcp_ipc_send_msg(fd, reply, DHCP_IPC_REPLY_SIZE +
3040Sstevel@tonic-gate 	    reply->data_length));
3050Sstevel@tonic-gate }
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate /*
3080Sstevel@tonic-gate  * dhcp_ipc_send_request(): transmits a request using the agent's ipc protocol
3090Sstevel@tonic-gate  *
3100Sstevel@tonic-gate  *   input: int: the file descriptor to transmit on
3110Sstevel@tonic-gate  *	    dhcp_ipc_request_t *: the request to send
3120Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
3130Sstevel@tonic-gate  */
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate static int
3160Sstevel@tonic-gate dhcp_ipc_send_request(int fd, dhcp_ipc_request_t *request)
3170Sstevel@tonic-gate {
3180Sstevel@tonic-gate 	/*
3190Sstevel@tonic-gate 	 * for now, ipc_ids aren't really used, but they're intended
3200Sstevel@tonic-gate 	 * to make it easy to send several requests and then collect
3210Sstevel@tonic-gate 	 * all of the replies (and pair them with the requests).
3220Sstevel@tonic-gate 	 */
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 	request->ipc_id = gethrtime();
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	return (dhcp_ipc_send_msg(fd, request, DHCP_IPC_REQUEST_SIZE +
3270Sstevel@tonic-gate 	    request->data_length));
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate /*
3310Sstevel@tonic-gate  * dhcp_ipc_make_request(): sends the provided request to the agent and reaps
3320Sstevel@tonic-gate  *			    the reply
3330Sstevel@tonic-gate  *
3340Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request to make
3350Sstevel@tonic-gate  *	    dhcp_ipc_reply_t **: the reply (dynamically allocated)
3360Sstevel@tonic-gate  *	    int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
3370Sstevel@tonic-gate  *		     or DHCP_IPC_WAIT_DEFAULT
3380Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
3390Sstevel@tonic-gate  */
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate int
3420Sstevel@tonic-gate dhcp_ipc_make_request(dhcp_ipc_request_t *request, dhcp_ipc_reply_t **reply,
3430Sstevel@tonic-gate     int32_t timeout)
3440Sstevel@tonic-gate {
3453431Scarlsonj 	int			fd, on, retval;
3463431Scarlsonj 	struct sockaddr_in	sinv;
3470Sstevel@tonic-gate 
3483431Scarlsonj 	fd = socket(AF_INET, SOCK_STREAM, 0);
3493431Scarlsonj 	if (fd == -1)
3503431Scarlsonj 		return (DHCP_IPC_E_SOCKET);
3510Sstevel@tonic-gate 
3523431Scarlsonj 	/*
3533431Scarlsonj 	 * Bind a privileged port if we have sufficient privilege to do so.
3543431Scarlsonj 	 * Continue as non-privileged otherwise.
3553431Scarlsonj 	 */
3563431Scarlsonj 	on = 1;
3573431Scarlsonj 	(void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on));
3580Sstevel@tonic-gate 
3593431Scarlsonj 	(void) memset(&sinv, 0, sizeof (sinv));
3603431Scarlsonj 	sinv.sin_family	 = AF_INET;
3613431Scarlsonj 	if (bind(fd, (struct sockaddr *)&sinv, sizeof (sinv)) == -1) {
3623431Scarlsonj 		(void) dhcp_ipc_close(fd);
3633431Scarlsonj 		return (DHCP_IPC_E_BIND);
3640Sstevel@tonic-gate 	}
3650Sstevel@tonic-gate 
3663431Scarlsonj 	sinv.sin_port = htons(IPPORT_DHCPAGENT);
3673431Scarlsonj 	sinv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
3683431Scarlsonj 	retval = connect(fd, (struct sockaddr *)&sinv, sizeof (sinv));
3690Sstevel@tonic-gate 	if (retval == -1) {
3700Sstevel@tonic-gate 		(void) dhcp_ipc_close(fd);
3710Sstevel@tonic-gate 		return (DHCP_IPC_E_CONNECT);
3720Sstevel@tonic-gate 	}
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	request->timeout = timeout;
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	retval = dhcp_ipc_send_request(fd, request);
3770Sstevel@tonic-gate 	if (retval == 0)
3780Sstevel@tonic-gate 		retval = dhcp_ipc_recv_reply(fd, reply);
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	(void) dhcp_ipc_close(fd);
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	return (retval);
3830Sstevel@tonic-gate }
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate /*
3860Sstevel@tonic-gate  * dhcp_ipc_init(): initializes the ipc channel for use by the agent
3870Sstevel@tonic-gate  *
3880Sstevel@tonic-gate  *   input: int *: the file descriptor to accept on (returned)
3890Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
3900Sstevel@tonic-gate  */
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate int
3930Sstevel@tonic-gate dhcp_ipc_init(int *listen_fd)
3940Sstevel@tonic-gate {
3950Sstevel@tonic-gate 	struct sockaddr_in	sin;
3960Sstevel@tonic-gate 	int			on = 1;
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 	sin.sin_family		= AF_INET;
4010Sstevel@tonic-gate 	sin.sin_port		= htons(IPPORT_DHCPAGENT);
4020Sstevel@tonic-gate 	sin.sin_addr.s_addr	= htonl(INADDR_LOOPBACK);
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 	*listen_fd = socket(AF_INET, SOCK_STREAM, 0);
4050Sstevel@tonic-gate 	if (*listen_fd == -1)
4060Sstevel@tonic-gate 		return (DHCP_IPC_E_SOCKET);
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	/*
4090Sstevel@tonic-gate 	 * we use SO_REUSEADDR here since in the case where there
4100Sstevel@tonic-gate 	 * really is another daemon running that is using the agent's
4110Sstevel@tonic-gate 	 * port, bind(3N) will fail.  so we can't lose.
4120Sstevel@tonic-gate 	 */
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	(void) setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &on,
4150Sstevel@tonic-gate 	    sizeof (on));
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 	if (bind(*listen_fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
4180Sstevel@tonic-gate 		(void) close(*listen_fd);
4190Sstevel@tonic-gate 		return (DHCP_IPC_E_BIND);
4200Sstevel@tonic-gate 	}
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	if (listen(*listen_fd, DHCP_IPC_LISTEN_BACKLOG) == -1) {
4230Sstevel@tonic-gate 		(void) close(*listen_fd);
4240Sstevel@tonic-gate 		return (DHCP_IPC_E_LISTEN);
4250Sstevel@tonic-gate 	}
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	return (0);
4280Sstevel@tonic-gate }
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate /*
4310Sstevel@tonic-gate  * dhcp_ipc_accept(): accepts an incoming connection for the agent
4320Sstevel@tonic-gate  *
4330Sstevel@tonic-gate  *   input: int: the file descriptor to accept on
4340Sstevel@tonic-gate  *	    int *: the accepted file descriptor (returned)
4350Sstevel@tonic-gate  *	    int *: nonzero if the client is privileged (returned)
4360Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
4370Sstevel@tonic-gate  *    note: sets the socket into nonblocking mode
4380Sstevel@tonic-gate  */
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate int
4410Sstevel@tonic-gate dhcp_ipc_accept(int listen_fd, int *fd, int *is_priv)
4420Sstevel@tonic-gate {
4430Sstevel@tonic-gate 	struct sockaddr_in	sin_peer;
4440Sstevel@tonic-gate 	int			sin_len = sizeof (sin_peer);
4450Sstevel@tonic-gate 	int			sockflags;
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate 	/*
4480Sstevel@tonic-gate 	 * if we were extremely concerned with portability, we would
4490Sstevel@tonic-gate 	 * set the socket into nonblocking mode before doing the
4500Sstevel@tonic-gate 	 * accept(3N), since on BSD-based networking stacks, there is
4510Sstevel@tonic-gate 	 * a potential race that can occur if the socket which
4520Sstevel@tonic-gate 	 * connected to us performs a TCP RST before we accept, since
4530Sstevel@tonic-gate 	 * BSD handles this case entirely in the kernel and as a
4540Sstevel@tonic-gate 	 * result even though select said we will not block, we can
4550Sstevel@tonic-gate 	 * end up blocking since there is no longer a connection to
4560Sstevel@tonic-gate 	 * accept.  on SVR4-based systems, this should be okay,
4570Sstevel@tonic-gate 	 * and we will get EPROTO back, even though POSIX.1g says
4580Sstevel@tonic-gate 	 * we should get ECONNABORTED.
4590Sstevel@tonic-gate 	 */
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	*fd = accept(listen_fd, (struct sockaddr *)&sin_peer, &sin_len);
4620Sstevel@tonic-gate 	if (*fd == -1)
4630Sstevel@tonic-gate 		return (DHCP_IPC_E_ACCEPT);
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	/* get credentials */
4660Sstevel@tonic-gate 	*is_priv = ntohs(sin_peer.sin_port) < IPPORT_RESERVED;
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	/*
4690Sstevel@tonic-gate 	 * kick the socket into non-blocking mode so that later
4700Sstevel@tonic-gate 	 * operations on the socket don't block and hold up the whole
4710Sstevel@tonic-gate 	 * application.  with the event demuxing approach, this may
4720Sstevel@tonic-gate 	 * seem unnecessary, but in order to get partial reads/writes
4730Sstevel@tonic-gate 	 * and to handle our internal protocol for passing data
4740Sstevel@tonic-gate 	 * between the agent and its consumers, this is needed.
4750Sstevel@tonic-gate 	 */
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	if ((sockflags = fcntl(*fd, F_GETFL, 0)) == -1) {
4780Sstevel@tonic-gate 		(void) close(*fd);
4790Sstevel@tonic-gate 		return (DHCP_IPC_E_FCNTL);
4800Sstevel@tonic-gate 	}
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	if (fcntl(*fd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
4830Sstevel@tonic-gate 		(void) close(*fd);
4840Sstevel@tonic-gate 		return (DHCP_IPC_E_FCNTL);
4850Sstevel@tonic-gate 	}
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 	return (0);
4880Sstevel@tonic-gate }
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate /*
4910Sstevel@tonic-gate  * dhcp_ipc_close(): closes an ipc descriptor
4920Sstevel@tonic-gate  *
4930Sstevel@tonic-gate  *   input: int: the file descriptor to close
4940Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
4950Sstevel@tonic-gate  */
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate int
4980Sstevel@tonic-gate dhcp_ipc_close(int fd)
4990Sstevel@tonic-gate {
5000Sstevel@tonic-gate 	return ((close(fd) == -1) ? DHCP_IPC_E_CLOSE : 0);
5010Sstevel@tonic-gate }
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate /*
5040Sstevel@tonic-gate  * dhcp_ipc_strerror(): maps an ipc error code into a human-readable string
5050Sstevel@tonic-gate  *
5060Sstevel@tonic-gate  *   input: int: the ipc error code to map
5070Sstevel@tonic-gate  *  output: const char *: the corresponding human-readable string
5080Sstevel@tonic-gate  */
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate const char *
5110Sstevel@tonic-gate dhcp_ipc_strerror(int error)
5120Sstevel@tonic-gate {
5130Sstevel@tonic-gate 	/* note: this must be kept in sync with DHCP_IPC_E_* definitions */
5140Sstevel@tonic-gate 	const char *syscalls[] = {
5150Sstevel@tonic-gate 		"<unknown>", "socket", "fcntl", "read", "accept", "close",
5163431Scarlsonj 		"bind", "listen", "malloc", "connect", "writev", "poll"
5170Sstevel@tonic-gate 	};
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 	const char	*error_string;
5200Sstevel@tonic-gate 	static char	buffer[BUFMAX];
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	switch (error) {
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 	/*
5250Sstevel@tonic-gate 	 * none of these errors actually go over the wire.
5260Sstevel@tonic-gate 	 * hence, we assume that errno is still fresh.
5270Sstevel@tonic-gate 	 */
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	case DHCP_IPC_E_SOCKET:			/* FALLTHRU */
5300Sstevel@tonic-gate 	case DHCP_IPC_E_FCNTL:			/* FALLTHRU */
5310Sstevel@tonic-gate 	case DHCP_IPC_E_READ:			/* FALLTHRU */
5320Sstevel@tonic-gate 	case DHCP_IPC_E_ACCEPT:			/* FALLTHRU */
5330Sstevel@tonic-gate 	case DHCP_IPC_E_CLOSE:			/* FALLTHRU */
5340Sstevel@tonic-gate 	case DHCP_IPC_E_BIND:			/* FALLTHRU */
5350Sstevel@tonic-gate 	case DHCP_IPC_E_LISTEN:			/* FALLTHRU */
5360Sstevel@tonic-gate 	case DHCP_IPC_E_CONNECT:		/* FALLTHRU */
5373431Scarlsonj 	case DHCP_IPC_E_WRITEV:			/* FALLTHRU */
5383431Scarlsonj 	case DHCP_IPC_E_POLL:
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 		error_string = strerror(errno);
5410Sstevel@tonic-gate 		if (error_string == NULL)
5420Sstevel@tonic-gate 			error_string = "unknown error";
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "%s: %s",
5450Sstevel@tonic-gate 		    syscalls[error], error_string);
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 		error_string = buffer;
5480Sstevel@tonic-gate 		break;
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 	case DHCP_IPC_E_MEMORY:
5510Sstevel@tonic-gate 		error_string = "out of memory";
5520Sstevel@tonic-gate 		break;
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	case DHCP_IPC_E_TIMEOUT:
5550Sstevel@tonic-gate 		error_string = "wait timed out, operation still pending...";
5560Sstevel@tonic-gate 		break;
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	case DHCP_IPC_E_INVIF:
5590Sstevel@tonic-gate 		error_string = "interface does not exist or cannot be managed "
5600Sstevel@tonic-gate 		    "using DHCP";
5610Sstevel@tonic-gate 		break;
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	case DHCP_IPC_E_INT:
5640Sstevel@tonic-gate 		error_string = "internal error (might work later)";
5650Sstevel@tonic-gate 		break;
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 	case DHCP_IPC_E_PERM:
5680Sstevel@tonic-gate 		error_string = "permission denied";
5690Sstevel@tonic-gate 		break;
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	case DHCP_IPC_E_OUTSTATE:
5720Sstevel@tonic-gate 		error_string = "interface not in appropriate state for command";
5730Sstevel@tonic-gate 		break;
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	case DHCP_IPC_E_PEND:
5760Sstevel@tonic-gate 		error_string = "interface currently has a pending command "
5770Sstevel@tonic-gate 		    "(try later)";
5780Sstevel@tonic-gate 		break;
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	case DHCP_IPC_E_BOOTP:
5810Sstevel@tonic-gate 		error_string = "interface is administered with BOOTP, not DHCP";
5820Sstevel@tonic-gate 		break;
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	case DHCP_IPC_E_CMD_UNKNOWN:
5850Sstevel@tonic-gate 		error_string = "unknown command";
5860Sstevel@tonic-gate 		break;
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 	case DHCP_IPC_E_UNKIF:
5890Sstevel@tonic-gate 		error_string = "interface is not under DHCP control";
5900Sstevel@tonic-gate 		break;
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 	case DHCP_IPC_E_PROTO:
5930Sstevel@tonic-gate 		error_string = "ipc protocol violation";
5940Sstevel@tonic-gate 		break;
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 	case DHCP_IPC_E_FAILEDIF:
5970Sstevel@tonic-gate 		error_string = "interface is in a FAILED state and must be "
5980Sstevel@tonic-gate 		    "manually restarted";
5990Sstevel@tonic-gate 		break;
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 	case DHCP_IPC_E_NOPRIMARY:
6020Sstevel@tonic-gate 		error_string = "primary interface requested but no primary "
6030Sstevel@tonic-gate 		    "interface is set";
6040Sstevel@tonic-gate 		break;
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 	case DHCP_IPC_E_NOIPIF:
6070Sstevel@tonic-gate 		error_string = "interface currently has no IP address";
6080Sstevel@tonic-gate 		break;
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	case DHCP_IPC_E_DOWNIF:
6110Sstevel@tonic-gate 		error_string = "interface is currently down";
6120Sstevel@tonic-gate 		break;
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	case DHCP_IPC_E_NOVALUE:
6150Sstevel@tonic-gate 		error_string = "no value was found for this option";
6160Sstevel@tonic-gate 		break;
6170Sstevel@tonic-gate 
6183431Scarlsonj 	case DHCP_IPC_E_RUNNING:
6193431Scarlsonj 		error_string = "DHCP is already running";
6203431Scarlsonj 		break;
6213431Scarlsonj 
6223431Scarlsonj 	case DHCP_IPC_E_SRVFAILED:
6233431Scarlsonj 		error_string = "DHCP server refused request";
6243431Scarlsonj 		break;
6253431Scarlsonj 
6263431Scarlsonj 	case DHCP_IPC_E_EOF:
6273431Scarlsonj 		error_string = "ipc connection closed";
6280Sstevel@tonic-gate 		break;
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	default:
6310Sstevel@tonic-gate 		error_string = "unknown error";
6320Sstevel@tonic-gate 		break;
6330Sstevel@tonic-gate 	}
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	/*
6360Sstevel@tonic-gate 	 * TODO: internationalize this error string
6370Sstevel@tonic-gate 	 */
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 	return (error_string);
6400Sstevel@tonic-gate }
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate /*
6434106Scarlsonj  * dhcp_string_to_request(): maps a string into a request code
6444106Scarlsonj  *
6454106Scarlsonj  *    input: const char *: the string to map
6464106Scarlsonj  *   output: dhcp_ipc_type_t: the request code, or -1 if unknown
6474106Scarlsonj  */
6484106Scarlsonj 
6494106Scarlsonj dhcp_ipc_type_t
6504106Scarlsonj dhcp_string_to_request(const char *request)
6514106Scarlsonj {
6524106Scarlsonj 	unsigned int	i;
6534106Scarlsonj 
6544106Scarlsonj 	for (i = 0; i < DHCP_NIPC; i++)
6554106Scarlsonj 		if (strcmp(ipc_typestr[i], request) == 0)
6564106Scarlsonj 			return ((dhcp_ipc_type_t)i);
6574106Scarlsonj 
6584106Scarlsonj 	return ((dhcp_ipc_type_t)-1);
6594106Scarlsonj }
6604106Scarlsonj 
6614106Scarlsonj /*
6623431Scarlsonj  * dhcp_ipc_type_to_string(): maps an ipc command code into a human-readable
6633431Scarlsonj  *			      string
6643431Scarlsonj  *
6653431Scarlsonj  *   input: int: the ipc command code to map
6663431Scarlsonj  *  output: const char *: the corresponding human-readable string
6673431Scarlsonj  */
6683431Scarlsonj 
6693431Scarlsonj const char *
6703431Scarlsonj dhcp_ipc_type_to_string(dhcp_ipc_type_t type)
6713431Scarlsonj {
6723431Scarlsonj 	if (type < 0 || type >= DHCP_NIPC)
6733431Scarlsonj 		return ("unknown");
6743431Scarlsonj 	else
6754106Scarlsonj 		return (ipc_typestr[(int)type]);
6763431Scarlsonj }
6773431Scarlsonj 
6783431Scarlsonj /*
6790Sstevel@tonic-gate  * getinfo_ifnames(): checks the value of a specified option on a list of
6800Sstevel@tonic-gate  *		      interface names.
6810Sstevel@tonic-gate  *   input: const char *: a list of interface names to query (in order) for
6820Sstevel@tonic-gate  *			  the option; "" queries the primary interface
6830Sstevel@tonic-gate  *	    dhcp_optnum_t *: a description of the desired option
6840Sstevel@tonic-gate  *	    DHCP_OPT **:  filled in with the (dynamically allocated) value of
6850Sstevel@tonic-gate  *			  the option upon success.
6860Sstevel@tonic-gate  *  output: int: DHCP_IPC_E_* on error, 0 on success or if no value was
6870Sstevel@tonic-gate  *	         found but no error occurred either (*result will be NULL)
6880Sstevel@tonic-gate  */
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate static int
6910Sstevel@tonic-gate getinfo_ifnames(const char *ifn, dhcp_optnum_t *optnum, DHCP_OPT **result)
6920Sstevel@tonic-gate {
6930Sstevel@tonic-gate 	dhcp_ipc_request_t	*request;
6940Sstevel@tonic-gate 	dhcp_ipc_reply_t	*reply;
6950Sstevel@tonic-gate 	char			*ifnames, *ifnames_head;
6960Sstevel@tonic-gate 	DHCP_OPT		*opt;
6970Sstevel@tonic-gate 	size_t			opt_size;
6980Sstevel@tonic-gate 	int			retval = 0;
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 	*result = NULL;
7010Sstevel@tonic-gate 	ifnames_head = ifnames = strdup(ifn);
7020Sstevel@tonic-gate 	if (ifnames == NULL)
7030Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	request = dhcp_ipc_alloc_request(DHCP_GET_TAG, "", optnum,
7060Sstevel@tonic-gate 	    sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 	if (request == NULL) {
7090Sstevel@tonic-gate 		free(ifnames_head);
7100Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
7110Sstevel@tonic-gate 	}
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	ifnames = strtok(ifnames, " ");
7140Sstevel@tonic-gate 	if (ifnames == NULL)
7150Sstevel@tonic-gate 		ifnames = "";
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	for (; ifnames != NULL; ifnames = strtok(NULL, " ")) {
7180Sstevel@tonic-gate 
719*8562SPeter.Memishian@Sun.COM 		(void) strlcpy(request->ifname, ifnames, LIFNAMSIZ);
7200Sstevel@tonic-gate 		retval = dhcp_ipc_make_request(request, &reply, 0);
7210Sstevel@tonic-gate 		if (retval != 0)
7220Sstevel@tonic-gate 			break;
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 		if (reply->return_code == 0) {
7250Sstevel@tonic-gate 			opt = dhcp_ipc_get_data(reply, &opt_size, NULL);
7260Sstevel@tonic-gate 			if (opt_size > 2 && (opt->len == opt_size - 2)) {
7270Sstevel@tonic-gate 				*result = malloc(opt_size);
7280Sstevel@tonic-gate 				if (*result == NULL)
7290Sstevel@tonic-gate 					retval = DHCP_IPC_E_MEMORY;
7300Sstevel@tonic-gate 				else
7310Sstevel@tonic-gate 					(void) memcpy(*result, opt, opt_size);
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate 				free(reply);
7340Sstevel@tonic-gate 				break;
7350Sstevel@tonic-gate 			}
7360Sstevel@tonic-gate 		}
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate 		free(reply);
7390Sstevel@tonic-gate 		if (ifnames[0] == '\0')
7400Sstevel@tonic-gate 			break;
7410Sstevel@tonic-gate 	}
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	free(request);
7440Sstevel@tonic-gate 	free(ifnames_head);
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	return (retval);
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate /*
7500Sstevel@tonic-gate  * get_ifnames(): returns a space-separated list of interface names that
7510Sstevel@tonic-gate  *		  match the specified flags
7520Sstevel@tonic-gate  *
7530Sstevel@tonic-gate  *   input: int: flags which must be on in each interface returned
7540Sstevel@tonic-gate  *	    int: flags which must be off in each interface returned
7550Sstevel@tonic-gate  *  output: char *: a dynamically-allocated list of interface names, or
7560Sstevel@tonic-gate  *		    NULL upon failure.
7570Sstevel@tonic-gate  */
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate static char *
7600Sstevel@tonic-gate get_ifnames(int flags_on, int flags_off)
7610Sstevel@tonic-gate {
7620Sstevel@tonic-gate 	struct ifconf	ifc;
7630Sstevel@tonic-gate 	int		n_ifs, i, sock_fd;
7640Sstevel@tonic-gate 	char		*ifnames;
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
7680Sstevel@tonic-gate 	if (sock_fd == -1)
7690Sstevel@tonic-gate 		return (NULL);
7700Sstevel@tonic-gate 
7710Sstevel@tonic-gate 	if ((ioctl(sock_fd, SIOCGIFNUM, &n_ifs) == -1) || (n_ifs <= 0)) {
7720Sstevel@tonic-gate 		(void) close(sock_fd);
7730Sstevel@tonic-gate 		return (NULL);
7740Sstevel@tonic-gate 	}
7750Sstevel@tonic-gate 
776*8562SPeter.Memishian@Sun.COM 	ifnames = calloc(1, n_ifs * (LIFNAMSIZ + 1));
7770Sstevel@tonic-gate 	ifc.ifc_len = n_ifs * sizeof (struct ifreq);
7780Sstevel@tonic-gate 	ifc.ifc_req = calloc(n_ifs, sizeof (struct ifreq));
7790Sstevel@tonic-gate 	if (ifc.ifc_req != NULL && ifnames != NULL) {
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 		if (ioctl(sock_fd, SIOCGIFCONF, &ifc) == -1) {
7820Sstevel@tonic-gate 			(void) close(sock_fd);
7830Sstevel@tonic-gate 			free(ifnames);
7840Sstevel@tonic-gate 			free(ifc.ifc_req);
7850Sstevel@tonic-gate 			return (NULL);
7860Sstevel@tonic-gate 		}
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 		for (i = 0; i < n_ifs; i++) {
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 			if (ioctl(sock_fd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0)
7910Sstevel@tonic-gate 				if ((ifc.ifc_req[i].ifr_flags &
7920Sstevel@tonic-gate 				    (flags_on | flags_off)) != flags_on)
7930Sstevel@tonic-gate 					continue;
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 			(void) strcat(ifnames, ifc.ifc_req[i].ifr_name);
7960Sstevel@tonic-gate 			(void) strcat(ifnames, " ");
7970Sstevel@tonic-gate 		}
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 		if (strlen(ifnames) > 1)
8000Sstevel@tonic-gate 			ifnames[strlen(ifnames) - 1] = '\0';
8010Sstevel@tonic-gate 	}
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	(void) close(sock_fd);
8040Sstevel@tonic-gate 	free(ifc.ifc_req);
8050Sstevel@tonic-gate 	return (ifnames);
8060Sstevel@tonic-gate }
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate /*
8090Sstevel@tonic-gate  * dhcp_ipc_getinfo(): attempts to retrieve a value for the specified DHCP
8100Sstevel@tonic-gate  *		       option; tries primary interface, then all DHCP-owned
8110Sstevel@tonic-gate  *		       interfaces, then INFORMs on the remaining interfaces
8120Sstevel@tonic-gate  *		       (these interfaces are dropped prior to returning).
8130Sstevel@tonic-gate  *   input: dhcp_optnum_t *: a description of the desired option
8140Sstevel@tonic-gate  *	    DHCP_OPT **:  filled in with the (dynamically allocated) value of
8150Sstevel@tonic-gate  *			  the option upon success.
8160Sstevel@tonic-gate  *	    int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
8170Sstevel@tonic-gate  *		     or DHCP_IPC_WAIT_DEFAULT.
8180Sstevel@tonic-gate  *  output: int: DHCP_IPC_E_* on error, 0 upon success.
8190Sstevel@tonic-gate  */
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate int
8220Sstevel@tonic-gate dhcp_ipc_getinfo(dhcp_optnum_t *optnum, DHCP_OPT **result, int32_t timeout)
8230Sstevel@tonic-gate {
8240Sstevel@tonic-gate 	dhcp_ipc_request_t	*request;
8250Sstevel@tonic-gate 	dhcp_ipc_reply_t	*reply;
8260Sstevel@tonic-gate 	char			*ifnames, *ifnames_copy, *ifnames_head;
8270Sstevel@tonic-gate 	int			retval;
8280Sstevel@tonic-gate 	time_t			start_time = time(NULL);
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	if (timeout == DHCP_IPC_WAIT_DEFAULT)
8310Sstevel@tonic-gate 		timeout = DHCP_IPC_DEFAULT_WAIT;
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	/*
8340Sstevel@tonic-gate 	 * wait at most 5 seconds for the agent to start.
8350Sstevel@tonic-gate 	 */
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate 	if (dhcp_start_agent((timeout > 5 || timeout < 0) ? 5 : timeout) == -1)
8380Sstevel@tonic-gate 		return (DHCP_IPC_E_INT);
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	/*
8410Sstevel@tonic-gate 	 * check the primary interface for the option value first.
8420Sstevel@tonic-gate 	 */
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate 	retval = getinfo_ifnames("", optnum, result);
8450Sstevel@tonic-gate 	if ((retval != 0) || (retval == 0 && *result != NULL))
8460Sstevel@tonic-gate 		return (retval);
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	/*
8490Sstevel@tonic-gate 	 * no luck.  get a list of the interfaces under DHCP control
8500Sstevel@tonic-gate 	 * and perform a GET_TAG on each one.
8510Sstevel@tonic-gate 	 */
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 	ifnames = get_ifnames(IFF_DHCPRUNNING, 0);
8540Sstevel@tonic-gate 	if (ifnames != NULL && strlen(ifnames) != 0) {
8550Sstevel@tonic-gate 		retval = getinfo_ifnames(ifnames, optnum, result);
8560Sstevel@tonic-gate 		if ((retval != 0) || (retval == 0 && *result != NULL)) {
8570Sstevel@tonic-gate 			free(ifnames);
8580Sstevel@tonic-gate 			return (retval);
8590Sstevel@tonic-gate 		}
8600Sstevel@tonic-gate 	}
8610Sstevel@tonic-gate 	free(ifnames);
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 	/*
8640Sstevel@tonic-gate 	 * still no luck.  retrieve a list of all interfaces on the
8650Sstevel@tonic-gate 	 * system that could use DHCP but aren't.  send INFORMs out on
8660Sstevel@tonic-gate 	 * each one. after that, sit in a loop for the next `timeout'
8670Sstevel@tonic-gate 	 * seconds, trying every second to see if a response for the
8680Sstevel@tonic-gate 	 * option we want has come in on one of the interfaces.
8690Sstevel@tonic-gate 	 */
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate 	ifnames = get_ifnames(IFF_UP|IFF_RUNNING, IFF_LOOPBACK|IFF_DHCPRUNNING);
8720Sstevel@tonic-gate 	if (ifnames == NULL || strlen(ifnames) == 0) {
8730Sstevel@tonic-gate 		free(ifnames);
8740Sstevel@tonic-gate 		return (DHCP_IPC_E_NOVALUE);
8750Sstevel@tonic-gate 	}
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 	ifnames_head = ifnames_copy = strdup(ifnames);
8780Sstevel@tonic-gate 	if (ifnames_copy == NULL) {
8790Sstevel@tonic-gate 		free(ifnames);
8800Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
8810Sstevel@tonic-gate 	}
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	request = dhcp_ipc_alloc_request(DHCP_INFORM, "", NULL, 0,
8840Sstevel@tonic-gate 	    DHCP_TYPE_NONE);
8850Sstevel@tonic-gate 	if (request == NULL) {
8860Sstevel@tonic-gate 		free(ifnames);
8870Sstevel@tonic-gate 		free(ifnames_head);
8880Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
8890Sstevel@tonic-gate 	}
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 	ifnames_copy = strtok(ifnames_copy, " ");
8920Sstevel@tonic-gate 	for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
893*8562SPeter.Memishian@Sun.COM 		(void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
8940Sstevel@tonic-gate 		if (dhcp_ipc_make_request(request, &reply, 0) == 0)
8950Sstevel@tonic-gate 			free(reply);
8960Sstevel@tonic-gate 	}
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 	for (;;) {
8990Sstevel@tonic-gate 		if ((timeout != DHCP_IPC_WAIT_FOREVER) &&
9000Sstevel@tonic-gate 		    (time(NULL) - start_time > timeout)) {
9010Sstevel@tonic-gate 			retval = DHCP_IPC_E_TIMEOUT;
9020Sstevel@tonic-gate 			break;
9030Sstevel@tonic-gate 		}
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 		retval = getinfo_ifnames(ifnames, optnum, result);
9060Sstevel@tonic-gate 		if (retval != 0 || (retval == 0 && *result != NULL))
9070Sstevel@tonic-gate 			break;
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 		(void) sleep(1);
9100Sstevel@tonic-gate 	}
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	/*
9130Sstevel@tonic-gate 	 * drop any interfaces that weren't under DHCP control before
9140Sstevel@tonic-gate 	 * we got here; this keeps this function more of a black box
9150Sstevel@tonic-gate 	 * and the behavior more consistent from call to call.
9160Sstevel@tonic-gate 	 */
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 	request->message_type = DHCP_DROP;
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 	ifnames_copy = strcpy(ifnames_head, ifnames);
9210Sstevel@tonic-gate 	ifnames_copy = strtok(ifnames_copy, " ");
9220Sstevel@tonic-gate 	for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
923*8562SPeter.Memishian@Sun.COM 		(void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
9240Sstevel@tonic-gate 		if (dhcp_ipc_make_request(request, &reply, 0) == 0)
9250Sstevel@tonic-gate 			free(reply);
9260Sstevel@tonic-gate 	}
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 	free(request);
9290Sstevel@tonic-gate 	free(ifnames_head);
9300Sstevel@tonic-gate 	free(ifnames);
9310Sstevel@tonic-gate 	return (retval);
9320Sstevel@tonic-gate }
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate /*
9350Sstevel@tonic-gate  * dhcp_ipc_timed_read(): reads from a descriptor using a maximum timeout
9360Sstevel@tonic-gate  *
9370Sstevel@tonic-gate  *   input: int: the file descriptor to read from
9380Sstevel@tonic-gate  *	    void *: the buffer to read into
9390Sstevel@tonic-gate  *	    unsigned int: the total length of data to read
9400Sstevel@tonic-gate  *	    int *: the number of milliseconds to wait; the number of
9413431Scarlsonj  *		   milliseconds left are returned (-1 is "forever")
9423431Scarlsonj  *  output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
9430Sstevel@tonic-gate  */
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate static int
9460Sstevel@tonic-gate dhcp_ipc_timed_read(int fd, void *buffer, unsigned int length, int *msec)
9470Sstevel@tonic-gate {
9480Sstevel@tonic-gate 	unsigned int	n_total = 0;
9490Sstevel@tonic-gate 	ssize_t		n_read;
9500Sstevel@tonic-gate 	struct pollfd	pollfd;
9513431Scarlsonj 	hrtime_t	start, end;
9523431Scarlsonj 	int		retv;
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 	pollfd.fd	= fd;
9550Sstevel@tonic-gate 	pollfd.events	= POLLIN;
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 	while (n_total < length) {
9580Sstevel@tonic-gate 
9593431Scarlsonj 		start = gethrtime();
9600Sstevel@tonic-gate 
9613431Scarlsonj 		retv = poll(&pollfd, 1, *msec);
9623431Scarlsonj 		if (retv == 0) {
9633431Scarlsonj 			/* This can happen only if *msec is not -1 */
9640Sstevel@tonic-gate 			*msec = 0;
9653431Scarlsonj 			return (DHCP_IPC_E_TIMEOUT);
9663431Scarlsonj 		}
9670Sstevel@tonic-gate 
9683431Scarlsonj 		if (*msec != -1) {
9693431Scarlsonj 			end = gethrtime();
9703431Scarlsonj 			*msec -= (end - start) / (NANOSEC / MILLISEC);
9713431Scarlsonj 			if (*msec < 0)
9723431Scarlsonj 				*msec = 0;
9733431Scarlsonj 		}
9740Sstevel@tonic-gate 
9753431Scarlsonj 		if (retv == -1) {
9763431Scarlsonj 			if (errno != EINTR)
9773431Scarlsonj 				return (DHCP_IPC_E_POLL);
9783431Scarlsonj 			else if (*msec == 0)
9793431Scarlsonj 				return (DHCP_IPC_E_TIMEOUT);
9803431Scarlsonj 			continue;
9813431Scarlsonj 		}
9820Sstevel@tonic-gate 
9833431Scarlsonj 		if (!(pollfd.revents & POLLIN)) {
9843431Scarlsonj 			errno = EINVAL;
9853431Scarlsonj 			return (DHCP_IPC_E_POLL);
9863431Scarlsonj 		}
9870Sstevel@tonic-gate 
9883431Scarlsonj 		n_read = read(fd, (caddr_t)buffer + n_total, length - n_total);
9890Sstevel@tonic-gate 
9903431Scarlsonj 		if (n_read == -1) {
9913431Scarlsonj 			if (errno != EINTR)
9923431Scarlsonj 				return (DHCP_IPC_E_READ);
9933431Scarlsonj 			else if (*msec == 0)
9943431Scarlsonj 				return (DHCP_IPC_E_TIMEOUT);
9953431Scarlsonj 			continue;
9963431Scarlsonj 		}
9970Sstevel@tonic-gate 
9983431Scarlsonj 		if (n_read == 0) {
9993431Scarlsonj 			return (n_total == 0 ? DHCP_IPC_E_EOF :
10003431Scarlsonj 			    DHCP_IPC_E_PROTO);
10010Sstevel@tonic-gate 		}
10023431Scarlsonj 
10033431Scarlsonj 		n_total += n_read;
10043431Scarlsonj 
10053431Scarlsonj 		if (*msec == 0 && n_total < length)
10063431Scarlsonj 			return (DHCP_IPC_E_TIMEOUT);
10070Sstevel@tonic-gate 	}
10080Sstevel@tonic-gate 
10093431Scarlsonj 	return (DHCP_IPC_SUCCESS);
10100Sstevel@tonic-gate }
1011