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