1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <string.h>
30*0Sstevel@tonic-gate #include <unistd.h>
31*0Sstevel@tonic-gate #include <stdlib.h>
32*0Sstevel@tonic-gate #include <sys/uio.h>
33*0Sstevel@tonic-gate #include <sys/socket.h>
34*0Sstevel@tonic-gate #include <sys/types.h>
35*0Sstevel@tonic-gate #include <fcntl.h>
36*0Sstevel@tonic-gate #include <errno.h>
37*0Sstevel@tonic-gate #include <netinet/in.h>
38*0Sstevel@tonic-gate #include <net/if.h>
39*0Sstevel@tonic-gate #include <sys/sockio.h>
40*0Sstevel@tonic-gate #include <sys/fcntl.h>
41*0Sstevel@tonic-gate #include <stdio.h>		/* snprintf */
42*0Sstevel@tonic-gate #include <arpa/inet.h>		/* ntohl, ntohs, etc */
43*0Sstevel@tonic-gate 
44*0Sstevel@tonic-gate #include "dhcpagent_ipc.h"
45*0Sstevel@tonic-gate #include "dhcpagent_util.h"
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate /*
48*0Sstevel@tonic-gate  * the protocol used here is a simple request/reply scheme: a client
49*0Sstevel@tonic-gate  * sends a dhcp_ipc_request_t message to the agent, and the agent
50*0Sstevel@tonic-gate  * sends a dhcp_ipc_reply_t back to the client.  since the requests
51*0Sstevel@tonic-gate  * and replies can be variable-length, they are prefixed on "the wire"
52*0Sstevel@tonic-gate  * by a 32-bit number that tells the other end how many bytes to
53*0Sstevel@tonic-gate  * expect.
54*0Sstevel@tonic-gate  *
55*0Sstevel@tonic-gate  * the format of a request consists of a single dhcp_ipc_request_t;
56*0Sstevel@tonic-gate  * note that the length of this dhcp_ipc_request_t is variable (using
57*0Sstevel@tonic-gate  * the standard c array-of-size-1 trick).  the type of the payload is
58*0Sstevel@tonic-gate  * given by `data_type', which is guaranteed to be `data_length' bytes
59*0Sstevel@tonic-gate  * long starting at `buffer'.  note that `buffer' is guaranteed to be
60*0Sstevel@tonic-gate  * 32-bit aligned but it is poor taste to rely on this.
61*0Sstevel@tonic-gate  *
62*0Sstevel@tonic-gate  * the format of a reply is much the same: a single dhcp_ipc_reply_t;
63*0Sstevel@tonic-gate  * note again that the length of the dhcp_ipc_reply_t is variable.
64*0Sstevel@tonic-gate  * the type of the payload is given by `data_type', which is
65*0Sstevel@tonic-gate  * guaranteed to be `data_length' bytes long starting at `buffer'.
66*0Sstevel@tonic-gate  * once again, note that `buffer' is guaranteed to be 32-bit aligned
67*0Sstevel@tonic-gate  * but it is poor taste to rely on this.
68*0Sstevel@tonic-gate  *
69*0Sstevel@tonic-gate  * requests and replies can be paired up by comparing `ipc_id' fields.
70*0Sstevel@tonic-gate  */
71*0Sstevel@tonic-gate 
72*0Sstevel@tonic-gate #define	BUFMAX	256
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate static int	dhcp_ipc_rresvport(in_port_t *);
75*0Sstevel@tonic-gate static int	dhcp_ipc_timed_read(int, void *, unsigned int, int *);
76*0Sstevel@tonic-gate static int	getinfo_ifnames(const char *, dhcp_optnum_t *, DHCP_OPT **);
77*0Sstevel@tonic-gate static char	*get_ifnames(int, int);
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate /*
80*0Sstevel@tonic-gate  * dhcp_ipc_alloc_request(): allocates a dhcp_ipc_request_t of the given type
81*0Sstevel@tonic-gate  *			     and interface, with a timeout of 0.
82*0Sstevel@tonic-gate  *
83*0Sstevel@tonic-gate  *   input: dhcp_ipc_type_t: the type of ipc request to allocate
84*0Sstevel@tonic-gate  *	    const char *: the interface to associate the request with
85*0Sstevel@tonic-gate  *	    void *: the payload to send with the message (NULL if none)
86*0Sstevel@tonic-gate  *	    uint32_t: the payload size (0 if none)
87*0Sstevel@tonic-gate  *	    dhcp_data_type_t: the description of the type of payload
88*0Sstevel@tonic-gate  *  output: dhcp_ipc_request_t *: the request on success, NULL on failure
89*0Sstevel@tonic-gate  */
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate dhcp_ipc_request_t *
92*0Sstevel@tonic-gate dhcp_ipc_alloc_request(dhcp_ipc_type_t type, const char *ifname, void *buffer,
93*0Sstevel@tonic-gate     uint32_t buffer_size, dhcp_data_type_t data_type)
94*0Sstevel@tonic-gate {
95*0Sstevel@tonic-gate 	dhcp_ipc_request_t *request = calloc(1, DHCP_IPC_REQUEST_SIZE +
96*0Sstevel@tonic-gate 	    buffer_size);
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate 	if (request == NULL)
99*0Sstevel@tonic-gate 		return (NULL);
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate 	request->message_type   = type;
102*0Sstevel@tonic-gate 	request->data_length    = buffer_size;
103*0Sstevel@tonic-gate 	request->data_type	= data_type;
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 	if (ifname != NULL)
106*0Sstevel@tonic-gate 		(void) strlcpy(request->ifname, ifname, IFNAMSIZ);
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate 	if (buffer != NULL)
109*0Sstevel@tonic-gate 		(void) memcpy(request->buffer, buffer, buffer_size);
110*0Sstevel@tonic-gate 
111*0Sstevel@tonic-gate 	return (request);
112*0Sstevel@tonic-gate }
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate /*
115*0Sstevel@tonic-gate  * dhcp_ipc_alloc_reply(): allocates a dhcp_ipc_reply_t
116*0Sstevel@tonic-gate  *
117*0Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request the reply is for
118*0Sstevel@tonic-gate  *	    int: the return code (0 for success, DHCP_IPC_E_* otherwise)
119*0Sstevel@tonic-gate  *	    void *: the payload to send with the message (NULL if none)
120*0Sstevel@tonic-gate  *	    uint32_t: the payload size (0 if none)
121*0Sstevel@tonic-gate  *	    dhcp_data_type_t: the description of the type of payload
122*0Sstevel@tonic-gate  *  output: dhcp_ipc_reply_t *: the reply on success, NULL on failure
123*0Sstevel@tonic-gate  */
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate dhcp_ipc_reply_t *
126*0Sstevel@tonic-gate dhcp_ipc_alloc_reply(dhcp_ipc_request_t *request, int return_code, void *buffer,
127*0Sstevel@tonic-gate     uint32_t buffer_size, dhcp_data_type_t data_type)
128*0Sstevel@tonic-gate {
129*0Sstevel@tonic-gate 	dhcp_ipc_reply_t *reply = calloc(1, DHCP_IPC_REPLY_SIZE + buffer_size);
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate 	if (reply == NULL)
132*0Sstevel@tonic-gate 		return (NULL);
133*0Sstevel@tonic-gate 
134*0Sstevel@tonic-gate 	reply->message_type	= request->message_type;
135*0Sstevel@tonic-gate 	reply->ipc_id		= request->ipc_id;
136*0Sstevel@tonic-gate 	reply->return_code	= return_code;
137*0Sstevel@tonic-gate 	reply->data_length	= buffer_size;
138*0Sstevel@tonic-gate 	reply->data_type	= data_type;
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate 	if (buffer != NULL)
141*0Sstevel@tonic-gate 		(void) memcpy(reply->buffer, buffer, buffer_size);
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate 	return (reply);
144*0Sstevel@tonic-gate }
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate /*
147*0Sstevel@tonic-gate  * dhcp_ipc_get_data(): gets the data and data type from a dhcp_ipc_reply_t
148*0Sstevel@tonic-gate  *
149*0Sstevel@tonic-gate  *   input: dhcp_ipc_reply_t *: the reply to get data from
150*0Sstevel@tonic-gate  *	    size_t *: the size of the resulting data
151*0Sstevel@tonic-gate  *	    dhcp_data_type_t *: the type of the message (returned)
152*0Sstevel@tonic-gate  *  output: void *: a pointer to the data, if there is any.
153*0Sstevel@tonic-gate  */
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate void *
156*0Sstevel@tonic-gate dhcp_ipc_get_data(dhcp_ipc_reply_t *reply, size_t *size, dhcp_data_type_t *type)
157*0Sstevel@tonic-gate {
158*0Sstevel@tonic-gate 	if (reply == NULL || reply->data_length == 0) {
159*0Sstevel@tonic-gate 		*size = 0;
160*0Sstevel@tonic-gate 		return (NULL);
161*0Sstevel@tonic-gate 	}
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate 	if (type != NULL)
164*0Sstevel@tonic-gate 		*type = reply->data_type;
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	*size = reply->data_length;
167*0Sstevel@tonic-gate 	return (reply->buffer);
168*0Sstevel@tonic-gate }
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate /*
171*0Sstevel@tonic-gate  * dhcp_ipc_recv_msg(): gets a message using the agent's ipc protocol
172*0Sstevel@tonic-gate  *
173*0Sstevel@tonic-gate  *   input: int: the file descriptor to get the message from
174*0Sstevel@tonic-gate  *	    void **: the address of a pointer to store the message
175*0Sstevel@tonic-gate  *		     (dynamically allocated)
176*0Sstevel@tonic-gate  *	    uint32_t: the minimum length of the packet
177*0Sstevel@tonic-gate  *	    int: the # of milliseconds to wait for the message (-1 is forever)
178*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
179*0Sstevel@tonic-gate  */
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate static int
182*0Sstevel@tonic-gate dhcp_ipc_recv_msg(int fd, void **msg, uint32_t base_length, int msec)
183*0Sstevel@tonic-gate {
184*0Sstevel@tonic-gate 	ssize_t			retval;
185*0Sstevel@tonic-gate 	dhcp_ipc_reply_t	*ipc_msg;
186*0Sstevel@tonic-gate 	uint32_t		length;
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate 	retval = dhcp_ipc_timed_read(fd, &length, sizeof (uint32_t), &msec);
189*0Sstevel@tonic-gate 	if (retval != sizeof (uint32_t))
190*0Sstevel@tonic-gate 		return (DHCP_IPC_E_READ);
191*0Sstevel@tonic-gate 
192*0Sstevel@tonic-gate 	*msg = malloc(length);
193*0Sstevel@tonic-gate 	if (*msg == NULL)
194*0Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate 	retval = dhcp_ipc_timed_read(fd, *msg, length, &msec);
197*0Sstevel@tonic-gate 	if (retval != length) {
198*0Sstevel@tonic-gate 		free(*msg);
199*0Sstevel@tonic-gate 		return (DHCP_IPC_E_READ);
200*0Sstevel@tonic-gate 	}
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 	if (length < base_length) {
203*0Sstevel@tonic-gate 		free(*msg);
204*0Sstevel@tonic-gate 		return (DHCP_IPC_E_READ);
205*0Sstevel@tonic-gate 	}
206*0Sstevel@tonic-gate 
207*0Sstevel@tonic-gate 	/*
208*0Sstevel@tonic-gate 	 * the data_length field is in the same place in either ipc message.
209*0Sstevel@tonic-gate 	 */
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate 	ipc_msg = (dhcp_ipc_reply_t *)(*msg);
212*0Sstevel@tonic-gate 	if (ipc_msg->data_length + base_length != length) {
213*0Sstevel@tonic-gate 		free(*msg);
214*0Sstevel@tonic-gate 		return (DHCP_IPC_E_READ);
215*0Sstevel@tonic-gate 	}
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	return (0);
218*0Sstevel@tonic-gate }
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate /*
221*0Sstevel@tonic-gate  * dhcp_ipc_recv_request(): gets a request using the agent's ipc protocol
222*0Sstevel@tonic-gate  *
223*0Sstevel@tonic-gate  *   input: int: the file descriptor to get the message from
224*0Sstevel@tonic-gate  *	    dhcp_ipc_request_t **: address of a pointer to store the request
225*0Sstevel@tonic-gate  *				 (dynamically allocated)
226*0Sstevel@tonic-gate  *	    int: the # of milliseconds to wait for the message (-1 is forever)
227*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
228*0Sstevel@tonic-gate  */
229*0Sstevel@tonic-gate 
230*0Sstevel@tonic-gate int
231*0Sstevel@tonic-gate dhcp_ipc_recv_request(int fd, dhcp_ipc_request_t **request, int msec)
232*0Sstevel@tonic-gate {
233*0Sstevel@tonic-gate 	int	retval;
234*0Sstevel@tonic-gate 
235*0Sstevel@tonic-gate 	retval = dhcp_ipc_recv_msg(fd, (void **)request, DHCP_IPC_REQUEST_SIZE,
236*0Sstevel@tonic-gate 	    msec);
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate 	/* guarantee that ifname will be NUL-terminated */
239*0Sstevel@tonic-gate 	if (retval == 0)
240*0Sstevel@tonic-gate 		(*request)->ifname[IFNAMSIZ - 1] = '\0';
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	return (retval);
243*0Sstevel@tonic-gate }
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate /*
246*0Sstevel@tonic-gate  * dhcp_ipc_recv_reply(): gets a reply using the agent's ipc protocol
247*0Sstevel@tonic-gate  *
248*0Sstevel@tonic-gate  *   input: int: the file descriptor to get the message from
249*0Sstevel@tonic-gate  *	    dhcp_ipc_reply_t **: address of a pointer to store the reply
250*0Sstevel@tonic-gate  *				 (dynamically allocated)
251*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
252*0Sstevel@tonic-gate  */
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate static int
255*0Sstevel@tonic-gate dhcp_ipc_recv_reply(int fd, dhcp_ipc_reply_t **reply)
256*0Sstevel@tonic-gate {
257*0Sstevel@tonic-gate 	return (dhcp_ipc_recv_msg(fd, (void **)reply, DHCP_IPC_REPLY_SIZE, -1));
258*0Sstevel@tonic-gate }
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate /*
261*0Sstevel@tonic-gate  * dhcp_ipc_send_msg(): transmits a message using the agent's ipc protocol
262*0Sstevel@tonic-gate  *
263*0Sstevel@tonic-gate  *   input: int: the file descriptor to transmit on
264*0Sstevel@tonic-gate  *	    void *: the message to send
265*0Sstevel@tonic-gate  *	    uint32_t: the message length
266*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
267*0Sstevel@tonic-gate  */
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate static int
270*0Sstevel@tonic-gate dhcp_ipc_send_msg(int fd, void *msg, uint32_t message_length)
271*0Sstevel@tonic-gate {
272*0Sstevel@tonic-gate 	struct iovec	iovec[2];
273*0Sstevel@tonic-gate 
274*0Sstevel@tonic-gate 	iovec[0].iov_base = (caddr_t)&message_length;
275*0Sstevel@tonic-gate 	iovec[0].iov_len  = sizeof (uint32_t);
276*0Sstevel@tonic-gate 	iovec[1].iov_base = msg;
277*0Sstevel@tonic-gate 	iovec[1].iov_len  = message_length;
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate 	if (writev(fd, iovec, sizeof (iovec) / sizeof (*iovec)) == -1)
280*0Sstevel@tonic-gate 		return (DHCP_IPC_E_WRITEV);
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate 	return (0);
283*0Sstevel@tonic-gate }
284*0Sstevel@tonic-gate 
285*0Sstevel@tonic-gate /*
286*0Sstevel@tonic-gate  * dhcp_ipc_send_reply(): transmits a reply using the agent's ipc protocol
287*0Sstevel@tonic-gate  *
288*0Sstevel@tonic-gate  *   input: int: the file descriptor to transmit on
289*0Sstevel@tonic-gate  *	    dhcp_ipc_reply_t *: the reply to send
290*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
291*0Sstevel@tonic-gate  */
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate int
294*0Sstevel@tonic-gate dhcp_ipc_send_reply(int fd, dhcp_ipc_reply_t *reply)
295*0Sstevel@tonic-gate {
296*0Sstevel@tonic-gate 	return (dhcp_ipc_send_msg(fd, reply, DHCP_IPC_REPLY_SIZE +
297*0Sstevel@tonic-gate 	    reply->data_length));
298*0Sstevel@tonic-gate }
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate /*
301*0Sstevel@tonic-gate  * dhcp_ipc_send_request(): transmits a request using the agent's ipc protocol
302*0Sstevel@tonic-gate  *
303*0Sstevel@tonic-gate  *   input: int: the file descriptor to transmit on
304*0Sstevel@tonic-gate  *	    dhcp_ipc_request_t *: the request to send
305*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
306*0Sstevel@tonic-gate  */
307*0Sstevel@tonic-gate 
308*0Sstevel@tonic-gate static int
309*0Sstevel@tonic-gate dhcp_ipc_send_request(int fd, dhcp_ipc_request_t *request)
310*0Sstevel@tonic-gate {
311*0Sstevel@tonic-gate 	/*
312*0Sstevel@tonic-gate 	 * for now, ipc_ids aren't really used, but they're intended
313*0Sstevel@tonic-gate 	 * to make it easy to send several requests and then collect
314*0Sstevel@tonic-gate 	 * all of the replies (and pair them with the requests).
315*0Sstevel@tonic-gate 	 */
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate 	request->ipc_id = gethrtime();
318*0Sstevel@tonic-gate 
319*0Sstevel@tonic-gate 	return (dhcp_ipc_send_msg(fd, request, DHCP_IPC_REQUEST_SIZE +
320*0Sstevel@tonic-gate 	    request->data_length));
321*0Sstevel@tonic-gate }
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate /*
324*0Sstevel@tonic-gate  * dhcp_ipc_make_request(): sends the provided request to the agent and reaps
325*0Sstevel@tonic-gate  *			    the reply
326*0Sstevel@tonic-gate  *
327*0Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request to make
328*0Sstevel@tonic-gate  *	    dhcp_ipc_reply_t **: the reply (dynamically allocated)
329*0Sstevel@tonic-gate  *	    int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
330*0Sstevel@tonic-gate  *		     or DHCP_IPC_WAIT_DEFAULT
331*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
332*0Sstevel@tonic-gate  */
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate int
335*0Sstevel@tonic-gate dhcp_ipc_make_request(dhcp_ipc_request_t *request, dhcp_ipc_reply_t **reply,
336*0Sstevel@tonic-gate     int32_t timeout)
337*0Sstevel@tonic-gate {
338*0Sstevel@tonic-gate 	int			fd, retval;
339*0Sstevel@tonic-gate 	struct sockaddr_in	sin_peer;
340*0Sstevel@tonic-gate 	in_port_t		source_port = IPPORT_RESERVED - 1;
341*0Sstevel@tonic-gate 
342*0Sstevel@tonic-gate 	(void) memset(&sin_peer, 0, sizeof (sin_peer));
343*0Sstevel@tonic-gate 
344*0Sstevel@tonic-gate 	sin_peer.sin_family	 = AF_INET;
345*0Sstevel@tonic-gate 	sin_peer.sin_port	 = htons(IPPORT_DHCPAGENT);
346*0Sstevel@tonic-gate 	sin_peer.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 	if ((fd = dhcp_ipc_rresvport(&source_port)) == -1) {
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 		/*
351*0Sstevel@tonic-gate 		 * user isn't privileged.  just make a socket.
352*0Sstevel@tonic-gate 		 */
353*0Sstevel@tonic-gate 
354*0Sstevel@tonic-gate 		fd = socket(AF_INET, SOCK_STREAM, 0);
355*0Sstevel@tonic-gate 		if (fd == -1)
356*0Sstevel@tonic-gate 			return (DHCP_IPC_E_SOCKET);
357*0Sstevel@tonic-gate 	}
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 	retval = connect(fd, (struct sockaddr *)&sin_peer, sizeof (sin_peer));
360*0Sstevel@tonic-gate 	if (retval == -1) {
361*0Sstevel@tonic-gate 		(void) dhcp_ipc_close(fd);
362*0Sstevel@tonic-gate 		return (DHCP_IPC_E_CONNECT);
363*0Sstevel@tonic-gate 	}
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate 	request->timeout = timeout;
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	retval = dhcp_ipc_send_request(fd, request);
368*0Sstevel@tonic-gate 	if (retval == 0)
369*0Sstevel@tonic-gate 		retval = dhcp_ipc_recv_reply(fd, reply);
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate 	(void) dhcp_ipc_close(fd);
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate 	return (retval);
374*0Sstevel@tonic-gate }
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate /*
377*0Sstevel@tonic-gate  * dhcp_ipc_init(): initializes the ipc channel for use by the agent
378*0Sstevel@tonic-gate  *
379*0Sstevel@tonic-gate  *   input: int *: the file descriptor to accept on (returned)
380*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
381*0Sstevel@tonic-gate  */
382*0Sstevel@tonic-gate 
383*0Sstevel@tonic-gate int
384*0Sstevel@tonic-gate dhcp_ipc_init(int *listen_fd)
385*0Sstevel@tonic-gate {
386*0Sstevel@tonic-gate 	struct sockaddr_in	sin;
387*0Sstevel@tonic-gate 	int			on = 1;
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate 	sin.sin_family		= AF_INET;
392*0Sstevel@tonic-gate 	sin.sin_port		= htons(IPPORT_DHCPAGENT);
393*0Sstevel@tonic-gate 	sin.sin_addr.s_addr	= htonl(INADDR_LOOPBACK);
394*0Sstevel@tonic-gate 
395*0Sstevel@tonic-gate 	*listen_fd = socket(AF_INET, SOCK_STREAM, 0);
396*0Sstevel@tonic-gate 	if (*listen_fd == -1)
397*0Sstevel@tonic-gate 		return (DHCP_IPC_E_SOCKET);
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate 	/*
400*0Sstevel@tonic-gate 	 * we use SO_REUSEADDR here since in the case where there
401*0Sstevel@tonic-gate 	 * really is another daemon running that is using the agent's
402*0Sstevel@tonic-gate 	 * port, bind(3N) will fail.  so we can't lose.
403*0Sstevel@tonic-gate 	 */
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate 	(void) setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &on,
406*0Sstevel@tonic-gate 	    sizeof (on));
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate 	if (bind(*listen_fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
409*0Sstevel@tonic-gate 		(void) close(*listen_fd);
410*0Sstevel@tonic-gate 		return (DHCP_IPC_E_BIND);
411*0Sstevel@tonic-gate 	}
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate 	if (listen(*listen_fd, DHCP_IPC_LISTEN_BACKLOG) == -1) {
414*0Sstevel@tonic-gate 		(void) close(*listen_fd);
415*0Sstevel@tonic-gate 		return (DHCP_IPC_E_LISTEN);
416*0Sstevel@tonic-gate 	}
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate 	return (0);
419*0Sstevel@tonic-gate }
420*0Sstevel@tonic-gate 
421*0Sstevel@tonic-gate /*
422*0Sstevel@tonic-gate  * dhcp_ipc_accept(): accepts an incoming connection for the agent
423*0Sstevel@tonic-gate  *
424*0Sstevel@tonic-gate  *   input: int: the file descriptor to accept on
425*0Sstevel@tonic-gate  *	    int *: the accepted file descriptor (returned)
426*0Sstevel@tonic-gate  *	    int *: nonzero if the client is privileged (returned)
427*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
428*0Sstevel@tonic-gate  *    note: sets the socket into nonblocking mode
429*0Sstevel@tonic-gate  */
430*0Sstevel@tonic-gate 
431*0Sstevel@tonic-gate int
432*0Sstevel@tonic-gate dhcp_ipc_accept(int listen_fd, int *fd, int *is_priv)
433*0Sstevel@tonic-gate {
434*0Sstevel@tonic-gate 	struct sockaddr_in	sin_peer;
435*0Sstevel@tonic-gate 	int			sin_len = sizeof (sin_peer);
436*0Sstevel@tonic-gate 	int			sockflags;
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate 	/*
439*0Sstevel@tonic-gate 	 * if we were extremely concerned with portability, we would
440*0Sstevel@tonic-gate 	 * set the socket into nonblocking mode before doing the
441*0Sstevel@tonic-gate 	 * accept(3N), since on BSD-based networking stacks, there is
442*0Sstevel@tonic-gate 	 * a potential race that can occur if the socket which
443*0Sstevel@tonic-gate 	 * connected to us performs a TCP RST before we accept, since
444*0Sstevel@tonic-gate 	 * BSD handles this case entirely in the kernel and as a
445*0Sstevel@tonic-gate 	 * result even though select said we will not block, we can
446*0Sstevel@tonic-gate 	 * end up blocking since there is no longer a connection to
447*0Sstevel@tonic-gate 	 * accept.  on SVR4-based systems, this should be okay,
448*0Sstevel@tonic-gate 	 * and we will get EPROTO back, even though POSIX.1g says
449*0Sstevel@tonic-gate 	 * we should get ECONNABORTED.
450*0Sstevel@tonic-gate 	 */
451*0Sstevel@tonic-gate 
452*0Sstevel@tonic-gate 	*fd = accept(listen_fd, (struct sockaddr *)&sin_peer, &sin_len);
453*0Sstevel@tonic-gate 	if (*fd == -1)
454*0Sstevel@tonic-gate 		return (DHCP_IPC_E_ACCEPT);
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 	/* get credentials */
457*0Sstevel@tonic-gate 	*is_priv = ntohs(sin_peer.sin_port) < IPPORT_RESERVED;
458*0Sstevel@tonic-gate 
459*0Sstevel@tonic-gate 	/*
460*0Sstevel@tonic-gate 	 * kick the socket into non-blocking mode so that later
461*0Sstevel@tonic-gate 	 * operations on the socket don't block and hold up the whole
462*0Sstevel@tonic-gate 	 * application.  with the event demuxing approach, this may
463*0Sstevel@tonic-gate 	 * seem unnecessary, but in order to get partial reads/writes
464*0Sstevel@tonic-gate 	 * and to handle our internal protocol for passing data
465*0Sstevel@tonic-gate 	 * between the agent and its consumers, this is needed.
466*0Sstevel@tonic-gate 	 */
467*0Sstevel@tonic-gate 
468*0Sstevel@tonic-gate 	if ((sockflags = fcntl(*fd, F_GETFL, 0)) == -1) {
469*0Sstevel@tonic-gate 		(void) close(*fd);
470*0Sstevel@tonic-gate 		return (DHCP_IPC_E_FCNTL);
471*0Sstevel@tonic-gate 	}
472*0Sstevel@tonic-gate 
473*0Sstevel@tonic-gate 	if (fcntl(*fd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
474*0Sstevel@tonic-gate 		(void) close(*fd);
475*0Sstevel@tonic-gate 		return (DHCP_IPC_E_FCNTL);
476*0Sstevel@tonic-gate 	}
477*0Sstevel@tonic-gate 
478*0Sstevel@tonic-gate 	return (0);
479*0Sstevel@tonic-gate }
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate /*
482*0Sstevel@tonic-gate  * dhcp_ipc_close(): closes an ipc descriptor
483*0Sstevel@tonic-gate  *
484*0Sstevel@tonic-gate  *   input: int: the file descriptor to close
485*0Sstevel@tonic-gate  *  output: int: 0 on success, DHCP_IPC_E_* otherwise
486*0Sstevel@tonic-gate  */
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate int
489*0Sstevel@tonic-gate dhcp_ipc_close(int fd)
490*0Sstevel@tonic-gate {
491*0Sstevel@tonic-gate 	return ((close(fd) == -1) ? DHCP_IPC_E_CLOSE : 0);
492*0Sstevel@tonic-gate }
493*0Sstevel@tonic-gate 
494*0Sstevel@tonic-gate /*
495*0Sstevel@tonic-gate  * dhcp_ipc_strerror(): maps an ipc error code into a human-readable string
496*0Sstevel@tonic-gate  *
497*0Sstevel@tonic-gate  *   input: int: the ipc error code to map
498*0Sstevel@tonic-gate  *  output: const char *: the corresponding human-readable string
499*0Sstevel@tonic-gate  */
500*0Sstevel@tonic-gate 
501*0Sstevel@tonic-gate const char *
502*0Sstevel@tonic-gate dhcp_ipc_strerror(int error)
503*0Sstevel@tonic-gate {
504*0Sstevel@tonic-gate 	/* note: this must be kept in sync with DHCP_IPC_E_* definitions */
505*0Sstevel@tonic-gate 	const char *syscalls[] = {
506*0Sstevel@tonic-gate 		"<unknown>", "socket", "fcntl", "read", "accept", "close",
507*0Sstevel@tonic-gate 		"bind", "listen", "malloc", "connect", "writev"
508*0Sstevel@tonic-gate 	};
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate 	const char	*error_string;
511*0Sstevel@tonic-gate 	static char	buffer[BUFMAX];
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 	switch (error) {
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	/*
516*0Sstevel@tonic-gate 	 * none of these errors actually go over the wire.
517*0Sstevel@tonic-gate 	 * hence, we assume that errno is still fresh.
518*0Sstevel@tonic-gate 	 */
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate 	case DHCP_IPC_E_SOCKET:			/* FALLTHRU */
521*0Sstevel@tonic-gate 	case DHCP_IPC_E_FCNTL:			/* FALLTHRU */
522*0Sstevel@tonic-gate 	case DHCP_IPC_E_READ:			/* FALLTHRU */
523*0Sstevel@tonic-gate 	case DHCP_IPC_E_ACCEPT:			/* FALLTHRU */
524*0Sstevel@tonic-gate 	case DHCP_IPC_E_CLOSE:			/* FALLTHRU */
525*0Sstevel@tonic-gate 	case DHCP_IPC_E_BIND:			/* FALLTHRU */
526*0Sstevel@tonic-gate 	case DHCP_IPC_E_LISTEN:			/* FALLTHRU */
527*0Sstevel@tonic-gate 	case DHCP_IPC_E_CONNECT:		/* FALLTHRU */
528*0Sstevel@tonic-gate 	case DHCP_IPC_E_WRITEV:
529*0Sstevel@tonic-gate 
530*0Sstevel@tonic-gate 		error_string = strerror(errno);
531*0Sstevel@tonic-gate 		if (error_string == NULL)
532*0Sstevel@tonic-gate 			error_string = "unknown error";
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "%s: %s",
535*0Sstevel@tonic-gate 		    syscalls[error], error_string);
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate 		error_string = buffer;
538*0Sstevel@tonic-gate 		break;
539*0Sstevel@tonic-gate 
540*0Sstevel@tonic-gate 	case DHCP_IPC_E_MEMORY:
541*0Sstevel@tonic-gate 		error_string = "out of memory";
542*0Sstevel@tonic-gate 		break;
543*0Sstevel@tonic-gate 
544*0Sstevel@tonic-gate 	case DHCP_IPC_E_TIMEOUT:
545*0Sstevel@tonic-gate 		error_string = "wait timed out, operation still pending...";
546*0Sstevel@tonic-gate 		break;
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate 	case DHCP_IPC_E_INVIF:
549*0Sstevel@tonic-gate 		error_string = "interface does not exist or cannot be managed "
550*0Sstevel@tonic-gate 		    "using DHCP";
551*0Sstevel@tonic-gate 		break;
552*0Sstevel@tonic-gate 
553*0Sstevel@tonic-gate 	case DHCP_IPC_E_INT:
554*0Sstevel@tonic-gate 		error_string = "internal error (might work later)";
555*0Sstevel@tonic-gate 		break;
556*0Sstevel@tonic-gate 
557*0Sstevel@tonic-gate 	case DHCP_IPC_E_PERM:
558*0Sstevel@tonic-gate 		error_string = "permission denied";
559*0Sstevel@tonic-gate 		break;
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 	case DHCP_IPC_E_OUTSTATE:
562*0Sstevel@tonic-gate 		error_string = "interface not in appropriate state for command";
563*0Sstevel@tonic-gate 		break;
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate 	case DHCP_IPC_E_PEND:
566*0Sstevel@tonic-gate 		error_string = "interface currently has a pending command "
567*0Sstevel@tonic-gate 		    "(try later)";
568*0Sstevel@tonic-gate 		break;
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate 	case DHCP_IPC_E_BOOTP:
571*0Sstevel@tonic-gate 		error_string = "interface is administered with BOOTP, not DHCP";
572*0Sstevel@tonic-gate 		break;
573*0Sstevel@tonic-gate 
574*0Sstevel@tonic-gate 	case DHCP_IPC_E_CMD_UNKNOWN:
575*0Sstevel@tonic-gate 		error_string = "unknown command";
576*0Sstevel@tonic-gate 		break;
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 	case DHCP_IPC_E_UNKIF:
579*0Sstevel@tonic-gate 		error_string = "interface is not under DHCP control";
580*0Sstevel@tonic-gate 		break;
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate 	case DHCP_IPC_E_PROTO:
583*0Sstevel@tonic-gate 		error_string = "ipc protocol violation";
584*0Sstevel@tonic-gate 		break;
585*0Sstevel@tonic-gate 
586*0Sstevel@tonic-gate 	case DHCP_IPC_E_FAILEDIF:
587*0Sstevel@tonic-gate 		error_string = "interface is in a FAILED state and must be "
588*0Sstevel@tonic-gate 		    "manually restarted";
589*0Sstevel@tonic-gate 		break;
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 	case DHCP_IPC_E_NOPRIMARY:
592*0Sstevel@tonic-gate 		error_string = "primary interface requested but no primary "
593*0Sstevel@tonic-gate 		    "interface is set";
594*0Sstevel@tonic-gate 		break;
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate 	case DHCP_IPC_E_NOIPIF:
597*0Sstevel@tonic-gate 		error_string = "interface currently has no IP address";
598*0Sstevel@tonic-gate 		break;
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 	case DHCP_IPC_E_DOWNIF:
601*0Sstevel@tonic-gate 		error_string = "interface is currently down";
602*0Sstevel@tonic-gate 		break;
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 	case DHCP_IPC_E_NOVALUE:
605*0Sstevel@tonic-gate 		error_string = "no value was found for this option";
606*0Sstevel@tonic-gate 		break;
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate 	case DHCP_IPC_E_NOIFCID:
609*0Sstevel@tonic-gate 		error_string = "interface does not have a configured DHCP "
610*0Sstevel@tonic-gate 		    "client id";
611*0Sstevel@tonic-gate 		break;
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 	default:
614*0Sstevel@tonic-gate 		error_string = "unknown error";
615*0Sstevel@tonic-gate 		break;
616*0Sstevel@tonic-gate 	}
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 	/*
619*0Sstevel@tonic-gate 	 * TODO: internationalize this error string
620*0Sstevel@tonic-gate 	 */
621*0Sstevel@tonic-gate 
622*0Sstevel@tonic-gate 	return (error_string);
623*0Sstevel@tonic-gate }
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate /*
626*0Sstevel@tonic-gate  * getinfo_ifnames(): checks the value of a specified option on a list of
627*0Sstevel@tonic-gate  *		      interface names.
628*0Sstevel@tonic-gate  *   input: const char *: a list of interface names to query (in order) for
629*0Sstevel@tonic-gate  *			  the option; "" queries the primary interface
630*0Sstevel@tonic-gate  *	    dhcp_optnum_t *: a description of the desired option
631*0Sstevel@tonic-gate  *	    DHCP_OPT **:  filled in with the (dynamically allocated) value of
632*0Sstevel@tonic-gate  *			  the option upon success.
633*0Sstevel@tonic-gate  *  output: int: DHCP_IPC_E_* on error, 0 on success or if no value was
634*0Sstevel@tonic-gate  *	         found but no error occurred either (*result will be NULL)
635*0Sstevel@tonic-gate  */
636*0Sstevel@tonic-gate 
637*0Sstevel@tonic-gate static int
638*0Sstevel@tonic-gate getinfo_ifnames(const char *ifn, dhcp_optnum_t *optnum, DHCP_OPT **result)
639*0Sstevel@tonic-gate {
640*0Sstevel@tonic-gate 	dhcp_ipc_request_t	*request;
641*0Sstevel@tonic-gate 	dhcp_ipc_reply_t	*reply;
642*0Sstevel@tonic-gate 	char			*ifnames, *ifnames_head;
643*0Sstevel@tonic-gate 	DHCP_OPT		*opt;
644*0Sstevel@tonic-gate 	size_t			opt_size;
645*0Sstevel@tonic-gate 	int			retval = 0;
646*0Sstevel@tonic-gate 
647*0Sstevel@tonic-gate 	*result = NULL;
648*0Sstevel@tonic-gate 	ifnames_head = ifnames = strdup(ifn);
649*0Sstevel@tonic-gate 	if (ifnames == NULL)
650*0Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
651*0Sstevel@tonic-gate 
652*0Sstevel@tonic-gate 	request = dhcp_ipc_alloc_request(DHCP_GET_TAG, "", optnum,
653*0Sstevel@tonic-gate 	    sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
654*0Sstevel@tonic-gate 
655*0Sstevel@tonic-gate 	if (request == NULL) {
656*0Sstevel@tonic-gate 		free(ifnames_head);
657*0Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
658*0Sstevel@tonic-gate 	}
659*0Sstevel@tonic-gate 
660*0Sstevel@tonic-gate 	ifnames = strtok(ifnames, " ");
661*0Sstevel@tonic-gate 	if (ifnames == NULL)
662*0Sstevel@tonic-gate 		ifnames = "";
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 	for (; ifnames != NULL; ifnames = strtok(NULL, " ")) {
665*0Sstevel@tonic-gate 
666*0Sstevel@tonic-gate 		(void) strlcpy(request->ifname, ifnames, IFNAMSIZ);
667*0Sstevel@tonic-gate 		retval = dhcp_ipc_make_request(request, &reply, 0);
668*0Sstevel@tonic-gate 		if (retval != 0)
669*0Sstevel@tonic-gate 			break;
670*0Sstevel@tonic-gate 
671*0Sstevel@tonic-gate 		if (reply->return_code == 0) {
672*0Sstevel@tonic-gate 			opt = dhcp_ipc_get_data(reply, &opt_size, NULL);
673*0Sstevel@tonic-gate 			if (opt_size > 2 && (opt->len == opt_size - 2)) {
674*0Sstevel@tonic-gate 				*result = malloc(opt_size);
675*0Sstevel@tonic-gate 				if (*result == NULL)
676*0Sstevel@tonic-gate 					retval = DHCP_IPC_E_MEMORY;
677*0Sstevel@tonic-gate 				else
678*0Sstevel@tonic-gate 					(void) memcpy(*result, opt, opt_size);
679*0Sstevel@tonic-gate 
680*0Sstevel@tonic-gate 				free(reply);
681*0Sstevel@tonic-gate 				break;
682*0Sstevel@tonic-gate 			}
683*0Sstevel@tonic-gate 		}
684*0Sstevel@tonic-gate 
685*0Sstevel@tonic-gate 		free(reply);
686*0Sstevel@tonic-gate 		if (ifnames[0] == '\0')
687*0Sstevel@tonic-gate 			break;
688*0Sstevel@tonic-gate 	}
689*0Sstevel@tonic-gate 
690*0Sstevel@tonic-gate 	free(request);
691*0Sstevel@tonic-gate 	free(ifnames_head);
692*0Sstevel@tonic-gate 
693*0Sstevel@tonic-gate 	return (retval);
694*0Sstevel@tonic-gate }
695*0Sstevel@tonic-gate 
696*0Sstevel@tonic-gate /*
697*0Sstevel@tonic-gate  * get_ifnames(): returns a space-separated list of interface names that
698*0Sstevel@tonic-gate  *		  match the specified flags
699*0Sstevel@tonic-gate  *
700*0Sstevel@tonic-gate  *   input: int: flags which must be on in each interface returned
701*0Sstevel@tonic-gate  *	    int: flags which must be off in each interface returned
702*0Sstevel@tonic-gate  *  output: char *: a dynamically-allocated list of interface names, or
703*0Sstevel@tonic-gate  *		    NULL upon failure.
704*0Sstevel@tonic-gate  */
705*0Sstevel@tonic-gate 
706*0Sstevel@tonic-gate static char *
707*0Sstevel@tonic-gate get_ifnames(int flags_on, int flags_off)
708*0Sstevel@tonic-gate {
709*0Sstevel@tonic-gate 	struct ifconf	ifc;
710*0Sstevel@tonic-gate 	int		n_ifs, i, sock_fd;
711*0Sstevel@tonic-gate 	char		*ifnames;
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate 	sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
715*0Sstevel@tonic-gate 	if (sock_fd == -1)
716*0Sstevel@tonic-gate 		return (NULL);
717*0Sstevel@tonic-gate 
718*0Sstevel@tonic-gate 	if ((ioctl(sock_fd, SIOCGIFNUM, &n_ifs) == -1) || (n_ifs <= 0)) {
719*0Sstevel@tonic-gate 		(void) close(sock_fd);
720*0Sstevel@tonic-gate 		return (NULL);
721*0Sstevel@tonic-gate 	}
722*0Sstevel@tonic-gate 
723*0Sstevel@tonic-gate 	ifnames = calloc(1, n_ifs * (IFNAMSIZ + 1));
724*0Sstevel@tonic-gate 	ifc.ifc_len = n_ifs * sizeof (struct ifreq);
725*0Sstevel@tonic-gate 	ifc.ifc_req = calloc(n_ifs, sizeof (struct ifreq));
726*0Sstevel@tonic-gate 	if (ifc.ifc_req != NULL && ifnames != NULL) {
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 		if (ioctl(sock_fd, SIOCGIFCONF, &ifc) == -1) {
729*0Sstevel@tonic-gate 			(void) close(sock_fd);
730*0Sstevel@tonic-gate 			free(ifnames);
731*0Sstevel@tonic-gate 			free(ifc.ifc_req);
732*0Sstevel@tonic-gate 			return (NULL);
733*0Sstevel@tonic-gate 		}
734*0Sstevel@tonic-gate 
735*0Sstevel@tonic-gate 		for (i = 0; i < n_ifs; i++) {
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate 			if (ioctl(sock_fd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0)
738*0Sstevel@tonic-gate 				if ((ifc.ifc_req[i].ifr_flags &
739*0Sstevel@tonic-gate 				    (flags_on | flags_off)) != flags_on)
740*0Sstevel@tonic-gate 					continue;
741*0Sstevel@tonic-gate 
742*0Sstevel@tonic-gate 			(void) strcat(ifnames, ifc.ifc_req[i].ifr_name);
743*0Sstevel@tonic-gate 			(void) strcat(ifnames, " ");
744*0Sstevel@tonic-gate 		}
745*0Sstevel@tonic-gate 
746*0Sstevel@tonic-gate 		if (strlen(ifnames) > 1)
747*0Sstevel@tonic-gate 			ifnames[strlen(ifnames) - 1] = '\0';
748*0Sstevel@tonic-gate 	}
749*0Sstevel@tonic-gate 
750*0Sstevel@tonic-gate 	(void) close(sock_fd);
751*0Sstevel@tonic-gate 	free(ifc.ifc_req);
752*0Sstevel@tonic-gate 	return (ifnames);
753*0Sstevel@tonic-gate }
754*0Sstevel@tonic-gate 
755*0Sstevel@tonic-gate /*
756*0Sstevel@tonic-gate  * dhcp_ipc_getinfo(): attempts to retrieve a value for the specified DHCP
757*0Sstevel@tonic-gate  *		       option; tries primary interface, then all DHCP-owned
758*0Sstevel@tonic-gate  *		       interfaces, then INFORMs on the remaining interfaces
759*0Sstevel@tonic-gate  *		       (these interfaces are dropped prior to returning).
760*0Sstevel@tonic-gate  *   input: dhcp_optnum_t *: a description of the desired option
761*0Sstevel@tonic-gate  *	    DHCP_OPT **:  filled in with the (dynamically allocated) value of
762*0Sstevel@tonic-gate  *			  the option upon success.
763*0Sstevel@tonic-gate  *	    int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
764*0Sstevel@tonic-gate  *		     or DHCP_IPC_WAIT_DEFAULT.
765*0Sstevel@tonic-gate  *  output: int: DHCP_IPC_E_* on error, 0 upon success.
766*0Sstevel@tonic-gate  */
767*0Sstevel@tonic-gate 
768*0Sstevel@tonic-gate int
769*0Sstevel@tonic-gate dhcp_ipc_getinfo(dhcp_optnum_t *optnum, DHCP_OPT **result, int32_t timeout)
770*0Sstevel@tonic-gate {
771*0Sstevel@tonic-gate 	dhcp_ipc_request_t	*request;
772*0Sstevel@tonic-gate 	dhcp_ipc_reply_t	*reply;
773*0Sstevel@tonic-gate 	char			*ifnames, *ifnames_copy, *ifnames_head;
774*0Sstevel@tonic-gate 	int			retval;
775*0Sstevel@tonic-gate 	time_t			start_time = time(NULL);
776*0Sstevel@tonic-gate 
777*0Sstevel@tonic-gate 	if (timeout == DHCP_IPC_WAIT_DEFAULT)
778*0Sstevel@tonic-gate 		timeout = DHCP_IPC_DEFAULT_WAIT;
779*0Sstevel@tonic-gate 
780*0Sstevel@tonic-gate 	/*
781*0Sstevel@tonic-gate 	 * wait at most 5 seconds for the agent to start.
782*0Sstevel@tonic-gate 	 */
783*0Sstevel@tonic-gate 
784*0Sstevel@tonic-gate 	if (dhcp_start_agent((timeout > 5 || timeout < 0) ? 5 : timeout) == -1)
785*0Sstevel@tonic-gate 		return (DHCP_IPC_E_INT);
786*0Sstevel@tonic-gate 
787*0Sstevel@tonic-gate 	/*
788*0Sstevel@tonic-gate 	 * check the primary interface for the option value first.
789*0Sstevel@tonic-gate 	 */
790*0Sstevel@tonic-gate 
791*0Sstevel@tonic-gate 	retval = getinfo_ifnames("", optnum, result);
792*0Sstevel@tonic-gate 	if ((retval != 0) || (retval == 0 && *result != NULL))
793*0Sstevel@tonic-gate 		return (retval);
794*0Sstevel@tonic-gate 
795*0Sstevel@tonic-gate 	/*
796*0Sstevel@tonic-gate 	 * no luck.  get a list of the interfaces under DHCP control
797*0Sstevel@tonic-gate 	 * and perform a GET_TAG on each one.
798*0Sstevel@tonic-gate 	 */
799*0Sstevel@tonic-gate 
800*0Sstevel@tonic-gate 	ifnames = get_ifnames(IFF_DHCPRUNNING, 0);
801*0Sstevel@tonic-gate 	if (ifnames != NULL && strlen(ifnames) != 0) {
802*0Sstevel@tonic-gate 		retval = getinfo_ifnames(ifnames, optnum, result);
803*0Sstevel@tonic-gate 		if ((retval != 0) || (retval == 0 && *result != NULL)) {
804*0Sstevel@tonic-gate 			free(ifnames);
805*0Sstevel@tonic-gate 			return (retval);
806*0Sstevel@tonic-gate 		}
807*0Sstevel@tonic-gate 	}
808*0Sstevel@tonic-gate 	free(ifnames);
809*0Sstevel@tonic-gate 
810*0Sstevel@tonic-gate 	/*
811*0Sstevel@tonic-gate 	 * still no luck.  retrieve a list of all interfaces on the
812*0Sstevel@tonic-gate 	 * system that could use DHCP but aren't.  send INFORMs out on
813*0Sstevel@tonic-gate 	 * each one. after that, sit in a loop for the next `timeout'
814*0Sstevel@tonic-gate 	 * seconds, trying every second to see if a response for the
815*0Sstevel@tonic-gate 	 * option we want has come in on one of the interfaces.
816*0Sstevel@tonic-gate 	 */
817*0Sstevel@tonic-gate 
818*0Sstevel@tonic-gate 	ifnames = get_ifnames(IFF_UP|IFF_RUNNING, IFF_LOOPBACK|IFF_DHCPRUNNING);
819*0Sstevel@tonic-gate 	if (ifnames == NULL || strlen(ifnames) == 0) {
820*0Sstevel@tonic-gate 		free(ifnames);
821*0Sstevel@tonic-gate 		return (DHCP_IPC_E_NOVALUE);
822*0Sstevel@tonic-gate 	}
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate 	ifnames_head = ifnames_copy = strdup(ifnames);
825*0Sstevel@tonic-gate 	if (ifnames_copy == NULL) {
826*0Sstevel@tonic-gate 		free(ifnames);
827*0Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
828*0Sstevel@tonic-gate 	}
829*0Sstevel@tonic-gate 
830*0Sstevel@tonic-gate 	request = dhcp_ipc_alloc_request(DHCP_INFORM, "", NULL, 0,
831*0Sstevel@tonic-gate 	    DHCP_TYPE_NONE);
832*0Sstevel@tonic-gate 	if (request == NULL) {
833*0Sstevel@tonic-gate 		free(ifnames);
834*0Sstevel@tonic-gate 		free(ifnames_head);
835*0Sstevel@tonic-gate 		return (DHCP_IPC_E_MEMORY);
836*0Sstevel@tonic-gate 	}
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 	ifnames_copy = strtok(ifnames_copy, " ");
839*0Sstevel@tonic-gate 	for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
840*0Sstevel@tonic-gate 		(void) strlcpy(request->ifname, ifnames_copy, IFNAMSIZ);
841*0Sstevel@tonic-gate 		if (dhcp_ipc_make_request(request, &reply, 0) == 0)
842*0Sstevel@tonic-gate 			free(reply);
843*0Sstevel@tonic-gate 	}
844*0Sstevel@tonic-gate 
845*0Sstevel@tonic-gate 	for (;;) {
846*0Sstevel@tonic-gate 		if ((timeout != DHCP_IPC_WAIT_FOREVER) &&
847*0Sstevel@tonic-gate 		    (time(NULL) - start_time > timeout)) {
848*0Sstevel@tonic-gate 			retval = DHCP_IPC_E_TIMEOUT;
849*0Sstevel@tonic-gate 			break;
850*0Sstevel@tonic-gate 		}
851*0Sstevel@tonic-gate 
852*0Sstevel@tonic-gate 		retval = getinfo_ifnames(ifnames, optnum, result);
853*0Sstevel@tonic-gate 		if (retval != 0 || (retval == 0 && *result != NULL))
854*0Sstevel@tonic-gate 			break;
855*0Sstevel@tonic-gate 
856*0Sstevel@tonic-gate 		(void) sleep(1);
857*0Sstevel@tonic-gate 	}
858*0Sstevel@tonic-gate 
859*0Sstevel@tonic-gate 	/*
860*0Sstevel@tonic-gate 	 * drop any interfaces that weren't under DHCP control before
861*0Sstevel@tonic-gate 	 * we got here; this keeps this function more of a black box
862*0Sstevel@tonic-gate 	 * and the behavior more consistent from call to call.
863*0Sstevel@tonic-gate 	 */
864*0Sstevel@tonic-gate 
865*0Sstevel@tonic-gate 	request->message_type = DHCP_DROP;
866*0Sstevel@tonic-gate 
867*0Sstevel@tonic-gate 	ifnames_copy = strcpy(ifnames_head, ifnames);
868*0Sstevel@tonic-gate 	ifnames_copy = strtok(ifnames_copy, " ");
869*0Sstevel@tonic-gate 	for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
870*0Sstevel@tonic-gate 		(void) strlcpy(request->ifname, ifnames_copy, IFNAMSIZ);
871*0Sstevel@tonic-gate 		if (dhcp_ipc_make_request(request, &reply, 0) == 0)
872*0Sstevel@tonic-gate 			free(reply);
873*0Sstevel@tonic-gate 	}
874*0Sstevel@tonic-gate 
875*0Sstevel@tonic-gate 	free(request);
876*0Sstevel@tonic-gate 	free(ifnames_head);
877*0Sstevel@tonic-gate 	free(ifnames);
878*0Sstevel@tonic-gate 	return (retval);
879*0Sstevel@tonic-gate }
880*0Sstevel@tonic-gate 
881*0Sstevel@tonic-gate /*
882*0Sstevel@tonic-gate  * NOTE: we provide our own version of this function because currently
883*0Sstevel@tonic-gate  *       (sunos 5.7), if we link against the one in libnsl, we will
884*0Sstevel@tonic-gate  *       increase the size of our binary by more than 482K due to
885*0Sstevel@tonic-gate  *	 perversions in linking.  besides, this one is tighter :-)
886*0Sstevel@tonic-gate  */
887*0Sstevel@tonic-gate 
888*0Sstevel@tonic-gate static int
889*0Sstevel@tonic-gate dhcp_ipc_rresvport(in_port_t *start_port)
890*0Sstevel@tonic-gate {
891*0Sstevel@tonic-gate 	struct sockaddr_in	sin;
892*0Sstevel@tonic-gate 	int			s, saved_errno;
893*0Sstevel@tonic-gate 
894*0Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
895*0Sstevel@tonic-gate 	sin.sin_family		= AF_INET;
896*0Sstevel@tonic-gate 	sin.sin_addr.s_addr	= htonl(INADDR_ANY);
897*0Sstevel@tonic-gate 
898*0Sstevel@tonic-gate 	if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
899*0Sstevel@tonic-gate 		return (-1);
900*0Sstevel@tonic-gate 
901*0Sstevel@tonic-gate 	errno = EAGAIN;
902*0Sstevel@tonic-gate 	while (*start_port > IPPORT_RESERVED / 2) {
903*0Sstevel@tonic-gate 
904*0Sstevel@tonic-gate 		sin.sin_port = htons((*start_port)--);
905*0Sstevel@tonic-gate 
906*0Sstevel@tonic-gate 		if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) == 0)
907*0Sstevel@tonic-gate 			return (s);
908*0Sstevel@tonic-gate 
909*0Sstevel@tonic-gate 		if (errno != EADDRINUSE) {
910*0Sstevel@tonic-gate 			saved_errno = errno;
911*0Sstevel@tonic-gate 			break;
912*0Sstevel@tonic-gate 		}
913*0Sstevel@tonic-gate 	}
914*0Sstevel@tonic-gate 
915*0Sstevel@tonic-gate 	(void) close(s);
916*0Sstevel@tonic-gate 	errno = saved_errno;
917*0Sstevel@tonic-gate 	return (-1);
918*0Sstevel@tonic-gate }
919*0Sstevel@tonic-gate 
920*0Sstevel@tonic-gate /*
921*0Sstevel@tonic-gate  * dhcp_ipc_timed_read(): reads from a descriptor using a maximum timeout
922*0Sstevel@tonic-gate  *
923*0Sstevel@tonic-gate  *   input: int: the file descriptor to read from
924*0Sstevel@tonic-gate  *	    void *: the buffer to read into
925*0Sstevel@tonic-gate  *	    unsigned int: the total length of data to read
926*0Sstevel@tonic-gate  *	    int *: the number of milliseconds to wait; the number of
927*0Sstevel@tonic-gate  *		   milliseconds left are returned
928*0Sstevel@tonic-gate  *  output: int: -1 on failure, otherwise the number of bytes read
929*0Sstevel@tonic-gate  */
930*0Sstevel@tonic-gate 
931*0Sstevel@tonic-gate static int
932*0Sstevel@tonic-gate dhcp_ipc_timed_read(int fd, void *buffer, unsigned int length, int *msec)
933*0Sstevel@tonic-gate {
934*0Sstevel@tonic-gate 	unsigned int	n_total = 0;
935*0Sstevel@tonic-gate 	ssize_t		n_read;
936*0Sstevel@tonic-gate 	struct pollfd	pollfd;
937*0Sstevel@tonic-gate 	struct timeval	start, end, elapsed;
938*0Sstevel@tonic-gate 
939*0Sstevel@tonic-gate 	/* make sure that any errors we return are ours */
940*0Sstevel@tonic-gate 	errno = 0;
941*0Sstevel@tonic-gate 
942*0Sstevel@tonic-gate 	pollfd.fd	= fd;
943*0Sstevel@tonic-gate 	pollfd.events	= POLLIN;
944*0Sstevel@tonic-gate 
945*0Sstevel@tonic-gate 	while (n_total < length) {
946*0Sstevel@tonic-gate 
947*0Sstevel@tonic-gate 		if (gettimeofday(&start, NULL) == -1)
948*0Sstevel@tonic-gate 			return (-1);
949*0Sstevel@tonic-gate 
950*0Sstevel@tonic-gate 		switch (poll(&pollfd, 1, *msec)) {
951*0Sstevel@tonic-gate 
952*0Sstevel@tonic-gate 		case 0:
953*0Sstevel@tonic-gate 			*msec = 0;
954*0Sstevel@tonic-gate 			return (n_total);
955*0Sstevel@tonic-gate 
956*0Sstevel@tonic-gate 		case -1:
957*0Sstevel@tonic-gate 			*msec = 0;
958*0Sstevel@tonic-gate 			return (-1);
959*0Sstevel@tonic-gate 
960*0Sstevel@tonic-gate 		default:
961*0Sstevel@tonic-gate 			if ((pollfd.revents & POLLIN) == 0)
962*0Sstevel@tonic-gate 				return (-1);
963*0Sstevel@tonic-gate 
964*0Sstevel@tonic-gate 			if (gettimeofday(&end, NULL) == -1)
965*0Sstevel@tonic-gate 				return (-1);
966*0Sstevel@tonic-gate 
967*0Sstevel@tonic-gate 			elapsed.tv_sec  = end.tv_sec  - start.tv_sec;
968*0Sstevel@tonic-gate 			elapsed.tv_usec = end.tv_usec - start.tv_usec;
969*0Sstevel@tonic-gate 			if (elapsed.tv_usec < 0) {
970*0Sstevel@tonic-gate 				elapsed.tv_sec--;
971*0Sstevel@tonic-gate 				elapsed.tv_usec += 1000000;	/* one second */
972*0Sstevel@tonic-gate 			}
973*0Sstevel@tonic-gate 
974*0Sstevel@tonic-gate 			n_read = read(fd, (caddr_t)buffer + n_total,
975*0Sstevel@tonic-gate 			    length - n_total);
976*0Sstevel@tonic-gate 
977*0Sstevel@tonic-gate 			if (n_read == -1)
978*0Sstevel@tonic-gate 				return (-1);
979*0Sstevel@tonic-gate 
980*0Sstevel@tonic-gate 			n_total += n_read;
981*0Sstevel@tonic-gate 			*msec -= elapsed.tv_sec * 1000 + elapsed.tv_usec / 1000;
982*0Sstevel@tonic-gate 			if (*msec <= 0 || n_read == 0)
983*0Sstevel@tonic-gate 				return (n_total);
984*0Sstevel@tonic-gate 			break;
985*0Sstevel@tonic-gate 		}
986*0Sstevel@tonic-gate 	}
987*0Sstevel@tonic-gate 
988*0Sstevel@tonic-gate 	return (n_total);
989*0Sstevel@tonic-gate }
990