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