17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5e11c3f44Smeem  * Common Development and Distribution License (the "License").
6e11c3f44Smeem  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
20e11c3f44Smeem  *
21e11c3f44Smeem  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
227c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
2380d556f9SHans Rosenfeld  *
2480d556f9SHans Rosenfeld  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
28*bbf21555SRichard Lowe  * Low-level interfaces for communicating with in.mpathd(8).
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  * These routines are not intended for use outside of libipmp.
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <alloca.h>
347c478bd9Sstevel@tonic-gate #include <assert.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <fcntl.h>
377c478bd9Sstevel@tonic-gate #include <stdlib.h>
387c478bd9Sstevel@tonic-gate #include <string.h>
397c478bd9Sstevel@tonic-gate #include <unistd.h>
407c478bd9Sstevel@tonic-gate #include <poll.h>
417c478bd9Sstevel@tonic-gate #include <sys/socket.h>
427c478bd9Sstevel@tonic-gate #include <netinet/in.h>
437c478bd9Sstevel@tonic-gate #include <netinet/tcp.h>
447c478bd9Sstevel@tonic-gate #include <sys/types.h>
4580d556f9SHans Rosenfeld #include <sys/isa_defs.h>
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #include "ipmp.h"
487c478bd9Sstevel@tonic-gate #include "ipmp_mpathd.h"
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate /*
517c478bd9Sstevel@tonic-gate  * Connect to the multipathing daemon.  Returns an IPMP error code; upon
527c478bd9Sstevel@tonic-gate  * success, `fdp' points to the newly opened socket.
537c478bd9Sstevel@tonic-gate  */
547c478bd9Sstevel@tonic-gate int
ipmp_connect(int * fdp)557c478bd9Sstevel@tonic-gate ipmp_connect(int *fdp)
567c478bd9Sstevel@tonic-gate {
577c478bd9Sstevel@tonic-gate 	int	fd;
587c478bd9Sstevel@tonic-gate 	int	error;
597c478bd9Sstevel@tonic-gate 	int	on = 1;
607c478bd9Sstevel@tonic-gate 	int	flags;
617c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate 	fd = socket(AF_INET, SOCK_STREAM, 0);
647c478bd9Sstevel@tonic-gate 	if (fd == -1)
657c478bd9Sstevel@tonic-gate 		return (IPMP_FAILURE);
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate 	/*
68e11c3f44Smeem 	 * If we have sufficient privilege, enable TCP_ANONPRIVBIND so the
69e11c3f44Smeem 	 * kernel will choose a privileged source port (since in.mpathd only
70e11c3f44Smeem 	 * accepts requests on loopback, this is sufficient for security).
71e11c3f44Smeem 	 * If not, drive on since MI_QUERY and MI_PING commands are allowed
72e11c3f44Smeem 	 * from non-privileged ports.
737c478bd9Sstevel@tonic-gate 	 */
74e11c3f44Smeem 	(void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on));
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate 	/*
77e11c3f44Smeem 	 * Bind to a port chosen by the kernel.
787c478bd9Sstevel@tonic-gate 	 */
797c478bd9Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
807c478bd9Sstevel@tonic-gate 	sin.sin_port = htons(0);
817c478bd9Sstevel@tonic-gate 	sin.sin_family = AF_INET;
827c478bd9Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate 	if (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1)
857c478bd9Sstevel@tonic-gate 		goto fail;
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate 	/*
887c478bd9Sstevel@tonic-gate 	 * Attempt to connect to in.mpathd.
897c478bd9Sstevel@tonic-gate 	 */
907c478bd9Sstevel@tonic-gate 	sin.sin_port = htons(MPATHD_PORT);
917c478bd9Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	if (connect(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
947c478bd9Sstevel@tonic-gate 		if (errno == ECONNREFUSED) {
957c478bd9Sstevel@tonic-gate 			(void) close(fd);
967c478bd9Sstevel@tonic-gate 			return (IPMP_ENOMPATHD);
977c478bd9Sstevel@tonic-gate 		}
987c478bd9Sstevel@tonic-gate 		goto fail;
997c478bd9Sstevel@tonic-gate 	}
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	/*
1027c478bd9Sstevel@tonic-gate 	 * Kick the socket into nonblocking mode.
1037c478bd9Sstevel@tonic-gate 	 */
1047c478bd9Sstevel@tonic-gate 	flags = fcntl(fd, F_GETFL, 0);
1057c478bd9Sstevel@tonic-gate 	if (flags != -1)
1067c478bd9Sstevel@tonic-gate 		(void) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate 	*fdp = fd;
1097c478bd9Sstevel@tonic-gate 	return (IPMP_SUCCESS);
1107c478bd9Sstevel@tonic-gate fail:
1117c478bd9Sstevel@tonic-gate 	error = errno;
1127c478bd9Sstevel@tonic-gate 	(void) close(fd);
1137c478bd9Sstevel@tonic-gate 	errno = error;
1147c478bd9Sstevel@tonic-gate 	return (IPMP_FAILURE);
1157c478bd9Sstevel@tonic-gate }
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate /*
1187c478bd9Sstevel@tonic-gate  * Read the TLV triplet from descriptor `fd' and store its type, length and
1197c478bd9Sstevel@tonic-gate  * value in `*typep', `*lenp', and `*valuep' respectively, before the current
1207c478bd9Sstevel@tonic-gate  * time becomes `endtp'.  The buffer pointed to by `*valuep' will be
1217c478bd9Sstevel@tonic-gate  * dynamically allocated.  Returns an IPMP error code.
1227c478bd9Sstevel@tonic-gate  */
1237c478bd9Sstevel@tonic-gate int
ipmp_readtlv(int fd,ipmp_infotype_t * typep,size_t * lenp,void ** valuep,const struct timeval * endtp)1247c478bd9Sstevel@tonic-gate ipmp_readtlv(int fd, ipmp_infotype_t *typep, size_t *lenp, void **valuep,
1257c478bd9Sstevel@tonic-gate     const struct timeval *endtp)
1267c478bd9Sstevel@tonic-gate {
1277c478bd9Sstevel@tonic-gate 	int	retval;
1287c478bd9Sstevel@tonic-gate 	void	*value;
12980d556f9SHans Rosenfeld 	uint32_t tlen;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	retval = ipmp_read(fd, typep, sizeof (*typep), endtp);
1327c478bd9Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
1337c478bd9Sstevel@tonic-gate 		return (retval);
1347c478bd9Sstevel@tonic-gate 
13580d556f9SHans Rosenfeld 	retval = ipmp_read(fd, &tlen, sizeof (tlen), endtp);
1367c478bd9Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
1377c478bd9Sstevel@tonic-gate 		return (retval);
1387c478bd9Sstevel@tonic-gate 
13980d556f9SHans Rosenfeld 	*lenp = tlen;
14080d556f9SHans Rosenfeld 
1417c478bd9Sstevel@tonic-gate 	value = malloc(*lenp);
1427c478bd9Sstevel@tonic-gate 	if (value == NULL) {
1437c478bd9Sstevel@tonic-gate 		/*
1447c478bd9Sstevel@tonic-gate 		 * Even though we cannot allocate space for the value, we
1457c478bd9Sstevel@tonic-gate 		 * still slurp it off so the input stream doesn't get left
1467c478bd9Sstevel@tonic-gate 		 * in a weird place.
1477c478bd9Sstevel@tonic-gate 		 */
1487c478bd9Sstevel@tonic-gate 		value = alloca(*lenp);
1497c478bd9Sstevel@tonic-gate 		(void) ipmp_read(fd, value, *lenp, endtp);
1507c478bd9Sstevel@tonic-gate 		return (IPMP_ENOMEM);
1517c478bd9Sstevel@tonic-gate 	}
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	retval = ipmp_read(fd, value, *lenp, endtp);
1547c478bd9Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS) {
1557c478bd9Sstevel@tonic-gate 		free(value);
1567c478bd9Sstevel@tonic-gate 		return (retval);
1577c478bd9Sstevel@tonic-gate 	}
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	*valuep = value;
1607c478bd9Sstevel@tonic-gate 	return (IPMP_SUCCESS);
1617c478bd9Sstevel@tonic-gate }
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate /*
1647c478bd9Sstevel@tonic-gate  * Write `buflen' bytes from `buffer' to open file `fd'.  Returns IPMP_SUCCESS
1657c478bd9Sstevel@tonic-gate  * if all requested bytes were written, or an error code if not.
1667c478bd9Sstevel@tonic-gate  */
1677c478bd9Sstevel@tonic-gate int
ipmp_write(int fd,const void * buffer,size_t buflen)1687c478bd9Sstevel@tonic-gate ipmp_write(int fd, const void *buffer, size_t buflen)
1697c478bd9Sstevel@tonic-gate {
1707c478bd9Sstevel@tonic-gate 	size_t		nwritten;
1717c478bd9Sstevel@tonic-gate 	ssize_t		nbytes;
1727c478bd9Sstevel@tonic-gate 	const char	*buf = buffer;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
1757c478bd9Sstevel@tonic-gate 		nbytes = write(fd, &buf[nwritten], buflen - nwritten);
1767c478bd9Sstevel@tonic-gate 		if (nbytes == -1)
1777c478bd9Sstevel@tonic-gate 			return (IPMP_FAILURE);
1787c478bd9Sstevel@tonic-gate 		if (nbytes == 0) {
1797c478bd9Sstevel@tonic-gate 			errno = EIO;
1807c478bd9Sstevel@tonic-gate 			return (IPMP_FAILURE);
1817c478bd9Sstevel@tonic-gate 		}
1827c478bd9Sstevel@tonic-gate 	}
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	assert(nwritten == buflen);
1857c478bd9Sstevel@tonic-gate 	return (IPMP_SUCCESS);
1867c478bd9Sstevel@tonic-gate }
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate /*
1897c478bd9Sstevel@tonic-gate  * Write the TLV triplet named by `type', `len' and `value' to file descriptor
1907c478bd9Sstevel@tonic-gate  * `fd'.  Returns an IPMP error code.
1917c478bd9Sstevel@tonic-gate  */
1927c478bd9Sstevel@tonic-gate int
ipmp_writetlv(int fd,ipmp_infotype_t type,size_t len,void * value)1937c478bd9Sstevel@tonic-gate ipmp_writetlv(int fd, ipmp_infotype_t type, size_t len, void *value)
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate 	int	retval;
19680d556f9SHans Rosenfeld 	uint32_t tlen;
19780d556f9SHans Rosenfeld 
19880d556f9SHans Rosenfeld #if defined(_LP64)
19980d556f9SHans Rosenfeld 	if (len > UINT32_MAX)
20080d556f9SHans Rosenfeld 		return (IPMP_EPROTO);
20180d556f9SHans Rosenfeld #endif
20280d556f9SHans Rosenfeld 
20380d556f9SHans Rosenfeld 	tlen = (uint32_t)len;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	retval = ipmp_write(fd, &type, sizeof (type));
2067c478bd9Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
2077c478bd9Sstevel@tonic-gate 		return (retval);
2087c478bd9Sstevel@tonic-gate 
20980d556f9SHans Rosenfeld 	retval = ipmp_write(fd, &tlen, sizeof (uint32_t));
2107c478bd9Sstevel@tonic-gate 	if (retval != IPMP_SUCCESS)
2117c478bd9Sstevel@tonic-gate 		return (retval);
2127c478bd9Sstevel@tonic-gate 
21380d556f9SHans Rosenfeld 	return (ipmp_write(fd, value, tlen));
2147c478bd9Sstevel@tonic-gate }
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate /*
2177c478bd9Sstevel@tonic-gate  * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed
2187c478bd9Sstevel@tonic-gate  * to by `buf' before the current time becomes `endtp'; a `endtp' of NULL
2197c478bd9Sstevel@tonic-gate  * means forever.  Returns an IPMP error code.
2207c478bd9Sstevel@tonic-gate  */
2217c478bd9Sstevel@tonic-gate int
ipmp_read(int fd,void * buffer,size_t buflen,const struct timeval * endtp)2227c478bd9Sstevel@tonic-gate ipmp_read(int fd, void *buffer, size_t buflen, const struct timeval *endtp)
2237c478bd9Sstevel@tonic-gate {
2247c478bd9Sstevel@tonic-gate 	int		retval;
2257c478bd9Sstevel@tonic-gate 	int		timeleft = -1;
2267c478bd9Sstevel@tonic-gate 	struct timeval	curtime;
2277c478bd9Sstevel@tonic-gate 	ssize_t		nbytes = 0;	/* total bytes processed */
2287c478bd9Sstevel@tonic-gate 	ssize_t		prbytes;	/* per-round bytes processed */
2297c478bd9Sstevel@tonic-gate 	struct pollfd	pfd;
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 	while (nbytes < buflen) {
2327c478bd9Sstevel@tonic-gate 		/*
2337c478bd9Sstevel@tonic-gate 		 * If a timeout was specified, then compute the amount of time
2347c478bd9Sstevel@tonic-gate 		 * left before timing out.
2357c478bd9Sstevel@tonic-gate 		 */
2367c478bd9Sstevel@tonic-gate 		if (endtp != NULL) {
2377c478bd9Sstevel@tonic-gate 			if (gettimeofday(&curtime, NULL) == -1)
2387c478bd9Sstevel@tonic-gate 				break;
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 			timeleft = (endtp->tv_sec - curtime.tv_sec) * MILLISEC;
2417c478bd9Sstevel@tonic-gate 			timeleft += (endtp->tv_usec - curtime.tv_usec) / 1000;
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 			/*
2447c478bd9Sstevel@tonic-gate 			 * If we should've already timed out, then just
2457c478bd9Sstevel@tonic-gate 			 * have poll() return immediately.
2467c478bd9Sstevel@tonic-gate 			 */
2477c478bd9Sstevel@tonic-gate 			if (timeleft < 0)
2487c478bd9Sstevel@tonic-gate 				timeleft = 0;
2497c478bd9Sstevel@tonic-gate 		}
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 		pfd.fd = fd;
2527c478bd9Sstevel@tonic-gate 		pfd.events = POLLIN;
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 		/*
2557c478bd9Sstevel@tonic-gate 		 * Wait for data to come in or for the timeout to fire.
2567c478bd9Sstevel@tonic-gate 		 */
2577c478bd9Sstevel@tonic-gate 		retval = poll(&pfd, 1, timeleft);
2587c478bd9Sstevel@tonic-gate 		if (retval <= 0) {
2597c478bd9Sstevel@tonic-gate 			if (retval == 0)
2607c478bd9Sstevel@tonic-gate 				errno = ETIME;
2617c478bd9Sstevel@tonic-gate 			break;
2627c478bd9Sstevel@tonic-gate 		}
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 		/*
2657c478bd9Sstevel@tonic-gate 		 * Descriptor is ready; have at it.
2667c478bd9Sstevel@tonic-gate 		 */
2677c478bd9Sstevel@tonic-gate 		prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes);
2687c478bd9Sstevel@tonic-gate 		if (prbytes <= 0) {
2697c478bd9Sstevel@tonic-gate 			if (prbytes == -1 && errno == EINTR)
2707c478bd9Sstevel@tonic-gate 				continue;
2717c478bd9Sstevel@tonic-gate 			break;
2727c478bd9Sstevel@tonic-gate 		}
2737c478bd9Sstevel@tonic-gate 		nbytes += prbytes;
2747c478bd9Sstevel@tonic-gate 	}
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	return (nbytes == buflen ? IPMP_SUCCESS : IPMP_FAILURE);
2777c478bd9Sstevel@tonic-gate }
278