xref: /onnv-gate/usr/src/lib/libipmp/common/ipmp_mpathd.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * Low-level interfaces for communicating with in.mpathd(1M).
31*0Sstevel@tonic-gate  *
32*0Sstevel@tonic-gate  * These routines are not intended for use outside of libipmp.
33*0Sstevel@tonic-gate  */
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate #include <alloca.h>
36*0Sstevel@tonic-gate #include <assert.h>
37*0Sstevel@tonic-gate #include <errno.h>
38*0Sstevel@tonic-gate #include <fcntl.h>
39*0Sstevel@tonic-gate #include <stdlib.h>
40*0Sstevel@tonic-gate #include <string.h>
41*0Sstevel@tonic-gate #include <unistd.h>
42*0Sstevel@tonic-gate #include <poll.h>
43*0Sstevel@tonic-gate #include <sys/socket.h>
44*0Sstevel@tonic-gate #include <netinet/in.h>
45*0Sstevel@tonic-gate #include <netinet/tcp.h>
46*0Sstevel@tonic-gate #include <sys/types.h>
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate #include "ipmp.h"
49*0Sstevel@tonic-gate #include "ipmp_mpathd.h"
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate /*
52*0Sstevel@tonic-gate  * Connect to the multipathing daemon.  Returns an IPMP error code; upon
53*0Sstevel@tonic-gate  * success, `fdp' points to the newly opened socket.
54*0Sstevel@tonic-gate  */
55*0Sstevel@tonic-gate int
56*0Sstevel@tonic-gate ipmp_connect(int *fdp)
57*0Sstevel@tonic-gate {
58*0Sstevel@tonic-gate 	int	fd;
59*0Sstevel@tonic-gate 	int	error;
60*0Sstevel@tonic-gate 	int	on = 1;
61*0Sstevel@tonic-gate 	int	flags;
62*0Sstevel@tonic-gate 	struct sockaddr_in sin;
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate 	fd = socket(AF_INET, SOCK_STREAM, 0);
65*0Sstevel@tonic-gate 	if (fd == -1)
66*0Sstevel@tonic-gate 		return (IPMP_FAILURE);
67*0Sstevel@tonic-gate 
68*0Sstevel@tonic-gate 	/*
69*0Sstevel@tonic-gate 	 * Enable TCP_ANONPRIVBIND so the kernel will choose our source port.
70*0Sstevel@tonic-gate 	 * Since we're using loopback sockets, requiring use of privileged
71*0Sstevel@tonic-gate 	 * source ports is sufficient for security.
72*0Sstevel@tonic-gate 	 */
73*0Sstevel@tonic-gate 	if (setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on,
74*0Sstevel@tonic-gate 	    sizeof (on)) == -1)
75*0Sstevel@tonic-gate 		goto fail;
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate 	/*
78*0Sstevel@tonic-gate 	 * Bind to a privileged port chosen by the kernel.
79*0Sstevel@tonic-gate 	 */
80*0Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
81*0Sstevel@tonic-gate 	sin.sin_port = htons(0);
82*0Sstevel@tonic-gate 	sin.sin_family = AF_INET;
83*0Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate 	if (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1)
86*0Sstevel@tonic-gate 		goto fail;
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate 	/*
89*0Sstevel@tonic-gate 	 * Attempt to connect to in.mpathd.
90*0Sstevel@tonic-gate 	 */
91*0Sstevel@tonic-gate 	sin.sin_port = htons(MPATHD_PORT);
92*0Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
93*0Sstevel@tonic-gate 
94*0Sstevel@tonic-gate 	if (connect(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
95*0Sstevel@tonic-gate 		if (errno == ECONNREFUSED) {
96*0Sstevel@tonic-gate 			(void) close(fd);
97*0Sstevel@tonic-gate 			return (IPMP_ENOMPATHD);
98*0Sstevel@tonic-gate 		}
99*0Sstevel@tonic-gate 		goto fail;
100*0Sstevel@tonic-gate 	}
101*0Sstevel@tonic-gate 
102*0Sstevel@tonic-gate 	/*
103*0Sstevel@tonic-gate 	 * Kick the socket into nonblocking mode.
104*0Sstevel@tonic-gate 	 */
105*0Sstevel@tonic-gate 	flags = fcntl(fd, F_GETFL, 0);
106*0Sstevel@tonic-gate 	if (flags != -1)
107*0Sstevel@tonic-gate 		(void) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
108*0Sstevel@tonic-gate 
109*0Sstevel@tonic-gate 	*fdp = fd;
110*0Sstevel@tonic-gate 	return (IPMP_SUCCESS);
111*0Sstevel@tonic-gate fail:
112*0Sstevel@tonic-gate 	error = errno;
113*0Sstevel@tonic-gate 	(void) close(fd);
114*0Sstevel@tonic-gate 	errno = error;
115*0Sstevel@tonic-gate 	return (IPMP_FAILURE);
116*0Sstevel@tonic-gate }
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate /*
119*0Sstevel@tonic-gate  * Read the TLV triplet from descriptor `fd' and store its type, length and
120*0Sstevel@tonic-gate  * value in `*typep', `*lenp', and `*valuep' respectively, before the current
121*0Sstevel@tonic-gate  * time becomes `endtp'.  The buffer pointed to by `*valuep' will be
122*0Sstevel@tonic-gate  * dynamically allocated.  Returns an IPMP error code.
123*0Sstevel@tonic-gate  */
124*0Sstevel@tonic-gate int
125*0Sstevel@tonic-gate ipmp_readtlv(int fd, ipmp_infotype_t *typep, size_t *lenp, void **valuep,
126*0Sstevel@tonic-gate     const struct timeval *endtp)
127*0Sstevel@tonic-gate {
128*0Sstevel@tonic-gate 	int	retval;
129*0Sstevel@tonic-gate 	void	*value;
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate 	retval = ipmp_read(fd, typep, sizeof (*typep), endtp);
132*0Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
133*0Sstevel@tonic-gate 		return (retval);
134*0Sstevel@tonic-gate 
135*0Sstevel@tonic-gate 	retval = ipmp_read(fd, lenp, sizeof (*lenp), endtp);
136*0Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
137*0Sstevel@tonic-gate 		return (retval);
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 	value = malloc(*lenp);
140*0Sstevel@tonic-gate 	if (value == NULL) {
141*0Sstevel@tonic-gate 		/*
142*0Sstevel@tonic-gate 		 * Even though we cannot allocate space for the value, we
143*0Sstevel@tonic-gate 		 * still slurp it off so the input stream doesn't get left
144*0Sstevel@tonic-gate 		 * in a weird place.
145*0Sstevel@tonic-gate 		 */
146*0Sstevel@tonic-gate 		value = alloca(*lenp);
147*0Sstevel@tonic-gate 		(void) ipmp_read(fd, value, *lenp, endtp);
148*0Sstevel@tonic-gate 		return (IPMP_ENOMEM);
149*0Sstevel@tonic-gate 	}
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate 	retval = ipmp_read(fd, value, *lenp, endtp);
152*0Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS) {
153*0Sstevel@tonic-gate 		free(value);
154*0Sstevel@tonic-gate 		return (retval);
155*0Sstevel@tonic-gate 	}
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate 	*valuep = value;
158*0Sstevel@tonic-gate 	return (IPMP_SUCCESS);
159*0Sstevel@tonic-gate }
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate /*
162*0Sstevel@tonic-gate  * Write `buflen' bytes from `buffer' to open file `fd'.  Returns IPMP_SUCCESS
163*0Sstevel@tonic-gate  * if all requested bytes were written, or an error code if not.
164*0Sstevel@tonic-gate  */
165*0Sstevel@tonic-gate int
166*0Sstevel@tonic-gate ipmp_write(int fd, const void *buffer, size_t buflen)
167*0Sstevel@tonic-gate {
168*0Sstevel@tonic-gate 	size_t		nwritten;
169*0Sstevel@tonic-gate 	ssize_t		nbytes;
170*0Sstevel@tonic-gate 	const char	*buf = buffer;
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate 	for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
173*0Sstevel@tonic-gate 		nbytes = write(fd, &buf[nwritten], buflen - nwritten);
174*0Sstevel@tonic-gate 		if (nbytes == -1)
175*0Sstevel@tonic-gate 			return (IPMP_FAILURE);
176*0Sstevel@tonic-gate 		if (nbytes == 0) {
177*0Sstevel@tonic-gate 			errno = EIO;
178*0Sstevel@tonic-gate 			return (IPMP_FAILURE);
179*0Sstevel@tonic-gate 		}
180*0Sstevel@tonic-gate 	}
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate 	assert(nwritten == buflen);
183*0Sstevel@tonic-gate 	return (IPMP_SUCCESS);
184*0Sstevel@tonic-gate }
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate /*
187*0Sstevel@tonic-gate  * Write the TLV triplet named by `type', `len' and `value' to file descriptor
188*0Sstevel@tonic-gate  * `fd'.  Returns an IPMP error code.
189*0Sstevel@tonic-gate  */
190*0Sstevel@tonic-gate int
191*0Sstevel@tonic-gate ipmp_writetlv(int fd, ipmp_infotype_t type, size_t len, void *value)
192*0Sstevel@tonic-gate {
193*0Sstevel@tonic-gate 	int	retval;
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate 	retval = ipmp_write(fd, &type, sizeof (type));
196*0Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
197*0Sstevel@tonic-gate 		return (retval);
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate 	retval = ipmp_write(fd, &len, sizeof (len));
200*0Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
201*0Sstevel@tonic-gate 		return (retval);
202*0Sstevel@tonic-gate 
203*0Sstevel@tonic-gate 	return (ipmp_write(fd, value, len));
204*0Sstevel@tonic-gate }
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate /*
207*0Sstevel@tonic-gate  * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed
208*0Sstevel@tonic-gate  * to by `buf' before the current time becomes `endtp'; a `endtp' of NULL
209*0Sstevel@tonic-gate  * means forever.  Returns an IPMP error code.
210*0Sstevel@tonic-gate  */
211*0Sstevel@tonic-gate int
212*0Sstevel@tonic-gate ipmp_read(int fd, void *buffer, size_t buflen, const struct timeval *endtp)
213*0Sstevel@tonic-gate {
214*0Sstevel@tonic-gate 	int		retval;
215*0Sstevel@tonic-gate 	int		timeleft = -1;
216*0Sstevel@tonic-gate 	struct timeval	curtime;
217*0Sstevel@tonic-gate 	ssize_t		nbytes = 0;	/* total bytes processed */
218*0Sstevel@tonic-gate 	ssize_t		prbytes;	/* per-round bytes processed */
219*0Sstevel@tonic-gate 	struct pollfd	pfd;
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 	while (nbytes < buflen) {
222*0Sstevel@tonic-gate 		/*
223*0Sstevel@tonic-gate 		 * If a timeout was specified, then compute the amount of time
224*0Sstevel@tonic-gate 		 * left before timing out.
225*0Sstevel@tonic-gate 		 */
226*0Sstevel@tonic-gate 		if (endtp != NULL) {
227*0Sstevel@tonic-gate 			if (gettimeofday(&curtime, NULL) == -1)
228*0Sstevel@tonic-gate 				break;
229*0Sstevel@tonic-gate 
230*0Sstevel@tonic-gate 			timeleft = (endtp->tv_sec - curtime.tv_sec) * MILLISEC;
231*0Sstevel@tonic-gate 			timeleft += (endtp->tv_usec - curtime.tv_usec) / 1000;
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 			/*
234*0Sstevel@tonic-gate 			 * If we should've already timed out, then just
235*0Sstevel@tonic-gate 			 * have poll() return immediately.
236*0Sstevel@tonic-gate 			 */
237*0Sstevel@tonic-gate 			if (timeleft < 0)
238*0Sstevel@tonic-gate 				timeleft = 0;
239*0Sstevel@tonic-gate 		}
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 		pfd.fd = fd;
242*0Sstevel@tonic-gate 		pfd.events = POLLIN;
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 		/*
245*0Sstevel@tonic-gate 		 * Wait for data to come in or for the timeout to fire.
246*0Sstevel@tonic-gate 		 */
247*0Sstevel@tonic-gate 		retval = poll(&pfd, 1, timeleft);
248*0Sstevel@tonic-gate 		if (retval <= 0) {
249*0Sstevel@tonic-gate 			if (retval == 0)
250*0Sstevel@tonic-gate 				errno = ETIME;
251*0Sstevel@tonic-gate 			break;
252*0Sstevel@tonic-gate 		}
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate 		/*
255*0Sstevel@tonic-gate 		 * Descriptor is ready; have at it.
256*0Sstevel@tonic-gate 		 */
257*0Sstevel@tonic-gate 		prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes);
258*0Sstevel@tonic-gate 		if (prbytes <= 0) {
259*0Sstevel@tonic-gate 			if (prbytes == -1 && errno == EINTR)
260*0Sstevel@tonic-gate 				continue;
261*0Sstevel@tonic-gate 			break;
262*0Sstevel@tonic-gate 		}
263*0Sstevel@tonic-gate 		nbytes += prbytes;
264*0Sstevel@tonic-gate 	}
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate 	return (nbytes == buflen ? IPMP_SUCCESS : IPMP_FAILURE);
267*0Sstevel@tonic-gate }
268