17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5d04ccbb3Scarlsonj  * Common Development and Distribution License (the "License").
6d04ccbb3Scarlsonj  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2265c8f1c0Smeem  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
2548bbca81SDaniel Hoffman /*
2648bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
2748bbca81SDaniel Hoffman  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #include <unistd.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <sys/uio.h>
337c478bd9Sstevel@tonic-gate #include <sys/socket.h>
347c478bd9Sstevel@tonic-gate #include <sys/types.h>
357c478bd9Sstevel@tonic-gate #include <fcntl.h>
367c478bd9Sstevel@tonic-gate #include <errno.h>
37a1196271SJames Carlson #include <limits.h>
387c478bd9Sstevel@tonic-gate #include <netinet/in.h>
39d04ccbb3Scarlsonj #include <netinet/tcp.h>
407c478bd9Sstevel@tonic-gate #include <net/if.h>
417c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
427c478bd9Sstevel@tonic-gate #include <sys/fcntl.h>
43d04ccbb3Scarlsonj #include <sys/time.h>
447c478bd9Sstevel@tonic-gate #include <stdio.h>		/* snprintf */
457c478bd9Sstevel@tonic-gate #include <arpa/inet.h>		/* ntohl, ntohs, etc */
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #include "dhcpagent_ipc.h"
487c478bd9Sstevel@tonic-gate #include "dhcpagent_util.h"
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate /*
517c478bd9Sstevel@tonic-gate  * the protocol used here is a simple request/reply scheme: a client
527c478bd9Sstevel@tonic-gate  * sends a dhcp_ipc_request_t message to the agent, and the agent
537c478bd9Sstevel@tonic-gate  * sends a dhcp_ipc_reply_t back to the client.  since the requests
547c478bd9Sstevel@tonic-gate  * and replies can be variable-length, they are prefixed on "the wire"
557c478bd9Sstevel@tonic-gate  * by a 32-bit number that tells the other end how many bytes to
567c478bd9Sstevel@tonic-gate  * expect.
577c478bd9Sstevel@tonic-gate  *
587c478bd9Sstevel@tonic-gate  * the format of a request consists of a single dhcp_ipc_request_t;
597c478bd9Sstevel@tonic-gate  * note that the length of this dhcp_ipc_request_t is variable (using
607c478bd9Sstevel@tonic-gate  * the standard c array-of-size-1 trick).  the type of the payload is
617c478bd9Sstevel@tonic-gate  * given by `data_type', which is guaranteed to be `data_length' bytes
627c478bd9Sstevel@tonic-gate  * long starting at `buffer'.  note that `buffer' is guaranteed to be
637c478bd9Sstevel@tonic-gate  * 32-bit aligned but it is poor taste to rely on this.
647c478bd9Sstevel@tonic-gate  *
657c478bd9Sstevel@tonic-gate  * the format of a reply is much the same: a single dhcp_ipc_reply_t;
667c478bd9Sstevel@tonic-gate  * note again that the length of the dhcp_ipc_reply_t is variable.
677c478bd9Sstevel@tonic-gate  * the type of the payload is given by `data_type', which is
687c478bd9Sstevel@tonic-gate  * guaranteed to be `data_length' bytes long starting at `buffer'.
697c478bd9Sstevel@tonic-gate  * once again, note that `buffer' is guaranteed to be 32-bit aligned
707c478bd9Sstevel@tonic-gate  * but it is poor taste to rely on this.
717c478bd9Sstevel@tonic-gate  *
727c478bd9Sstevel@tonic-gate  * requests and replies can be paired up by comparing `ipc_id' fields.
737c478bd9Sstevel@tonic-gate  */
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate #define	BUFMAX	256
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate static int	dhcp_ipc_timed_read(int, void *, unsigned int, int *);
787c478bd9Sstevel@tonic-gate static int	getinfo_ifnames(const char *, dhcp_optnum_t *, DHCP_OPT **);
797c478bd9Sstevel@tonic-gate static char	*get_ifnames(int, int);
807c478bd9Sstevel@tonic-gate 
81cfb9c9abScarlsonj /* must be kept in sync with enum in dhcpagent_ipc.h */
82cfb9c9abScarlsonj static const char *ipc_typestr[] = {
83cfb9c9abScarlsonj 	"drop", "extend", "ping", "release", "start", "status",
84cfb9c9abScarlsonj 	"inform", "get_tag"
85cfb9c9abScarlsonj };
86cfb9c9abScarlsonj 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * dhcp_ipc_alloc_request(): allocates a dhcp_ipc_request_t of the given type
897c478bd9Sstevel@tonic-gate  *			     and interface, with a timeout of 0.
907c478bd9Sstevel@tonic-gate  *
917c478bd9Sstevel@tonic-gate  *   input: dhcp_ipc_type_t: the type of ipc request to allocate
927c478bd9Sstevel@tonic-gate  *	    const char *: the interface to associate the request with
93d04ccbb3Scarlsonj  *	    const void *: the payload to send with the message (NULL if none)
947c478bd9Sstevel@tonic-gate  *	    uint32_t: the payload size (0 if none)
957c478bd9Sstevel@tonic-gate  *	    dhcp_data_type_t: the description of the type of payload
967c478bd9Sstevel@tonic-gate  *  output: dhcp_ipc_request_t *: the request on success, NULL on failure
977c478bd9Sstevel@tonic-gate  */
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate dhcp_ipc_request_t *
dhcp_ipc_alloc_request(dhcp_ipc_type_t type,const char * ifname,const void * buffer,uint32_t buffer_size,dhcp_data_type_t data_type)100d04ccbb3Scarlsonj dhcp_ipc_alloc_request(dhcp_ipc_type_t type, const char *ifname,
101d04ccbb3Scarlsonj     const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
1027c478bd9Sstevel@tonic-gate {
1037c478bd9Sstevel@tonic-gate 	dhcp_ipc_request_t *request = calloc(1, DHCP_IPC_REQUEST_SIZE +
1047c478bd9Sstevel@tonic-gate 	    buffer_size);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	if (request == NULL)
1077c478bd9Sstevel@tonic-gate 		return (NULL);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 	request->message_type   = type;
1107c478bd9Sstevel@tonic-gate 	request->data_length    = buffer_size;
1117c478bd9Sstevel@tonic-gate 	request->data_type	= data_type;
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 	if (ifname != NULL)
11465c8f1c0Smeem 		(void) strlcpy(request->ifname, ifname, LIFNAMSIZ);
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 	if (buffer != NULL)
1177c478bd9Sstevel@tonic-gate 		(void) memcpy(request->buffer, buffer, buffer_size);
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	return (request);
1207c478bd9Sstevel@tonic-gate }
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate /*
1237c478bd9Sstevel@tonic-gate  * dhcp_ipc_alloc_reply(): allocates a dhcp_ipc_reply_t
1247c478bd9Sstevel@tonic-gate  *
1257c478bd9Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request the reply is for
1267c478bd9Sstevel@tonic-gate  *	    int: the return code (0 for success, DHCP_IPC_E_* otherwise)
127d04ccbb3Scarlsonj  *	    const void *: the payload to send with the message (NULL if none)
1287c478bd9Sstevel@tonic-gate  *	    uint32_t: the payload size (0 if none)
1297c478bd9Sstevel@tonic-gate  *	    dhcp_data_type_t: the description of the type of payload
1307c478bd9Sstevel@tonic-gate  *  output: dhcp_ipc_reply_t *: the reply on success, NULL on failure
1317c478bd9Sstevel@tonic-gate  */
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate dhcp_ipc_reply_t *
dhcp_ipc_alloc_reply(dhcp_ipc_request_t * request,int return_code,const void * buffer,uint32_t buffer_size,dhcp_data_type_t data_type)134d04ccbb3Scarlsonj dhcp_ipc_alloc_reply(dhcp_ipc_request_t *request, int return_code,
135d04ccbb3Scarlsonj     const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
1367c478bd9Sstevel@tonic-gate {
1377c478bd9Sstevel@tonic-gate 	dhcp_ipc_reply_t *reply = calloc(1, DHCP_IPC_REPLY_SIZE + buffer_size);
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate 	if (reply == NULL)
1407c478bd9Sstevel@tonic-gate 		return (NULL);
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 	reply->message_type	= request->message_type;
1437c478bd9Sstevel@tonic-gate 	reply->ipc_id		= request->ipc_id;
1447c478bd9Sstevel@tonic-gate 	reply->return_code	= return_code;
1457c478bd9Sstevel@tonic-gate 	reply->data_length	= buffer_size;
1467c478bd9Sstevel@tonic-gate 	reply->data_type	= data_type;
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	if (buffer != NULL)
1497c478bd9Sstevel@tonic-gate 		(void) memcpy(reply->buffer, buffer, buffer_size);
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 	return (reply);
1527c478bd9Sstevel@tonic-gate }
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate /*
1557c478bd9Sstevel@tonic-gate  * dhcp_ipc_get_data(): gets the data and data type from a dhcp_ipc_reply_t
1567c478bd9Sstevel@tonic-gate  *
1577c478bd9Sstevel@tonic-gate  *   input: dhcp_ipc_reply_t *: the reply to get data from
1587c478bd9Sstevel@tonic-gate  *	    size_t *: the size of the resulting data
1597c478bd9Sstevel@tonic-gate  *	    dhcp_data_type_t *: the type of the message (returned)
1607c478bd9Sstevel@tonic-gate  *  output: void *: a pointer to the data, if there is any.
1617c478bd9Sstevel@tonic-gate  */
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate void *
dhcp_ipc_get_data(dhcp_ipc_reply_t * reply,size_t * size,dhcp_data_type_t * type)1647c478bd9Sstevel@tonic-gate dhcp_ipc_get_data(dhcp_ipc_reply_t *reply, size_t *size, dhcp_data_type_t *type)
1657c478bd9Sstevel@tonic-gate {
1667c478bd9Sstevel@tonic-gate 	if (reply == NULL || reply->data_length == 0) {
1677c478bd9Sstevel@tonic-gate 		*size = 0;
1687c478bd9Sstevel@tonic-gate 		return (NULL);
1697c478bd9Sstevel@tonic-gate 	}
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	if (type != NULL)
1727c478bd9Sstevel@tonic-gate 		*type = reply->data_type;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	*size = reply->data_length;
1757c478bd9Sstevel@tonic-gate 	return (reply->buffer);
1767c478bd9Sstevel@tonic-gate }
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate /*
1797c478bd9Sstevel@tonic-gate  * dhcp_ipc_recv_msg(): gets a message using the agent's ipc protocol
1807c478bd9Sstevel@tonic-gate  *
1817c478bd9Sstevel@tonic-gate  *   input: int: the file descriptor to get the message from
1827c478bd9Sstevel@tonic-gate  *	    void **: the address of a pointer to store the message
1837c478bd9Sstevel@tonic-gate  *		     (dynamically allocated)
1847c478bd9Sstevel@tonic-gate  *	    uint32_t: the minimum length of the packet
1857c478bd9Sstevel@tonic-gate  *	    int: the # of milliseconds to wait for the message (-1 is forever)
186d04ccbb3Scarlsonj  *  output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
1877c478bd9Sstevel@tonic-gate  */
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate static int
dhcp_ipc_recv_msg(int fd,void ** msg,uint32_t base_length,int msec)1907c478bd9Sstevel@tonic-gate dhcp_ipc_recv_msg(int fd, void **msg, uint32_t base_length, int msec)
1917c478bd9Sstevel@tonic-gate {
192d04ccbb3Scarlsonj 	int			retval;
1937c478bd9Sstevel@tonic-gate 	dhcp_ipc_reply_t	*ipc_msg;
1947c478bd9Sstevel@tonic-gate 	uint32_t		length;
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	retval = dhcp_ipc_timed_read(fd, &length, sizeof (uint32_t), &msec);
197d04ccbb3Scarlsonj 	if (retval != DHCP_IPC_SUCCESS)
198d04ccbb3Scarlsonj 		return (retval);
199d04ccbb3Scarlsonj 
200d04ccbb3Scarlsonj 	if (length == 0)
201d04ccbb3Scarlsonj 		return (DHCP_IPC_E_PROTO);
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	*msg = malloc(length);
2047c478bd9Sstevel@tonic-gate 	if (*msg == NULL)
2057c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	retval = dhcp_ipc_timed_read(fd, *msg, length, &msec);
208d04ccbb3Scarlsonj 	if (retval != DHCP_IPC_SUCCESS) {
2097c478bd9Sstevel@tonic-gate 		free(*msg);
210d04ccbb3Scarlsonj 		return (retval);
2117c478bd9Sstevel@tonic-gate 	}
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	if (length < base_length) {
2147c478bd9Sstevel@tonic-gate 		free(*msg);
215d04ccbb3Scarlsonj 		return (DHCP_IPC_E_PROTO);
2167c478bd9Sstevel@tonic-gate 	}
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	/*
2197c478bd9Sstevel@tonic-gate 	 * the data_length field is in the same place in either ipc message.
2207c478bd9Sstevel@tonic-gate 	 */
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	ipc_msg = (dhcp_ipc_reply_t *)(*msg);
2237c478bd9Sstevel@tonic-gate 	if (ipc_msg->data_length + base_length != length) {
2247c478bd9Sstevel@tonic-gate 		free(*msg);
225d04ccbb3Scarlsonj 		return (DHCP_IPC_E_PROTO);
2267c478bd9Sstevel@tonic-gate 	}
2277c478bd9Sstevel@tonic-gate 
228d04ccbb3Scarlsonj 	return (DHCP_IPC_SUCCESS);
2297c478bd9Sstevel@tonic-gate }
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate /*
2327c478bd9Sstevel@tonic-gate  * dhcp_ipc_recv_request(): gets a request using the agent's ipc protocol
2337c478bd9Sstevel@tonic-gate  *
2347c478bd9Sstevel@tonic-gate  *   input: int: the file descriptor to get the message from
2357c478bd9Sstevel@tonic-gate  *	    dhcp_ipc_request_t **: address of a pointer to store the request
2367c478bd9Sstevel@tonic-gate  *				 (dynamically allocated)
2377c478bd9Sstevel@tonic-gate  *	    int: the # of milliseconds to wait for the message (-1 is forever)
2387c478bd9Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
2397c478bd9Sstevel@tonic-gate  */
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate int
dhcp_ipc_recv_request(int fd,dhcp_ipc_request_t ** request,int msec)2427c478bd9Sstevel@tonic-gate dhcp_ipc_recv_request(int fd, dhcp_ipc_request_t **request, int msec)
2437c478bd9Sstevel@tonic-gate {
2447c478bd9Sstevel@tonic-gate 	int	retval;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	retval = dhcp_ipc_recv_msg(fd, (void **)request, DHCP_IPC_REQUEST_SIZE,
2477c478bd9Sstevel@tonic-gate 	    msec);
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 	/* guarantee that ifname will be NUL-terminated */
2507c478bd9Sstevel@tonic-gate 	if (retval == 0)
25165c8f1c0Smeem 		(*request)->ifname[LIFNAMSIZ - 1] = '\0';
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 	return (retval);
2547c478bd9Sstevel@tonic-gate }
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate /*
2577c478bd9Sstevel@tonic-gate  * dhcp_ipc_recv_reply(): gets a reply using the agent's ipc protocol
2587c478bd9Sstevel@tonic-gate  *
2597c478bd9Sstevel@tonic-gate  *   input: int: the file descriptor to get the message from
2607c478bd9Sstevel@tonic-gate  *	    dhcp_ipc_reply_t **: address of a pointer to store the reply
2617c478bd9Sstevel@tonic-gate  *				 (dynamically allocated)
262a1196271SJames Carlson  *	    int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
263a1196271SJames Carlson  *		     or DHCP_IPC_WAIT_DEFAULT
2647c478bd9Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
2657c478bd9Sstevel@tonic-gate  */
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate static int
dhcp_ipc_recv_reply(int fd,dhcp_ipc_reply_t ** reply,int32_t timeout)268a1196271SJames Carlson dhcp_ipc_recv_reply(int fd, dhcp_ipc_reply_t **reply, int32_t timeout)
2697c478bd9Sstevel@tonic-gate {
270a1196271SJames Carlson 	/*
271a1196271SJames Carlson 	 * If the caller doesn't want to wait forever, and the amount of time
27248bbca81SDaniel Hoffman 	 * it wants to wait is expressible as an integer number of milliseconds
273a1196271SJames Carlson 	 * (as needed by the msg function), then we wait that amount of time
274a1196271SJames Carlson 	 * plus an extra two seconds for the daemon to do its work.  The extra
275a1196271SJames Carlson 	 * two seconds is arbitrary; it should allow plenty of time for the
276a1196271SJames Carlson 	 * daemon to respond within the existing timeout, as specified in the
277a1196271SJames Carlson 	 * original request, so the only time we give up is when the daemon is
278a1196271SJames Carlson 	 * stopped or otherwise malfunctioning.
279a1196271SJames Carlson 	 *
280a1196271SJames Carlson 	 * Note that the wait limit (milliseconds in an 'int') is over 24 days,
281a1196271SJames Carlson 	 * so it's unlikely that any request will actually be that long, and
282a1196271SJames Carlson 	 * it's unlikely that anyone will care if we wait forever on a request
283a1196271SJames Carlson 	 * for a 30 day timer.  The point is to protect against daemon
284a1196271SJames Carlson 	 * malfunction in the usual cases, not to provide an absolute command
285a1196271SJames Carlson 	 * timer.
286a1196271SJames Carlson 	 */
287a1196271SJames Carlson 	if (timeout == DHCP_IPC_WAIT_DEFAULT)
288a1196271SJames Carlson 		timeout = DHCP_IPC_DEFAULT_WAIT;
289a1196271SJames Carlson 	if (timeout != DHCP_IPC_WAIT_FOREVER && timeout < INT_MAX / 1000 - 2)
290a1196271SJames Carlson 		timeout = (timeout + 2) * 1000;
291a1196271SJames Carlson 	else
292a1196271SJames Carlson 		timeout = -1;
293a1196271SJames Carlson 	return (dhcp_ipc_recv_msg(fd, (void **)reply, DHCP_IPC_REPLY_SIZE,
294a1196271SJames Carlson 	    timeout));
2957c478bd9Sstevel@tonic-gate }
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate /*
2987c478bd9Sstevel@tonic-gate  * dhcp_ipc_send_msg(): transmits a message using the agent's ipc protocol
2997c478bd9Sstevel@tonic-gate  *
3007c478bd9Sstevel@tonic-gate  *   input: int: the file descriptor to transmit on
3017c478bd9Sstevel@tonic-gate  *	    void *: the message to send
3027c478bd9Sstevel@tonic-gate  *	    uint32_t: the message length
3037c478bd9Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
3047c478bd9Sstevel@tonic-gate  */
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate static int
dhcp_ipc_send_msg(int fd,void * msg,uint32_t message_length)3077c478bd9Sstevel@tonic-gate dhcp_ipc_send_msg(int fd, void *msg, uint32_t message_length)
3087c478bd9Sstevel@tonic-gate {
3097c478bd9Sstevel@tonic-gate 	struct iovec	iovec[2];
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	iovec[0].iov_base = (caddr_t)&message_length;
3127c478bd9Sstevel@tonic-gate 	iovec[0].iov_len  = sizeof (uint32_t);
3137c478bd9Sstevel@tonic-gate 	iovec[1].iov_base = msg;
3147c478bd9Sstevel@tonic-gate 	iovec[1].iov_len  = message_length;
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	if (writev(fd, iovec, sizeof (iovec) / sizeof (*iovec)) == -1)
3177c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_WRITEV);
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	return (0);
3207c478bd9Sstevel@tonic-gate }
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate /*
3237c478bd9Sstevel@tonic-gate  * dhcp_ipc_send_reply(): transmits a reply using the agent's ipc protocol
3247c478bd9Sstevel@tonic-gate  *
3257c478bd9Sstevel@tonic-gate  *   input: int: the file descriptor to transmit on
3267c478bd9Sstevel@tonic-gate  *	    dhcp_ipc_reply_t *: the reply to send
3277c478bd9Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
3287c478bd9Sstevel@tonic-gate  */
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate int
dhcp_ipc_send_reply(int fd,dhcp_ipc_reply_t * reply)3317c478bd9Sstevel@tonic-gate dhcp_ipc_send_reply(int fd, dhcp_ipc_reply_t *reply)
3327c478bd9Sstevel@tonic-gate {
3337c478bd9Sstevel@tonic-gate 	return (dhcp_ipc_send_msg(fd, reply, DHCP_IPC_REPLY_SIZE +
3347c478bd9Sstevel@tonic-gate 	    reply->data_length));
3357c478bd9Sstevel@tonic-gate }
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate /*
3387c478bd9Sstevel@tonic-gate  * dhcp_ipc_send_request(): transmits a request using the agent's ipc protocol
3397c478bd9Sstevel@tonic-gate  *
3407c478bd9Sstevel@tonic-gate  *   input: int: the file descriptor to transmit on
3417c478bd9Sstevel@tonic-gate  *	    dhcp_ipc_request_t *: the request to send
3427c478bd9Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
3437c478bd9Sstevel@tonic-gate  */
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate static int
dhcp_ipc_send_request(int fd,dhcp_ipc_request_t * request)3467c478bd9Sstevel@tonic-gate dhcp_ipc_send_request(int fd, dhcp_ipc_request_t *request)
3477c478bd9Sstevel@tonic-gate {
3487c478bd9Sstevel@tonic-gate 	/*
3497c478bd9Sstevel@tonic-gate 	 * for now, ipc_ids aren't really used, but they're intended
3507c478bd9Sstevel@tonic-gate 	 * to make it easy to send several requests and then collect
3517c478bd9Sstevel@tonic-gate 	 * all of the replies (and pair them with the requests).
3527c478bd9Sstevel@tonic-gate 	 */
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 	request->ipc_id = gethrtime();
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 	return (dhcp_ipc_send_msg(fd, request, DHCP_IPC_REQUEST_SIZE +
3577c478bd9Sstevel@tonic-gate 	    request->data_length));
3587c478bd9Sstevel@tonic-gate }
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate /*
3617c478bd9Sstevel@tonic-gate  * dhcp_ipc_make_request(): sends the provided request to the agent and reaps
3627c478bd9Sstevel@tonic-gate  *			    the reply
3637c478bd9Sstevel@tonic-gate  *
3647c478bd9Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request to make
3657c478bd9Sstevel@tonic-gate  *	    dhcp_ipc_reply_t **: the reply (dynamically allocated)
3667c478bd9Sstevel@tonic-gate  *	    int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
3677c478bd9Sstevel@tonic-gate  *		     or DHCP_IPC_WAIT_DEFAULT
3687c478bd9Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
3697c478bd9Sstevel@tonic-gate  */
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate int
dhcp_ipc_make_request(dhcp_ipc_request_t * request,dhcp_ipc_reply_t ** reply,int32_t timeout)3727c478bd9Sstevel@tonic-gate dhcp_ipc_make_request(dhcp_ipc_request_t *request, dhcp_ipc_reply_t **reply,
3737c478bd9Sstevel@tonic-gate     int32_t timeout)
3747c478bd9Sstevel@tonic-gate {
375d04ccbb3Scarlsonj 	int			fd, on, retval;
376d04ccbb3Scarlsonj 	struct sockaddr_in	sinv;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	fd = socket(AF_INET, SOCK_STREAM, 0);
3797c478bd9Sstevel@tonic-gate 	if (fd == -1)
3807c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_SOCKET);
381d04ccbb3Scarlsonj 
382d04ccbb3Scarlsonj 	/*
383d04ccbb3Scarlsonj 	 * Bind a privileged port if we have sufficient privilege to do so.
384d04ccbb3Scarlsonj 	 * Continue as non-privileged otherwise.
385d04ccbb3Scarlsonj 	 */
386d04ccbb3Scarlsonj 	on = 1;
387d04ccbb3Scarlsonj 	(void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on));
388d04ccbb3Scarlsonj 
389d04ccbb3Scarlsonj 	(void) memset(&sinv, 0, sizeof (sinv));
390d04ccbb3Scarlsonj 	sinv.sin_family	 = AF_INET;
391d04ccbb3Scarlsonj 	if (bind(fd, (struct sockaddr *)&sinv, sizeof (sinv)) == -1) {
392d04ccbb3Scarlsonj 		(void) dhcp_ipc_close(fd);
393d04ccbb3Scarlsonj 		return (DHCP_IPC_E_BIND);
3947c478bd9Sstevel@tonic-gate 	}
3957c478bd9Sstevel@tonic-gate 
396d04ccbb3Scarlsonj 	sinv.sin_port = htons(IPPORT_DHCPAGENT);
397d04ccbb3Scarlsonj 	sinv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
398d04ccbb3Scarlsonj 	retval = connect(fd, (struct sockaddr *)&sinv, sizeof (sinv));
3997c478bd9Sstevel@tonic-gate 	if (retval == -1) {
4007c478bd9Sstevel@tonic-gate 		(void) dhcp_ipc_close(fd);
4017c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_CONNECT);
4027c478bd9Sstevel@tonic-gate 	}
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 	request->timeout = timeout;
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	retval = dhcp_ipc_send_request(fd, request);
4077c478bd9Sstevel@tonic-gate 	if (retval == 0)
408a1196271SJames Carlson 		retval = dhcp_ipc_recv_reply(fd, reply, timeout);
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	(void) dhcp_ipc_close(fd);
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	return (retval);
4137c478bd9Sstevel@tonic-gate }
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate /*
4167c478bd9Sstevel@tonic-gate  * dhcp_ipc_init(): initializes the ipc channel for use by the agent
4177c478bd9Sstevel@tonic-gate  *
4187c478bd9Sstevel@tonic-gate  *   input: int *: the file descriptor to accept on (returned)
4197c478bd9Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
4207c478bd9Sstevel@tonic-gate  */
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate int
dhcp_ipc_init(int * listen_fd)4237c478bd9Sstevel@tonic-gate dhcp_ipc_init(int *listen_fd)
4247c478bd9Sstevel@tonic-gate {
4257c478bd9Sstevel@tonic-gate 	struct sockaddr_in	sin;
4267c478bd9Sstevel@tonic-gate 	int			on = 1;
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	sin.sin_family		= AF_INET;
4317c478bd9Sstevel@tonic-gate 	sin.sin_port		= htons(IPPORT_DHCPAGENT);
4327c478bd9Sstevel@tonic-gate 	sin.sin_addr.s_addr	= htonl(INADDR_LOOPBACK);
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	*listen_fd = socket(AF_INET, SOCK_STREAM, 0);
4357c478bd9Sstevel@tonic-gate 	if (*listen_fd == -1)
4367c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_SOCKET);
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	/*
4397c478bd9Sstevel@tonic-gate 	 * we use SO_REUSEADDR here since in the case where there
4407c478bd9Sstevel@tonic-gate 	 * really is another daemon running that is using the agent's
441*bbf21555SRichard Lowe 	 * port, bind(3SOCKET) will fail.  so we can't lose.
4427c478bd9Sstevel@tonic-gate 	 */
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	(void) setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &on,
4457c478bd9Sstevel@tonic-gate 	    sizeof (on));
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	if (bind(*listen_fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
4487c478bd9Sstevel@tonic-gate 		(void) close(*listen_fd);
4497c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_BIND);
4507c478bd9Sstevel@tonic-gate 	}
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 	if (listen(*listen_fd, DHCP_IPC_LISTEN_BACKLOG) == -1) {
4537c478bd9Sstevel@tonic-gate 		(void) close(*listen_fd);
4547c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_LISTEN);
4557c478bd9Sstevel@tonic-gate 	}
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	return (0);
4587c478bd9Sstevel@tonic-gate }
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate /*
4617c478bd9Sstevel@tonic-gate  * dhcp_ipc_accept(): accepts an incoming connection for the agent
4627c478bd9Sstevel@tonic-gate  *
4637c478bd9Sstevel@tonic-gate  *   input: int: the file descriptor to accept on
4647c478bd9Sstevel@tonic-gate  *	    int *: the accepted file descriptor (returned)
4657c478bd9Sstevel@tonic-gate  *	    int *: nonzero if the client is privileged (returned)
4667c478bd9Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
4677c478bd9Sstevel@tonic-gate  *    note: sets the socket into nonblocking mode
4687c478bd9Sstevel@tonic-gate  */
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate int
dhcp_ipc_accept(int listen_fd,int * fd,int * is_priv)4717c478bd9Sstevel@tonic-gate dhcp_ipc_accept(int listen_fd, int *fd, int *is_priv)
4727c478bd9Sstevel@tonic-gate {
4737c478bd9Sstevel@tonic-gate 	struct sockaddr_in	sin_peer;
4747c478bd9Sstevel@tonic-gate 	int			sin_len = sizeof (sin_peer);
4757c478bd9Sstevel@tonic-gate 	int			sockflags;
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	/*
4787c478bd9Sstevel@tonic-gate 	 * if we were extremely concerned with portability, we would
4797c478bd9Sstevel@tonic-gate 	 * set the socket into nonblocking mode before doing the
480*bbf21555SRichard Lowe 	 * accept(3SOCKET), since on BSD-based networking stacks, there is
4817c478bd9Sstevel@tonic-gate 	 * a potential race that can occur if the socket which
4827c478bd9Sstevel@tonic-gate 	 * connected to us performs a TCP RST before we accept, since
4837c478bd9Sstevel@tonic-gate 	 * BSD handles this case entirely in the kernel and as a
4847c478bd9Sstevel@tonic-gate 	 * result even though select said we will not block, we can
4857c478bd9Sstevel@tonic-gate 	 * end up blocking since there is no longer a connection to
4867c478bd9Sstevel@tonic-gate 	 * accept.  on SVR4-based systems, this should be okay,
4877c478bd9Sstevel@tonic-gate 	 * and we will get EPROTO back, even though POSIX.1g says
4887c478bd9Sstevel@tonic-gate 	 * we should get ECONNABORTED.
4897c478bd9Sstevel@tonic-gate 	 */
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	*fd = accept(listen_fd, (struct sockaddr *)&sin_peer, &sin_len);
4927c478bd9Sstevel@tonic-gate 	if (*fd == -1)
4937c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_ACCEPT);
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	/* get credentials */
4967c478bd9Sstevel@tonic-gate 	*is_priv = ntohs(sin_peer.sin_port) < IPPORT_RESERVED;
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	/*
4997c478bd9Sstevel@tonic-gate 	 * kick the socket into non-blocking mode so that later
5007c478bd9Sstevel@tonic-gate 	 * operations on the socket don't block and hold up the whole
5017c478bd9Sstevel@tonic-gate 	 * application.  with the event demuxing approach, this may
5027c478bd9Sstevel@tonic-gate 	 * seem unnecessary, but in order to get partial reads/writes
5037c478bd9Sstevel@tonic-gate 	 * and to handle our internal protocol for passing data
5047c478bd9Sstevel@tonic-gate 	 * between the agent and its consumers, this is needed.
5057c478bd9Sstevel@tonic-gate 	 */
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	if ((sockflags = fcntl(*fd, F_GETFL, 0)) == -1) {
5087c478bd9Sstevel@tonic-gate 		(void) close(*fd);
5097c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_FCNTL);
5107c478bd9Sstevel@tonic-gate 	}
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	if (fcntl(*fd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
5137c478bd9Sstevel@tonic-gate 		(void) close(*fd);
5147c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_FCNTL);
5157c478bd9Sstevel@tonic-gate 	}
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 	return (0);
5187c478bd9Sstevel@tonic-gate }
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate /*
5217c478bd9Sstevel@tonic-gate  * dhcp_ipc_close(): closes an ipc descriptor
5227c478bd9Sstevel@tonic-gate  *
5237c478bd9Sstevel@tonic-gate  *   input: int: the file descriptor to close
5247c478bd9Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
5257c478bd9Sstevel@tonic-gate  */
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate int
dhcp_ipc_close(int fd)5287c478bd9Sstevel@tonic-gate dhcp_ipc_close(int fd)
5297c478bd9Sstevel@tonic-gate {
5307c478bd9Sstevel@tonic-gate 	return ((close(fd) == -1) ? DHCP_IPC_E_CLOSE : 0);
5317c478bd9Sstevel@tonic-gate }
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate /*
5347c478bd9Sstevel@tonic-gate  * dhcp_ipc_strerror(): maps an ipc error code into a human-readable string
5357c478bd9Sstevel@tonic-gate  *
5367c478bd9Sstevel@tonic-gate  *   input: int: the ipc error code to map
5377c478bd9Sstevel@tonic-gate  *  output: const char *: the corresponding human-readable string
5387c478bd9Sstevel@tonic-gate  */
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate const char *
dhcp_ipc_strerror(int error)5417c478bd9Sstevel@tonic-gate dhcp_ipc_strerror(int error)
5427c478bd9Sstevel@tonic-gate {
5437c478bd9Sstevel@tonic-gate 	/* note: this must be kept in sync with DHCP_IPC_E_* definitions */
5447c478bd9Sstevel@tonic-gate 	const char *syscalls[] = {
5457c478bd9Sstevel@tonic-gate 		"<unknown>", "socket", "fcntl", "read", "accept", "close",
546d04ccbb3Scarlsonj 		"bind", "listen", "malloc", "connect", "writev", "poll"
5477c478bd9Sstevel@tonic-gate 	};
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	const char	*error_string;
5507c478bd9Sstevel@tonic-gate 	static char	buffer[BUFMAX];
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 	switch (error) {
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	/*
5557c478bd9Sstevel@tonic-gate 	 * none of these errors actually go over the wire.
5567c478bd9Sstevel@tonic-gate 	 * hence, we assume that errno is still fresh.
5577c478bd9Sstevel@tonic-gate 	 */
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_SOCKET:			/* FALLTHRU */
5607c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_FCNTL:			/* FALLTHRU */
5617c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_READ:			/* FALLTHRU */
5627c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_ACCEPT:			/* FALLTHRU */
5637c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_CLOSE:			/* FALLTHRU */
5647c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_BIND:			/* FALLTHRU */
5657c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_LISTEN:			/* FALLTHRU */
5667c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_CONNECT:		/* FALLTHRU */
567d04ccbb3Scarlsonj 	case DHCP_IPC_E_WRITEV:			/* FALLTHRU */
568d04ccbb3Scarlsonj 	case DHCP_IPC_E_POLL:
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 		error_string = strerror(errno);
5717c478bd9Sstevel@tonic-gate 		if (error_string == NULL)
5727c478bd9Sstevel@tonic-gate 			error_string = "unknown error";
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "%s: %s",
5757c478bd9Sstevel@tonic-gate 		    syscalls[error], error_string);
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 		error_string = buffer;
5787c478bd9Sstevel@tonic-gate 		break;
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_MEMORY:
5817c478bd9Sstevel@tonic-gate 		error_string = "out of memory";
5827c478bd9Sstevel@tonic-gate 		break;
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_TIMEOUT:
5857c478bd9Sstevel@tonic-gate 		error_string = "wait timed out, operation still pending...";
5867c478bd9Sstevel@tonic-gate 		break;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_INVIF:
5897c478bd9Sstevel@tonic-gate 		error_string = "interface does not exist or cannot be managed "
5907c478bd9Sstevel@tonic-gate 		    "using DHCP";
5917c478bd9Sstevel@tonic-gate 		break;
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_INT:
5947c478bd9Sstevel@tonic-gate 		error_string = "internal error (might work later)";
5957c478bd9Sstevel@tonic-gate 		break;
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_PERM:
5987c478bd9Sstevel@tonic-gate 		error_string = "permission denied";
5997c478bd9Sstevel@tonic-gate 		break;
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_OUTSTATE:
6027c478bd9Sstevel@tonic-gate 		error_string = "interface not in appropriate state for command";
6037c478bd9Sstevel@tonic-gate 		break;
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_PEND:
6067c478bd9Sstevel@tonic-gate 		error_string = "interface currently has a pending command "
6077c478bd9Sstevel@tonic-gate 		    "(try later)";
6087c478bd9Sstevel@tonic-gate 		break;
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_BOOTP:
6117c478bd9Sstevel@tonic-gate 		error_string = "interface is administered with BOOTP, not DHCP";
6127c478bd9Sstevel@tonic-gate 		break;
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_CMD_UNKNOWN:
6157c478bd9Sstevel@tonic-gate 		error_string = "unknown command";
6167c478bd9Sstevel@tonic-gate 		break;
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_UNKIF:
6197c478bd9Sstevel@tonic-gate 		error_string = "interface is not under DHCP control";
6207c478bd9Sstevel@tonic-gate 		break;
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_PROTO:
6237c478bd9Sstevel@tonic-gate 		error_string = "ipc protocol violation";
6247c478bd9Sstevel@tonic-gate 		break;
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_FAILEDIF:
6277c478bd9Sstevel@tonic-gate 		error_string = "interface is in a FAILED state and must be "
6287c478bd9Sstevel@tonic-gate 		    "manually restarted";
6297c478bd9Sstevel@tonic-gate 		break;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_NOPRIMARY:
6327c478bd9Sstevel@tonic-gate 		error_string = "primary interface requested but no primary "
6337c478bd9Sstevel@tonic-gate 		    "interface is set";
6347c478bd9Sstevel@tonic-gate 		break;
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_NOIPIF:
6377c478bd9Sstevel@tonic-gate 		error_string = "interface currently has no IP address";
6387c478bd9Sstevel@tonic-gate 		break;
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_DOWNIF:
6417c478bd9Sstevel@tonic-gate 		error_string = "interface is currently down";
6427c478bd9Sstevel@tonic-gate 		break;
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 	case DHCP_IPC_E_NOVALUE:
6457c478bd9Sstevel@tonic-gate 		error_string = "no value was found for this option";
6467c478bd9Sstevel@tonic-gate 		break;
6477c478bd9Sstevel@tonic-gate 
648d04ccbb3Scarlsonj 	case DHCP_IPC_E_RUNNING:
649d04ccbb3Scarlsonj 		error_string = "DHCP is already running";
650d04ccbb3Scarlsonj 		break;
651d04ccbb3Scarlsonj 
652d04ccbb3Scarlsonj 	case DHCP_IPC_E_SRVFAILED:
653d04ccbb3Scarlsonj 		error_string = "DHCP server refused request";
654d04ccbb3Scarlsonj 		break;
655d04ccbb3Scarlsonj 
656d04ccbb3Scarlsonj 	case DHCP_IPC_E_EOF:
657d04ccbb3Scarlsonj 		error_string = "ipc connection closed";
6587c478bd9Sstevel@tonic-gate 		break;
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	default:
6617c478bd9Sstevel@tonic-gate 		error_string = "unknown error";
6627c478bd9Sstevel@tonic-gate 		break;
6637c478bd9Sstevel@tonic-gate 	}
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate 	/*
6667c478bd9Sstevel@tonic-gate 	 * TODO: internationalize this error string
6677c478bd9Sstevel@tonic-gate 	 */
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	return (error_string);
6707c478bd9Sstevel@tonic-gate }
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate /*
673cfb9c9abScarlsonj  * dhcp_string_to_request(): maps a string into a request code
674cfb9c9abScarlsonj  *
675cfb9c9abScarlsonj  *    input: const char *: the string to map
676cfb9c9abScarlsonj  *   output: dhcp_ipc_type_t: the request code, or -1 if unknown
677cfb9c9abScarlsonj  */
678cfb9c9abScarlsonj 
679cfb9c9abScarlsonj dhcp_ipc_type_t
dhcp_string_to_request(const char * request)680cfb9c9abScarlsonj dhcp_string_to_request(const char *request)
681cfb9c9abScarlsonj {
682cfb9c9abScarlsonj 	unsigned int	i;
683cfb9c9abScarlsonj 
684cfb9c9abScarlsonj 	for (i = 0; i < DHCP_NIPC; i++)
685cfb9c9abScarlsonj 		if (strcmp(ipc_typestr[i], request) == 0)
686cfb9c9abScarlsonj 			return ((dhcp_ipc_type_t)i);
687cfb9c9abScarlsonj 
688cfb9c9abScarlsonj 	return ((dhcp_ipc_type_t)-1);
689cfb9c9abScarlsonj }
690cfb9c9abScarlsonj 
691cfb9c9abScarlsonj /*
692d04ccbb3Scarlsonj  * dhcp_ipc_type_to_string(): maps an ipc command code into a human-readable
693d04ccbb3Scarlsonj  *			      string
694d04ccbb3Scarlsonj  *
695d04ccbb3Scarlsonj  *   input: int: the ipc command code to map
696d04ccbb3Scarlsonj  *  output: const char *: the corresponding human-readable string
697d04ccbb3Scarlsonj  */
698d04ccbb3Scarlsonj 
699d04ccbb3Scarlsonj const char *
dhcp_ipc_type_to_string(dhcp_ipc_type_t type)700d04ccbb3Scarlsonj dhcp_ipc_type_to_string(dhcp_ipc_type_t type)
701d04ccbb3Scarlsonj {
702d04ccbb3Scarlsonj 	if (type < 0 || type >= DHCP_NIPC)
703d04ccbb3Scarlsonj 		return ("unknown");
704d04ccbb3Scarlsonj 	else
705cfb9c9abScarlsonj 		return (ipc_typestr[(int)type]);
706d04ccbb3Scarlsonj }
707d04ccbb3Scarlsonj 
708d04ccbb3Scarlsonj /*
7097c478bd9Sstevel@tonic-gate  * getinfo_ifnames(): checks the value of a specified option on a list of
7107c478bd9Sstevel@tonic-gate  *		      interface names.
7117c478bd9Sstevel@tonic-gate  *   input: const char *: a list of interface names to query (in order) for
7127c478bd9Sstevel@tonic-gate  *			  the option; "" queries the primary interface
7137c478bd9Sstevel@tonic-gate  *	    dhcp_optnum_t *: a description of the desired option
7147c478bd9Sstevel@tonic-gate  *	    DHCP_OPT **:  filled in with the (dynamically allocated) value of
7157c478bd9Sstevel@tonic-gate  *			  the option upon success.
7167c478bd9Sstevel@tonic-gate  *  output: int: DHCP_IPC_E_* on error, 0 on success or if no value was
7177c478bd9Sstevel@tonic-gate  *	         found but no error occurred either (*result will be NULL)
7187c478bd9Sstevel@tonic-gate  */
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate static int
getinfo_ifnames(const char * ifn,dhcp_optnum_t * optnum,DHCP_OPT ** result)7217c478bd9Sstevel@tonic-gate getinfo_ifnames(const char *ifn, dhcp_optnum_t *optnum, DHCP_OPT **result)
7227c478bd9Sstevel@tonic-gate {
7237c478bd9Sstevel@tonic-gate 	dhcp_ipc_request_t	*request;
7247c478bd9Sstevel@tonic-gate 	dhcp_ipc_reply_t	*reply;
7257c478bd9Sstevel@tonic-gate 	char			*ifnames, *ifnames_head;
7267c478bd9Sstevel@tonic-gate 	DHCP_OPT		*opt;
7277c478bd9Sstevel@tonic-gate 	size_t			opt_size;
7287c478bd9Sstevel@tonic-gate 	int			retval = 0;
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	*result = NULL;
7317c478bd9Sstevel@tonic-gate 	ifnames_head = ifnames = strdup(ifn);
7327c478bd9Sstevel@tonic-gate 	if (ifnames == NULL)
7337c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	request = dhcp_ipc_alloc_request(DHCP_GET_TAG, "", optnum,
7367c478bd9Sstevel@tonic-gate 	    sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 	if (request == NULL) {
7397c478bd9Sstevel@tonic-gate 		free(ifnames_head);
7407c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
7417c478bd9Sstevel@tonic-gate 	}
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate 	ifnames = strtok(ifnames, " ");
7447c478bd9Sstevel@tonic-gate 	if (ifnames == NULL)
7457c478bd9Sstevel@tonic-gate 		ifnames = "";
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 	for (; ifnames != NULL; ifnames = strtok(NULL, " ")) {
7487c478bd9Sstevel@tonic-gate 
74965c8f1c0Smeem 		(void) strlcpy(request->ifname, ifnames, LIFNAMSIZ);
7507c478bd9Sstevel@tonic-gate 		retval = dhcp_ipc_make_request(request, &reply, 0);
7517c478bd9Sstevel@tonic-gate 		if (retval != 0)
7527c478bd9Sstevel@tonic-gate 			break;
7537c478bd9Sstevel@tonic-gate 
7547c478bd9Sstevel@tonic-gate 		if (reply->return_code == 0) {
7557c478bd9Sstevel@tonic-gate 			opt = dhcp_ipc_get_data(reply, &opt_size, NULL);
7567c478bd9Sstevel@tonic-gate 			if (opt_size > 2 && (opt->len == opt_size - 2)) {
7577c478bd9Sstevel@tonic-gate 				*result = malloc(opt_size);
7587c478bd9Sstevel@tonic-gate 				if (*result == NULL)
7597c478bd9Sstevel@tonic-gate 					retval = DHCP_IPC_E_MEMORY;
7607c478bd9Sstevel@tonic-gate 				else
7617c478bd9Sstevel@tonic-gate 					(void) memcpy(*result, opt, opt_size);
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 				free(reply);
7647c478bd9Sstevel@tonic-gate 				break;
7657c478bd9Sstevel@tonic-gate 			}
7667c478bd9Sstevel@tonic-gate 		}
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 		free(reply);
7697c478bd9Sstevel@tonic-gate 		if (ifnames[0] == '\0')
7707c478bd9Sstevel@tonic-gate 			break;
7717c478bd9Sstevel@tonic-gate 	}
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	free(request);
7747c478bd9Sstevel@tonic-gate 	free(ifnames_head);
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	return (retval);
7777c478bd9Sstevel@tonic-gate }
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate /*
7807c478bd9Sstevel@tonic-gate  * get_ifnames(): returns a space-separated list of interface names that
7817c478bd9Sstevel@tonic-gate  *		  match the specified flags
7827c478bd9Sstevel@tonic-gate  *
7837c478bd9Sstevel@tonic-gate  *   input: int: flags which must be on in each interface returned
7847c478bd9Sstevel@tonic-gate  *	    int: flags which must be off in each interface returned
7857c478bd9Sstevel@tonic-gate  *  output: char *: a dynamically-allocated list of interface names, or
7867c478bd9Sstevel@tonic-gate  *		    NULL upon failure.
7877c478bd9Sstevel@tonic-gate  */
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate static char *
get_ifnames(int flags_on,int flags_off)7907c478bd9Sstevel@tonic-gate get_ifnames(int flags_on, int flags_off)
7917c478bd9Sstevel@tonic-gate {
7927c478bd9Sstevel@tonic-gate 	struct ifconf	ifc;
7937c478bd9Sstevel@tonic-gate 	int		n_ifs, i, sock_fd;
7947c478bd9Sstevel@tonic-gate 	char		*ifnames;
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 	sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
7987c478bd9Sstevel@tonic-gate 	if (sock_fd == -1)
7997c478bd9Sstevel@tonic-gate 		return (NULL);
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	if ((ioctl(sock_fd, SIOCGIFNUM, &n_ifs) == -1) || (n_ifs <= 0)) {
8027c478bd9Sstevel@tonic-gate 		(void) close(sock_fd);
8037c478bd9Sstevel@tonic-gate 		return (NULL);
8047c478bd9Sstevel@tonic-gate 	}
8057c478bd9Sstevel@tonic-gate 
80665c8f1c0Smeem 	ifnames = calloc(1, n_ifs * (LIFNAMSIZ + 1));
8077c478bd9Sstevel@tonic-gate 	ifc.ifc_len = n_ifs * sizeof (struct ifreq);
8087c478bd9Sstevel@tonic-gate 	ifc.ifc_req = calloc(n_ifs, sizeof (struct ifreq));
8097c478bd9Sstevel@tonic-gate 	if (ifc.ifc_req != NULL && ifnames != NULL) {
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 		if (ioctl(sock_fd, SIOCGIFCONF, &ifc) == -1) {
8127c478bd9Sstevel@tonic-gate 			(void) close(sock_fd);
8137c478bd9Sstevel@tonic-gate 			free(ifnames);
8147c478bd9Sstevel@tonic-gate 			free(ifc.ifc_req);
8157c478bd9Sstevel@tonic-gate 			return (NULL);
8167c478bd9Sstevel@tonic-gate 		}
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 		for (i = 0; i < n_ifs; i++) {
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate 			if (ioctl(sock_fd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0)
8217c478bd9Sstevel@tonic-gate 				if ((ifc.ifc_req[i].ifr_flags &
8227c478bd9Sstevel@tonic-gate 				    (flags_on | flags_off)) != flags_on)
8237c478bd9Sstevel@tonic-gate 					continue;
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 			(void) strcat(ifnames, ifc.ifc_req[i].ifr_name);
8267c478bd9Sstevel@tonic-gate 			(void) strcat(ifnames, " ");
8277c478bd9Sstevel@tonic-gate 		}
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 		if (strlen(ifnames) > 1)
8307c478bd9Sstevel@tonic-gate 			ifnames[strlen(ifnames) - 1] = '\0';
8317c478bd9Sstevel@tonic-gate 	}
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 	(void) close(sock_fd);
8347c478bd9Sstevel@tonic-gate 	free(ifc.ifc_req);
8357c478bd9Sstevel@tonic-gate 	return (ifnames);
8367c478bd9Sstevel@tonic-gate }
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate /*
8397c478bd9Sstevel@tonic-gate  * dhcp_ipc_getinfo(): attempts to retrieve a value for the specified DHCP
8407c478bd9Sstevel@tonic-gate  *		       option; tries primary interface, then all DHCP-owned
8417c478bd9Sstevel@tonic-gate  *		       interfaces, then INFORMs on the remaining interfaces
8427c478bd9Sstevel@tonic-gate  *		       (these interfaces are dropped prior to returning).
8437c478bd9Sstevel@tonic-gate  *   input: dhcp_optnum_t *: a description of the desired option
8447c478bd9Sstevel@tonic-gate  *	    DHCP_OPT **:  filled in with the (dynamically allocated) value of
8457c478bd9Sstevel@tonic-gate  *			  the option upon success.
8467c478bd9Sstevel@tonic-gate  *	    int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
8477c478bd9Sstevel@tonic-gate  *		     or DHCP_IPC_WAIT_DEFAULT.
8487c478bd9Sstevel@tonic-gate  *  output: int: DHCP_IPC_E_* on error, 0 upon success.
8497c478bd9Sstevel@tonic-gate  */
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate int
dhcp_ipc_getinfo(dhcp_optnum_t * optnum,DHCP_OPT ** result,int32_t timeout)8527c478bd9Sstevel@tonic-gate dhcp_ipc_getinfo(dhcp_optnum_t *optnum, DHCP_OPT **result, int32_t timeout)
8537c478bd9Sstevel@tonic-gate {
8547c478bd9Sstevel@tonic-gate 	dhcp_ipc_request_t	*request;
8557c478bd9Sstevel@tonic-gate 	dhcp_ipc_reply_t	*reply;
8567c478bd9Sstevel@tonic-gate 	char			*ifnames, *ifnames_copy, *ifnames_head;
8577c478bd9Sstevel@tonic-gate 	int			retval;
8587c478bd9Sstevel@tonic-gate 	time_t			start_time = time(NULL);
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 	if (timeout == DHCP_IPC_WAIT_DEFAULT)
8617c478bd9Sstevel@tonic-gate 		timeout = DHCP_IPC_DEFAULT_WAIT;
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 	/*
8647c478bd9Sstevel@tonic-gate 	 * wait at most 5 seconds for the agent to start.
8657c478bd9Sstevel@tonic-gate 	 */
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 	if (dhcp_start_agent((timeout > 5 || timeout < 0) ? 5 : timeout) == -1)
8687c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_INT);
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	/*
8717c478bd9Sstevel@tonic-gate 	 * check the primary interface for the option value first.
8727c478bd9Sstevel@tonic-gate 	 */
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate 	retval = getinfo_ifnames("", optnum, result);
8757c478bd9Sstevel@tonic-gate 	if ((retval != 0) || (retval == 0 && *result != NULL))
8767c478bd9Sstevel@tonic-gate 		return (retval);
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 	/*
8797c478bd9Sstevel@tonic-gate 	 * no luck.  get a list of the interfaces under DHCP control
8807c478bd9Sstevel@tonic-gate 	 * and perform a GET_TAG on each one.
8817c478bd9Sstevel@tonic-gate 	 */
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 	ifnames = get_ifnames(IFF_DHCPRUNNING, 0);
8847c478bd9Sstevel@tonic-gate 	if (ifnames != NULL && strlen(ifnames) != 0) {
8857c478bd9Sstevel@tonic-gate 		retval = getinfo_ifnames(ifnames, optnum, result);
8867c478bd9Sstevel@tonic-gate 		if ((retval != 0) || (retval == 0 && *result != NULL)) {
8877c478bd9Sstevel@tonic-gate 			free(ifnames);
8887c478bd9Sstevel@tonic-gate 			return (retval);
8897c478bd9Sstevel@tonic-gate 		}
8907c478bd9Sstevel@tonic-gate 	}
8917c478bd9Sstevel@tonic-gate 	free(ifnames);
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 	/*
8947c478bd9Sstevel@tonic-gate 	 * still no luck.  retrieve a list of all interfaces on the
8957c478bd9Sstevel@tonic-gate 	 * system that could use DHCP but aren't.  send INFORMs out on
8967c478bd9Sstevel@tonic-gate 	 * each one. after that, sit in a loop for the next `timeout'
8977c478bd9Sstevel@tonic-gate 	 * seconds, trying every second to see if a response for the
8987c478bd9Sstevel@tonic-gate 	 * option we want has come in on one of the interfaces.
8997c478bd9Sstevel@tonic-gate 	 */
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	ifnames = get_ifnames(IFF_UP|IFF_RUNNING, IFF_LOOPBACK|IFF_DHCPRUNNING);
9027c478bd9Sstevel@tonic-gate 	if (ifnames == NULL || strlen(ifnames) == 0) {
9037c478bd9Sstevel@tonic-gate 		free(ifnames);
9047c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_NOVALUE);
9057c478bd9Sstevel@tonic-gate 	}
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 	ifnames_head = ifnames_copy = strdup(ifnames);
9087c478bd9Sstevel@tonic-gate 	if (ifnames_copy == NULL) {
9097c478bd9Sstevel@tonic-gate 		free(ifnames);
9107c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
9117c478bd9Sstevel@tonic-gate 	}
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	request = dhcp_ipc_alloc_request(DHCP_INFORM, "", NULL, 0,
9147c478bd9Sstevel@tonic-gate 	    DHCP_TYPE_NONE);
9157c478bd9Sstevel@tonic-gate 	if (request == NULL) {
9167c478bd9Sstevel@tonic-gate 		free(ifnames);
9177c478bd9Sstevel@tonic-gate 		free(ifnames_head);
9187c478bd9Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
9197c478bd9Sstevel@tonic-gate 	}
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 	ifnames_copy = strtok(ifnames_copy, " ");
9227c478bd9Sstevel@tonic-gate 	for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
92365c8f1c0Smeem 		(void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
9247c478bd9Sstevel@tonic-gate 		if (dhcp_ipc_make_request(request, &reply, 0) == 0)
9257c478bd9Sstevel@tonic-gate 			free(reply);
9267c478bd9Sstevel@tonic-gate 	}
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	for (;;) {
9297c478bd9Sstevel@tonic-gate 		if ((timeout != DHCP_IPC_WAIT_FOREVER) &&
9307c478bd9Sstevel@tonic-gate 		    (time(NULL) - start_time > timeout)) {
9317c478bd9Sstevel@tonic-gate 			retval = DHCP_IPC_E_TIMEOUT;
9327c478bd9Sstevel@tonic-gate 			break;
9337c478bd9Sstevel@tonic-gate 		}
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 		retval = getinfo_ifnames(ifnames, optnum, result);
9367c478bd9Sstevel@tonic-gate 		if (retval != 0 || (retval == 0 && *result != NULL))
9377c478bd9Sstevel@tonic-gate 			break;
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 		(void) sleep(1);
9407c478bd9Sstevel@tonic-gate 	}
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate 	/*
9437c478bd9Sstevel@tonic-gate 	 * drop any interfaces that weren't under DHCP control before
9447c478bd9Sstevel@tonic-gate 	 * we got here; this keeps this function more of a black box
9457c478bd9Sstevel@tonic-gate 	 * and the behavior more consistent from call to call.
9467c478bd9Sstevel@tonic-gate 	 */
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	request->message_type = DHCP_DROP;
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 	ifnames_copy = strcpy(ifnames_head, ifnames);
9517c478bd9Sstevel@tonic-gate 	ifnames_copy = strtok(ifnames_copy, " ");
9527c478bd9Sstevel@tonic-gate 	for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
95365c8f1c0Smeem 		(void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
9547c478bd9Sstevel@tonic-gate 		if (dhcp_ipc_make_request(request, &reply, 0) == 0)
9557c478bd9Sstevel@tonic-gate 			free(reply);
9567c478bd9Sstevel@tonic-gate 	}
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 	free(request);
9597c478bd9Sstevel@tonic-gate 	free(ifnames_head);
9607c478bd9Sstevel@tonic-gate 	free(ifnames);
9617c478bd9Sstevel@tonic-gate 	return (retval);
9627c478bd9Sstevel@tonic-gate }
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate /*
9657c478bd9Sstevel@tonic-gate  * dhcp_ipc_timed_read(): reads from a descriptor using a maximum timeout
9667c478bd9Sstevel@tonic-gate  *
9677c478bd9Sstevel@tonic-gate  *   input: int: the file descriptor to read from
9687c478bd9Sstevel@tonic-gate  *	    void *: the buffer to read into
9697c478bd9Sstevel@tonic-gate  *	    unsigned int: the total length of data to read
9707c478bd9Sstevel@tonic-gate  *	    int *: the number of milliseconds to wait; the number of
971d04ccbb3Scarlsonj  *		   milliseconds left are returned (-1 is "forever")
972d04ccbb3Scarlsonj  *  output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
9737c478bd9Sstevel@tonic-gate  */
9747c478bd9Sstevel@tonic-gate 
9757c478bd9Sstevel@tonic-gate static int
dhcp_ipc_timed_read(int fd,void * buffer,unsigned int length,int * msec)9767c478bd9Sstevel@tonic-gate dhcp_ipc_timed_read(int fd, void *buffer, unsigned int length, int *msec)
9777c478bd9Sstevel@tonic-gate {
9787c478bd9Sstevel@tonic-gate 	unsigned int	n_total = 0;
9797c478bd9Sstevel@tonic-gate 	ssize_t		n_read;
9807c478bd9Sstevel@tonic-gate 	struct pollfd	pollfd;
981d04ccbb3Scarlsonj 	hrtime_t	start, end;
982d04ccbb3Scarlsonj 	int		retv;
9837c478bd9Sstevel@tonic-gate 
9847c478bd9Sstevel@tonic-gate 	pollfd.fd	= fd;
9857c478bd9Sstevel@tonic-gate 	pollfd.events	= POLLIN;
9867c478bd9Sstevel@tonic-gate 
9877c478bd9Sstevel@tonic-gate 	while (n_total < length) {
9887c478bd9Sstevel@tonic-gate 
989d04ccbb3Scarlsonj 		start = gethrtime();
9907c478bd9Sstevel@tonic-gate 
991d04ccbb3Scarlsonj 		retv = poll(&pollfd, 1, *msec);
992d04ccbb3Scarlsonj 		if (retv == 0) {
993d04ccbb3Scarlsonj 			/* This can happen only if *msec is not -1 */
9947c478bd9Sstevel@tonic-gate 			*msec = 0;
995d04ccbb3Scarlsonj 			return (DHCP_IPC_E_TIMEOUT);
9967c478bd9Sstevel@tonic-gate 		}
9977c478bd9Sstevel@tonic-gate 
998d04ccbb3Scarlsonj 		if (*msec != -1) {
999d04ccbb3Scarlsonj 			end = gethrtime();
100019449258SJosef 'Jeff' Sipek 			*msec -= NSEC2MSEC(end - start);
1001d04ccbb3Scarlsonj 			if (*msec < 0)
1002d04ccbb3Scarlsonj 				*msec = 0;
1003d04ccbb3Scarlsonj 		}
10047c478bd9Sstevel@tonic-gate 
1005d04ccbb3Scarlsonj 		if (retv == -1) {
1006d04ccbb3Scarlsonj 			if (errno != EINTR)
1007d04ccbb3Scarlsonj 				return (DHCP_IPC_E_POLL);
1008d04ccbb3Scarlsonj 			else if (*msec == 0)
1009d04ccbb3Scarlsonj 				return (DHCP_IPC_E_TIMEOUT);
1010d04ccbb3Scarlsonj 			continue;
1011d04ccbb3Scarlsonj 		}
1012d04ccbb3Scarlsonj 
1013d04ccbb3Scarlsonj 		if (!(pollfd.revents & POLLIN)) {
1014d04ccbb3Scarlsonj 			errno = EINVAL;
1015d04ccbb3Scarlsonj 			return (DHCP_IPC_E_POLL);
1016d04ccbb3Scarlsonj 		}
1017d04ccbb3Scarlsonj 
1018d04ccbb3Scarlsonj 		n_read = read(fd, (caddr_t)buffer + n_total, length - n_total);
1019d04ccbb3Scarlsonj 
1020d04ccbb3Scarlsonj 		if (n_read == -1) {
1021d04ccbb3Scarlsonj 			if (errno != EINTR)
1022d04ccbb3Scarlsonj 				return (DHCP_IPC_E_READ);
1023d04ccbb3Scarlsonj 			else if (*msec == 0)
1024d04ccbb3Scarlsonj 				return (DHCP_IPC_E_TIMEOUT);
1025d04ccbb3Scarlsonj 			continue;
1026d04ccbb3Scarlsonj 		}
1027d04ccbb3Scarlsonj 
1028d04ccbb3Scarlsonj 		if (n_read == 0) {
1029d04ccbb3Scarlsonj 			return (n_total == 0 ? DHCP_IPC_E_EOF :
1030d04ccbb3Scarlsonj 			    DHCP_IPC_E_PROTO);
1031d04ccbb3Scarlsonj 		}
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 		n_total += n_read;
1034d04ccbb3Scarlsonj 
1035d04ccbb3Scarlsonj 		if (*msec == 0 && n_total < length)
1036d04ccbb3Scarlsonj 			return (DHCP_IPC_E_TIMEOUT);
10377c478bd9Sstevel@tonic-gate 	}
10387c478bd9Sstevel@tonic-gate 
1039d04ccbb3Scarlsonj 	return (DHCP_IPC_SUCCESS);
10407c478bd9Sstevel@tonic-gate }
1041