1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 * 21 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 */ 24 25 /* 26 * Low-level interfaces for communicating with in.mpathd(1M). 27 * 28 * These routines are not intended for use outside of libipmp. 29 */ 30 31 #include <alloca.h> 32 #include <assert.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <poll.h> 39 #include <sys/socket.h> 40 #include <netinet/in.h> 41 #include <netinet/tcp.h> 42 #include <sys/types.h> 43 44 #include "ipmp.h" 45 #include "ipmp_mpathd.h" 46 47 /* 48 * Connect to the multipathing daemon. Returns an IPMP error code; upon 49 * success, `fdp' points to the newly opened socket. 50 */ 51 int 52 ipmp_connect(int *fdp) 53 { 54 int fd; 55 int error; 56 int on = 1; 57 int flags; 58 struct sockaddr_in sin; 59 60 fd = socket(AF_INET, SOCK_STREAM, 0); 61 if (fd == -1) 62 return (IPMP_FAILURE); 63 64 /* 65 * If we have sufficient privilege, enable TCP_ANONPRIVBIND so the 66 * kernel will choose a privileged source port (since in.mpathd only 67 * accepts requests on loopback, this is sufficient for security). 68 * If not, drive on since MI_QUERY and MI_PING commands are allowed 69 * from non-privileged ports. 70 */ 71 (void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on)); 72 73 /* 74 * Bind to a port chosen by the kernel. 75 */ 76 (void) memset(&sin, 0, sizeof (struct sockaddr_in)); 77 sin.sin_port = htons(0); 78 sin.sin_family = AF_INET; 79 sin.sin_addr.s_addr = htonl(INADDR_ANY); 80 81 if (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) 82 goto fail; 83 84 /* 85 * Attempt to connect to in.mpathd. 86 */ 87 sin.sin_port = htons(MPATHD_PORT); 88 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 89 90 if (connect(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) { 91 if (errno == ECONNREFUSED) { 92 (void) close(fd); 93 return (IPMP_ENOMPATHD); 94 } 95 goto fail; 96 } 97 98 /* 99 * Kick the socket into nonblocking mode. 100 */ 101 flags = fcntl(fd, F_GETFL, 0); 102 if (flags != -1) 103 (void) fcntl(fd, F_SETFL, flags | O_NONBLOCK); 104 105 *fdp = fd; 106 return (IPMP_SUCCESS); 107 fail: 108 error = errno; 109 (void) close(fd); 110 errno = error; 111 return (IPMP_FAILURE); 112 } 113 114 /* 115 * Read the TLV triplet from descriptor `fd' and store its type, length and 116 * value in `*typep', `*lenp', and `*valuep' respectively, before the current 117 * time becomes `endtp'. The buffer pointed to by `*valuep' will be 118 * dynamically allocated. Returns an IPMP error code. 119 */ 120 int 121 ipmp_readtlv(int fd, ipmp_infotype_t *typep, size_t *lenp, void **valuep, 122 const struct timeval *endtp) 123 { 124 int retval; 125 void *value; 126 127 retval = ipmp_read(fd, typep, sizeof (*typep), endtp); 128 if (retval != IPMP_SUCCESS) 129 return (retval); 130 131 retval = ipmp_read(fd, lenp, sizeof (*lenp), endtp); 132 if (retval != IPMP_SUCCESS) 133 return (retval); 134 135 value = malloc(*lenp); 136 if (value == NULL) { 137 /* 138 * Even though we cannot allocate space for the value, we 139 * still slurp it off so the input stream doesn't get left 140 * in a weird place. 141 */ 142 value = alloca(*lenp); 143 (void) ipmp_read(fd, value, *lenp, endtp); 144 return (IPMP_ENOMEM); 145 } 146 147 retval = ipmp_read(fd, value, *lenp, endtp); 148 if (retval != IPMP_SUCCESS) { 149 free(value); 150 return (retval); 151 } 152 153 *valuep = value; 154 return (IPMP_SUCCESS); 155 } 156 157 /* 158 * Write `buflen' bytes from `buffer' to open file `fd'. Returns IPMP_SUCCESS 159 * if all requested bytes were written, or an error code if not. 160 */ 161 int 162 ipmp_write(int fd, const void *buffer, size_t buflen) 163 { 164 size_t nwritten; 165 ssize_t nbytes; 166 const char *buf = buffer; 167 168 for (nwritten = 0; nwritten < buflen; nwritten += nbytes) { 169 nbytes = write(fd, &buf[nwritten], buflen - nwritten); 170 if (nbytes == -1) 171 return (IPMP_FAILURE); 172 if (nbytes == 0) { 173 errno = EIO; 174 return (IPMP_FAILURE); 175 } 176 } 177 178 assert(nwritten == buflen); 179 return (IPMP_SUCCESS); 180 } 181 182 /* 183 * Write the TLV triplet named by `type', `len' and `value' to file descriptor 184 * `fd'. Returns an IPMP error code. 185 */ 186 int 187 ipmp_writetlv(int fd, ipmp_infotype_t type, size_t len, void *value) 188 { 189 int retval; 190 191 retval = ipmp_write(fd, &type, sizeof (type)); 192 if (retval != IPMP_SUCCESS) 193 return (retval); 194 195 retval = ipmp_write(fd, &len, sizeof (len)); 196 if (retval != IPMP_SUCCESS) 197 return (retval); 198 199 return (ipmp_write(fd, value, len)); 200 } 201 202 /* 203 * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed 204 * to by `buf' before the current time becomes `endtp'; a `endtp' of NULL 205 * means forever. Returns an IPMP error code. 206 */ 207 int 208 ipmp_read(int fd, void *buffer, size_t buflen, const struct timeval *endtp) 209 { 210 int retval; 211 int timeleft = -1; 212 struct timeval curtime; 213 ssize_t nbytes = 0; /* total bytes processed */ 214 ssize_t prbytes; /* per-round bytes processed */ 215 struct pollfd pfd; 216 217 while (nbytes < buflen) { 218 /* 219 * If a timeout was specified, then compute the amount of time 220 * left before timing out. 221 */ 222 if (endtp != NULL) { 223 if (gettimeofday(&curtime, NULL) == -1) 224 break; 225 226 timeleft = (endtp->tv_sec - curtime.tv_sec) * MILLISEC; 227 timeleft += (endtp->tv_usec - curtime.tv_usec) / 1000; 228 229 /* 230 * If we should've already timed out, then just 231 * have poll() return immediately. 232 */ 233 if (timeleft < 0) 234 timeleft = 0; 235 } 236 237 pfd.fd = fd; 238 pfd.events = POLLIN; 239 240 /* 241 * Wait for data to come in or for the timeout to fire. 242 */ 243 retval = poll(&pfd, 1, timeleft); 244 if (retval <= 0) { 245 if (retval == 0) 246 errno = ETIME; 247 break; 248 } 249 250 /* 251 * Descriptor is ready; have at it. 252 */ 253 prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes); 254 if (prbytes <= 0) { 255 if (prbytes == -1 && errno == EINTR) 256 continue; 257 break; 258 } 259 nbytes += prbytes; 260 } 261 262 return (nbytes == buflen ? IPMP_SUCCESS : IPMP_FAILURE); 263 } 264