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