xref: /onnv-gate/usr/src/lib/libipmp/common/ipmp_mpathd.c (revision 8485:633e5b5eb268)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*8485SPeter.Memishian@Sun.COM  * Common Development and Distribution License (the "License").
6*8485SPeter.Memishian@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
20*8485SPeter.Memishian@Sun.COM  *
21*8485SPeter.Memishian@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
220Sstevel@tonic-gate  * Use is subject to license terms.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate  * Low-level interfaces for communicating with in.mpathd(1M).
270Sstevel@tonic-gate  *
280Sstevel@tonic-gate  * These routines are not intended for use outside of libipmp.
290Sstevel@tonic-gate  */
300Sstevel@tonic-gate 
310Sstevel@tonic-gate #include <alloca.h>
320Sstevel@tonic-gate #include <assert.h>
330Sstevel@tonic-gate #include <errno.h>
340Sstevel@tonic-gate #include <fcntl.h>
350Sstevel@tonic-gate #include <stdlib.h>
360Sstevel@tonic-gate #include <string.h>
370Sstevel@tonic-gate #include <unistd.h>
380Sstevel@tonic-gate #include <poll.h>
390Sstevel@tonic-gate #include <sys/socket.h>
400Sstevel@tonic-gate #include <netinet/in.h>
410Sstevel@tonic-gate #include <netinet/tcp.h>
420Sstevel@tonic-gate #include <sys/types.h>
430Sstevel@tonic-gate 
440Sstevel@tonic-gate #include "ipmp.h"
450Sstevel@tonic-gate #include "ipmp_mpathd.h"
460Sstevel@tonic-gate 
470Sstevel@tonic-gate /*
480Sstevel@tonic-gate  * Connect to the multipathing daemon.  Returns an IPMP error code; upon
490Sstevel@tonic-gate  * success, `fdp' points to the newly opened socket.
500Sstevel@tonic-gate  */
510Sstevel@tonic-gate int
ipmp_connect(int * fdp)520Sstevel@tonic-gate ipmp_connect(int *fdp)
530Sstevel@tonic-gate {
540Sstevel@tonic-gate 	int	fd;
550Sstevel@tonic-gate 	int	error;
560Sstevel@tonic-gate 	int	on = 1;
570Sstevel@tonic-gate 	int	flags;
580Sstevel@tonic-gate 	struct sockaddr_in sin;
590Sstevel@tonic-gate 
600Sstevel@tonic-gate 	fd = socket(AF_INET, SOCK_STREAM, 0);
610Sstevel@tonic-gate 	if (fd == -1)
620Sstevel@tonic-gate 		return (IPMP_FAILURE);
630Sstevel@tonic-gate 
640Sstevel@tonic-gate 	/*
65*8485SPeter.Memishian@Sun.COM 	 * If we have sufficient privilege, enable TCP_ANONPRIVBIND so the
66*8485SPeter.Memishian@Sun.COM 	 * kernel will choose a privileged source port (since in.mpathd only
67*8485SPeter.Memishian@Sun.COM 	 * accepts requests on loopback, this is sufficient for security).
68*8485SPeter.Memishian@Sun.COM 	 * If not, drive on since MI_QUERY and MI_PING commands are allowed
69*8485SPeter.Memishian@Sun.COM 	 * from non-privileged ports.
700Sstevel@tonic-gate 	 */
71*8485SPeter.Memishian@Sun.COM 	(void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on));
720Sstevel@tonic-gate 
730Sstevel@tonic-gate 	/*
74*8485SPeter.Memishian@Sun.COM 	 * Bind to a port chosen by the kernel.
750Sstevel@tonic-gate 	 */
760Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
770Sstevel@tonic-gate 	sin.sin_port = htons(0);
780Sstevel@tonic-gate 	sin.sin_family = AF_INET;
790Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	if (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1)
820Sstevel@tonic-gate 		goto fail;
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 	/*
850Sstevel@tonic-gate 	 * Attempt to connect to in.mpathd.
860Sstevel@tonic-gate 	 */
870Sstevel@tonic-gate 	sin.sin_port = htons(MPATHD_PORT);
880Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 	if (connect(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
910Sstevel@tonic-gate 		if (errno == ECONNREFUSED) {
920Sstevel@tonic-gate 			(void) close(fd);
930Sstevel@tonic-gate 			return (IPMP_ENOMPATHD);
940Sstevel@tonic-gate 		}
950Sstevel@tonic-gate 		goto fail;
960Sstevel@tonic-gate 	}
970Sstevel@tonic-gate 
980Sstevel@tonic-gate 	/*
990Sstevel@tonic-gate 	 * Kick the socket into nonblocking mode.
1000Sstevel@tonic-gate 	 */
1010Sstevel@tonic-gate 	flags = fcntl(fd, F_GETFL, 0);
1020Sstevel@tonic-gate 	if (flags != -1)
1030Sstevel@tonic-gate 		(void) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	*fdp = fd;
1060Sstevel@tonic-gate 	return (IPMP_SUCCESS);
1070Sstevel@tonic-gate fail:
1080Sstevel@tonic-gate 	error = errno;
1090Sstevel@tonic-gate 	(void) close(fd);
1100Sstevel@tonic-gate 	errno = error;
1110Sstevel@tonic-gate 	return (IPMP_FAILURE);
1120Sstevel@tonic-gate }
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate /*
1150Sstevel@tonic-gate  * Read the TLV triplet from descriptor `fd' and store its type, length and
1160Sstevel@tonic-gate  * value in `*typep', `*lenp', and `*valuep' respectively, before the current
1170Sstevel@tonic-gate  * time becomes `endtp'.  The buffer pointed to by `*valuep' will be
1180Sstevel@tonic-gate  * dynamically allocated.  Returns an IPMP error code.
1190Sstevel@tonic-gate  */
1200Sstevel@tonic-gate int
ipmp_readtlv(int fd,ipmp_infotype_t * typep,size_t * lenp,void ** valuep,const struct timeval * endtp)1210Sstevel@tonic-gate ipmp_readtlv(int fd, ipmp_infotype_t *typep, size_t *lenp, void **valuep,
1220Sstevel@tonic-gate     const struct timeval *endtp)
1230Sstevel@tonic-gate {
1240Sstevel@tonic-gate 	int	retval;
1250Sstevel@tonic-gate 	void	*value;
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	retval = ipmp_read(fd, typep, sizeof (*typep), endtp);
1280Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
1290Sstevel@tonic-gate 		return (retval);
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	retval = ipmp_read(fd, lenp, sizeof (*lenp), endtp);
1320Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
1330Sstevel@tonic-gate 		return (retval);
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	value = malloc(*lenp);
1360Sstevel@tonic-gate 	if (value == NULL) {
1370Sstevel@tonic-gate 		/*
1380Sstevel@tonic-gate 		 * Even though we cannot allocate space for the value, we
1390Sstevel@tonic-gate 		 * still slurp it off so the input stream doesn't get left
1400Sstevel@tonic-gate 		 * in a weird place.
1410Sstevel@tonic-gate 		 */
1420Sstevel@tonic-gate 		value = alloca(*lenp);
1430Sstevel@tonic-gate 		(void) ipmp_read(fd, value, *lenp, endtp);
1440Sstevel@tonic-gate 		return (IPMP_ENOMEM);
1450Sstevel@tonic-gate 	}
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate 	retval = ipmp_read(fd, value, *lenp, endtp);
1480Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS) {
1490Sstevel@tonic-gate 		free(value);
1500Sstevel@tonic-gate 		return (retval);
1510Sstevel@tonic-gate 	}
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	*valuep = value;
1540Sstevel@tonic-gate 	return (IPMP_SUCCESS);
1550Sstevel@tonic-gate }
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate /*
1580Sstevel@tonic-gate  * Write `buflen' bytes from `buffer' to open file `fd'.  Returns IPMP_SUCCESS
1590Sstevel@tonic-gate  * if all requested bytes were written, or an error code if not.
1600Sstevel@tonic-gate  */
1610Sstevel@tonic-gate int
ipmp_write(int fd,const void * buffer,size_t buflen)1620Sstevel@tonic-gate ipmp_write(int fd, const void *buffer, size_t buflen)
1630Sstevel@tonic-gate {
1640Sstevel@tonic-gate 	size_t		nwritten;
1650Sstevel@tonic-gate 	ssize_t		nbytes;
1660Sstevel@tonic-gate 	const char	*buf = buffer;
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
1690Sstevel@tonic-gate 		nbytes = write(fd, &buf[nwritten], buflen - nwritten);
1700Sstevel@tonic-gate 		if (nbytes == -1)
1710Sstevel@tonic-gate 			return (IPMP_FAILURE);
1720Sstevel@tonic-gate 		if (nbytes == 0) {
1730Sstevel@tonic-gate 			errno = EIO;
1740Sstevel@tonic-gate 			return (IPMP_FAILURE);
1750Sstevel@tonic-gate 		}
1760Sstevel@tonic-gate 	}
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	assert(nwritten == buflen);
1790Sstevel@tonic-gate 	return (IPMP_SUCCESS);
1800Sstevel@tonic-gate }
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate /*
1830Sstevel@tonic-gate  * Write the TLV triplet named by `type', `len' and `value' to file descriptor
1840Sstevel@tonic-gate  * `fd'.  Returns an IPMP error code.
1850Sstevel@tonic-gate  */
1860Sstevel@tonic-gate int
ipmp_writetlv(int fd,ipmp_infotype_t type,size_t len,void * value)1870Sstevel@tonic-gate ipmp_writetlv(int fd, ipmp_infotype_t type, size_t len, void *value)
1880Sstevel@tonic-gate {
1890Sstevel@tonic-gate 	int	retval;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	retval = ipmp_write(fd, &type, sizeof (type));
1920Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
1930Sstevel@tonic-gate 		return (retval);
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	retval = ipmp_write(fd, &len, sizeof (len));
1960Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
1970Sstevel@tonic-gate 		return (retval);
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	return (ipmp_write(fd, value, len));
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate  * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed
2040Sstevel@tonic-gate  * to by `buf' before the current time becomes `endtp'; a `endtp' of NULL
2050Sstevel@tonic-gate  * means forever.  Returns an IPMP error code.
2060Sstevel@tonic-gate  */
2070Sstevel@tonic-gate int
ipmp_read(int fd,void * buffer,size_t buflen,const struct timeval * endtp)2080Sstevel@tonic-gate ipmp_read(int fd, void *buffer, size_t buflen, const struct timeval *endtp)
2090Sstevel@tonic-gate {
2100Sstevel@tonic-gate 	int		retval;
2110Sstevel@tonic-gate 	int		timeleft = -1;
2120Sstevel@tonic-gate 	struct timeval	curtime;
2130Sstevel@tonic-gate 	ssize_t		nbytes = 0;	/* total bytes processed */
2140Sstevel@tonic-gate 	ssize_t		prbytes;	/* per-round bytes processed */
2150Sstevel@tonic-gate 	struct pollfd	pfd;
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	while (nbytes < buflen) {
2180Sstevel@tonic-gate 		/*
2190Sstevel@tonic-gate 		 * If a timeout was specified, then compute the amount of time
2200Sstevel@tonic-gate 		 * left before timing out.
2210Sstevel@tonic-gate 		 */
2220Sstevel@tonic-gate 		if (endtp != NULL) {
2230Sstevel@tonic-gate 			if (gettimeofday(&curtime, NULL) == -1)
2240Sstevel@tonic-gate 				break;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 			timeleft = (endtp->tv_sec - curtime.tv_sec) * MILLISEC;
2270Sstevel@tonic-gate 			timeleft += (endtp->tv_usec - curtime.tv_usec) / 1000;
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 			/*
2300Sstevel@tonic-gate 			 * If we should've already timed out, then just
2310Sstevel@tonic-gate 			 * have poll() return immediately.
2320Sstevel@tonic-gate 			 */
2330Sstevel@tonic-gate 			if (timeleft < 0)
2340Sstevel@tonic-gate 				timeleft = 0;
2350Sstevel@tonic-gate 		}
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 		pfd.fd = fd;
2380Sstevel@tonic-gate 		pfd.events = POLLIN;
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 		/*
2410Sstevel@tonic-gate 		 * Wait for data to come in or for the timeout to fire.
2420Sstevel@tonic-gate 		 */
2430Sstevel@tonic-gate 		retval = poll(&pfd, 1, timeleft);
2440Sstevel@tonic-gate 		if (retval <= 0) {
2450Sstevel@tonic-gate 			if (retval == 0)
2460Sstevel@tonic-gate 				errno = ETIME;
2470Sstevel@tonic-gate 			break;
2480Sstevel@tonic-gate 		}
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 		/*
2510Sstevel@tonic-gate 		 * Descriptor is ready; have at it.
2520Sstevel@tonic-gate 		 */
2530Sstevel@tonic-gate 		prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes);
2540Sstevel@tonic-gate 		if (prbytes <= 0) {
2550Sstevel@tonic-gate 			if (prbytes == -1 && errno == EINTR)
2560Sstevel@tonic-gate 				continue;
2570Sstevel@tonic-gate 			break;
2580Sstevel@tonic-gate 		}
2590Sstevel@tonic-gate 		nbytes += prbytes;
2600Sstevel@tonic-gate 	}
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	return (nbytes == buflen ? IPMP_SUCCESS : IPMP_FAILURE);
2630Sstevel@tonic-gate }
264