10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
53431Scarlsonj * Common Development and Distribution License (the "License").
63431Scarlsonj * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
228562SPeter.Memishian@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #include <string.h>
270Sstevel@tonic-gate #include <unistd.h>
280Sstevel@tonic-gate #include <stdlib.h>
290Sstevel@tonic-gate #include <sys/uio.h>
300Sstevel@tonic-gate #include <sys/socket.h>
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <fcntl.h>
330Sstevel@tonic-gate #include <errno.h>
34*9633Sjames.d.carlson@sun.com #include <limits.h>
350Sstevel@tonic-gate #include <netinet/in.h>
363431Scarlsonj #include <netinet/tcp.h>
370Sstevel@tonic-gate #include <net/if.h>
380Sstevel@tonic-gate #include <sys/sockio.h>
390Sstevel@tonic-gate #include <sys/fcntl.h>
403431Scarlsonj #include <sys/time.h>
410Sstevel@tonic-gate #include <stdio.h> /* snprintf */
420Sstevel@tonic-gate #include <arpa/inet.h> /* ntohl, ntohs, etc */
430Sstevel@tonic-gate
440Sstevel@tonic-gate #include "dhcpagent_ipc.h"
450Sstevel@tonic-gate #include "dhcpagent_util.h"
460Sstevel@tonic-gate
470Sstevel@tonic-gate /*
480Sstevel@tonic-gate * the protocol used here is a simple request/reply scheme: a client
490Sstevel@tonic-gate * sends a dhcp_ipc_request_t message to the agent, and the agent
500Sstevel@tonic-gate * sends a dhcp_ipc_reply_t back to the client. since the requests
510Sstevel@tonic-gate * and replies can be variable-length, they are prefixed on "the wire"
520Sstevel@tonic-gate * by a 32-bit number that tells the other end how many bytes to
530Sstevel@tonic-gate * expect.
540Sstevel@tonic-gate *
550Sstevel@tonic-gate * the format of a request consists of a single dhcp_ipc_request_t;
560Sstevel@tonic-gate * note that the length of this dhcp_ipc_request_t is variable (using
570Sstevel@tonic-gate * the standard c array-of-size-1 trick). the type of the payload is
580Sstevel@tonic-gate * given by `data_type', which is guaranteed to be `data_length' bytes
590Sstevel@tonic-gate * long starting at `buffer'. note that `buffer' is guaranteed to be
600Sstevel@tonic-gate * 32-bit aligned but it is poor taste to rely on this.
610Sstevel@tonic-gate *
620Sstevel@tonic-gate * the format of a reply is much the same: a single dhcp_ipc_reply_t;
630Sstevel@tonic-gate * note again that the length of the dhcp_ipc_reply_t is variable.
640Sstevel@tonic-gate * the type of the payload is given by `data_type', which is
650Sstevel@tonic-gate * guaranteed to be `data_length' bytes long starting at `buffer'.
660Sstevel@tonic-gate * once again, note that `buffer' is guaranteed to be 32-bit aligned
670Sstevel@tonic-gate * but it is poor taste to rely on this.
680Sstevel@tonic-gate *
690Sstevel@tonic-gate * requests and replies can be paired up by comparing `ipc_id' fields.
700Sstevel@tonic-gate */
710Sstevel@tonic-gate
720Sstevel@tonic-gate #define BUFMAX 256
730Sstevel@tonic-gate
740Sstevel@tonic-gate static int dhcp_ipc_timed_read(int, void *, unsigned int, int *);
750Sstevel@tonic-gate static int getinfo_ifnames(const char *, dhcp_optnum_t *, DHCP_OPT **);
760Sstevel@tonic-gate static char *get_ifnames(int, int);
770Sstevel@tonic-gate
784106Scarlsonj /* must be kept in sync with enum in dhcpagent_ipc.h */
794106Scarlsonj static const char *ipc_typestr[] = {
804106Scarlsonj "drop", "extend", "ping", "release", "start", "status",
814106Scarlsonj "inform", "get_tag"
824106Scarlsonj };
834106Scarlsonj
840Sstevel@tonic-gate /*
850Sstevel@tonic-gate * dhcp_ipc_alloc_request(): allocates a dhcp_ipc_request_t of the given type
860Sstevel@tonic-gate * and interface, with a timeout of 0.
870Sstevel@tonic-gate *
880Sstevel@tonic-gate * input: dhcp_ipc_type_t: the type of ipc request to allocate
890Sstevel@tonic-gate * const char *: the interface to associate the request with
903431Scarlsonj * const void *: the payload to send with the message (NULL if none)
910Sstevel@tonic-gate * uint32_t: the payload size (0 if none)
920Sstevel@tonic-gate * dhcp_data_type_t: the description of the type of payload
930Sstevel@tonic-gate * output: dhcp_ipc_request_t *: the request on success, NULL on failure
940Sstevel@tonic-gate */
950Sstevel@tonic-gate
960Sstevel@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)973431Scarlsonj dhcp_ipc_alloc_request(dhcp_ipc_type_t type, const char *ifname,
983431Scarlsonj const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
990Sstevel@tonic-gate {
1000Sstevel@tonic-gate dhcp_ipc_request_t *request = calloc(1, DHCP_IPC_REQUEST_SIZE +
1010Sstevel@tonic-gate buffer_size);
1020Sstevel@tonic-gate
1030Sstevel@tonic-gate if (request == NULL)
1040Sstevel@tonic-gate return (NULL);
1050Sstevel@tonic-gate
1060Sstevel@tonic-gate request->message_type = type;
1070Sstevel@tonic-gate request->data_length = buffer_size;
1080Sstevel@tonic-gate request->data_type = data_type;
1090Sstevel@tonic-gate
1100Sstevel@tonic-gate if (ifname != NULL)
1118562SPeter.Memishian@Sun.COM (void) strlcpy(request->ifname, ifname, LIFNAMSIZ);
1120Sstevel@tonic-gate
1130Sstevel@tonic-gate if (buffer != NULL)
1140Sstevel@tonic-gate (void) memcpy(request->buffer, buffer, buffer_size);
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate return (request);
1170Sstevel@tonic-gate }
1180Sstevel@tonic-gate
1190Sstevel@tonic-gate /*
1200Sstevel@tonic-gate * dhcp_ipc_alloc_reply(): allocates a dhcp_ipc_reply_t
1210Sstevel@tonic-gate *
1220Sstevel@tonic-gate * input: dhcp_ipc_request_t *: the request the reply is for
1230Sstevel@tonic-gate * int: the return code (0 for success, DHCP_IPC_E_* otherwise)
1243431Scarlsonj * const void *: the payload to send with the message (NULL if none)
1250Sstevel@tonic-gate * uint32_t: the payload size (0 if none)
1260Sstevel@tonic-gate * dhcp_data_type_t: the description of the type of payload
1270Sstevel@tonic-gate * output: dhcp_ipc_reply_t *: the reply on success, NULL on failure
1280Sstevel@tonic-gate */
1290Sstevel@tonic-gate
1300Sstevel@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)1313431Scarlsonj dhcp_ipc_alloc_reply(dhcp_ipc_request_t *request, int return_code,
1323431Scarlsonj const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
1330Sstevel@tonic-gate {
1340Sstevel@tonic-gate dhcp_ipc_reply_t *reply = calloc(1, DHCP_IPC_REPLY_SIZE + buffer_size);
1350Sstevel@tonic-gate
1360Sstevel@tonic-gate if (reply == NULL)
1370Sstevel@tonic-gate return (NULL);
1380Sstevel@tonic-gate
1390Sstevel@tonic-gate reply->message_type = request->message_type;
1400Sstevel@tonic-gate reply->ipc_id = request->ipc_id;
1410Sstevel@tonic-gate reply->return_code = return_code;
1420Sstevel@tonic-gate reply->data_length = buffer_size;
1430Sstevel@tonic-gate reply->data_type = data_type;
1440Sstevel@tonic-gate
1450Sstevel@tonic-gate if (buffer != NULL)
1460Sstevel@tonic-gate (void) memcpy(reply->buffer, buffer, buffer_size);
1470Sstevel@tonic-gate
1480Sstevel@tonic-gate return (reply);
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate /*
1520Sstevel@tonic-gate * dhcp_ipc_get_data(): gets the data and data type from a dhcp_ipc_reply_t
1530Sstevel@tonic-gate *
1540Sstevel@tonic-gate * input: dhcp_ipc_reply_t *: the reply to get data from
1550Sstevel@tonic-gate * size_t *: the size of the resulting data
1560Sstevel@tonic-gate * dhcp_data_type_t *: the type of the message (returned)
1570Sstevel@tonic-gate * output: void *: a pointer to the data, if there is any.
1580Sstevel@tonic-gate */
1590Sstevel@tonic-gate
1600Sstevel@tonic-gate void *
dhcp_ipc_get_data(dhcp_ipc_reply_t * reply,size_t * size,dhcp_data_type_t * type)1610Sstevel@tonic-gate dhcp_ipc_get_data(dhcp_ipc_reply_t *reply, size_t *size, dhcp_data_type_t *type)
1620Sstevel@tonic-gate {
1630Sstevel@tonic-gate if (reply == NULL || reply->data_length == 0) {
1640Sstevel@tonic-gate *size = 0;
1650Sstevel@tonic-gate return (NULL);
1660Sstevel@tonic-gate }
1670Sstevel@tonic-gate
1680Sstevel@tonic-gate if (type != NULL)
1690Sstevel@tonic-gate *type = reply->data_type;
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate *size = reply->data_length;
1720Sstevel@tonic-gate return (reply->buffer);
1730Sstevel@tonic-gate }
1740Sstevel@tonic-gate
1750Sstevel@tonic-gate /*
1760Sstevel@tonic-gate * dhcp_ipc_recv_msg(): gets a message using the agent's ipc protocol
1770Sstevel@tonic-gate *
1780Sstevel@tonic-gate * input: int: the file descriptor to get the message from
1790Sstevel@tonic-gate * void **: the address of a pointer to store the message
1800Sstevel@tonic-gate * (dynamically allocated)
1810Sstevel@tonic-gate * uint32_t: the minimum length of the packet
1820Sstevel@tonic-gate * int: the # of milliseconds to wait for the message (-1 is forever)
1833431Scarlsonj * output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
1840Sstevel@tonic-gate */
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate static int
dhcp_ipc_recv_msg(int fd,void ** msg,uint32_t base_length,int msec)1870Sstevel@tonic-gate dhcp_ipc_recv_msg(int fd, void **msg, uint32_t base_length, int msec)
1880Sstevel@tonic-gate {
1893431Scarlsonj int retval;
1900Sstevel@tonic-gate dhcp_ipc_reply_t *ipc_msg;
1910Sstevel@tonic-gate uint32_t length;
1920Sstevel@tonic-gate
1930Sstevel@tonic-gate retval = dhcp_ipc_timed_read(fd, &length, sizeof (uint32_t), &msec);
1943431Scarlsonj if (retval != DHCP_IPC_SUCCESS)
1953431Scarlsonj return (retval);
1963431Scarlsonj
1973431Scarlsonj if (length == 0)
1983431Scarlsonj return (DHCP_IPC_E_PROTO);
1990Sstevel@tonic-gate
2000Sstevel@tonic-gate *msg = malloc(length);
2010Sstevel@tonic-gate if (*msg == NULL)
2020Sstevel@tonic-gate return (DHCP_IPC_E_MEMORY);
2030Sstevel@tonic-gate
2040Sstevel@tonic-gate retval = dhcp_ipc_timed_read(fd, *msg, length, &msec);
2053431Scarlsonj if (retval != DHCP_IPC_SUCCESS) {
2060Sstevel@tonic-gate free(*msg);
2073431Scarlsonj return (retval);
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate
2100Sstevel@tonic-gate if (length < base_length) {
2110Sstevel@tonic-gate free(*msg);
2123431Scarlsonj return (DHCP_IPC_E_PROTO);
2130Sstevel@tonic-gate }
2140Sstevel@tonic-gate
2150Sstevel@tonic-gate /*
2160Sstevel@tonic-gate * the data_length field is in the same place in either ipc message.
2170Sstevel@tonic-gate */
2180Sstevel@tonic-gate
2190Sstevel@tonic-gate ipc_msg = (dhcp_ipc_reply_t *)(*msg);
2200Sstevel@tonic-gate if (ipc_msg->data_length + base_length != length) {
2210Sstevel@tonic-gate free(*msg);
2223431Scarlsonj return (DHCP_IPC_E_PROTO);
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate
2253431Scarlsonj return (DHCP_IPC_SUCCESS);
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate /*
2290Sstevel@tonic-gate * dhcp_ipc_recv_request(): gets a request using the agent's ipc protocol
2300Sstevel@tonic-gate *
2310Sstevel@tonic-gate * input: int: the file descriptor to get the message from
2320Sstevel@tonic-gate * dhcp_ipc_request_t **: address of a pointer to store the request
2330Sstevel@tonic-gate * (dynamically allocated)
2340Sstevel@tonic-gate * int: the # of milliseconds to wait for the message (-1 is forever)
2350Sstevel@tonic-gate * output: int: 0 on success, DHCP_IPC_E_* otherwise
2360Sstevel@tonic-gate */
2370Sstevel@tonic-gate
2380Sstevel@tonic-gate int
dhcp_ipc_recv_request(int fd,dhcp_ipc_request_t ** request,int msec)2390Sstevel@tonic-gate dhcp_ipc_recv_request(int fd, dhcp_ipc_request_t **request, int msec)
2400Sstevel@tonic-gate {
2410Sstevel@tonic-gate int retval;
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate retval = dhcp_ipc_recv_msg(fd, (void **)request, DHCP_IPC_REQUEST_SIZE,
2440Sstevel@tonic-gate msec);
2450Sstevel@tonic-gate
2460Sstevel@tonic-gate /* guarantee that ifname will be NUL-terminated */
2470Sstevel@tonic-gate if (retval == 0)
2488562SPeter.Memishian@Sun.COM (*request)->ifname[LIFNAMSIZ - 1] = '\0';
2490Sstevel@tonic-gate
2500Sstevel@tonic-gate return (retval);
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate
2530Sstevel@tonic-gate /*
2540Sstevel@tonic-gate * dhcp_ipc_recv_reply(): gets a reply using the agent's ipc protocol
2550Sstevel@tonic-gate *
2560Sstevel@tonic-gate * input: int: the file descriptor to get the message from
2570Sstevel@tonic-gate * dhcp_ipc_reply_t **: address of a pointer to store the reply
2580Sstevel@tonic-gate * (dynamically allocated)
259*9633Sjames.d.carlson@sun.com * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
260*9633Sjames.d.carlson@sun.com * or DHCP_IPC_WAIT_DEFAULT
2610Sstevel@tonic-gate * output: int: 0 on success, DHCP_IPC_E_* otherwise
2620Sstevel@tonic-gate */
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate static int
dhcp_ipc_recv_reply(int fd,dhcp_ipc_reply_t ** reply,int32_t timeout)265*9633Sjames.d.carlson@sun.com dhcp_ipc_recv_reply(int fd, dhcp_ipc_reply_t **reply, int32_t timeout)
2660Sstevel@tonic-gate {
267*9633Sjames.d.carlson@sun.com /*
268*9633Sjames.d.carlson@sun.com * If the caller doesn't want to wait forever, and the amount of time
269*9633Sjames.d.carlson@sun.com * he wants to wait is expressible as an integer number of milliseconds
270*9633Sjames.d.carlson@sun.com * (as needed by the msg function), then we wait that amount of time
271*9633Sjames.d.carlson@sun.com * plus an extra two seconds for the daemon to do its work. The extra
272*9633Sjames.d.carlson@sun.com * two seconds is arbitrary; it should allow plenty of time for the
273*9633Sjames.d.carlson@sun.com * daemon to respond within the existing timeout, as specified in the
274*9633Sjames.d.carlson@sun.com * original request, so the only time we give up is when the daemon is
275*9633Sjames.d.carlson@sun.com * stopped or otherwise malfunctioning.
276*9633Sjames.d.carlson@sun.com *
277*9633Sjames.d.carlson@sun.com * Note that the wait limit (milliseconds in an 'int') is over 24 days,
278*9633Sjames.d.carlson@sun.com * so it's unlikely that any request will actually be that long, and
279*9633Sjames.d.carlson@sun.com * it's unlikely that anyone will care if we wait forever on a request
280*9633Sjames.d.carlson@sun.com * for a 30 day timer. The point is to protect against daemon
281*9633Sjames.d.carlson@sun.com * malfunction in the usual cases, not to provide an absolute command
282*9633Sjames.d.carlson@sun.com * timer.
283*9633Sjames.d.carlson@sun.com */
284*9633Sjames.d.carlson@sun.com if (timeout == DHCP_IPC_WAIT_DEFAULT)
285*9633Sjames.d.carlson@sun.com timeout = DHCP_IPC_DEFAULT_WAIT;
286*9633Sjames.d.carlson@sun.com if (timeout != DHCP_IPC_WAIT_FOREVER && timeout < INT_MAX / 1000 - 2)
287*9633Sjames.d.carlson@sun.com timeout = (timeout + 2) * 1000;
288*9633Sjames.d.carlson@sun.com else
289*9633Sjames.d.carlson@sun.com timeout = -1;
290*9633Sjames.d.carlson@sun.com return (dhcp_ipc_recv_msg(fd, (void **)reply, DHCP_IPC_REPLY_SIZE,
291*9633Sjames.d.carlson@sun.com timeout));
2920Sstevel@tonic-gate }
2930Sstevel@tonic-gate
2940Sstevel@tonic-gate /*
2950Sstevel@tonic-gate * dhcp_ipc_send_msg(): transmits a message using the agent's ipc protocol
2960Sstevel@tonic-gate *
2970Sstevel@tonic-gate * input: int: the file descriptor to transmit on
2980Sstevel@tonic-gate * void *: the message to send
2990Sstevel@tonic-gate * uint32_t: the message length
3000Sstevel@tonic-gate * output: int: 0 on success, DHCP_IPC_E_* otherwise
3010Sstevel@tonic-gate */
3020Sstevel@tonic-gate
3030Sstevel@tonic-gate static int
dhcp_ipc_send_msg(int fd,void * msg,uint32_t message_length)3040Sstevel@tonic-gate dhcp_ipc_send_msg(int fd, void *msg, uint32_t message_length)
3050Sstevel@tonic-gate {
3060Sstevel@tonic-gate struct iovec iovec[2];
3070Sstevel@tonic-gate
3080Sstevel@tonic-gate iovec[0].iov_base = (caddr_t)&message_length;
3090Sstevel@tonic-gate iovec[0].iov_len = sizeof (uint32_t);
3100Sstevel@tonic-gate iovec[1].iov_base = msg;
3110Sstevel@tonic-gate iovec[1].iov_len = message_length;
3120Sstevel@tonic-gate
3130Sstevel@tonic-gate if (writev(fd, iovec, sizeof (iovec) / sizeof (*iovec)) == -1)
3140Sstevel@tonic-gate return (DHCP_IPC_E_WRITEV);
3150Sstevel@tonic-gate
3160Sstevel@tonic-gate return (0);
3170Sstevel@tonic-gate }
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate /*
3200Sstevel@tonic-gate * dhcp_ipc_send_reply(): transmits a reply using the agent's ipc protocol
3210Sstevel@tonic-gate *
3220Sstevel@tonic-gate * input: int: the file descriptor to transmit on
3230Sstevel@tonic-gate * dhcp_ipc_reply_t *: the reply to send
3240Sstevel@tonic-gate * output: int: 0 on success, DHCP_IPC_E_* otherwise
3250Sstevel@tonic-gate */
3260Sstevel@tonic-gate
3270Sstevel@tonic-gate int
dhcp_ipc_send_reply(int fd,dhcp_ipc_reply_t * reply)3280Sstevel@tonic-gate dhcp_ipc_send_reply(int fd, dhcp_ipc_reply_t *reply)
3290Sstevel@tonic-gate {
3300Sstevel@tonic-gate return (dhcp_ipc_send_msg(fd, reply, DHCP_IPC_REPLY_SIZE +
3310Sstevel@tonic-gate reply->data_length));
3320Sstevel@tonic-gate }
3330Sstevel@tonic-gate
3340Sstevel@tonic-gate /*
3350Sstevel@tonic-gate * dhcp_ipc_send_request(): transmits a request using the agent's ipc protocol
3360Sstevel@tonic-gate *
3370Sstevel@tonic-gate * input: int: the file descriptor to transmit on
3380Sstevel@tonic-gate * dhcp_ipc_request_t *: the request to send
3390Sstevel@tonic-gate * output: int: 0 on success, DHCP_IPC_E_* otherwise
3400Sstevel@tonic-gate */
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate static int
dhcp_ipc_send_request(int fd,dhcp_ipc_request_t * request)3430Sstevel@tonic-gate dhcp_ipc_send_request(int fd, dhcp_ipc_request_t *request)
3440Sstevel@tonic-gate {
3450Sstevel@tonic-gate /*
3460Sstevel@tonic-gate * for now, ipc_ids aren't really used, but they're intended
3470Sstevel@tonic-gate * to make it easy to send several requests and then collect
3480Sstevel@tonic-gate * all of the replies (and pair them with the requests).
3490Sstevel@tonic-gate */
3500Sstevel@tonic-gate
3510Sstevel@tonic-gate request->ipc_id = gethrtime();
3520Sstevel@tonic-gate
3530Sstevel@tonic-gate return (dhcp_ipc_send_msg(fd, request, DHCP_IPC_REQUEST_SIZE +
3540Sstevel@tonic-gate request->data_length));
3550Sstevel@tonic-gate }
3560Sstevel@tonic-gate
3570Sstevel@tonic-gate /*
3580Sstevel@tonic-gate * dhcp_ipc_make_request(): sends the provided request to the agent and reaps
3590Sstevel@tonic-gate * the reply
3600Sstevel@tonic-gate *
3610Sstevel@tonic-gate * input: dhcp_ipc_request_t *: the request to make
3620Sstevel@tonic-gate * dhcp_ipc_reply_t **: the reply (dynamically allocated)
3630Sstevel@tonic-gate * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
3640Sstevel@tonic-gate * or DHCP_IPC_WAIT_DEFAULT
3650Sstevel@tonic-gate * output: int: 0 on success, DHCP_IPC_E_* otherwise
3660Sstevel@tonic-gate */
3670Sstevel@tonic-gate
3680Sstevel@tonic-gate int
dhcp_ipc_make_request(dhcp_ipc_request_t * request,dhcp_ipc_reply_t ** reply,int32_t timeout)3690Sstevel@tonic-gate dhcp_ipc_make_request(dhcp_ipc_request_t *request, dhcp_ipc_reply_t **reply,
3700Sstevel@tonic-gate int32_t timeout)
3710Sstevel@tonic-gate {
3723431Scarlsonj int fd, on, retval;
3733431Scarlsonj struct sockaddr_in sinv;
3740Sstevel@tonic-gate
3753431Scarlsonj fd = socket(AF_INET, SOCK_STREAM, 0);
3763431Scarlsonj if (fd == -1)
3773431Scarlsonj return (DHCP_IPC_E_SOCKET);
3780Sstevel@tonic-gate
3793431Scarlsonj /*
3803431Scarlsonj * Bind a privileged port if we have sufficient privilege to do so.
3813431Scarlsonj * Continue as non-privileged otherwise.
3823431Scarlsonj */
3833431Scarlsonj on = 1;
3843431Scarlsonj (void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on));
3850Sstevel@tonic-gate
3863431Scarlsonj (void) memset(&sinv, 0, sizeof (sinv));
3873431Scarlsonj sinv.sin_family = AF_INET;
3883431Scarlsonj if (bind(fd, (struct sockaddr *)&sinv, sizeof (sinv)) == -1) {
3893431Scarlsonj (void) dhcp_ipc_close(fd);
3903431Scarlsonj return (DHCP_IPC_E_BIND);
3910Sstevel@tonic-gate }
3920Sstevel@tonic-gate
3933431Scarlsonj sinv.sin_port = htons(IPPORT_DHCPAGENT);
3943431Scarlsonj sinv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
3953431Scarlsonj retval = connect(fd, (struct sockaddr *)&sinv, sizeof (sinv));
3960Sstevel@tonic-gate if (retval == -1) {
3970Sstevel@tonic-gate (void) dhcp_ipc_close(fd);
3980Sstevel@tonic-gate return (DHCP_IPC_E_CONNECT);
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate
4010Sstevel@tonic-gate request->timeout = timeout;
4020Sstevel@tonic-gate
4030Sstevel@tonic-gate retval = dhcp_ipc_send_request(fd, request);
4040Sstevel@tonic-gate if (retval == 0)
405*9633Sjames.d.carlson@sun.com retval = dhcp_ipc_recv_reply(fd, reply, timeout);
4060Sstevel@tonic-gate
4070Sstevel@tonic-gate (void) dhcp_ipc_close(fd);
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate return (retval);
4100Sstevel@tonic-gate }
4110Sstevel@tonic-gate
4120Sstevel@tonic-gate /*
4130Sstevel@tonic-gate * dhcp_ipc_init(): initializes the ipc channel for use by the agent
4140Sstevel@tonic-gate *
4150Sstevel@tonic-gate * input: int *: the file descriptor to accept on (returned)
4160Sstevel@tonic-gate * output: int: 0 on success, DHCP_IPC_E_* otherwise
4170Sstevel@tonic-gate */
4180Sstevel@tonic-gate
4190Sstevel@tonic-gate int
dhcp_ipc_init(int * listen_fd)4200Sstevel@tonic-gate dhcp_ipc_init(int *listen_fd)
4210Sstevel@tonic-gate {
4220Sstevel@tonic-gate struct sockaddr_in sin;
4230Sstevel@tonic-gate int on = 1;
4240Sstevel@tonic-gate
4250Sstevel@tonic-gate (void) memset(&sin, 0, sizeof (struct sockaddr_in));
4260Sstevel@tonic-gate
4270Sstevel@tonic-gate sin.sin_family = AF_INET;
4280Sstevel@tonic-gate sin.sin_port = htons(IPPORT_DHCPAGENT);
4290Sstevel@tonic-gate sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
4300Sstevel@tonic-gate
4310Sstevel@tonic-gate *listen_fd = socket(AF_INET, SOCK_STREAM, 0);
4320Sstevel@tonic-gate if (*listen_fd == -1)
4330Sstevel@tonic-gate return (DHCP_IPC_E_SOCKET);
4340Sstevel@tonic-gate
4350Sstevel@tonic-gate /*
4360Sstevel@tonic-gate * we use SO_REUSEADDR here since in the case where there
4370Sstevel@tonic-gate * really is another daemon running that is using the agent's
4380Sstevel@tonic-gate * port, bind(3N) will fail. so we can't lose.
4390Sstevel@tonic-gate */
4400Sstevel@tonic-gate
4410Sstevel@tonic-gate (void) setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &on,
4420Sstevel@tonic-gate sizeof (on));
4430Sstevel@tonic-gate
4440Sstevel@tonic-gate if (bind(*listen_fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
4450Sstevel@tonic-gate (void) close(*listen_fd);
4460Sstevel@tonic-gate return (DHCP_IPC_E_BIND);
4470Sstevel@tonic-gate }
4480Sstevel@tonic-gate
4490Sstevel@tonic-gate if (listen(*listen_fd, DHCP_IPC_LISTEN_BACKLOG) == -1) {
4500Sstevel@tonic-gate (void) close(*listen_fd);
4510Sstevel@tonic-gate return (DHCP_IPC_E_LISTEN);
4520Sstevel@tonic-gate }
4530Sstevel@tonic-gate
4540Sstevel@tonic-gate return (0);
4550Sstevel@tonic-gate }
4560Sstevel@tonic-gate
4570Sstevel@tonic-gate /*
4580Sstevel@tonic-gate * dhcp_ipc_accept(): accepts an incoming connection for the agent
4590Sstevel@tonic-gate *
4600Sstevel@tonic-gate * input: int: the file descriptor to accept on
4610Sstevel@tonic-gate * int *: the accepted file descriptor (returned)
4620Sstevel@tonic-gate * int *: nonzero if the client is privileged (returned)
4630Sstevel@tonic-gate * output: int: 0 on success, DHCP_IPC_E_* otherwise
4640Sstevel@tonic-gate * note: sets the socket into nonblocking mode
4650Sstevel@tonic-gate */
4660Sstevel@tonic-gate
4670Sstevel@tonic-gate int
dhcp_ipc_accept(int listen_fd,int * fd,int * is_priv)4680Sstevel@tonic-gate dhcp_ipc_accept(int listen_fd, int *fd, int *is_priv)
4690Sstevel@tonic-gate {
4700Sstevel@tonic-gate struct sockaddr_in sin_peer;
4710Sstevel@tonic-gate int sin_len = sizeof (sin_peer);
4720Sstevel@tonic-gate int sockflags;
4730Sstevel@tonic-gate
4740Sstevel@tonic-gate /*
4750Sstevel@tonic-gate * if we were extremely concerned with portability, we would
4760Sstevel@tonic-gate * set the socket into nonblocking mode before doing the
4770Sstevel@tonic-gate * accept(3N), since on BSD-based networking stacks, there is
4780Sstevel@tonic-gate * a potential race that can occur if the socket which
4790Sstevel@tonic-gate * connected to us performs a TCP RST before we accept, since
4800Sstevel@tonic-gate * BSD handles this case entirely in the kernel and as a
4810Sstevel@tonic-gate * result even though select said we will not block, we can
4820Sstevel@tonic-gate * end up blocking since there is no longer a connection to
4830Sstevel@tonic-gate * accept. on SVR4-based systems, this should be okay,
4840Sstevel@tonic-gate * and we will get EPROTO back, even though POSIX.1g says
4850Sstevel@tonic-gate * we should get ECONNABORTED.
4860Sstevel@tonic-gate */
4870Sstevel@tonic-gate
4880Sstevel@tonic-gate *fd = accept(listen_fd, (struct sockaddr *)&sin_peer, &sin_len);
4890Sstevel@tonic-gate if (*fd == -1)
4900Sstevel@tonic-gate return (DHCP_IPC_E_ACCEPT);
4910Sstevel@tonic-gate
4920Sstevel@tonic-gate /* get credentials */
4930Sstevel@tonic-gate *is_priv = ntohs(sin_peer.sin_port) < IPPORT_RESERVED;
4940Sstevel@tonic-gate
4950Sstevel@tonic-gate /*
4960Sstevel@tonic-gate * kick the socket into non-blocking mode so that later
4970Sstevel@tonic-gate * operations on the socket don't block and hold up the whole
4980Sstevel@tonic-gate * application. with the event demuxing approach, this may
4990Sstevel@tonic-gate * seem unnecessary, but in order to get partial reads/writes
5000Sstevel@tonic-gate * and to handle our internal protocol for passing data
5010Sstevel@tonic-gate * between the agent and its consumers, this is needed.
5020Sstevel@tonic-gate */
5030Sstevel@tonic-gate
5040Sstevel@tonic-gate if ((sockflags = fcntl(*fd, F_GETFL, 0)) == -1) {
5050Sstevel@tonic-gate (void) close(*fd);
5060Sstevel@tonic-gate return (DHCP_IPC_E_FCNTL);
5070Sstevel@tonic-gate }
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate if (fcntl(*fd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
5100Sstevel@tonic-gate (void) close(*fd);
5110Sstevel@tonic-gate return (DHCP_IPC_E_FCNTL);
5120Sstevel@tonic-gate }
5130Sstevel@tonic-gate
5140Sstevel@tonic-gate return (0);
5150Sstevel@tonic-gate }
5160Sstevel@tonic-gate
5170Sstevel@tonic-gate /*
5180Sstevel@tonic-gate * dhcp_ipc_close(): closes an ipc descriptor
5190Sstevel@tonic-gate *
5200Sstevel@tonic-gate * input: int: the file descriptor to close
5210Sstevel@tonic-gate * output: int: 0 on success, DHCP_IPC_E_* otherwise
5220Sstevel@tonic-gate */
5230Sstevel@tonic-gate
5240Sstevel@tonic-gate int
dhcp_ipc_close(int fd)5250Sstevel@tonic-gate dhcp_ipc_close(int fd)
5260Sstevel@tonic-gate {
5270Sstevel@tonic-gate return ((close(fd) == -1) ? DHCP_IPC_E_CLOSE : 0);
5280Sstevel@tonic-gate }
5290Sstevel@tonic-gate
5300Sstevel@tonic-gate /*
5310Sstevel@tonic-gate * dhcp_ipc_strerror(): maps an ipc error code into a human-readable string
5320Sstevel@tonic-gate *
5330Sstevel@tonic-gate * input: int: the ipc error code to map
5340Sstevel@tonic-gate * output: const char *: the corresponding human-readable string
5350Sstevel@tonic-gate */
5360Sstevel@tonic-gate
5370Sstevel@tonic-gate const char *
dhcp_ipc_strerror(int error)5380Sstevel@tonic-gate dhcp_ipc_strerror(int error)
5390Sstevel@tonic-gate {
5400Sstevel@tonic-gate /* note: this must be kept in sync with DHCP_IPC_E_* definitions */
5410Sstevel@tonic-gate const char *syscalls[] = {
5420Sstevel@tonic-gate "<unknown>", "socket", "fcntl", "read", "accept", "close",
5433431Scarlsonj "bind", "listen", "malloc", "connect", "writev", "poll"
5440Sstevel@tonic-gate };
5450Sstevel@tonic-gate
5460Sstevel@tonic-gate const char *error_string;
5470Sstevel@tonic-gate static char buffer[BUFMAX];
5480Sstevel@tonic-gate
5490Sstevel@tonic-gate switch (error) {
5500Sstevel@tonic-gate
5510Sstevel@tonic-gate /*
5520Sstevel@tonic-gate * none of these errors actually go over the wire.
5530Sstevel@tonic-gate * hence, we assume that errno is still fresh.
5540Sstevel@tonic-gate */
5550Sstevel@tonic-gate
5560Sstevel@tonic-gate case DHCP_IPC_E_SOCKET: /* FALLTHRU */
5570Sstevel@tonic-gate case DHCP_IPC_E_FCNTL: /* FALLTHRU */
5580Sstevel@tonic-gate case DHCP_IPC_E_READ: /* FALLTHRU */
5590Sstevel@tonic-gate case DHCP_IPC_E_ACCEPT: /* FALLTHRU */
5600Sstevel@tonic-gate case DHCP_IPC_E_CLOSE: /* FALLTHRU */
5610Sstevel@tonic-gate case DHCP_IPC_E_BIND: /* FALLTHRU */
5620Sstevel@tonic-gate case DHCP_IPC_E_LISTEN: /* FALLTHRU */
5630Sstevel@tonic-gate case DHCP_IPC_E_CONNECT: /* FALLTHRU */
5643431Scarlsonj case DHCP_IPC_E_WRITEV: /* FALLTHRU */
5653431Scarlsonj case DHCP_IPC_E_POLL:
5660Sstevel@tonic-gate
5670Sstevel@tonic-gate error_string = strerror(errno);
5680Sstevel@tonic-gate if (error_string == NULL)
5690Sstevel@tonic-gate error_string = "unknown error";
5700Sstevel@tonic-gate
5710Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "%s: %s",
5720Sstevel@tonic-gate syscalls[error], error_string);
5730Sstevel@tonic-gate
5740Sstevel@tonic-gate error_string = buffer;
5750Sstevel@tonic-gate break;
5760Sstevel@tonic-gate
5770Sstevel@tonic-gate case DHCP_IPC_E_MEMORY:
5780Sstevel@tonic-gate error_string = "out of memory";
5790Sstevel@tonic-gate break;
5800Sstevel@tonic-gate
5810Sstevel@tonic-gate case DHCP_IPC_E_TIMEOUT:
5820Sstevel@tonic-gate error_string = "wait timed out, operation still pending...";
5830Sstevel@tonic-gate break;
5840Sstevel@tonic-gate
5850Sstevel@tonic-gate case DHCP_IPC_E_INVIF:
5860Sstevel@tonic-gate error_string = "interface does not exist or cannot be managed "
5870Sstevel@tonic-gate "using DHCP";
5880Sstevel@tonic-gate break;
5890Sstevel@tonic-gate
5900Sstevel@tonic-gate case DHCP_IPC_E_INT:
5910Sstevel@tonic-gate error_string = "internal error (might work later)";
5920Sstevel@tonic-gate break;
5930Sstevel@tonic-gate
5940Sstevel@tonic-gate case DHCP_IPC_E_PERM:
5950Sstevel@tonic-gate error_string = "permission denied";
5960Sstevel@tonic-gate break;
5970Sstevel@tonic-gate
5980Sstevel@tonic-gate case DHCP_IPC_E_OUTSTATE:
5990Sstevel@tonic-gate error_string = "interface not in appropriate state for command";
6000Sstevel@tonic-gate break;
6010Sstevel@tonic-gate
6020Sstevel@tonic-gate case DHCP_IPC_E_PEND:
6030Sstevel@tonic-gate error_string = "interface currently has a pending command "
6040Sstevel@tonic-gate "(try later)";
6050Sstevel@tonic-gate break;
6060Sstevel@tonic-gate
6070Sstevel@tonic-gate case DHCP_IPC_E_BOOTP:
6080Sstevel@tonic-gate error_string = "interface is administered with BOOTP, not DHCP";
6090Sstevel@tonic-gate break;
6100Sstevel@tonic-gate
6110Sstevel@tonic-gate case DHCP_IPC_E_CMD_UNKNOWN:
6120Sstevel@tonic-gate error_string = "unknown command";
6130Sstevel@tonic-gate break;
6140Sstevel@tonic-gate
6150Sstevel@tonic-gate case DHCP_IPC_E_UNKIF:
6160Sstevel@tonic-gate error_string = "interface is not under DHCP control";
6170Sstevel@tonic-gate break;
6180Sstevel@tonic-gate
6190Sstevel@tonic-gate case DHCP_IPC_E_PROTO:
6200Sstevel@tonic-gate error_string = "ipc protocol violation";
6210Sstevel@tonic-gate break;
6220Sstevel@tonic-gate
6230Sstevel@tonic-gate case DHCP_IPC_E_FAILEDIF:
6240Sstevel@tonic-gate error_string = "interface is in a FAILED state and must be "
6250Sstevel@tonic-gate "manually restarted";
6260Sstevel@tonic-gate break;
6270Sstevel@tonic-gate
6280Sstevel@tonic-gate case DHCP_IPC_E_NOPRIMARY:
6290Sstevel@tonic-gate error_string = "primary interface requested but no primary "
6300Sstevel@tonic-gate "interface is set";
6310Sstevel@tonic-gate break;
6320Sstevel@tonic-gate
6330Sstevel@tonic-gate case DHCP_IPC_E_NOIPIF:
6340Sstevel@tonic-gate error_string = "interface currently has no IP address";
6350Sstevel@tonic-gate break;
6360Sstevel@tonic-gate
6370Sstevel@tonic-gate case DHCP_IPC_E_DOWNIF:
6380Sstevel@tonic-gate error_string = "interface is currently down";
6390Sstevel@tonic-gate break;
6400Sstevel@tonic-gate
6410Sstevel@tonic-gate case DHCP_IPC_E_NOVALUE:
6420Sstevel@tonic-gate error_string = "no value was found for this option";
6430Sstevel@tonic-gate break;
6440Sstevel@tonic-gate
6453431Scarlsonj case DHCP_IPC_E_RUNNING:
6463431Scarlsonj error_string = "DHCP is already running";
6473431Scarlsonj break;
6483431Scarlsonj
6493431Scarlsonj case DHCP_IPC_E_SRVFAILED:
6503431Scarlsonj error_string = "DHCP server refused request";
6513431Scarlsonj break;
6523431Scarlsonj
6533431Scarlsonj case DHCP_IPC_E_EOF:
6543431Scarlsonj error_string = "ipc connection closed";
6550Sstevel@tonic-gate break;
6560Sstevel@tonic-gate
6570Sstevel@tonic-gate default:
6580Sstevel@tonic-gate error_string = "unknown error";
6590Sstevel@tonic-gate break;
6600Sstevel@tonic-gate }
6610Sstevel@tonic-gate
6620Sstevel@tonic-gate /*
6630Sstevel@tonic-gate * TODO: internationalize this error string
6640Sstevel@tonic-gate */
6650Sstevel@tonic-gate
6660Sstevel@tonic-gate return (error_string);
6670Sstevel@tonic-gate }
6680Sstevel@tonic-gate
6690Sstevel@tonic-gate /*
6704106Scarlsonj * dhcp_string_to_request(): maps a string into a request code
6714106Scarlsonj *
6724106Scarlsonj * input: const char *: the string to map
6734106Scarlsonj * output: dhcp_ipc_type_t: the request code, or -1 if unknown
6744106Scarlsonj */
6754106Scarlsonj
6764106Scarlsonj dhcp_ipc_type_t
dhcp_string_to_request(const char * request)6774106Scarlsonj dhcp_string_to_request(const char *request)
6784106Scarlsonj {
6794106Scarlsonj unsigned int i;
6804106Scarlsonj
6814106Scarlsonj for (i = 0; i < DHCP_NIPC; i++)
6824106Scarlsonj if (strcmp(ipc_typestr[i], request) == 0)
6834106Scarlsonj return ((dhcp_ipc_type_t)i);
6844106Scarlsonj
6854106Scarlsonj return ((dhcp_ipc_type_t)-1);
6864106Scarlsonj }
6874106Scarlsonj
6884106Scarlsonj /*
6893431Scarlsonj * dhcp_ipc_type_to_string(): maps an ipc command code into a human-readable
6903431Scarlsonj * string
6913431Scarlsonj *
6923431Scarlsonj * input: int: the ipc command code to map
6933431Scarlsonj * output: const char *: the corresponding human-readable string
6943431Scarlsonj */
6953431Scarlsonj
6963431Scarlsonj const char *
dhcp_ipc_type_to_string(dhcp_ipc_type_t type)6973431Scarlsonj dhcp_ipc_type_to_string(dhcp_ipc_type_t type)
6983431Scarlsonj {
6993431Scarlsonj if (type < 0 || type >= DHCP_NIPC)
7003431Scarlsonj return ("unknown");
7013431Scarlsonj else
7024106Scarlsonj return (ipc_typestr[(int)type]);
7033431Scarlsonj }
7043431Scarlsonj
7053431Scarlsonj /*
7060Sstevel@tonic-gate * getinfo_ifnames(): checks the value of a specified option on a list of
7070Sstevel@tonic-gate * interface names.
7080Sstevel@tonic-gate * input: const char *: a list of interface names to query (in order) for
7090Sstevel@tonic-gate * the option; "" queries the primary interface
7100Sstevel@tonic-gate * dhcp_optnum_t *: a description of the desired option
7110Sstevel@tonic-gate * DHCP_OPT **: filled in with the (dynamically allocated) value of
7120Sstevel@tonic-gate * the option upon success.
7130Sstevel@tonic-gate * output: int: DHCP_IPC_E_* on error, 0 on success or if no value was
7140Sstevel@tonic-gate * found but no error occurred either (*result will be NULL)
7150Sstevel@tonic-gate */
7160Sstevel@tonic-gate
7170Sstevel@tonic-gate static int
getinfo_ifnames(const char * ifn,dhcp_optnum_t * optnum,DHCP_OPT ** result)7180Sstevel@tonic-gate getinfo_ifnames(const char *ifn, dhcp_optnum_t *optnum, DHCP_OPT **result)
7190Sstevel@tonic-gate {
7200Sstevel@tonic-gate dhcp_ipc_request_t *request;
7210Sstevel@tonic-gate dhcp_ipc_reply_t *reply;
7220Sstevel@tonic-gate char *ifnames, *ifnames_head;
7230Sstevel@tonic-gate DHCP_OPT *opt;
7240Sstevel@tonic-gate size_t opt_size;
7250Sstevel@tonic-gate int retval = 0;
7260Sstevel@tonic-gate
7270Sstevel@tonic-gate *result = NULL;
7280Sstevel@tonic-gate ifnames_head = ifnames = strdup(ifn);
7290Sstevel@tonic-gate if (ifnames == NULL)
7300Sstevel@tonic-gate return (DHCP_IPC_E_MEMORY);
7310Sstevel@tonic-gate
7320Sstevel@tonic-gate request = dhcp_ipc_alloc_request(DHCP_GET_TAG, "", optnum,
7330Sstevel@tonic-gate sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
7340Sstevel@tonic-gate
7350Sstevel@tonic-gate if (request == NULL) {
7360Sstevel@tonic-gate free(ifnames_head);
7370Sstevel@tonic-gate return (DHCP_IPC_E_MEMORY);
7380Sstevel@tonic-gate }
7390Sstevel@tonic-gate
7400Sstevel@tonic-gate ifnames = strtok(ifnames, " ");
7410Sstevel@tonic-gate if (ifnames == NULL)
7420Sstevel@tonic-gate ifnames = "";
7430Sstevel@tonic-gate
7440Sstevel@tonic-gate for (; ifnames != NULL; ifnames = strtok(NULL, " ")) {
7450Sstevel@tonic-gate
7468562SPeter.Memishian@Sun.COM (void) strlcpy(request->ifname, ifnames, LIFNAMSIZ);
7470Sstevel@tonic-gate retval = dhcp_ipc_make_request(request, &reply, 0);
7480Sstevel@tonic-gate if (retval != 0)
7490Sstevel@tonic-gate break;
7500Sstevel@tonic-gate
7510Sstevel@tonic-gate if (reply->return_code == 0) {
7520Sstevel@tonic-gate opt = dhcp_ipc_get_data(reply, &opt_size, NULL);
7530Sstevel@tonic-gate if (opt_size > 2 && (opt->len == opt_size - 2)) {
7540Sstevel@tonic-gate *result = malloc(opt_size);
7550Sstevel@tonic-gate if (*result == NULL)
7560Sstevel@tonic-gate retval = DHCP_IPC_E_MEMORY;
7570Sstevel@tonic-gate else
7580Sstevel@tonic-gate (void) memcpy(*result, opt, opt_size);
7590Sstevel@tonic-gate
7600Sstevel@tonic-gate free(reply);
7610Sstevel@tonic-gate break;
7620Sstevel@tonic-gate }
7630Sstevel@tonic-gate }
7640Sstevel@tonic-gate
7650Sstevel@tonic-gate free(reply);
7660Sstevel@tonic-gate if (ifnames[0] == '\0')
7670Sstevel@tonic-gate break;
7680Sstevel@tonic-gate }
7690Sstevel@tonic-gate
7700Sstevel@tonic-gate free(request);
7710Sstevel@tonic-gate free(ifnames_head);
7720Sstevel@tonic-gate
7730Sstevel@tonic-gate return (retval);
7740Sstevel@tonic-gate }
7750Sstevel@tonic-gate
7760Sstevel@tonic-gate /*
7770Sstevel@tonic-gate * get_ifnames(): returns a space-separated list of interface names that
7780Sstevel@tonic-gate * match the specified flags
7790Sstevel@tonic-gate *
7800Sstevel@tonic-gate * input: int: flags which must be on in each interface returned
7810Sstevel@tonic-gate * int: flags which must be off in each interface returned
7820Sstevel@tonic-gate * output: char *: a dynamically-allocated list of interface names, or
7830Sstevel@tonic-gate * NULL upon failure.
7840Sstevel@tonic-gate */
7850Sstevel@tonic-gate
7860Sstevel@tonic-gate static char *
get_ifnames(int flags_on,int flags_off)7870Sstevel@tonic-gate get_ifnames(int flags_on, int flags_off)
7880Sstevel@tonic-gate {
7890Sstevel@tonic-gate struct ifconf ifc;
7900Sstevel@tonic-gate int n_ifs, i, sock_fd;
7910Sstevel@tonic-gate char *ifnames;
7920Sstevel@tonic-gate
7930Sstevel@tonic-gate
7940Sstevel@tonic-gate sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
7950Sstevel@tonic-gate if (sock_fd == -1)
7960Sstevel@tonic-gate return (NULL);
7970Sstevel@tonic-gate
7980Sstevel@tonic-gate if ((ioctl(sock_fd, SIOCGIFNUM, &n_ifs) == -1) || (n_ifs <= 0)) {
7990Sstevel@tonic-gate (void) close(sock_fd);
8000Sstevel@tonic-gate return (NULL);
8010Sstevel@tonic-gate }
8020Sstevel@tonic-gate
8038562SPeter.Memishian@Sun.COM ifnames = calloc(1, n_ifs * (LIFNAMSIZ + 1));
8040Sstevel@tonic-gate ifc.ifc_len = n_ifs * sizeof (struct ifreq);
8050Sstevel@tonic-gate ifc.ifc_req = calloc(n_ifs, sizeof (struct ifreq));
8060Sstevel@tonic-gate if (ifc.ifc_req != NULL && ifnames != NULL) {
8070Sstevel@tonic-gate
8080Sstevel@tonic-gate if (ioctl(sock_fd, SIOCGIFCONF, &ifc) == -1) {
8090Sstevel@tonic-gate (void) close(sock_fd);
8100Sstevel@tonic-gate free(ifnames);
8110Sstevel@tonic-gate free(ifc.ifc_req);
8120Sstevel@tonic-gate return (NULL);
8130Sstevel@tonic-gate }
8140Sstevel@tonic-gate
8150Sstevel@tonic-gate for (i = 0; i < n_ifs; i++) {
8160Sstevel@tonic-gate
8170Sstevel@tonic-gate if (ioctl(sock_fd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0)
8180Sstevel@tonic-gate if ((ifc.ifc_req[i].ifr_flags &
8190Sstevel@tonic-gate (flags_on | flags_off)) != flags_on)
8200Sstevel@tonic-gate continue;
8210Sstevel@tonic-gate
8220Sstevel@tonic-gate (void) strcat(ifnames, ifc.ifc_req[i].ifr_name);
8230Sstevel@tonic-gate (void) strcat(ifnames, " ");
8240Sstevel@tonic-gate }
8250Sstevel@tonic-gate
8260Sstevel@tonic-gate if (strlen(ifnames) > 1)
8270Sstevel@tonic-gate ifnames[strlen(ifnames) - 1] = '\0';
8280Sstevel@tonic-gate }
8290Sstevel@tonic-gate
8300Sstevel@tonic-gate (void) close(sock_fd);
8310Sstevel@tonic-gate free(ifc.ifc_req);
8320Sstevel@tonic-gate return (ifnames);
8330Sstevel@tonic-gate }
8340Sstevel@tonic-gate
8350Sstevel@tonic-gate /*
8360Sstevel@tonic-gate * dhcp_ipc_getinfo(): attempts to retrieve a value for the specified DHCP
8370Sstevel@tonic-gate * option; tries primary interface, then all DHCP-owned
8380Sstevel@tonic-gate * interfaces, then INFORMs on the remaining interfaces
8390Sstevel@tonic-gate * (these interfaces are dropped prior to returning).
8400Sstevel@tonic-gate * input: dhcp_optnum_t *: a description of the desired option
8410Sstevel@tonic-gate * DHCP_OPT **: filled in with the (dynamically allocated) value of
8420Sstevel@tonic-gate * the option upon success.
8430Sstevel@tonic-gate * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
8440Sstevel@tonic-gate * or DHCP_IPC_WAIT_DEFAULT.
8450Sstevel@tonic-gate * output: int: DHCP_IPC_E_* on error, 0 upon success.
8460Sstevel@tonic-gate */
8470Sstevel@tonic-gate
8480Sstevel@tonic-gate int
dhcp_ipc_getinfo(dhcp_optnum_t * optnum,DHCP_OPT ** result,int32_t timeout)8490Sstevel@tonic-gate dhcp_ipc_getinfo(dhcp_optnum_t *optnum, DHCP_OPT **result, int32_t timeout)
8500Sstevel@tonic-gate {
8510Sstevel@tonic-gate dhcp_ipc_request_t *request;
8520Sstevel@tonic-gate dhcp_ipc_reply_t *reply;
8530Sstevel@tonic-gate char *ifnames, *ifnames_copy, *ifnames_head;
8540Sstevel@tonic-gate int retval;
8550Sstevel@tonic-gate time_t start_time = time(NULL);
8560Sstevel@tonic-gate
8570Sstevel@tonic-gate if (timeout == DHCP_IPC_WAIT_DEFAULT)
8580Sstevel@tonic-gate timeout = DHCP_IPC_DEFAULT_WAIT;
8590Sstevel@tonic-gate
8600Sstevel@tonic-gate /*
8610Sstevel@tonic-gate * wait at most 5 seconds for the agent to start.
8620Sstevel@tonic-gate */
8630Sstevel@tonic-gate
8640Sstevel@tonic-gate if (dhcp_start_agent((timeout > 5 || timeout < 0) ? 5 : timeout) == -1)
8650Sstevel@tonic-gate return (DHCP_IPC_E_INT);
8660Sstevel@tonic-gate
8670Sstevel@tonic-gate /*
8680Sstevel@tonic-gate * check the primary interface for the option value first.
8690Sstevel@tonic-gate */
8700Sstevel@tonic-gate
8710Sstevel@tonic-gate retval = getinfo_ifnames("", optnum, result);
8720Sstevel@tonic-gate if ((retval != 0) || (retval == 0 && *result != NULL))
8730Sstevel@tonic-gate return (retval);
8740Sstevel@tonic-gate
8750Sstevel@tonic-gate /*
8760Sstevel@tonic-gate * no luck. get a list of the interfaces under DHCP control
8770Sstevel@tonic-gate * and perform a GET_TAG on each one.
8780Sstevel@tonic-gate */
8790Sstevel@tonic-gate
8800Sstevel@tonic-gate ifnames = get_ifnames(IFF_DHCPRUNNING, 0);
8810Sstevel@tonic-gate if (ifnames != NULL && strlen(ifnames) != 0) {
8820Sstevel@tonic-gate retval = getinfo_ifnames(ifnames, optnum, result);
8830Sstevel@tonic-gate if ((retval != 0) || (retval == 0 && *result != NULL)) {
8840Sstevel@tonic-gate free(ifnames);
8850Sstevel@tonic-gate return (retval);
8860Sstevel@tonic-gate }
8870Sstevel@tonic-gate }
8880Sstevel@tonic-gate free(ifnames);
8890Sstevel@tonic-gate
8900Sstevel@tonic-gate /*
8910Sstevel@tonic-gate * still no luck. retrieve a list of all interfaces on the
8920Sstevel@tonic-gate * system that could use DHCP but aren't. send INFORMs out on
8930Sstevel@tonic-gate * each one. after that, sit in a loop for the next `timeout'
8940Sstevel@tonic-gate * seconds, trying every second to see if a response for the
8950Sstevel@tonic-gate * option we want has come in on one of the interfaces.
8960Sstevel@tonic-gate */
8970Sstevel@tonic-gate
8980Sstevel@tonic-gate ifnames = get_ifnames(IFF_UP|IFF_RUNNING, IFF_LOOPBACK|IFF_DHCPRUNNING);
8990Sstevel@tonic-gate if (ifnames == NULL || strlen(ifnames) == 0) {
9000Sstevel@tonic-gate free(ifnames);
9010Sstevel@tonic-gate return (DHCP_IPC_E_NOVALUE);
9020Sstevel@tonic-gate }
9030Sstevel@tonic-gate
9040Sstevel@tonic-gate ifnames_head = ifnames_copy = strdup(ifnames);
9050Sstevel@tonic-gate if (ifnames_copy == NULL) {
9060Sstevel@tonic-gate free(ifnames);
9070Sstevel@tonic-gate return (DHCP_IPC_E_MEMORY);
9080Sstevel@tonic-gate }
9090Sstevel@tonic-gate
9100Sstevel@tonic-gate request = dhcp_ipc_alloc_request(DHCP_INFORM, "", NULL, 0,
9110Sstevel@tonic-gate DHCP_TYPE_NONE);
9120Sstevel@tonic-gate if (request == NULL) {
9130Sstevel@tonic-gate free(ifnames);
9140Sstevel@tonic-gate free(ifnames_head);
9150Sstevel@tonic-gate return (DHCP_IPC_E_MEMORY);
9160Sstevel@tonic-gate }
9170Sstevel@tonic-gate
9180Sstevel@tonic-gate ifnames_copy = strtok(ifnames_copy, " ");
9190Sstevel@tonic-gate for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
9208562SPeter.Memishian@Sun.COM (void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
9210Sstevel@tonic-gate if (dhcp_ipc_make_request(request, &reply, 0) == 0)
9220Sstevel@tonic-gate free(reply);
9230Sstevel@tonic-gate }
9240Sstevel@tonic-gate
9250Sstevel@tonic-gate for (;;) {
9260Sstevel@tonic-gate if ((timeout != DHCP_IPC_WAIT_FOREVER) &&
9270Sstevel@tonic-gate (time(NULL) - start_time > timeout)) {
9280Sstevel@tonic-gate retval = DHCP_IPC_E_TIMEOUT;
9290Sstevel@tonic-gate break;
9300Sstevel@tonic-gate }
9310Sstevel@tonic-gate
9320Sstevel@tonic-gate retval = getinfo_ifnames(ifnames, optnum, result);
9330Sstevel@tonic-gate if (retval != 0 || (retval == 0 && *result != NULL))
9340Sstevel@tonic-gate break;
9350Sstevel@tonic-gate
9360Sstevel@tonic-gate (void) sleep(1);
9370Sstevel@tonic-gate }
9380Sstevel@tonic-gate
9390Sstevel@tonic-gate /*
9400Sstevel@tonic-gate * drop any interfaces that weren't under DHCP control before
9410Sstevel@tonic-gate * we got here; this keeps this function more of a black box
9420Sstevel@tonic-gate * and the behavior more consistent from call to call.
9430Sstevel@tonic-gate */
9440Sstevel@tonic-gate
9450Sstevel@tonic-gate request->message_type = DHCP_DROP;
9460Sstevel@tonic-gate
9470Sstevel@tonic-gate ifnames_copy = strcpy(ifnames_head, ifnames);
9480Sstevel@tonic-gate ifnames_copy = strtok(ifnames_copy, " ");
9490Sstevel@tonic-gate for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
9508562SPeter.Memishian@Sun.COM (void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
9510Sstevel@tonic-gate if (dhcp_ipc_make_request(request, &reply, 0) == 0)
9520Sstevel@tonic-gate free(reply);
9530Sstevel@tonic-gate }
9540Sstevel@tonic-gate
9550Sstevel@tonic-gate free(request);
9560Sstevel@tonic-gate free(ifnames_head);
9570Sstevel@tonic-gate free(ifnames);
9580Sstevel@tonic-gate return (retval);
9590Sstevel@tonic-gate }
9600Sstevel@tonic-gate
9610Sstevel@tonic-gate /*
9620Sstevel@tonic-gate * dhcp_ipc_timed_read(): reads from a descriptor using a maximum timeout
9630Sstevel@tonic-gate *
9640Sstevel@tonic-gate * input: int: the file descriptor to read from
9650Sstevel@tonic-gate * void *: the buffer to read into
9660Sstevel@tonic-gate * unsigned int: the total length of data to read
9670Sstevel@tonic-gate * int *: the number of milliseconds to wait; the number of
9683431Scarlsonj * milliseconds left are returned (-1 is "forever")
9693431Scarlsonj * output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
9700Sstevel@tonic-gate */
9710Sstevel@tonic-gate
9720Sstevel@tonic-gate static int
dhcp_ipc_timed_read(int fd,void * buffer,unsigned int length,int * msec)9730Sstevel@tonic-gate dhcp_ipc_timed_read(int fd, void *buffer, unsigned int length, int *msec)
9740Sstevel@tonic-gate {
9750Sstevel@tonic-gate unsigned int n_total = 0;
9760Sstevel@tonic-gate ssize_t n_read;
9770Sstevel@tonic-gate struct pollfd pollfd;
9783431Scarlsonj hrtime_t start, end;
9793431Scarlsonj int retv;
9800Sstevel@tonic-gate
9810Sstevel@tonic-gate pollfd.fd = fd;
9820Sstevel@tonic-gate pollfd.events = POLLIN;
9830Sstevel@tonic-gate
9840Sstevel@tonic-gate while (n_total < length) {
9850Sstevel@tonic-gate
9863431Scarlsonj start = gethrtime();
9870Sstevel@tonic-gate
9883431Scarlsonj retv = poll(&pollfd, 1, *msec);
9893431Scarlsonj if (retv == 0) {
9903431Scarlsonj /* This can happen only if *msec is not -1 */
9910Sstevel@tonic-gate *msec = 0;
9923431Scarlsonj return (DHCP_IPC_E_TIMEOUT);
9933431Scarlsonj }
9940Sstevel@tonic-gate
9953431Scarlsonj if (*msec != -1) {
9963431Scarlsonj end = gethrtime();
9973431Scarlsonj *msec -= (end - start) / (NANOSEC / MILLISEC);
9983431Scarlsonj if (*msec < 0)
9993431Scarlsonj *msec = 0;
10003431Scarlsonj }
10010Sstevel@tonic-gate
10023431Scarlsonj if (retv == -1) {
10033431Scarlsonj if (errno != EINTR)
10043431Scarlsonj return (DHCP_IPC_E_POLL);
10053431Scarlsonj else if (*msec == 0)
10063431Scarlsonj return (DHCP_IPC_E_TIMEOUT);
10073431Scarlsonj continue;
10083431Scarlsonj }
10090Sstevel@tonic-gate
10103431Scarlsonj if (!(pollfd.revents & POLLIN)) {
10113431Scarlsonj errno = EINVAL;
10123431Scarlsonj return (DHCP_IPC_E_POLL);
10133431Scarlsonj }
10140Sstevel@tonic-gate
10153431Scarlsonj n_read = read(fd, (caddr_t)buffer + n_total, length - n_total);
10160Sstevel@tonic-gate
10173431Scarlsonj if (n_read == -1) {
10183431Scarlsonj if (errno != EINTR)
10193431Scarlsonj return (DHCP_IPC_E_READ);
10203431Scarlsonj else if (*msec == 0)
10213431Scarlsonj return (DHCP_IPC_E_TIMEOUT);
10223431Scarlsonj continue;
10233431Scarlsonj }
10240Sstevel@tonic-gate
10253431Scarlsonj if (n_read == 0) {
10263431Scarlsonj return (n_total == 0 ? DHCP_IPC_E_EOF :
10273431Scarlsonj DHCP_IPC_E_PROTO);
10280Sstevel@tonic-gate }
10293431Scarlsonj
10303431Scarlsonj n_total += n_read;
10313431Scarlsonj
10323431Scarlsonj if (*msec == 0 && n_total < length)
10333431Scarlsonj return (DHCP_IPC_E_TIMEOUT);
10340Sstevel@tonic-gate }
10350Sstevel@tonic-gate
10363431Scarlsonj return (DHCP_IPC_SUCCESS);
10370Sstevel@tonic-gate }
1038