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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 * 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 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 * 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 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 * 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 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 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