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(8).
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
ipmp_connect(int * fdp)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
ipmp_readtlv(int fd,ipmp_infotype_t * typep,size_t * lenp,void ** valuep,const struct timeval * endtp)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
ipmp_write(int fd,const void * buffer,size_t buflen)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
ipmp_writetlv(int fd,ipmp_infotype_t type,size_t len,void * value)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
ipmp_read(int fd,void * buffer,size_t buflen,const struct timeval * endtp)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