xref: /illumos-gate/usr/src/lib/libipmp/common/ipmp_mpathd.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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
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