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.
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.
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]
21 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
26 * Low-level interfaces for communicating with in.mpathd(1M).
28 * These routines are not intended for use outside of libipmp.
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <netinet/tcp.h>
42 #include <sys/types.h>
45 #include "ipmp_mpathd.h"
48 * Connect to the multipathing daemon. Returns an IPMP error code; upon
49 * success, `fdp' points to the newly opened socket.
52 ipmp_connect(int *fdp
)
58 struct sockaddr_in sin
;
60 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
62 return (IPMP_FAILURE
);
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.
71 (void) setsockopt(fd
, IPPROTO_TCP
, TCP_ANONPRIVBIND
, &on
, sizeof (on
));
74 * Bind to a port chosen by the kernel.
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
);
81 if (bind(fd
, (struct sockaddr
*)&sin
, sizeof (sin
)) == -1)
85 * Attempt to connect to in.mpathd.
87 sin
.sin_port
= htons(MPATHD_PORT
);
88 sin
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
90 if (connect(fd
, (struct sockaddr
*)&sin
, sizeof (sin
)) == -1) {
91 if (errno
== ECONNREFUSED
) {
93 return (IPMP_ENOMPATHD
);
99 * Kick the socket into nonblocking mode.
101 flags
= fcntl(fd
, F_GETFL
, 0);
103 (void) fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
106 return (IPMP_SUCCESS
);
111 return (IPMP_FAILURE
);
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.
121 ipmp_readtlv(int fd
, ipmp_infotype_t
*typep
, size_t *lenp
, void **valuep
,
122 const struct timeval
*endtp
)
127 retval
= ipmp_read(fd
, typep
, sizeof (*typep
), endtp
);
128 if (retval
!= IPMP_SUCCESS
)
131 retval
= ipmp_read(fd
, lenp
, sizeof (*lenp
), endtp
);
132 if (retval
!= IPMP_SUCCESS
)
135 value
= malloc(*lenp
);
138 * Even though we cannot allocate space for the value, we
139 * still slurp it off so the input stream doesn't get left
142 value
= alloca(*lenp
);
143 (void) ipmp_read(fd
, value
, *lenp
, endtp
);
144 return (IPMP_ENOMEM
);
147 retval
= ipmp_read(fd
, value
, *lenp
, endtp
);
148 if (retval
!= IPMP_SUCCESS
) {
154 return (IPMP_SUCCESS
);
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.
162 ipmp_write(int fd
, const void *buffer
, size_t buflen
)
166 const char *buf
= buffer
;
168 for (nwritten
= 0; nwritten
< buflen
; nwritten
+= nbytes
) {
169 nbytes
= write(fd
, &buf
[nwritten
], buflen
- nwritten
);
171 return (IPMP_FAILURE
);
174 return (IPMP_FAILURE
);
178 assert(nwritten
== buflen
);
179 return (IPMP_SUCCESS
);
183 * Write the TLV triplet named by `type', `len' and `value' to file descriptor
184 * `fd'. Returns an IPMP error code.
187 ipmp_writetlv(int fd
, ipmp_infotype_t type
, size_t len
, void *value
)
191 retval
= ipmp_write(fd
, &type
, sizeof (type
));
192 if (retval
!= IPMP_SUCCESS
)
195 retval
= ipmp_write(fd
, &len
, sizeof (len
));
196 if (retval
!= IPMP_SUCCESS
)
199 return (ipmp_write(fd
, value
, len
));
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.
208 ipmp_read(int fd
, void *buffer
, size_t buflen
, const struct timeval
*endtp
)
212 struct timeval curtime
;
213 ssize_t nbytes
= 0; /* total bytes processed */
214 ssize_t prbytes
; /* per-round bytes processed */
217 while (nbytes
< buflen
) {
219 * If a timeout was specified, then compute the amount of time
220 * left before timing out.
223 if (gettimeofday(&curtime
, NULL
) == -1)
226 timeleft
= (endtp
->tv_sec
- curtime
.tv_sec
) * MILLISEC
;
227 timeleft
+= (endtp
->tv_usec
- curtime
.tv_usec
) / 1000;
230 * If we should've already timed out, then just
231 * have poll() return immediately.
241 * Wait for data to come in or for the timeout to fire.
243 retval
= poll(&pfd
, 1, timeleft
);
251 * Descriptor is ready; have at it.
253 prbytes
= read(fd
, (caddr_t
)buffer
+ nbytes
, buflen
- nbytes
);
255 if (prbytes
== -1 && errno
== EINTR
)
262 return (nbytes
== buflen
? IPMP_SUCCESS
: IPMP_FAILURE
);