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