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