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