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