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